import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../constants/cooking_units.dart'; import '../models/ingredient.dart'; import '../models/ingredient_list_entry.dart'; import '../models/unit.dart'; import '../src/enums.dart'; class IngredientBottomsheet extends StatefulWidget { const IngredientBottomsheet({super.key, required this.onSubmitted}); final void Function(IngredientListEntry ingredient) onSubmitted; @override State createState() => _IngredientBottomsheetState(); } class _IngredientBottomsheetState extends State { final TextEditingController _amountController = TextEditingController(); final TextEditingController _ingredientController = TextEditingController(); final TextEditingController _unitController = TextEditingController(); bool _isOptional = false; Unit? _selectedUnit; @override Widget build(BuildContext context) { return _bottomSheetContent(context); } Widget _bottomSheetContent(BuildContext context) { return Wrap( children: [ Padding( padding: EdgeInsets.only( top: 20, left: 20, right: 20, bottom: MediaQuery.of(context).viewInsets.bottom, ), child: Column( mainAxisSize: MainAxisSize.max, children: [ SizedBox( width: double.infinity, child: DropdownMenu( enableSearch: true, enableFilter: true, width: double.infinity, requestFocusOnTap: true, controller: _ingredientController, label: const Text('Ingredient'), textStyle: TextStyle(color: Theme.of(context).colorScheme.onSurface), dropdownMenuEntries: [], ), ), Padding(padding: EdgeInsets.only(top: 15)), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Flexible( flex: 1, child: TextField( maxLines: 1, maxLength: 4, inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'\d')), ], controller: _amountController, onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), keyboardType: TextInputType.number, textInputAction: TextInputAction.next, decoration: const InputDecoration( label: Text('Amount'), border: OutlineInputBorder(), ), style: TextStyle( color: Theme.of(context).colorScheme.onSurface), ), ), Padding(padding: EdgeInsets.only(left: 10)), Flexible( flex: 1, child: DropdownMenu( label: const Text('Unit'), requestFocusOnTap: false, width: double.infinity, controller: _unitController, onSelected: (value) => setState(() => _selectedUnit = value), dropdownMenuEntries: CookingUnits.values .map( (e) => DropdownMenuEntry(value: e, label: e.name)) .toList(), enableSearch: false, enableFilter: false, textStyle: TextStyle( color: Theme.of(context).colorScheme.onSurface, ), ), ), ], ), Row( children: [ Text( 'optional', style: TextStyle( color: Theme.of(context).colorScheme.onSurface), ), Checkbox( value: _isOptional, onChanged: (_) => setState( () => _isOptional = !_isOptional, ), ), ], ), ], ), ), const SizedBox( height: 200, width: 400, ), Padding( padding: const EdgeInsets.all(10), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: _cancelTapped, child: const Text('Cancel'), ), TextButton( onPressed: _finishTapped, child: const Text('Finish'), ), TextButton( onPressed: _nextTapped, child: const Text('Next'), ), ], ), ), ], ); } void _nextTapped() { final success = _submit(); if (success) { setState(() { _ingredientController.value = TextEditingValue.empty; _amountController.value = TextEditingValue.empty; _unitController.value = TextEditingValue.empty; _isOptional = false; _selectedUnit = null; }); } } void _finishTapped() { final success = _submit(); if (success) { Navigator.of(context).pop(); } } void _cancelTapped() { Navigator.of(context).pop(); } bool _submit() { final ingredient = Ingredient( title: _ingredientController.text, type: IngredientType.other); final amount = int.tryParse(_amountController.text); if (ingredient.title.isEmpty || _selectedUnit == null || amount == null) { return false; } final newEntry = IngredientListEntry( ingredient: ingredient, amount: amount, unit: _selectedUnit!, optional: _isOptional); widget.onSubmitted(newEntry); return true; } }