switched the addPizzaEventPage to a recipe list from yamls on the device

This commit is contained in:
broodjeaap89 2021-07-10 21:01:30 +02:00
parent 2ffe58e75c
commit c43d92c0f1
8 changed files with 296 additions and 144 deletions

View file

@ -0,0 +1,54 @@
recipe:
name: "Neapolitan Day Dough"
description: >
A Neapolitan Style pizza prepared in a day
ingredients:
method: ratio
items:
- name: flour
unit: g
value: 0.595
- name: water
unit: ml
value: 0.386
- name: salt
unit: g
value: 0.0178
- name: yeast
unit: g
value: 0.0012
steps:
- name: Make the dough
wait:
unit: hours
min: 4
max: 6
description: >
Combine all the ingredients to make the dough, we start with just the water and salt and add the yeast after adding some of the flour to not kill the yeast.
substeps:
- name: Salt+Water
description: >
Combine the salt and the water in a bowl, stir until the salt dissolves.
- name: +20% flour
description: >
Add ~20% of the flour to the water/salt mixture and mix everything together.
- name: Yeast
description: >
Add the yeast to the mixture.
- name: Flour
description: >
Add the rest of the flour to the mixture, knead the dough for 15-20 minutes.
- name: Rise
description: >
Split the dough into doughballs and place the dough in a sealed/covered container at room temperature.
- name: Preheat Oven
wait:
unit: minutes
min: 30
max: 120
description: >
Preheat the oven in advance to ensure it's as hot as it can be
A high (250 to 300 degrees celsius) is recommended)
- name: Pizza Time!
description: >
Time to make pizza!

View file

@ -0,0 +1,54 @@
recipe:
name: "New York"
description: >
A Neapolitan Style pizza prepared in a day
ingredients:
method: ratio
items:
- name: flour
unit: g
value: 0.545
- name: water
unit: ml
value: 0.436
- name: salt
unit: g
value: 0.0178
- name: yeast
unit: g
value: 0.0012
steps:
- name: Make the dough
wait:
unit: hours
min: 4
max: 6
description: >
Combine all the ingredients to make the dough, we start with just the water and salt and add the yeast after adding some of the flour to not kill the yeast.
substeps:
- name: Salt+Water
description: >
Combine the salt and the water in a bowl, stir until the salt dissolves.
- name: +20% flour
description: >
Add ~20% of the flour to the water/salt mixture and mix everything together.
- name: Yeast
description: >
Add the yeast to the mixture.
- name: Flour
description: >
Add the rest of the flour to the mixture, knead the dough for 15-20 minutes.
- name: Rise
description: >
Split the dough into doughballs and place the dough in a sealed/covered container at room temperature.
- name: Preheat Oven
wait:
unit: minutes
min: 30
max: 120
description: >
Preheat the oven in advance to ensure it's as hot as it can be
A high (250 to 300 degrees celsius) is recommended)
- name: Pizza Time!
description: >
Time to make pizza!

View file

