diff --git a/lib/models/nutrition/meal.dart b/lib/models/nutrition/meal.dart index 646d6adb..8a6822fa 100644 --- a/lib/models/nutrition/meal.dart +++ b/lib/models/nutrition/meal.dart @@ -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 mealItems = []; @@ -43,6 +46,7 @@ class Meal { this.id, int? plan, TimeOfDay? time, + String? name, List? 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 json) => _$MealFromJson(json); + Map toJson() => _$MealToJson(this); /// Calculations diff --git a/lib/models/nutrition/meal.g.dart b/lib/models/nutrition/meal.g.dart index 20ced4e7..3d079787 100644 --- a/lib/models/nutrition/meal.g.dart +++ b/lib/models/nutrition/meal.g.dart @@ -10,6 +10,7 @@ Meal _$MealFromJson(Map 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 _$MealToJson(Meal instance) => { 'id': instance.id, 'plan': instance.planId, 'time': timeToString(instance.time), + 'name': instance.name, }; diff --git a/lib/widgets/nutrition/forms.dart b/lib/widgets/nutrition/forms.dart index 9a550a2a..64962c13 100644 --- a/lib/widgets/nutrition/forms.dart +++ b/lib/widgets/nutrition/forms.dart @@ -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(); 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(context, listen: false) - .addMeal(_meal, _planId) + ? Provider.of(context, listen: false).addMeal(_meal, _planId) : Provider.of(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(context, listen: false) - .searchIngredient( + return await Provider.of(context, listen: false).searchIngredient( pattern, Localizations.localeOf(context).languageCode, ); @@ -179,8 +189,7 @@ class MealItemForm extends StatelessWidget { _form.currentState!.save(); try { - Provider.of(context, listen: false) - .addMealItem(_mealItem, _meal); + Provider.of(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(context, listen: false).editPlan(_plan); Navigator.of(context).pop(); } else { - _plan = await Provider.of(context, listen: false) - .addPlan(_plan); + _plan = await Provider.of(context, listen: false).addPlan(_plan); Navigator.of(context).pushReplacementNamed( NutritionalPlanScreen.routeName, arguments: _plan, diff --git a/lib/widgets/nutrition/meal.dart b/lib/widgets/nutrition/meal.dart index 46751c5c..f4854478 100644 --- a/lib/widgets/nutrition/meal.dart +++ b/lib/widgets/nutrition/meal.dart @@ -41,6 +41,7 @@ class MealWidget extends StatefulWidget { class _MealWidgetState extends State { bool _expanded = false; + void _toggleExpanded() { setState(() { _expanded = !_expanded; @@ -63,8 +64,7 @@ class _MealWidgetState extends State { IconButton( onPressed: () { // Delete the meal - Provider.of(context, listen: false) - .deleteMeal(widget._meal); + Provider.of(context, listen: false).deleteMeal(widget._meal); // and inform the user ScaffoldMessenger.of(context).showSnackBar( @@ -88,8 +88,7 @@ class _MealWidgetState extends State { icon: const Icon(Icons.history_edu), color: Colors.white, onPressed: () { - Provider.of(context, listen: false) - .logMealToDiary(widget._meal); + Provider.of(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(context, listen: false) - .deleteMealItem(_item); + Provider.of(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(); + }, + ), + ], ), ], ), diff --git a/pubspec.lock b/pubspec.lock index 1ff5c47a..663dccf3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -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: diff --git a/test/nutritional_meal_form_test.dart b/test/nutritional_meal_form_test.dart index 699e0002..931b5d7b 100644 --- a/test/nutritional_meal_form_test.dart +++ b/test/nutritional_meal_form_test.dart @@ -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); });