Allow user to give meals a description #70

Add name field to meal form
This commit is contained in:
Thilina Herath
2021-09-30 21:22:28 +08:00
parent aed4d083ae
commit d6eb3deda7
6 changed files with 56 additions and 32 deletions

View File

@@ -36,6 +36,9 @@ class Meal {
@JsonKey(toJson: timeToString, fromJson: stringToTime)
TimeOfDay? time;
@JsonKey(name: 'name')
late String name;
@JsonKey(ignore: true, name: 'meal_items', defaultValue: [])
List<MealItem> mealItems = [];
@@ -43,6 +46,7 @@ class Meal {
this.id,
int? plan,
TimeOfDay? time,
String? name,
List<MealItem>? mealItems,
}) {
if (plan != null) {
@@ -52,10 +56,12 @@ class Meal {
this.mealItems = mealItems ?? [];
this.time = time ?? TimeOfDay.now();
this.name = name ?? '';
}
// Boilerplate
factory Meal.fromJson(Map<String, dynamic> json) => _$MealFromJson(json);
Map<String, dynamic> toJson() => _$MealToJson(this);
/// Calculations

View File

@@ -10,6 +10,7 @@ Meal _$MealFromJson(Map<String, dynamic> json) {
return Meal(
id: json['id'] as int?,
time: stringToTime(json['time'] as String?),
name: json['name'] as String?,
)..planId = json['plan'] as int;
}
@@ -17,4 +18,5 @@ Map<String, dynamic> _$MealToJson(Meal instance) => <String, dynamic>{
'id': instance.id,
'plan': instance.planId,
'time': timeToString(instance.time),
'name': instance.name,
};

View File

@@ -20,10 +20,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:provider/provider.dart';
import 'package:wger/exceptions/http_exception.dart';
import 'package:wger/helpers/consts.dart';
import 'package:wger/helpers/json.dart';
import 'package:wger/helpers/ui.dart';
import 'package:wger/exceptions/http_exception.dart';
import 'package:wger/models/nutrition/meal.dart';
import 'package:wger/models/nutrition/meal_item.dart';
import 'package:wger/models/nutrition/nutritional_plan.dart';
@@ -36,10 +36,12 @@ class MealForm extends StatelessWidget {
final _form = GlobalKey<FormState>();
final _timeController = TextEditingController();
final _nameController = TextEditingController();
MealForm(this._planId, [meal]) {
this._meal = meal ?? Meal(plan: _planId);
_timeController.text = timeToString(_meal.time)!;
_nameController.text = _meal.name;
}
@override
@@ -71,6 +73,16 @@ class MealForm extends StatelessWidget {
},
onFieldSubmitted: (_) {},
),
TextFormField(
maxLength: 25,
key: Key('field-name'),
decoration: InputDecoration(labelText: AppLocalizations.of(context).name),
controller: _nameController,
onSaved: (newValue) {
_meal.name = newValue as String;
},
onFieldSubmitted: (_) {},
),
ElevatedButton(
key: Key(SUBMIT_BUTTON_KEY_NAME),
child: Text(AppLocalizations.of(context).save),
@@ -82,8 +94,7 @@ class MealForm extends StatelessWidget {
try {
_meal.id == null
? Provider.of<NutritionPlansProvider>(context, listen: false)
.addMeal(_meal, _planId)
? Provider.of<NutritionPlansProvider>(context, listen: false).addMeal(_meal, _planId)
: Provider.of<NutritionPlansProvider>(context, listen: false).editMeal(_meal);
} on WgerHttpException catch (error) {
showHttpExceptionErrorDialog(error, context);
@@ -127,8 +138,7 @@ class MealItemForm extends StatelessWidget {
decoration: InputDecoration(labelText: AppLocalizations.of(context).ingredient),
),
suggestionsCallback: (pattern) async {
return await Provider.of<NutritionPlansProvider>(context, listen: false)
.searchIngredient(
return await Provider.of<NutritionPlansProvider>(context, listen: false).searchIngredient(
pattern,
Localizations.localeOf(context).languageCode,
);
@@ -179,8 +189,7 @@ class MealItemForm extends StatelessWidget {
_form.currentState!.save();
try {
Provider.of<NutritionPlansProvider>(context, listen: false)
.addMealItem(_mealItem, _meal);
Provider.of<NutritionPlansProvider>(context, listen: false).addMealItem(_mealItem, _meal);
} on WgerHttpException catch (error) {
showHttpExceptionErrorDialog(error, context);
} catch (error) {
@@ -239,8 +248,7 @@ class PlanForm extends StatelessWidget {
await Provider.of<NutritionPlansProvider>(context, listen: false).editPlan(_plan);
Navigator.of(context).pop();
} else {
_plan = await Provider.of<NutritionPlansProvider>(context, listen: false)
.addPlan(_plan);
_plan = await Provider.of<NutritionPlansProvider>(context, listen: false).addPlan(_plan);
Navigator.of(context).pushReplacementNamed(
NutritionalPlanScreen.routeName,
arguments: _plan,

View File

@@ -41,6 +41,7 @@ class MealWidget extends StatefulWidget {
class _MealWidgetState extends State<MealWidget> {
bool _expanded = false;
void _toggleExpanded() {
setState(() {
_expanded = !_expanded;
@@ -63,8 +64,7 @@ class _MealWidgetState extends State<MealWidget> {
IconButton(
onPressed: () {
// Delete the meal
Provider.of<NutritionPlansProvider>(context, listen: false)
.deleteMeal(widget._meal);
Provider.of<NutritionPlansProvider>(context, listen: false).deleteMeal(widget._meal);
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
@@ -88,8 +88,7 @@ class _MealWidgetState extends State<MealWidget> {
icon: const Icon(Icons.history_edu),
color: Colors.white,
onPressed: () {
Provider.of<NutritionPlansProvider>(context, listen: false)
.logMealToDiary(widget._meal);
Provider.of<NutritionPlansProvider>(context, listen: false).logMealToDiary(widget._meal);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
@@ -178,8 +177,7 @@ class MealItemWidget extends StatelessWidget {
iconSize: ICON_SIZE_SMALL,
onPressed: () {
// Delete the meal item
Provider.of<NutritionPlansProvider>(context, listen: false)
.deleteMealItem(_item);
Provider.of<NutritionPlansProvider>(context, listen: false).deleteMealItem(_item);
// and inform the user
ScaffoldMessenger.of(context).showSnackBar(
@@ -221,20 +219,30 @@ class DismissibleMealHeader extends StatelessWidget {
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(color: Colors.white),
child: Row(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
_meal.time!.format(context),
if (_meal.name != '')
Text(
_meal.name,
style: Theme.of(context).textTheme.headline5,
),
),
IconButton(
visualDensity: VisualDensity.compact,
icon: _expanded ? Icon(Icons.unfold_less) : Icon(Icons.unfold_more),
onPressed: () {
_toggle();
},
Row(
children: [
Expanded(
child: Text(
_meal.time!.format(context),
style: Theme.of(context).textTheme.headline5,
),
),
IconButton(
visualDensity: VisualDensity.compact,
icon: _expanded ? Icon(Icons.unfold_less) : Icon(Icons.unfold_more),
onPressed: () {
_toggle();
},
),
],
),
],
),

View File

@@ -14,7 +14,7 @@ packages:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.2"
version: "1.7.1"
android_metadata:
dependency: "direct main"
description:
@@ -42,7 +42,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.1"
version: "2.6.1"
boolean_selector:
dependency: transitive
description:
@@ -133,7 +133,7 @@ packages:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
version: "1.2.0"
charts_common:
dependency: transitive
description:
@@ -510,7 +510,7 @@ packages:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
version: "1.3.0"
mime:
dependency: transitive
description:
@@ -802,7 +802,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
version: "0.3.0"
timing:
dependency: transitive
description:

View File

@@ -72,7 +72,7 @@ void main() {
await tester.pumpWidget(createHomeScreen(meal1));
await tester.pumpAndSettle();
expect(find.byType(TextFormField), findsOneWidget);
expect(find.byType(TextFormField), findsNWidgets(2));
expect(find.byType(ElevatedButton), findsOneWidget);
expect(find.byKey(Key(SUBMIT_BUTTON_KEY_NAME)), findsOneWidget);
});