catalog_page.dart 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'package:intl/intl.dart';
  4. import '../../../database/database.dart';
  5. import '../../../providers/article_provider.dart';
  6. import '../../widgets/article_image.dart';
  7. import '../../widgets/empty_state.dart';
  8. import '../../widgets/search_bar.dart' as sw;
  9. import '../../../core/responsive.dart';
  10. class CatalogPage extends ConsumerStatefulWidget {
  11. const CatalogPage({super.key});
  12. @override
  13. ConsumerState<CatalogPage> createState() => _CatalogPageState();
  14. }
  15. class _CatalogPageState extends ConsumerState<CatalogPage> {
  16. String _filter = '';
  17. final _searchCtrl = TextEditingController();
  18. @override
  19. void dispose() {
  20. _searchCtrl.dispose();
  21. super.dispose();
  22. }
  23. @override
  24. Widget build(BuildContext context) {
  25. final articlesAsync = ref.watch(activeArticleSearchProvider(_filter));
  26. return Scaffold(
  27. appBar: AppBar(title: const Text('Catalogo')),
  28. body: Column(
  29. children: [
  30. sw.SearchBarWidget(
  31. controller: _searchCtrl,
  32. hint: 'Cerca articolo...',
  33. onChanged: (v) => setState(() => _filter = v),
  34. onClear: () { _searchCtrl.clear(); setState(() => _filter = ''); },
  35. ),
  36. Expanded(
  37. child: articlesAsync.when(
  38. data: (articles) {
  39. if (articles.isEmpty) return const EmptyState(icon: Icons.inventory_2_outlined, title: 'Nessun articolo');
  40. if (ResponsiveBreakpoints.isPhone(context)) {
  41. return ListView.builder(
  42. itemCount: articles.length,
  43. itemBuilder: (_, i) => _ArticleListItem(article: articles[i]),
  44. );
  45. }
  46. return GridView.builder(
  47. padding: const EdgeInsets.all(8),
  48. gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
  49. crossAxisCount: 3,
  50. childAspectRatio: 1.2,
  51. crossAxisSpacing: 8,
  52. mainAxisSpacing: 8,
  53. ),
  54. itemCount: articles.length,
  55. itemBuilder: (_, i) => _ArticleGridItem(article: articles[i]),
  56. );
  57. },
  58. loading: () => const Center(child: CircularProgressIndicator()),
  59. error: (e, _) => Center(child: Text('Errore: $e')),
  60. ),
  61. ),
  62. ],
  63. ),
  64. );
  65. }
  66. }
  67. class _ArticleListItem extends StatelessWidget {
  68. final Articolo article;
  69. const _ArticleListItem({required this.article});
  70. @override
  71. Widget build(BuildContext context) {
  72. return Card(
  73. margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
  74. child: ListTile(
  75. leading: ArticleImage(idArticolo: article.idArticolo, codArticolo: article.codArticolo),
  76. title: Text(article.descrizione ?? ''),
  77. subtitle: Text(article.codArticolo ?? ''),
  78. trailing: article.nonDisponibile == 1
  79. ? Container(
  80. padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
  81. decoration: BoxDecoration(color: Colors.red.shade100, borderRadius: BorderRadius.circular(8)),
  82. child: const Text('N.D.', style: TextStyle(color: Colors.red, fontSize: 12)),
  83. )
  84. : null,
  85. ),
  86. );
  87. }
  88. }
  89. class _ArticleGridItem extends StatelessWidget {
  90. final Articolo article;
  91. const _ArticleGridItem({required this.article});
  92. @override
  93. Widget build(BuildContext context) {
  94. return Card(
  95. child: InkWell(
  96. borderRadius: BorderRadius.circular(12),
  97. child: Padding(
  98. padding: const EdgeInsets.all(8),
  99. child: Column(
  100. mainAxisSize: MainAxisSize.min,
  101. children: [
  102. ArticleImage(idArticolo: article.idArticolo, codArticolo: article.codArticolo, size: 64),
  103. const SizedBox(height: 8),
  104. Text(article.descrizione ?? '', maxLines: 2, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodySmall),
  105. Text(article.codArticolo ?? '', style: Theme.of(context).textTheme.labelSmall?.copyWith(color: Theme.of(context).colorScheme.outline)),
  106. if (article.nonDisponibile == 1)
  107. Container(
  108. padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1),
  109. decoration: BoxDecoration(color: Colors.red.shade100, borderRadius: BorderRadius.circular(8)),
  110. child: const Text('N.D.', style: TextStyle(color: Colors.red, fontSize: 10)),
  111. ),
  112. ],
  113. ),
  114. ),
  115. ),
  116. );
  117. }
  118. }