/// Validated form that inserts a new [Item] into [ItemController]. library; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../models/item.dart'; import '../providers/item_provider.dart'; import '../widgets/app_text_field.dart'; import '../widgets/responsive_body.dart'; class AddItemScreen extends StatefulWidget { const AddItemScreen({super.key}); @override State createState() => _AddItemScreenState(); } class _AddItemScreenState extends State { final _formKey = GlobalKey(); final _title = TextEditingController(); final _subtitle = TextEditingController(); final _description = TextEditingController(); ItemCategory? _category; bool _submitted = false; @override void dispose() { _title.dispose(); _subtitle.dispose(); _description.dispose(); super.dispose(); } String? _requiredLength(String? value, {required int min, String? label}) { final text = value?.trim() ?? ''; if (text.isEmpty) return 'Enter a ${label ?? 'value'}.'; if (text.length < min) { return '${label ?? 'This field'} must be at least $min characters.'; } return null; } void _submit() { setState(() => _submitted = true); final form = _formKey.currentState; if (form == null || !form.validate() || _category == null) return; context.read().addItem( title: _title.text, subtitle: _subtitle.text, description: _description.text, category: _category!, ); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Added “${_title.text.trim()}”')), ); Navigator.of(context).pop(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( appBar: AppBar(title: const Text('Add item')), body: ResponsiveBody( child: Form( key: _formKey, autovalidateMode: _submitted ? AutovalidateMode.onUserInteraction : AutovalidateMode.disabled, child: ListView( children: [ const SizedBox(height: 8), Text( 'Create a catalog entry. All fields are required.', style: theme.textTheme.bodyMedium, ), const SizedBox(height: 20), AppTextField( controller: _title, label: 'Title', hint: 'Give it a short name', autofocus: true, textInputAction: TextInputAction.next, validator: (value) => _requiredLength(value, min: 3, label: 'Title'), ), const SizedBox(height: 16), AppTextField( controller: _subtitle, label: 'Summary', hint: 'One sentence overview', textInputAction: TextInputAction.next, validator: (value) => _requiredLength(value, min: 8, label: 'Summary'), ), const SizedBox(height: 16), DropdownButtonFormField( initialValue: _category, decoration: const InputDecoration(labelText: 'Category'), items: [ for (final category in ItemCategory.values) DropdownMenuItem( value: category, child: Text(category.label), ), ], onChanged: (value) => setState(() => _category = value), validator: (value) => value == null ? 'Choose a category.' : null, ), const SizedBox(height: 16), AppTextField( controller: _description, label: 'Description', hint: 'A longer note the detail screen will show', maxLines: 5, keyboardType: TextInputType.multiline, validator: (value) => _requiredLength(value, min: 16, label: 'Description'), ), const SizedBox(height: 24), FilledButton( onPressed: _submit, child: const Text('Save item'), ), const SizedBox(height: 12), OutlinedButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel'), ), const SizedBox(height: 24), ], ), ), ), ); } }