diff --git a/lib/models/ingredient.dart b/lib/models/ingredient.dart index 398400e..a271e92 100644 --- a/lib/models/ingredient.dart +++ b/lib/models/ingredient.dart @@ -7,7 +7,7 @@ class Ingredient { Ingredient({ required this.title, - required this.possibleUnits, + this.possibleUnits = const [], this.preferredBrands = const [], }); diff --git a/lib/models/ingredient_list_entry.dart b/lib/models/ingredient_list_entry.dart new file mode 100644 index 0000000..e1643b9 --- /dev/null +++ b/lib/models/ingredient_list_entry.dart @@ -0,0 +1,11 @@ +import 'ingredient.dart'; +import 'unit.dart'; + +class IngredientListEntry { + final Ingredient ingredient; + final int amount; + final Unit unit; + final bool optional; + + IngredientListEntry(this.ingredient, this.amount, this.unit, this.optional); +} diff --git a/lib/pages/create_recipe_page.dart b/lib/pages/create_recipe_page.dart index 7373ef0..c384d28 100644 --- a/lib/pages/create_recipe_page.dart +++ b/lib/pages/create_recipe_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:rezepte/widgets/ingredients_bottomsheet.dart'; import '../models/difficulty.dart'; +import '../models/ingredient_list_entry.dart'; import '../services/providers/recipe_provider.dart'; class CreateRecipe extends StatefulWidget { @@ -14,6 +15,7 @@ class CreateRecipe extends StatefulWidget { class _CreateRecipeState extends State { late RecipeProvider recipeProvider; + final List ingredientEntries = []; @override Widget build(BuildContext context) { @@ -23,29 +25,40 @@ class _CreateRecipeState extends State { title: const Text('Create Recipe'), ), body: Form( - child: Column( - children: [ - TextFormField( - onChanged: (value) => recipeProvider.title = value, - decoration: const InputDecoration( - label: Text('Title'), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + children: [ + TextFormField( + onChanged: (value) => recipeProvider.title = value, + decoration: const InputDecoration( + label: Text('Title'), + ), ), - ), - TextFormField( - onChanged: (value) => recipeProvider.description = value, - decoration: const InputDecoration( - label: Text('Description'), + TextFormField( + onChanged: (value) => recipeProvider.description = value, + decoration: const InputDecoration( + label: Text('Description'), + ), ), - ), - DropdownMenu( - dropdownMenuEntries: DifficultyUtil.getDropdownList(), - onSelected: (value) => recipeProvider.difficulty = value, - ), - ElevatedButton( - onPressed: _openIngredientBottomSheet, - child: const Text('Add Ingredient'), - ), - ], + DropdownMenu( + dropdownMenuEntries: DifficultyUtil.getDropdownList(), + onSelected: (value) => recipeProvider.difficulty = value, + label: const Text('Difficulty'), + ), + ElevatedButton( + onPressed: _openIngredientBottomSheet, + child: const Text('Add Ingredient'), + ), + Expanded( + child: ListView.separated( + itemCount: ingredientEntries.length, + itemBuilder: _ingredientListBuilder, + separatorBuilder: (context, index) => const Divider(), + ), + ) + ], + ), ), ), ); @@ -54,7 +67,44 @@ class _CreateRecipeState extends State { void _openIngredientBottomSheet() { showModalBottomSheet( context: context, - builder: (context) => const IngredientsBottomsheet(), + builder: (context) => IngredientsBottomsheet( + onSubmitted: _onIngredientSubmitted, + ), ); } + + void _onIngredientSubmitted(IngredientListEntry ingredient) => setState(() { + ingredientEntries.add(ingredient); + }); + + Widget? _ingredientListBuilder(BuildContext context, int index) { + final ingredient = ingredientEntries.elementAt(index); + final textTheme = Theme.of(context).textTheme.titleMedium; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(ingredient.ingredient.title, style: textTheme), + Row( + children: [ + Text( + ingredient.amount.toString(), + style: textTheme, + ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 2)), + Text( + ingredient.unit.name, + style: textTheme, + ), + ], + ), + ], + ), + ); + } + + Widget _seperatorBuilder(BuildContext context, int index) { + return const Divider(indent: 20, endIndent: 20); + } } diff --git a/lib/theme.dart b/lib/theme.dart index 59929b4..a3d489f 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -3,14 +3,18 @@ import 'package:flutter/material.dart'; final _baseTheme = ThemeData( fontFamily: "Ubuntu", useMaterial3: true, +).copyWith( + dividerTheme: const DividerThemeData(indent: 20, endIndent: 20), ); final lightTheme = _baseTheme.copyWith( - colorScheme: ColorScheme.fromSeed( - seedColor: Colors.deepPurple, - brightness: Brightness.light, - ), -); + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.deepPurple, + brightness: Brightness.light, + ), + dividerTheme: DividerThemeData( + color: Colors.grey[600], + )); final darkTheme = _baseTheme.copyWith( colorScheme: ColorScheme.fromSeed( diff --git a/lib/widgets/ingredients_bottomsheet.dart b/lib/widgets/ingredients_bottomsheet.dart index 171c48a..4e38ef7 100644 --- a/lib/widgets/ingredients_bottomsheet.dart +++ b/lib/widgets/ingredients_bottomsheet.dart @@ -2,12 +2,17 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../constants.dart' as constants; import '../models/ingredient.dart'; +import '../models/ingredient_list_entry.dart'; import '../models/unit.dart'; import '../example_data.dart' as ea; import '../services/providers/recipe_provider.dart'; +typedef IngredientCallback = void Function(IngredientListEntry); + class IngredientsBottomsheet extends StatefulWidget { - const IngredientsBottomsheet({super.key}); + const IngredientsBottomsheet({required this.onSubmitted, super.key}); + + final IngredientCallback onSubmitted; @override State createState() => _IngredientsBottomsheetState(); @@ -15,13 +20,13 @@ class IngredientsBottomsheet extends StatefulWidget { class _IngredientsBottomsheetState extends State { final List> ingredientEntries = []; - final List> unitEntries = []; - Unit? selectedUnit; late RecipeProvider recipeProvider; + Unit? selectedUnit; + final List> unitEntries = []; - bool _isOptional = false; - TextEditingController _ingredientController = TextEditingController(); TextEditingController _amountController = TextEditingController(); + TextEditingController _ingredientController = TextEditingController(); + bool _isOptional = false; TextEditingController _unitController = TextEditingController(); @override @@ -137,6 +142,44 @@ class _IngredientsBottomsheetState extends State { ); } + void _nextTapped() { + final success = _submit(); + if (success) { + setState(() { + _ingredientController.value = TextEditingValue.empty; + _unitController.value = TextEditingValue.empty; + _amountController.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); + final unit = selectedUnit; + final amount = int.tryParse(_amountController.text); + if (ingredient.title.isEmpty || unit == null || amount == null) { + return false; + } + + final newEntry = IngredientListEntry(ingredient, amount, unit, _isOptional); + widget.onSubmitted(newEntry); + + return true; + } + @override Widget build(BuildContext context) { recipeProvider = Provider.of(context); @@ -145,24 +188,4 @@ class _IngredientsBottomsheetState extends State { builder: _bottomSheetContent, ); } - - void _nextTapped() { - _submit(); - setState(() { - _ingredientController.value = TextEditingValue.empty; - _unitController.value = TextEditingValue.empty; - _amountController.value = TextEditingValue.empty; - _isOptional = false; - }); - } - - void _finishTapped() { - // TODO implement} - } - void _cancelTapped() { - // TODO implement} - } - void _submit() { - // TODO implement - } }