import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../core/utils/date_time_utils.dart'; import '../../domain/entities/pantry_category.dart'; import '../../domain/value_objects/measurement_units.dart'; import '../controllers/pantry_inventory_controller.dart'; /// Opens the "Add Pantry Item" dialog. /// /// Resolves to `true` when the item was persisted, `false` when saving /// failed, and `null` when the user dismissed the dialog. Future showAddPantryItemDialog(BuildContext context) => showDialog( context: context, builder: (context) => const AddPantryItemDialog(), ); /// Form for stocking the pantry: name, quantity, unit, grocery aisle, and an /// optional expiration date. class AddPantryItemDialog extends ConsumerStatefulWidget { const AddPantryItemDialog({super.key}); @override ConsumerState createState() => _AddPantryItemDialogState(); } class _AddPantryItemDialogState extends ConsumerState { final GlobalKey _formKey = GlobalKey(); final TextEditingController _nameController = TextEditingController(); final TextEditingController _quantityController = TextEditingController(); late String _unit = pantryUnitOptions.first; PantryCategory _category = PantryCategory.other; DateTime? _expirationDate; bool _isSaving = false; @override Widget build(BuildContext context) { return AlertDialog( title: const Text('Add Pantry Item'), content: SizedBox( width: 440, child: Form( key: _formKey, child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ TextFormField( controller: _nameController, decoration: const InputDecoration( labelText: 'Name', hintText: 'e.g. chicken breast', border: OutlineInputBorder(), ), textCapitalization: TextCapitalization.sentences, validator: _nameValidator, ), const SizedBox(height: 16), Row( children: [ Expanded( child: TextFormField( controller: _quantityController, decoration: const InputDecoration( labelText: 'Quantity', border: OutlineInputBorder(), ), keyboardType: const TextInputType.numberWithOptions(decimal: true), validator: _quantityValidator, ), ), const SizedBox(width: 12), Expanded( child: DropdownButtonFormField( initialValue: _unit, decoration: const InputDecoration( labelText: 'Unit', border: OutlineInputBorder(), ), items: [ for (final unit in pantryUnitOptions) DropdownMenuItem(value: unit, child: Text(unit)), ], onChanged: (unit) { if (unit == null) return; setState(() => _unit = unit); }, ), ), ], ), const SizedBox(height: 16), DropdownButtonFormField( initialValue: _category, decoration: const InputDecoration( labelText: 'Aisle', border: OutlineInputBorder(), ), items: [ for (final category in PantryCategory.values) DropdownMenuItem( value: category, child: Text(category.displayName), ), ], onChanged: (category) { if (category == null) return; setState(() => _category = category); }, ), const SizedBox(height: 8), Row( children: [ Expanded( child: Text( _expirationDate == null ? 'No expiration date' : 'Expires ${formatMonthDay(_expirationDate!)}', style: Theme.of(context).textTheme.bodyMedium, ), ), if (_expirationDate != null) IconButton( tooltip: 'Clear date', icon: const Icon(Icons.clear), onPressed: () => setState(() => _expirationDate = null), ), TextButton( onPressed: _pickExpirationDate, child: const Text('Pick Date'), ), ], ), ], ), ), ), ), actions: [ TextButton( onPressed: _isSaving ? null : () => Navigator.of(context).pop(), child: const Text('Cancel'), ), FilledButton.icon( onPressed: _isSaving ? null : _save, icon: _isSaving ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.add), label: const Text('Add Item'), ), ], ); } Future _pickExpirationDate() async { final now = DateTime.now(); final picked = await showDatePicker( context: context, initialDate: _expirationDate ?? now, firstDate: DateTime(now.year - 1), lastDate: DateTime(now.year + 5), ); if (picked == null || !mounted) return; setState(() => _expirationDate = picked); } Future _save() async { if (!(_formKey.currentState?.validate() ?? false)) return; setState(() => _isSaving = true); final saved = await ref.read(pantryInventoryControllerProvider.notifier).addItem( name: _nameController.text, quantityOnHand: double.parse(_quantityController.text.trim()), unit: _unit, category: _category, expirationDate: _expirationDate, ); if (!mounted) return; Navigator.of(context).pop(saved); } String? _nameValidator(String? value) { final trimmed = (value ?? '').trim(); if (trimmed.isEmpty) return 'Enter a name'; if (trimmed.length > 128) return 'Name is too long (max 128)'; return null; } String? _quantityValidator(String? value) { final parsed = double.tryParse((value ?? '').trim()); if (parsed == null || parsed < 0) return 'Enter a valid quantity'; return null; } @override void dispose() { _nameController.dispose(); _quantityController.dispose(); super.dispose(); } }