mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Allow users to quickly increment the body weight
This commit is contained in:
@@ -101,7 +101,7 @@ void showHttpExceptionErrorDialog(WgerHttpException exception, BuildContext cont
|
||||
|
||||
// This call serves no purpose The dialog above doesn't seem to show
|
||||
// unless this dummy call is present
|
||||
// showDialog(context: context, builder: (context) => Container());
|
||||
showDialog(context: context, builder: (context) => Container());
|
||||
}
|
||||
|
||||
dynamic showDeleteDialog(
|
||||
|
||||
@@ -27,7 +27,7 @@ class WeightEntry {
|
||||
int? id;
|
||||
|
||||
@JsonKey(required: true, fromJson: stringToNum, toJson: numToString)
|
||||
late num weight;
|
||||
late num weight = 0;
|
||||
|
||||
@JsonKey(required: true, toJson: toDate)
|
||||
late DateTime date;
|
||||
@@ -44,7 +44,14 @@ class WeightEntry {
|
||||
}
|
||||
}
|
||||
|
||||
WeightEntry copyWith({int? id, int? weight, DateTime? date}) => WeightEntry(
|
||||
id: id,
|
||||
weight: weight ?? this.weight,
|
||||
date: date ?? this.date,
|
||||
);
|
||||
|
||||
// Boilerplate
|
||||
factory WeightEntry.fromJson(Map<String, dynamic> json) => _$WeightEntryFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$WeightEntryToJson(this);
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ class BodyWeightProvider with ChangeNotifier {
|
||||
_entries = [];
|
||||
}
|
||||
|
||||
/// Returns the latest (newest) weight entry or null if there are no entries
|
||||
WeightEntry? getLastEntry() {
|
||||
return _entries.isNotEmpty ? _entries.last : null;
|
||||
/// Returns the latest (newest) weight entry or null if there are none
|
||||
WeightEntry? getNewestEntry() {
|
||||
return _entries.isNotEmpty ? _entries.first : null;
|
||||
}
|
||||
|
||||
WeightEntry findById(int id) {
|
||||
|
||||
@@ -30,6 +30,8 @@ class WeightScreen extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final lastWeightEntry = context.read<BodyWeightProvider>().getNewestEntry();
|
||||
|
||||
return Scaffold(
|
||||
appBar: EmptyAppBar(AppLocalizations.of(context).weight),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
@@ -43,7 +45,7 @@ class WeightScreen extends StatelessWidget {
|
||||
FormScreen.routeName,
|
||||
arguments: FormScreenArguments(
|
||||
AppLocalizations.of(context).newEntry,
|
||||
WeightForm(),
|
||||
WeightForm(lastWeightEntry?.copyWith(id: null)),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -42,7 +42,8 @@ class NutritionalPlanDetailWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final nutritionalValues = _nutritionalPlan.nutritionalValues;
|
||||
final valuesPercentage = _nutritionalPlan.energyPercentage(nutritionalValues);
|
||||
final lastWeightEntry = Provider.of<BodyWeightProvider>(context, listen: false).getLastEntry();
|
||||
final lastWeightEntry =
|
||||
Provider.of<BodyWeightProvider>(context, listen: false).getNewestEntry();
|
||||
final valuesGperKg = lastWeightEntry != null
|
||||
? _nutritionalPlan.gPerBodyKg(lastWeightEntry.weight, nutritionalValues)
|
||||
: null;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wger/exceptions/http_exception.dart';
|
||||
import 'package:wger/helpers/json.dart';
|
||||
@@ -34,7 +35,7 @@ class WeightForm extends StatelessWidget {
|
||||
|
||||
WeightForm([WeightEntry? weightEntry]) {
|
||||
_weightEntry = weightEntry ?? WeightEntry(date: DateTime.now());
|
||||
weightController.text = _weightEntry.id == null ? '' : _weightEntry.weight.toString();
|
||||
weightController.text = _weightEntry.weight == 0 ? '' : _weightEntry.weight.toString();
|
||||
dateController.text = toDate(_weightEntry.date)!;
|
||||
}
|
||||
|
||||
@@ -46,10 +47,15 @@ class WeightForm extends StatelessWidget {
|
||||
children: [
|
||||
// Weight date
|
||||
TextFormField(
|
||||
readOnly: true, // Stop keyboard from appearing
|
||||
key: const Key('dateInput'),
|
||||
readOnly: true,
|
||||
// Stop keyboard from appearing
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).date,
|
||||
suffixIcon: const Icon(Icons.calendar_today),
|
||||
suffixIcon: const Icon(
|
||||
Icons.calendar_today,
|
||||
key: Key('calendarIcon'),
|
||||
),
|
||||
),
|
||||
enableInteractiveSelection: false,
|
||||
controller: dateController,
|
||||
@@ -83,7 +89,60 @@ class WeightForm extends StatelessWidget {
|
||||
|
||||
// Weight
|
||||
TextFormField(
|
||||
decoration: InputDecoration(labelText: AppLocalizations.of(context).weight),
|
||||
key: const Key('weightInput'),
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context).weight,
|
||||
prefix: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
key: const Key('quickMinus'),
|
||||
icon: const FaIcon(FontAwesomeIcons.circleMinus),
|
||||
onPressed: () {
|
||||
try {
|
||||
final num newValue = num.parse(weightController.text) - 1;
|
||||
weightController.text = newValue.toString();
|
||||
} on FormatException {}
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
key: const Key('quickMinusSmall'),
|
||||
icon: const FaIcon(FontAwesomeIcons.minus),
|
||||
onPressed: () {
|
||||
try {
|
||||
final num newValue = num.parse(weightController.text) - 0.25;
|
||||
weightController.text = newValue.toString();
|
||||
} on FormatException {}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
suffix: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
key: const Key('quickPlusSmall'),
|
||||
icon: const FaIcon(FontAwesomeIcons.plus),
|
||||
onPressed: () {
|
||||
try {
|
||||
final num newValue = num.parse(weightController.text) + 0.25;
|
||||
weightController.text = newValue.toString();
|
||||
} on FormatException {}
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
key: const Key('quickPlus'),
|
||||
icon: const FaIcon(FontAwesomeIcons.circlePlus),
|
||||
onPressed: () {
|
||||
try {
|
||||
final num newValue = num.parse(weightController.text) + 1;
|
||||
weightController.text = newValue.toString();
|
||||
} on FormatException {}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
controller: weightController,
|
||||
keyboardType: TextInputType.number,
|
||||
onSaved: (newValue) {
|
||||
@@ -113,11 +172,10 @@ class WeightForm extends StatelessWidget {
|
||||
|
||||
// Save the entry on the server
|
||||
try {
|
||||
final provider = Provider.of<BodyWeightProvider>(context, listen: false);
|
||||
_weightEntry.id == null
|
||||
? await Provider.of<BodyWeightProvider>(context, listen: false)
|
||||
.addEntry(_weightEntry)
|
||||
: await Provider.of<BodyWeightProvider>(context, listen: false)
|
||||
.editEntry(_weightEntry);
|
||||
? await provider.addEntry(_weightEntry)
|
||||
: await provider.editEntry(_weightEntry);
|
||||
} on WgerHttpException catch (error) {
|
||||
if (context.mounted) {
|
||||
showHttpExceptionErrorDialog(error, context);
|
||||
|
||||
60
pubspec.lock
60
pubspec.lock
@@ -341,10 +341,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
|
||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
version: "7.0.0"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -764,6 +764,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.7.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -808,26 +832,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.8.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
||||
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
version: "1.11.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -896,10 +920,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
version: "1.9.0"
|
||||
path_parsing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -968,10 +992,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
|
||||
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
version: "3.1.4"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1032,10 +1056,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
|
||||
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.4"
|
||||
version: "5.0.2"
|
||||
provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1461,10 +1485,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
|
||||
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.10.0"
|
||||
version: "13.0.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1493,10 +1517,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webdriver
|
||||
sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49"
|
||||
sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.0.3"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
86
test/weight/weight_form_test.dart
Normal file
86
test/weight/weight_form_test.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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:flutter_test/flutter_test.dart';
|
||||
import 'package:wger/models/body_weight/weight_entry.dart';
|
||||
import 'package:wger/widgets/weight/forms.dart';
|
||||
|
||||
import '../../test_data/body_weight.dart';
|
||||
|
||||
void main() {
|
||||
Widget createWeightForm({locale = 'en', weightEntry = WeightEntry}) {
|
||||
return MaterialApp(
|
||||
locale: Locale(locale),
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: Scaffold(
|
||||
body: WeightForm(weightEntry),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('The form is prefilled with the data from an entry', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(createWeightForm(weightEntry: testWeightEntry1));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('2021-01-01'), findsOneWidget);
|
||||
expect(find.text('80'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('It is possible to quick-change the weight', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(createWeightForm(weightEntry: testWeightEntry1));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('quickMinus')));
|
||||
expect(find.text('79'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('quickMinusSmall')));
|
||||
expect(find.text('78.75'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('quickPlus')));
|
||||
expect(find.text('79.75'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('quickPlusSmall')));
|
||||
expect(find.text('80.0'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets("Entering garbage doesn't break the quick-change", (WidgetTester tester) async {
|
||||
await tester.pumpWidget(createWeightForm(weightEntry: testWeightEntry1));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.byKey(const Key('weightInput')), 'shiba inu');
|
||||
|
||||
await tester.tap(find.byKey(const Key('quickMinus')));
|
||||
expect(find.text('shiba inu'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('quickMinusSmall')));
|
||||
expect(find.text('shiba inu'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('quickPlus')));
|
||||
expect(find.text('shiba inu'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byKey(const Key('quickPlusSmall')));
|
||||
expect(find.text('shiba inu'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Widget works if there is no last entry', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(createWeightForm(weightEntry: null));
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
}
|
||||
@@ -41,6 +41,7 @@ void main() {
|
||||
setUp(() {
|
||||
mockWeightProvider = MockBodyWeightProvider();
|
||||
when(mockWeightProvider.items).thenReturn(getWeightEntries());
|
||||
when(mockWeightProvider.getNewestEntry()).thenReturn(null);
|
||||
|
||||
mockUserProvider = MockUserProvider();
|
||||
when(mockUserProvider.profile).thenReturn(tProfile1);
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
import 'package:wger/models/body_weight/weight_entry.dart';
|
||||
|
||||
final weightEntry1 = WeightEntry(id: 1, weight: 80, date: DateTime(2021, 01, 01));
|
||||
final weightEntry2 = WeightEntry(id: 2, weight: 81, date: DateTime(2021, 01, 10));
|
||||
final testWeightEntry1 = WeightEntry(id: 1, weight: 80, date: DateTime(2021, 01, 01));
|
||||
final testWeightEntry2 = WeightEntry(id: 2, weight: 81, date: DateTime(2021, 01, 10));
|
||||
|
||||
List<WeightEntry> getWeightEntries() {
|
||||
return [weightEntry1, weightEntry2];
|
||||
return [testWeightEntry1, testWeightEntry2];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user