From db976d048683337cedf8b20ba3a3bcb3a180216f Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Wed, 25 Aug 2021 21:33:30 +0200 Subject: [PATCH] Add measurement category and entry overviews This is still a work in progress, besides the UX it's not possible yet to edit or add entries --- lib/l10n/app_en.arb | 8 +++ lib/main.dart | 4 ++ lib/models/body_weight/weight_entry.g.dart | 3 +- .../measurements/measurement_entry.dart | 2 +- .../measurements/measurement_entry.g.dart | 4 +- lib/screens/dashboard.dart | 3 + .../measurement_categories_screen.dart | 49 ++++++++++++++ lib/screens/measurement_entries_screen.dart | 49 ++++++++++++++ lib/widgets/measurements/categories.dart | 50 +++++++++++++++ lib/widgets/measurements/entries.dart | 64 +++++++++++++++++++ lib/widgets/weight/entries_list.dart | 7 ++ 11 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 lib/screens/measurement_categories_screen.dart create mode 100644 lib/screens/measurement_entries_screen.dart create mode 100644 lib/widgets/measurements/categories.dart create mode 100644 lib/widgets/measurements/entries.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 08482872..87dd9bac 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -258,6 +258,14 @@ "@weight": { "description": "The weight of a workout log or body weight entry" }, + "measurementCategories": "Measurement categories", + "@measurementCategories": { + "description": "Categories for the measurements such as biceps size, body fat, etc." + }, + "measurementEntries": "Measurement entries", + "@measurementEntries": { + "description": "Specific entries for the measurements" + }, "date": "Date", "@date": { "description": "The date of a workout log or body weight entry" diff --git a/lib/main.dart b/lib/main.dart index 852bc866..1db0d2f1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -31,6 +31,8 @@ import 'package:wger/screens/form_screen.dart'; import 'package:wger/screens/gallery_screen.dart'; import 'package:wger/screens/gym_mode.dart'; import 'package:wger/screens/home_tabs_screen.dart'; +import 'package:wger/screens/measurement_categories_screen.dart'; +import 'package:wger/screens/measurement_entries_screen.dart'; import 'package:wger/screens/nutritional_diary_screen.dart'; import 'package:wger/screens/nutritional_plan_screen.dart'; import 'package:wger/screens/nutritional_plans_screen.dart'; @@ -118,6 +120,8 @@ class MyApp extends StatelessWidget { GalleryScreen.routeName: (ctx) => GalleryScreen(), GymModeScreen.routeName: (ctx) => GymModeScreen(), HomeTabsScreen.routeName: (ctx) => HomeTabsScreen(), + MeasurementCategoriesScreen.routeName: (ctx) => MeasurementCategoriesScreen(), + MeasurementEntriesScreen.routeName: (ctx) => MeasurementEntriesScreen(), NutritionScreen.routeName: (ctx) => NutritionScreen(), NutritionalDiaryScreen.routeName: (ctx) => NutritionalDiaryScreen(), NutritionalPlanScreen.routeName: (ctx) => NutritionalPlanScreen(), diff --git a/lib/models/body_weight/weight_entry.g.dart b/lib/models/body_weight/weight_entry.g.dart index 98ab3eda..e4ac9e9b 100644 --- a/lib/models/body_weight/weight_entry.g.dart +++ b/lib/models/body_weight/weight_entry.g.dart @@ -15,7 +15,8 @@ WeightEntry _$WeightEntryFromJson(Map json) { ); } -Map _$WeightEntryToJson(WeightEntry instance) => { +Map _$WeightEntryToJson(WeightEntry instance) => + { 'id': instance.id, 'weight': numToString(instance.weight), 'date': toDate(instance.date), diff --git a/lib/models/measurements/measurement_entry.dart b/lib/models/measurements/measurement_entry.dart index 67418aa6..5949da83 100644 --- a/lib/models/measurements/measurement_entry.dart +++ b/lib/models/measurements/measurement_entry.dart @@ -14,7 +14,7 @@ class MeasurementEntry { @JsonKey(required: true, toJson: toDate) final DateTime date; - @JsonKey(required: true) + @JsonKey(required: true, fromJson: stringToNum, toJson: numToString) final num value; @JsonKey(required: true, defaultValue: '') diff --git a/lib/models/measurements/measurement_entry.g.dart b/lib/models/measurements/measurement_entry.g.dart index 1950d7a4..2c0437da 100644 --- a/lib/models/measurements/measurement_entry.g.dart +++ b/lib/models/measurements/measurement_entry.g.dart @@ -13,7 +13,7 @@ MeasurementEntry _$MeasurementEntryFromJson(Map json) { id: json['id'] as int, category: json['category'] as int, date: DateTime.parse(json['date'] as String), - value: json['value'] as num, + value: stringToNum(json['value'] as String?), notes: json['notes'] ?? '', ); } @@ -23,6 +23,6 @@ Map _$MeasurementEntryToJson(MeasurementEntry instance) => 'id': instance.id, 'category': instance.category, 'date': toDate(instance.date), - 'value': instance.value, + 'value': numToString(instance.value), 'notes': instance.notes, }; diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 8e23ec7b..385283dc 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -24,6 +24,7 @@ import 'package:wger/providers/auth.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/gallery.dart'; +import 'package:wger/providers/measurements.dart'; import 'package:wger/providers/nutrition.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/widgets/core/app_bar.dart'; @@ -58,6 +59,7 @@ class _DashboardScreenState extends State { final exercisesProvider = Provider.of(context, listen: false); final galleryProvider = Provider.of(context, listen: false); final weightProvider = Provider.of(context, listen: false); + final measurementProvider = Provider.of(context, listen: false); // Base data await Future.wait([ @@ -72,6 +74,7 @@ class _DashboardScreenState extends State { nutritionPlansProvider.fetchAndSetAllPlansSparse(), workoutPlansProvider.fetchAndSetAllPlansSparse(), weightProvider.fetchAndSetEntries(), + measurementProvider.fetchAndSetCategories(), ]); // Current nutritional plan diff --git a/lib/screens/measurement_categories_screen.dart b/lib/screens/measurement_categories_screen.dart new file mode 100644 index 00000000..1139ca80 --- /dev/null +++ b/lib/screens/measurement_categories_screen.dart @@ -0,0 +1,49 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 2021 wger Team + * + * wger Workout Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wger Workout Manager is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/screens/form_screen.dart'; +import 'package:wger/widgets/core/app_bar.dart'; +import 'package:wger/widgets/measurements/categories.dart'; +import 'package:wger/widgets/weight/forms.dart'; + +class MeasurementCategoriesScreen extends StatelessWidget { + static const routeName = '/measurement-categories'; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: WgerAppBar(AppLocalizations.of(context).measurementCategories), + floatingActionButton: FloatingActionButton( + child: const Icon(Icons.add), + onPressed: () async { + Navigator.pushNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).newEntry, + WeightForm(), + ), + ); + }, + ), + body: CategoriesList(), + ); + } +} diff --git a/lib/screens/measurement_entries_screen.dart b/lib/screens/measurement_entries_screen.dart new file mode 100644 index 00000000..0780f992 --- /dev/null +++ b/lib/screens/measurement_entries_screen.dart @@ -0,0 +1,49 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 2021 wger Team + * + * wger Workout Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wger Workout Manager is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:wger/screens/form_screen.dart'; +import 'package:wger/widgets/core/app_bar.dart'; +import 'package:wger/widgets/measurements/entries.dart'; +import 'package:wger/widgets/weight/forms.dart'; + +class MeasurementEntriesScreen extends StatelessWidget { + static const routeName = '/measurement-entries'; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: WgerAppBar(AppLocalizations.of(context).measurementEntries), + floatingActionButton: FloatingActionButton( + child: const Icon(Icons.add), + onPressed: () async { + Navigator.pushNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).newEntry, + WeightForm(), + ), + ); + }, + ), + body: EntriesList(), + ); + } +} diff --git a/lib/widgets/measurements/categories.dart b/lib/widgets/measurements/categories.dart new file mode 100644 index 00000000..8aa05664 --- /dev/null +++ b/lib/widgets/measurements/categories.dart @@ -0,0 +1,50 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 2021 wger Team + * + * wger Workout Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wger Workout Manager is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:wger/providers/measurements.dart'; +import 'package:wger/screens/measurement_entries_screen.dart'; + +class CategoriesList extends StatelessWidget { + @override + Widget build(BuildContext context) { + final _provider = Provider.of(context, listen: false); + + return ListView.builder( + padding: const EdgeInsets.all(10.0), + itemCount: _provider.categories.length, + itemBuilder: (context, index) { + final currentCategory = _provider.categories[index]; + return Card( + child: ListTile( + onTap: () { + Navigator.pushNamed( + context, + MeasurementEntriesScreen.routeName, + arguments: currentCategory.id, + ); + }, + title: Text(currentCategory.name), + subtitle: Text(currentCategory.unit), + ), + ); + }, + ); + } +} diff --git a/lib/widgets/measurements/entries.dart b/lib/widgets/measurements/entries.dart new file mode 100644 index 00000000..d66884b5 --- /dev/null +++ b/lib/widgets/measurements/entries.dart @@ -0,0 +1,64 @@ +/* + * This file is part of wger Workout Manager . + * Copyright (C) 2020, 2021 wger Team + * + * wger Workout Manager is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * wger Workout Manager is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import 'package:wger/models/measurements/measurement_category.dart'; +import 'package:wger/providers/measurements.dart'; + +class EntriesList extends StatelessWidget { + late MeasurementCategory _category; + + Future _loadEntries(BuildContext context) async { + final provider = Provider.of(context, listen: false); + final int categoryId = ModalRoute.of(context)!.settings.arguments as int; + _category = await provider.fetchAndSetCategoryEntries(categoryId); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _loadEntries(context), + builder: (ctx, authResultSnapshot) => + authResultSnapshot.connectionState == ConnectionState.waiting + ? Container( + height: 200, + child: Center( + child: CircularProgressIndicator(), + ), + ) + : ListView.builder( + padding: const EdgeInsets.all(10.0), + itemCount: _category.entries.length, + itemBuilder: (context, index) { + final currentEntry = _category.entries[index]; + return Card( + child: ListTile( + title: Text(currentEntry.value.toString()), + subtitle: Text( + DateFormat.yMd(Localizations.localeOf(context).languageCode) + .format(currentEntry.date), + ), + ), + ); + }, + ), + ); + } +} diff --git a/lib/widgets/weight/entries_list.dart b/lib/widgets/weight/entries_list.dart index 15edfb70..7e5f9dc5 100644 --- a/lib/widgets/weight/entries_list.dart +++ b/lib/widgets/weight/entries_list.dart @@ -22,6 +22,7 @@ import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:wger/providers/body_weight.dart'; import 'package:wger/screens/form_screen.dart'; +import 'package:wger/screens/measurement_categories_screen.dart'; import 'package:wger/widgets/weight/charts.dart'; import 'package:wger/widgets/weight/forms.dart'; @@ -39,6 +40,12 @@ class WeightEntriesList extends StatelessWidget { _weightProvider.items.map((e) => MeasurementChartEntry(e.weight, e.date)).toList()), ), Divider(), + TextButton( + onPressed: () => Navigator.pushNamed( + context, + MeasurementCategoriesScreen.routeName, + ), + child: Text('Go to measurements')), Expanded( child: ListView.builder( padding: const EdgeInsets.all(10.0),