323 lines
12 KiB
Dart
323 lines
12 KiB
Dart
import 'package:diameter/components/detail.dart';
|
|
import 'package:diameter/utils/dialog_utils.dart';
|
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
|
import 'package:diameter/models/x_ingredient.dart';
|
|
import 'package:diameter/models/meal.dart';
|
|
import 'package:diameter/models/x_recipe.dart';
|
|
import 'package:diameter/models/settings.dart';
|
|
import 'package:diameter/navigation.dart';
|
|
import 'package:diameter/screens/meal/meal_detail.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
class RecipeDetailScreen extends StatefulWidget {
|
|
static const String routeName = '/recipe';
|
|
final int id;
|
|
|
|
const RecipeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
|
|
|
@override
|
|
_RecipeDetailScreenState createState() => _RecipeDetailScreenState();
|
|
}
|
|
|
|
class _RecipeDetailScreenState extends State<RecipeDetailScreen> {
|
|
Recipe? _recipe;
|
|
List<Ingredient> _ingredients = [];
|
|
|
|
bool _isNew = true;
|
|
bool _isSaving = false;
|
|
|
|
final GlobalKey<FormState> _recipeForm = GlobalKey<FormState>();
|
|
final ScrollController _scrollController = ScrollController();
|
|
|
|
final _nameController = TextEditingController(text: '');
|
|
final _notesController = TextEditingController(text: '');
|
|
|
|
double _servings = 1;
|
|
|
|
final List<TextEditingController> _ingredientControllers = [];
|
|
|
|
List<Meal> _meals = [];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
reload();
|
|
|
|
_meals = Meal.getAll();
|
|
|
|
if (_recipe != null) {
|
|
_nameController.text = _recipe!.name;
|
|
_servings = _recipe!.servings ?? 1;
|
|
_notesController.text = _recipe!.notes ?? '';
|
|
|
|
if (_ingredients.isNotEmpty) {
|
|
for (Ingredient ingredient in _ingredients) {
|
|
_ingredientControllers.add(
|
|
TextEditingController(text: ingredient.ingredient.target?.value));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void reload({String? message}) {
|
|
if (widget.id != 0) {
|
|
setState(() {
|
|
_recipe = Recipe.get(widget.id);
|
|
_ingredients = Ingredient.getAllForRecipe(widget.id);
|
|
});
|
|
}
|
|
_isNew = _recipe == null;
|
|
|
|
setState(() {
|
|
if (message != null) {
|
|
var snackBar = SnackBar(
|
|
content: Text(message),
|
|
duration: const Duration(seconds: 2),
|
|
);
|
|
ScaffoldMessenger.of(context)
|
|
..removeCurrentSnackBar()
|
|
..showSnackBar(snackBar);
|
|
}
|
|
});
|
|
}
|
|
|
|
void onAddIngredient() {
|
|
final newIngredient = Ingredient(amount: 0);
|
|
setState(() {
|
|
newIngredient.recipe.target = _recipe;
|
|
_ingredients.add(newIngredient);
|
|
_ingredientControllers.add(TextEditingController(text: ''));
|
|
});
|
|
}
|
|
|
|
void handleSaveAction({bool close = false}) async {
|
|
setState(() {
|
|
_isSaving = true;
|
|
});
|
|
if (_recipeForm.currentState!.validate()) {
|
|
Recipe recipe = Recipe(
|
|
id: widget.id,
|
|
name: _nameController.text,
|
|
servings: _servings,
|
|
notes: _notesController.text,
|
|
);
|
|
Recipe.put(recipe);
|
|
List<Ingredient> ingredients = _ingredients.map((ingredient) {
|
|
if (ingredient.id != 0 &&
|
|
(!ingredient.ingredient.hasValue || ingredient.amount == 0)) {
|
|
ingredient.deleted = true;
|
|
}
|
|
return ingredient;
|
|
}).toList();
|
|
ingredients.retainWhere((ingredient) {
|
|
return ingredient.id != 0 ||
|
|
(ingredient.amount > 0 && ingredient.ingredient.hasValue);
|
|
});
|
|
Ingredient.putMany(ingredients);
|
|
|
|
if (close) {
|
|
Navigator.pop(context, ['${_isNew ? 'New' : ''} Recipe Saved', recipe]);
|
|
} else {
|
|
if (_isNew) {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => RecipeDetailScreen(id: recipe.id),
|
|
),
|
|
).then((result) => Navigator.pop(context, result));
|
|
} else {
|
|
reload(message: 'Recipe saved');
|
|
}
|
|
}
|
|
|
|
setState(() {
|
|
_isSaving = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
void handleCancelAction() {
|
|
if (Settings.get().showConfirmationDialogOnCancel &&
|
|
((_isNew &&
|
|
(_nameController.text != '' ||
|
|
_servings != 1 ||
|
|
_notesController.text != '')) ||
|
|
(!_isNew &&
|
|
(_nameController.text != _recipe!.name ||
|
|
_servings != _recipe!.servings ||
|
|
_notesController.text != (_recipe!.notes ?? ''))))) {
|
|
DialogUtils.showCancelConfirmationDialog(
|
|
context: context,
|
|
isNew: _isNew,
|
|
onSave: handleSaveAction,
|
|
);
|
|
} else {
|
|
Navigator.pop(context);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(_isNew ? 'New Recipe' : _recipe!.name),
|
|
),
|
|
drawer: const Navigation(currentLocation: RecipeDetailScreen.routeName),
|
|
body: Scrollbar(
|
|
controller: _scrollController,
|
|
child: SingleChildScrollView(
|
|
controller: _scrollController,
|
|
child: Column(
|
|
children: <Widget>[
|
|
FormWrapper(
|
|
formState: _recipeForm,
|
|
fields: [
|
|
TextFormField(
|
|
controller: _nameController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Name',
|
|
),
|
|
validator: (value) {
|
|
if (value!.trim().isEmpty) {
|
|
return 'Empty title';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
// NumberFormField(
|
|
// value: _servings,
|
|
// label: 'Servings',
|
|
// suffix: ' portions',
|
|
// min: 0,
|
|
// onChanged: (value) {
|
|
// if (value != null && value >= 0) {
|
|
// setState(() {
|
|
// _servings = value.toDouble();
|
|
// });
|
|
// }
|
|
// },
|
|
// ),
|
|
TextFormField(
|
|
keyboardType: TextInputType.multiline,
|
|
controller: _notesController,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Notes',
|
|
),
|
|
minLines: 2,
|
|
maxLines: 5,
|
|
),
|
|
const Divider(),
|
|
GestureDetector(
|
|
onTap: onAddIngredient,
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
'INGREDIENTS',
|
|
style: Theme.of(context).textTheme.subtitle2,
|
|
),
|
|
const Spacer(),
|
|
IconButton(
|
|
onPressed: onAddIngredient,
|
|
icon: const Icon(Icons.add),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
!_isNew && _ingredients.isNotEmpty
|
|
? ListBody(
|
|
children: _ingredients.map((item) {
|
|
final ingredient = item.ingredient.target;
|
|
final index = _ingredients.indexOf(item);
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10.0, vertical: 5.0),
|
|
child: Column(
|
|
children: <Widget>[
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: AutoCompleteDropdownButton<Meal>(
|
|
controller: _ingredientControllers[index],
|
|
selectedItem: ingredient,
|
|
label: 'Meal Category',
|
|
items: _meals,
|
|
onChanged: (value) {
|
|
setState(() {
|
|
_ingredients[index]
|
|
.ingredient
|
|
.target = value;
|
|
_ingredientControllers[index].text =
|
|
value?.value ?? '';
|
|
});
|
|
},
|
|
),
|
|
),
|
|
IconButton(
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) =>
|
|
ingredient == null
|
|
? const MealDetailScreen()
|
|
: MealDetailScreen(
|
|
id: ingredient.id),
|
|
),
|
|
).then((result) {
|
|
_ingredients[index].ingredient.target =
|
|
result?[1];
|
|
_ingredientControllers[index].text =
|
|
result?[1].value ?? '';
|
|
reload(message: result?[0]);
|
|
});
|
|
},
|
|
icon: Icon(ingredient == null
|
|
? Icons.add
|
|
: Icons.edit),
|
|
),
|
|
],
|
|
),
|
|
// Padding(
|
|
// padding: const EdgeInsets.only(top: 10.0),
|
|
// child: NumberFormField(
|
|
// controller:
|
|
// _ingredients[index].amount,
|
|
// label: 'Amount',
|
|
// suffix: Settings.nutritionMeasurementSuffix,
|
|
// min: 0,
|
|
// onChanged: (value) {
|
|
// if (value != null && value >= 0) {
|
|
// setState(() {
|
|
// _ingredients[index].amount = value.toDouble();
|
|
// });
|
|
// }
|
|
// },
|
|
// ),
|
|
// ),
|
|
],
|
|
),
|
|
);
|
|
}).toList(),
|
|
)
|
|
: Center(
|
|
child: Text(_isNew
|
|
? 'Save the Recipe in order to add ingredients!'
|
|
: 'You have not added any Ingredients yet!'),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
bottomNavigationBar: DetailBottomRow(
|
|
onCancel: handleCancelAction,
|
|
onAction: _isSaving ? null : handleSaveAction,
|
|
onMiddleAction: _isSaving ? null : () => handleSaveAction(close: true),
|
|
),
|
|
);
|
|
}
|
|
}
|