/// Named shopping lists of things to buy. library; import 'package:flutter/material.dart'; import 'models/shopping_list.dart'; import 'services/product_repository.dart'; import 'shopping_list_detail_screen.dart'; import 'widgets/confirm_dialog.dart'; import 'widgets/name_dialog.dart'; import 'widgets/settings_button.dart'; class ShoppingListsScreen extends StatefulWidget { const ShoppingListsScreen({super.key, required this.repository}); final ProductRepository repository; @override State createState() => _ShoppingListsScreenState(); } class _ShoppingListsScreenState extends State { late Future> _future; bool _editing = false; @override void initState() { super.initState(); _future = widget.repository.getShoppingLists(); } void _reload() { setState(() => _future = widget.repository.getShoppingLists()); } Future _create() async { final name = await showNameDialog( context, title: 'New shopping list', label: 'List name', confirmLabel: 'Create', ); if (name == null || !mounted) return; final id = await widget.repository.createShoppingList(name); if (!mounted) return; await _openList(id); } Future _openList(int id) async { await Navigator.of(context).push( MaterialPageRoute( builder: (_) => ShoppingListDetailScreen(repository: widget.repository, listId: id), ), ); if (mounted) _reload(); } Future _removeList(ShoppingList list) async { final confirmed = await confirmAction( context, title: 'Delete shopping list?', message: '"${list.name}" and its items will be deleted.', ); if (!confirmed || !mounted) return; await widget.repository.deleteShoppingList(list.id); if (mounted) _reload(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( appBar: AppBar( title: const Text('Shopping lists'), actions: [ IconButton( tooltip: _editing ? 'Done' : 'Edit', onPressed: () => setState(() => _editing = !_editing), icon: Icon(_editing ? Icons.check : Icons.edit_outlined), ), const SettingsButton(), ], ), floatingActionButton: FloatingActionButton( onPressed: _create, tooltip: 'New list', child: const Icon(Icons.add), ), body: FutureBuilder>( future: _future, builder: (context, snapshot) { if (snapshot.hasError) { return Center( child: Text( 'Could not load shopping lists.', style: theme.textTheme.bodyLarge, ), ); } if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final lists = snapshot.data!; if (lists.isEmpty) { return Center( child: Text( 'Create a list for your next shop.', textAlign: TextAlign.center, style: theme.textTheme.bodyLarge, ), ); } return ListView.separated( padding: const EdgeInsets.fromLTRB(8, 8, 8, 88), itemCount: lists.length, separatorBuilder: (_, _) => const SizedBox(height: 4), itemBuilder: (context, index) { final list = lists[index]; final remaining = list.itemCount - list.checkedCount; return ListTile( leading: CircleAvatar(child: Text('${list.itemCount}')), title: Text(list.name), subtitle: Text( list.itemCount == 0 ? 'Empty' : remaining == 0 ? 'All done' : remaining == 1 ? '1 item left' : '$remaining items left', ), trailing: _editing ? IconButton( tooltip: 'Delete', onPressed: () => _removeList(list), icon: const Icon(Icons.delete_outline), ) : const Icon(Icons.chevron_right), onTap: _editing ? null : () => _openList(list.id), ); }, ); }, ), ); } }