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
This commit is contained in:
Roland Geider
2021-08-25 21:33:30 +02:00
parent 5b5feca8b8
commit db976d0486
11 changed files with 239 additions and 4 deletions

View File

@@ -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"

View File

@@ -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(),

View File

@@ -15,7 +15,8 @@ WeightEntry _$WeightEntryFromJson(Map<String, dynamic> json) {
);
}
Map<String, dynamic> _$WeightEntryToJson(WeightEntry instance) => <String, dynamic>{
Map<String, dynamic> _$WeightEntryToJson(WeightEntry instance) =>
<String, dynamic>{
'id': instance.id,
'weight': numToString(instance.weight),
'date': toDate(instance.date),

View File

@@ -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: '')

View File

@@ -13,7 +13,7 @@ MeasurementEntry _$MeasurementEntryFromJson(Map<String, dynamic> 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<String, dynamic> _$MeasurementEntryToJson(MeasurementEntry instance) =>
'id': instance.id,
'category': instance.category,
'date': toDate(instance.date),
'value': instance.value,
'value': numToString(instance.value),
'notes': instance.notes,
};

View File

@@ -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<DashboardScreen> {
final exercisesProvider = Provider.of<ExercisesProvider>(context, listen: false);
final galleryProvider = Provider.of<GalleryProvider>(context, listen: false);
final weightProvider = Provider.of<BodyWeightProvider>(context, listen: false);
final measurementProvider = Provider.of<MeasurementProvider>(context, listen: false);
// Base data
await Future.wait([
@@ -72,6 +74,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
nutritionPlansProvider.fetchAndSetAllPlansSparse(),
workoutPlansProvider.fetchAndSetAllPlansSparse(),
weightProvider.fetchAndSetEntries(),
measurementProvider.fetchAndSetCategories(),
]);
// Current nutritional plan

View File

@@ -0,0 +1,49 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* 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 <http://www.gnu.org/licenses/>.
*/
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(),
);
}
}

View File

@@ -0,0 +1,49 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* 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 <http://www.gnu.org/licenses/>.
*/
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(),
);
}
}

View File

@@ -0,0 +1,50 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* 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 <http://www.gnu.org/licenses/>.
*/
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<MeasurementProvider>(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),
),
);
},
);
}
}

View File

@@ -0,0 +1,64 @@
/*
* This file is part of wger Workout Manager <https://github.com/wger-project>.
* 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 <http://www.gnu.org/licenses/>.
*/
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<void> _loadEntries(BuildContext context) async {
final provider = Provider.of<MeasurementProvider>(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),
),
),
);
},
),
);
}
}

View File

@@ -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),