diameter/lib/screens/recipe/recipe_detail.dart

323 lines
12 KiB
Dart
Raw Normal View History

2022-03-21 00:07:29 +00:00
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/ingredient.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/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),
),
);
}
}