@ -1,14 +1,16 @@
import 'package:pizzaplanner/entities/PizzaRecipe/PizzaRecipe.dart';
class PizzaEvent { class PizzaEvent {
final String name; final String name;
final String type; final PizzaRecipe recipe;
final int pizzaCount; final int pizzaCount;
final int doughBallSize; final int doughBallSize;
final DateTime dateTime; final DateTime dateTime;
PizzaEvent( PizzaEvent(
this.name, this.name,
this.type, this.recipe,
this.pizzaCount, this.pizzaCount,
this.doughBallSize, this.doughBallSize,
this.dateTime this.dateTime

View file

@ -15,8 +15,8 @@ class PizzaRecipe {
PizzaRecipe(this.name, this.description, this.ingredients, this.recipeSteps); PizzaRecipe(this.name, this.description, this.ingredients, this.recipeSteps);
static Future<PizzaRecipe> fromYaml() async{ static Future<PizzaRecipe> fromYaml(yamlPath) async{
String yamlString = await loadAsset("assets/recipes/neapolitan_cold.yaml"); String yamlString = await loadAsset(yamlPath);
var yaml = loadYaml(yamlString); var yaml = loadYaml(yamlString);
var recipe = yaml["recipe"]; var recipe = yaml["recipe"];
@ -38,11 +38,16 @@ class PizzaRecipe {
String stepName = step["name"]; String stepName = step["name"];
String stepDescription = step["description"]; String stepDescription = step["description"];
YamlMap waitMap = step.containsKey("wait") ? step["wait"] : YamlList(); String waitUnit = "none";
String waitUnit = waitMap["unit"]; int waitMin = 0;
int waitMin = waitMap["min"]; int waitMax = 0;
int waitMax = waitMap["max"];
print(step); if (step.containsKey("wait")) {
YamlMap waitMap = step["wait"];
waitUnit = waitMap["unit"];
waitMin = waitMap["min"];
waitMax = waitMap["max"];
}
YamlList subSteps = step.containsKey("substeps") ? step["substeps"] : YamlList(); YamlList subSteps = step.containsKey("substeps") ? step["substeps"] : YamlList();
var newSubSteps = List.generate(subSteps.length, (j) { var newSubSteps = List.generate(subSteps.length, (j) {
@ -66,5 +71,9 @@ class PizzaRecipe {
newRecipeSteps newRecipeSteps
); );
} }
String toString() {
return "PizzaRecipe(${this.name}, ${this.ingredients.ingredients.length}, ${this.recipeSteps.length})";
}
} }

View file

@ -5,6 +5,8 @@ import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttericon/font_awesome5_icons.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:pizzaplanner/entities/PizzaEvent.dart'; import 'package:pizzaplanner/entities/PizzaEvent.dart';
import 'package:pizzaplanner/entities/PizzaRecipe/PizzaRecipe.dart';
import 'package:pizzaplanner/util.dart';
class AddPizzaEventPage extends StatefulWidget { class AddPizzaEventPage extends StatefulWidget {
@override @override
@ -15,156 +17,167 @@ class AddPizzaEventPageState extends State<AddPizzaEventPage> {
final DateFormat dateFormatter = DateFormat("yyyy-MM-dd hh:mm"); final DateFormat dateFormatter = DateFormat("yyyy-MM-dd hh:mm");
String name = ""; String name = "";
String pizzaType = "Neapolitan"; bool initialized = false;
late PizzaRecipe pizzaRecipe;
late List<PizzaRecipe> pizzaTypes;
int pizzaCount = 1; int pizzaCount = 1;
int doughBallSize = 250; int doughBallSize = 250;
DateTime eventTime = DateTime.now(); DateTime eventTime = DateTime.now();
bool nameValidation = false; bool nameValidation = false;
@override
void initState() {
super.initState();
getRecipes().then((pTypes) {
this.pizzaTypes = pTypes;
this.pizzaRecipe = this.pizzaTypes.first;
setState(() {this.initialized = true;});
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("Add Pizza Event"), title: Text("Add Pizza Event"),
), ),
body: Container( body: Container(
padding: EdgeInsets.fromLTRB(40, 10, 40, 10), padding: EdgeInsets.fromLTRB(40, 10, 40, 10),
child: Column( child: Column(
children: <Widget>[
Row(
children: <Widget>[ children: <Widget>[
Icon(Icons.title), Row(
Container(width: 25), children: <Widget>[
Expanded( Icon(Icons.title),
child: TextField( Container(width: 25),
decoration: InputDecoration( Expanded(
hintText: "Event Name", child: TextField(
errorText: this.nameValidation ? "Name can\'t be empty" : null decoration: InputDecoration(
), hintText: "Event Name",
onChanged: (String newName) { errorText: this.nameValidation ? "Name can\'t be empty" : null
setState(() { ),
name = newName; onChanged: (String newName) {
}); setState(() {
}, name = newName;
), });
) },
] ),
), )
Row( ]
children: <Widget>[
Icon(FontAwesome5.pizza_slice),
Container(width: 25),
Expanded(
child: DropdownButton<String>(
value: pizzaType,
onChanged: (String? newType) {
setState(() => pizzaType = newType!);
},
items: <String>["Neapolitan", "New York", "Chicago"]
.map<DropdownMenuItem<String>>((String v) {
return DropdownMenuItem(
value: v,
child: Text(v)
);
}).toList()
),
)
]
),
Row(
children: <Widget>[
Icon(FontAwesome5.hashtag),
Expanded(
child: Slider(
value: pizzaCount.toDouble(),
min: 1,
max: 20,
divisions: 19,
label: this.pizzaCount.toString(),
onChanged: (double newPizzaCount) {
setState(() {this.pizzaCount = newPizzaCount.round();});
},
)
), ),
Container( Row(
width: 25, children: <Widget>[
child: Text(this.pizzaCount.toString()) Icon(FontAwesome5.pizza_slice),
) Container(width: 25),
] Expanded(
), child: this.initialized ? // Only render the dropdown if the recipes have been loaded from storage
DropdownButton<String>(
Row( value: this.pizzaRecipe.name,
children: <Widget>[ onChanged: (String? newType) {
Icon(FontAwesome5.weight_hanging), setState(() => this.pizzaRecipe = this.pizzaTypes.firstWhere((pizzaRecipe) => pizzaRecipe.name == newType));
Expanded( },
child: Slider( items: this.pizzaTypes.map((pizzaRecipe) {
value: doughBallSize.toDouble(), return DropdownMenuItem(
min: 100, value: pizzaRecipe.name,
max: 400, child: Text(pizzaRecipe.name)
divisions: 30, );
label: this.doughBallSize.toString(), }).toList()
onChanged: (double newDoughBallSize) { ) : CircularProgressIndicator()
setState(() {this.doughBallSize = newDoughBallSize.round();}); )
}, ]
)
), ),
Container( Row(
width: 25, children: <Widget>[
child: Text(this.doughBallSize.toString()) Icon(FontAwesome5.hashtag),
) Expanded(
] child: Slider(
), value: pizzaCount.toDouble(),
Row( min: 1,
children: <Widget>[ max: 20,
Icon(FontAwesome5.calendar_alt), divisions: 19,
Expanded( label: this.pizzaCount.toString(),
child: InkWell( onChanged: (double newPizzaCount) {
child: Center( setState(() {this.pizzaCount = newPizzaCount.round();});
child: Text(dateFormatter.format(this.eventTime)), },
)
), ),
onTap: () { Container(
DatePicker.showDateTimePicker(context, width: 25,
showTitleActions: true, child: Text(this.pizzaCount.toString())
minTime: DateTime.now(), )
currentTime: this.eventTime.difference(DateTime.now()).isNegative ? DateTime.now() : this.eventTime, ]
maxTime: DateTime.now().add(Duration(days: 365*10)), ),
onConfirm: (newEventTime) { Row(
setState((){ this.eventTime = newEventTime; }); children: <Widget>[
} Icon(FontAwesome5.weight_hanging),
); Expanded(
} child: Slider(
value: doughBallSize.toDouble(),
min: 100,
max: 400,
divisions: 30,
label: this.doughBallSize.toString(),
onChanged: (double newDoughBallSize) {
setState(() {this.doughBallSize = newDoughBallSize.round();});
},
)
),
Container(
width: 25,
child: Text(this.doughBallSize.toString())
)
]
),
Row(
children: <Widget>[
Icon(FontAwesome5.calendar_alt),
Expanded(
child: InkWell(
child: Center(
child: Text(dateFormatter.format(this.eventTime)),
),
onTap: () {
DatePicker.showDateTimePicker(context,
showTitleActions: true,
minTime: DateTime.now(),
currentTime: this.eventTime.difference(DateTime.now()).isNegative ? DateTime.now() : this.eventTime,
maxTime: DateTime.now().add(Duration(days: 365*10)),
onConfirm: (newEventTime) {
setState((){ this.eventTime = newEventTime; });
}
);
}
)
)
]
),
Spacer(),
SizedBox(
width: double.infinity,
height: 70,
child: Container(
color: Colors.blue,
child: TextButton(
child: Text("Add", style: TextStyle(color: Colors.white)),
onPressed: () {
if (this.name.length == 0){
setState(() { this.nameValidation = true; });
return;
}
Navigator.pop(context, PizzaEvent(
this.name,
this.pizzaRecipe,
this.pizzaCount,
this.doughBallSize,
this.eventTime
));
},
)
) )
)
]
),
Spacer(),
SizedBox(
width: double.infinity,
height: 70,
child: Container(
color: Colors.blue,
child: TextButton(
child: Text("Add", style: TextStyle(color: Colors.white)),
onPressed: () {
if (this.name.length == 0){
setState(() { this.nameValidation = true; });
return;
}
Navigator.pop(context, PizzaEvent(
this.name,
this.pizzaType,
this.pizzaCount,
this.doughBallSize,
this.eventTime
));
},
) )
) ]
) )
]
) )
)
); );
} }
} }

View file

@ -1,6 +1,8 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pizzaplanner/entities/PizzaEvent.dart'; import 'package:pizzaplanner/entities/PizzaEvent.dart';
import 'package:pizzaplanner/entities/PizzaRecipe/PizzaRecipe.dart';
import 'package:pizzaplanner/util.dart';
import 'package:pizzaplanner/widgets/PizzaEventWidget.dart'; import 'package:pizzaplanner/widgets/PizzaEventWidget.dart';
class PizzaEventsPage extends StatefulWidget { class PizzaEventsPage extends StatefulWidget {
@ -30,6 +32,7 @@ class PizzaEventsState extends State<PizzaEventsPage> {
context, context,
"/add", "/add",
); );
if (newPizzaEvent != null){ if (newPizzaEvent != null){
this.addPizzaEvent(newPizzaEvent); this.addPizzaEvent(newPizzaEvent);
} }

View file

@ -1,4 +1,21 @@
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle; import 'package:flutter/services.dart' show rootBundle;
import 'package:pizzaplanner/entities/PizzaRecipe/PizzaRecipe.dart';
Future<List<PizzaRecipe>> getRecipes() async {
final manifestContent = await rootBundle.loadString('AssetManifest.json');
final Map<String, dynamic> manifestMap = json.decode(manifestContent);
final List<String> fileList = manifestMap.keys.toList();
final List<PizzaRecipe> pizzaRecipes = [];
for (var filePath in fileList) {
if (filePath.startsWith("assets/recipes") && filePath.endsWith(".yaml")) {
PizzaRecipe pizzaRecipe = await PizzaRecipe.fromYaml(filePath);
pizzaRecipes.add(pizzaRecipe);
}
}
return pizzaRecipes;
}
Future<String> loadAsset(String path) async { Future<String> loadAsset(String path) async {
return await rootBundle.loadString(path); return await rootBundle.loadString(path);

View file

@ -70,7 +70,7 @@ class PizzaEventWidget extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Text(dateFormatter.format(pizzaEvent.dateTime)), Text(dateFormatter.format(pizzaEvent.dateTime)),
Text(pizzaEvent.type) Text(pizzaEvent.recipe.name)
], ],
), ),