diff --git a/lib/pages/create_recipe_pages/metadata_page.dart b/lib/pages/create_recipe_pages/metadata_page.dart index 62b7362..82bfe0c 100644 --- a/lib/pages/create_recipe_pages/metadata_page.dart +++ b/lib/pages/create_recipe_pages/metadata_page.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import '../../services/providers/recipe_provider.dart'; import '../../src/enums.dart'; -import '../../services/tools.dart' - show getEnumValueName, durationToFormattedString; -import '../../widgets/duration_picker.dart'; +import '../../services/tools.dart' show getEnumValueName; +import '../../widgets/clearing_dropdown_menu.dart'; +import '../../widgets/cooking_duration_button.dart'; class MetadataPage extends StatefulWidget { const MetadataPage({super.key}); @@ -20,155 +21,121 @@ class _MetadataPageState extends State { @override Widget build(BuildContext context) { _recipeProvider = context.watch(); - return Center( + return Padding( + padding: const EdgeInsets.fromLTRB(20, 10, 20, 0), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ TextField( + onChanged: (value) => _recipeProvider.updateTitle(value), decoration: InputDecoration( label: Text('Title'), + border: OutlineInputBorder(), + enabledBorder: _recipeProvider.recipe.title.isEmpty + ? OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).colorScheme.tertiary, + ), + ) + : null, ), + onSubmitted: (value) => TextInputAction.next, ), + Padding(padding: EdgeInsets.only(top: 10)), TextField( + onChanged: (value) => _recipeProvider.updateDescription(value), decoration: InputDecoration( label: Text('Description'), + border: OutlineInputBorder(), ), + onSubmitted: (value) => TextInputAction.next, ), - DropdownButton( - value: _recipeProvider.recipe.difficulty, - items: Difficulty.values - .map( - (e) => DropdownMenuItem( - value: e, - child: Text(getEnumValueName(e)), - ), - ) - .toList(), - onChanged: (value) => setState(() { - _recipeProvider.updateDifficulty(value ?? Difficulty.notSelected); - }), + Padding(padding: EdgeInsets.only(top: 10)), + TextField( + onChanged: (value) => + _recipeProvider.updateServingSize(int.parse(value)), + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'\d')), + ], + maxLength: 2, + decoration: InputDecoration( + label: Text('Serving Size'), + border: OutlineInputBorder(), + enabledBorder: _recipeProvider.recipe.servingSize == 0 + ? OutlineInputBorder( + borderSide: BorderSide( + color: Theme.of(context).colorScheme.tertiary, + ), + ) + : null, + ), + onSubmitted: (value) => TextInputAction.next, ), + Padding(padding: EdgeInsets.only(top: 10)), DropdownMenu( + enableFilter: false, + enableSearch: false, + requestFocusOnTap: false, + label: Text('Difficulty'), dropdownMenuEntries: - getDropdownMenuEntriesFromEnum(MealCategory.values), - enableFilter: true, - label: Text('Category'), + getDropdownMenuEntriesFromEnum(Difficulty.values), + onSelected: _recipeProvider.updateDifficulty, ), - DropdownMenu( - dropdownMenuEntries: getDropdownMenuEntriesFromEnum(Cuisine.values), - enableFilter: true, - label: Text('Cuisine'), + Padding(padding: EdgeInsets.only(top: 10)), + ClearingDropdownMenu( + entries: getDropdownMenuEntriesFromEnum(MealCategory.values), + label: 'Category', + onSelected: _recipeProvider.updateMealCategory, ), + Padding(padding: EdgeInsets.only(top: 10)), + ClearingDropdownMenu( + entries: getDropdownMenuEntriesFromEnum(Cuisine.values), + label: 'Cuisine', + onSelected: _recipeProvider.updateCuisine, + ), + Padding(padding: EdgeInsets.only(top: 10)), Row( - mainAxisSize: MainAxisSize.min, children: [ - Text('Preparation time'), - _recipeProvider.recipe.prepTime.inMinutes == 0 - ? IconButton( - onPressed: () => showDialog( - context: context, - builder: (context) => _getDurationDialog( - context, - _recipeProvider.updatePrepTime, - _recipeProvider.recipe.prepTime), - ), - icon: Icon(Icons.add), - ) - : TextButton( - onPressed: () => showDialog( - context: context, - builder: (context) => _getDurationDialog( - context, - _recipeProvider.updatePrepTime, - _recipeProvider.recipe.prepTime), - ), - child: Text( - durationToFormattedString( - _recipeProvider.recipe.prepTime), - ), - ) + Text( + 'Prep Time', + style: Theme.of(context).textTheme.titleMedium, + ), + Padding(padding: EdgeInsets.only(left: 10)), + CookingDurationButton( + selectedDuration: _recipeProvider.recipe.prepTime, + onDurationChanged: _recipeProvider.updatePrepTime, + ), ], ), + Padding(padding: EdgeInsets.only(top: 10)), Row( - mainAxisSize: MainAxisSize.min, children: [ - Text('Cooking time'), - _recipeProvider.recipe.prepTime.inMinutes == 0 - ? IconButton( - onPressed: () => showDialog( - context: context, - builder: (context) => _getDurationDialog( - context, - _recipeProvider.updateCookTime, - _recipeProvider.recipe.cookTime), - ), - icon: Icon(Icons.add), - ) - : TextButton( - onPressed: () => showDialog( - context: context, - builder: (context) => _getDurationDialog( - context, - _recipeProvider.updateCookTime, - _recipeProvider.recipe.cookTime), - ), - child: Text( - durationToFormattedString( - _recipeProvider.recipe.cookTime), - ), - ) + Text( + 'Cooking Time', + style: Theme.of(context).textTheme.titleMedium, + ), + Padding(padding: EdgeInsets.only(left: 10)), + CookingDurationButton( + selectedDuration: _recipeProvider.recipe.cookTime, + onDurationChanged: _recipeProvider.updateCookTime, + ), ], ), ], ), ); } -} -List> getDropdownMenuEntriesFromEnum( - List values) { - return values - .map( - (e) => DropdownMenuEntry( - value: e, - label: getEnumValueName(e), - ), - ) - .toList(); -} - -Dialog _getDurationDialog( - BuildContext context, - void Function(Duration duration) update, - Duration initialDuration, -) { - final width = MediaQuery.of(context).size.width * 0.8; - final height = MediaQuery.of(context).size.height * 0.6; - Duration selectedTime = initialDuration; - return Dialog( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - DurationPicker( - initialDuration: initialDuration, - onChangedCallback: (duration) => selectedTime = duration, - height: width * 1.25 > height ? height : width * 1.25, - width: width, - ), - Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: const EdgeInsets.fromLTRB(0, 20, 20, 20), - child: FloatingActionButton.extended( - label: Text('Save'), - icon: Icon(Icons.save), - onPressed: () { - update(selectedTime); - Navigator.of(context).pop(); - }, - ), + List> getDropdownMenuEntriesFromEnum( + List values) { + return values + .map( + (e) => DropdownMenuEntry( + value: e, + label: getEnumValueName(e), ), - ), - ], - ), - ); + ) + .toList(); + } } diff --git a/lib/widgets/cooking_duration_button.dart b/lib/widgets/cooking_duration_button.dart new file mode 100644 index 0000000..57057ef --- /dev/null +++ b/lib/widgets/cooking_duration_button.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; + +import 'duration_picker.dart'; +import '../services/tools.dart' show durationToFormattedString; + +class CookingDurationButton extends StatelessWidget { + const CookingDurationButton({ + super.key, + required this.selectedDuration, + required this.onDurationChanged, + this.label = '', + }); + + final String label; + final void Function(Duration duration) onDurationChanged; + final Duration selectedDuration; + + @override + Widget build(BuildContext context) { + String text = label; + if (label.isEmpty || selectedDuration.inMinutes != 0) { + text = durationToFormattedString(selectedDuration); + } + return FloatingActionButton.extended( + onPressed: () => showDialog( + context: context, + builder: (context) => _getDurationDialog( + context, + onDurationChanged, + selectedDuration, + ), + ), + isExtended: selectedDuration.inMinutes != 0, + label: Text( + text, + style: Theme.of(context).textTheme.titleMedium, + ), + icon: + selectedDuration.inMinutes != 0 ? Icon(Icons.edit) : Icon(Icons.add), + ); + } +} + +Dialog _getDurationDialog( + BuildContext context, + void Function(Duration duration) update, + Duration initialDuration, +) { + final width = MediaQuery.of(context).size.width * 0.8; + final height = MediaQuery.of(context).size.height * 0.6; + Duration selectedTime = initialDuration; + return Dialog( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + DurationPicker( + initialDuration: initialDuration, + onChangedCallback: (duration) => selectedTime = duration, + height: width * 1.25 > height ? height : width * 1.25, + width: width, + ), + Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: const EdgeInsets.fromLTRB(0, 20, 20, 20), + child: FloatingActionButton.extended( + label: Text('Save'), + icon: Icon(Icons.save), + onPressed: () { + update(selectedTime); + Navigator.of(context).pop(); + }, + ), + ), + ), + ], + ), + ); +}