Implemented Cooking Duration Button
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../services/providers/recipe_provider.dart';
|
import '../../services/providers/recipe_provider.dart';
|
||||||
import '../../src/enums.dart';
|
import '../../src/enums.dart';
|
||||||
import '../../services/tools.dart'
|
import '../../services/tools.dart' show getEnumValueName;
|
||||||
show getEnumValueName, durationToFormattedString;
|
import '../../widgets/clearing_dropdown_menu.dart';
|
||||||
import '../../widgets/duration_picker.dart';
|
import '../../widgets/cooking_duration_button.dart';
|
||||||
|
|
||||||
class MetadataPage extends StatefulWidget {
|
class MetadataPage extends StatefulWidget {
|
||||||
const MetadataPage({super.key});
|
const MetadataPage({super.key});
|
||||||
@@ -20,109 +21,111 @@ class _MetadataPageState extends State<MetadataPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
_recipeProvider = context.watch<RecipeProvider>();
|
_recipeProvider = context.watch<RecipeProvider>();
|
||||||
return Center(
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 10, 20, 0),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
TextField(
|
TextField(
|
||||||
|
onChanged: (value) => _recipeProvider.updateTitle(value),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: Text('Title'),
|
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(
|
TextField(
|
||||||
|
onChanged: (value) => _recipeProvider.updateDescription(value),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: Text('Description'),
|
label: Text('Description'),
|
||||||
|
border: OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
|
onSubmitted: (value) => TextInputAction.next,
|
||||||
),
|
),
|
||||||
DropdownButton(
|
Padding(padding: EdgeInsets.only(top: 10)),
|
||||||
value: _recipeProvider.recipe.difficulty,
|
TextField(
|
||||||
items: Difficulty.values
|
onChanged: (value) =>
|
||||||
.map(
|
_recipeProvider.updateServingSize(int.parse(value)),
|
||||||
(e) => DropdownMenuItem<Difficulty>(
|
keyboardType: TextInputType.number,
|
||||||
value: e,
|
inputFormatters: [
|
||||||
child: Text(getEnumValueName(e)),
|
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,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
: null,
|
||||||
onChanged: (value) => setState(() {
|
|
||||||
_recipeProvider.updateDifficulty(value ?? Difficulty.notSelected);
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
|
onSubmitted: (value) => TextInputAction.next,
|
||||||
|
),
|
||||||
|
Padding(padding: EdgeInsets.only(top: 10)),
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
|
enableFilter: false,
|
||||||
|
enableSearch: false,
|
||||||
|
requestFocusOnTap: false,
|
||||||
|
label: Text('Difficulty'),
|
||||||
dropdownMenuEntries:
|
dropdownMenuEntries:
|
||||||
getDropdownMenuEntriesFromEnum(MealCategory.values),
|
getDropdownMenuEntriesFromEnum(Difficulty.values),
|
||||||
enableFilter: true,
|
onSelected: _recipeProvider.updateDifficulty,
|
||||||
label: Text('Category'),
|
|
||||||
),
|
),
|
||||||
DropdownMenu(
|
Padding(padding: EdgeInsets.only(top: 10)),
|
||||||
dropdownMenuEntries: getDropdownMenuEntriesFromEnum(Cuisine.values),
|
ClearingDropdownMenu(
|
||||||
enableFilter: true,
|
entries: getDropdownMenuEntriesFromEnum(MealCategory.values),
|
||||||
label: Text('Cuisine'),
|
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(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
children: [
|
||||||
Text('Preparation time'),
|
Text(
|
||||||
_recipeProvider.recipe.prepTime.inMinutes == 0
|
'Prep Time',
|
||||||
? IconButton(
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
onPressed: () => showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => _getDurationDialog(
|
|
||||||
context,
|
|
||||||
_recipeProvider.updatePrepTime,
|
|
||||||
_recipeProvider.recipe.prepTime),
|
|
||||||
),
|
),
|
||||||
icon: Icon(Icons.add),
|
Padding(padding: EdgeInsets.only(left: 10)),
|
||||||
)
|
CookingDurationButton(
|
||||||
: TextButton(
|
selectedDuration: _recipeProvider.recipe.prepTime,
|
||||||
onPressed: () => showDialog(
|
onDurationChanged: _recipeProvider.updatePrepTime,
|
||||||
context: context,
|
|
||||||
builder: (context) => _getDurationDialog(
|
|
||||||
context,
|
|
||||||
_recipeProvider.updatePrepTime,
|
|
||||||
_recipeProvider.recipe.prepTime),
|
|
||||||
),
|
),
|
||||||
child: Text(
|
|
||||||
durationToFormattedString(
|
|
||||||
_recipeProvider.recipe.prepTime),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Padding(padding: EdgeInsets.only(top: 10)),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
children: [
|
||||||
Text('Cooking time'),
|
Text(
|
||||||
_recipeProvider.recipe.prepTime.inMinutes == 0
|
'Cooking Time',
|
||||||
? IconButton(
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
onPressed: () => showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => _getDurationDialog(
|
|
||||||
context,
|
|
||||||
_recipeProvider.updateCookTime,
|
|
||||||
_recipeProvider.recipe.cookTime),
|
|
||||||
),
|
),
|
||||||
icon: Icon(Icons.add),
|
Padding(padding: EdgeInsets.only(left: 10)),
|
||||||
)
|
CookingDurationButton(
|
||||||
: TextButton(
|
selectedDuration: _recipeProvider.recipe.cookTime,
|
||||||
onPressed: () => showDialog(
|
onDurationChanged: _recipeProvider.updateCookTime,
|
||||||
context: context,
|
|
||||||
builder: (context) => _getDurationDialog(
|
|
||||||
context,
|
|
||||||
_recipeProvider.updateCookTime,
|
|
||||||
_recipeProvider.recipe.cookTime),
|
|
||||||
),
|
),
|
||||||
child: Text(
|
|
||||||
durationToFormattedString(
|
|
||||||
_recipeProvider.recipe.cookTime),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
List<DropdownMenuEntry<T>> getDropdownMenuEntriesFromEnum<T extends Enum>(
|
List<DropdownMenuEntry<T>> getDropdownMenuEntriesFromEnum<T extends Enum>(
|
||||||
List<T> values) {
|
List<T> values) {
|
||||||
@@ -135,40 +138,4 @@ List<DropdownMenuEntry<T>> getDropdownMenuEntriesFromEnum<T extends Enum>(
|
|||||||
)
|
)
|
||||||
.toList();
|
.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();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
79
lib/widgets/cooking_duration_button.dart
Normal file
79
lib/widgets/cooking_duration_button.dart
Normal file
@@ -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();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user