Commit 527f442
Changed files (13)
app
lib
features
measurement_list
l10n
model
screens
test
features
measurement_list
app/lib/features/input/add_bodyweight_dialoge.dart
@@ -1,5 +1,7 @@
+import 'package:blood_pressure_app/model/storage/settings_store.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:health_data_store/health_data_store.dart';
@@ -18,7 +20,7 @@ class AddBodyweightDialoge extends StatelessWidget {
autofocus: true,
decoration: InputDecoration(
labelText: AppLocalizations.of(context)!.weight,
- suffix: const Text('kg'),
+ suffix: Text(context.select((Settings s) => s.weightUnit).name),
),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9,.]'))],
keyboardType: const TextInputType.numberWithOptions(decimal: true),
@@ -32,9 +34,12 @@ class AddBodyweightDialoge extends StatelessWidget {
}
return null;
},
- onFieldSubmitted: (text) {
+ onFieldSubmitted: (String text) {
final value = double.tryParse(text);
- if (value != null) Navigator.of(context).pop(Weight.kg(value));
+ if (value != null) {
+ final weight = context.read<Settings>().weightUnit.store(value);
+ Navigator.of(context).pop(weight);
+ }
},
),
),
app/lib/features/measurement_list/weight_list.dart
@@ -1,6 +1,7 @@
import 'package:blood_pressure_app/components/confirm_deletion_dialoge.dart';
import 'package:blood_pressure_app/data_util/repository_builder.dart';
import 'package:blood_pressure_app/model/storage/storage.dart';
+import 'package:blood_pressure_app/model/weight_unit.dart';
import 'package:flutter/material.dart';
import 'package:health_data_store/health_data_store.dart';
import 'package:intl/intl.dart';
@@ -17,6 +18,7 @@ class WeightList extends StatelessWidget {
@override
Widget build(BuildContext context) {
final format = DateFormat(context.select<Settings, String>((s) => s.dateFormatString));
+ final weightUnit = context.select((Settings s) => s.weightUnit);
return RepositoryBuilder<BodyweightRecord, BodyweightRepository>(
rangeType: rangeType,
onData: (context, records) {
@@ -24,7 +26,7 @@ class WeightList extends StatelessWidget {
return ListView.builder(
itemCount: records.length,
itemBuilder: (context, idx) => ListTile(
- title: Text(_buildWeightText(records[idx].weight)),
+ title: Text(_buildWeightText(weightUnit, records[idx].weight)),
subtitle: Text(format.format(records[idx].time)),
trailing: IconButton(
icon: const Icon(Icons.delete),
@@ -41,12 +43,12 @@ class WeightList extends StatelessWidget {
);
}
- String _buildWeightText(Weight w) {
- String weightStr = w.kg.toStringAsFixed(2);
+ String _buildWeightText(WeightUnit u, Weight w) {
+ String weightStr = u.extract(w).toStringAsFixed(2);
if (weightStr.endsWith('0')) weightStr = weightStr.substring(0, weightStr.length - 1);
if (weightStr.endsWith('0')) weightStr = weightStr.substring(0, weightStr.length - 1);
if (weightStr.endsWith('.')) weightStr = weightStr.substring(0, weightStr.length - 1);
- // TODO: preferred weight unit
- return '$weightStr kg';
+
+ return '$weightStr ${u.name}';
}
}
app/lib/l10n/app_en.arb
@@ -522,5 +522,9 @@
"weight": "Weight",
"@weight": {},
"enterWeight": "Enter weight",
- "@enterWeight": {}
+ "@enterWeight": {},
+ "preferredWeightUnit": "Preferred weight unit",
+ "@preferredWeightUnit": {
+ "description": "Setting for the unit the app will use for displaying weight"
+ }
}
\ No newline at end of file
app/lib/model/blood_pressure/pressure_unit.dart
@@ -1,4 +1,3 @@
-
import 'package:health_data_store/health_data_store.dart';
/// A unit blood pressure can be in.
@@ -19,11 +18,7 @@ enum PressureUnit {
static PressureUnit? decode(int? encoded) => switch(encoded) {
0 => PressureUnit.mmHg,
1 => PressureUnit.kPa,
- null => null,
- _ => (){
- assert(false);
- return null;
- }(),
+ _ => null,
};
/// Converts a value to a [Pressure] of this [PressureUnit].
app/lib/model/storage/settings_store.dart
@@ -6,6 +6,7 @@ import 'package:blood_pressure_app/model/blood_pressure/medicine/medicine.dart';
import 'package:blood_pressure_app/model/blood_pressure/pressure_unit.dart';
import 'package:blood_pressure_app/model/horizontal_graph_line.dart';
import 'package:blood_pressure_app/model/storage/convert_util.dart';
+import 'package:blood_pressure_app/model/weight_unit.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -50,6 +51,7 @@ class Settings extends ChangeNotifier {
int? highestMedIndex,
bool? bleInput,
bool? weightInput,
+ WeightUnit? weightUnit,
}) {
if (accentColor != null) _accentColor = accentColor;
if (sysColor != null) _sysColor = sysColor;
@@ -78,6 +80,7 @@ class Settings extends ChangeNotifier {
if (knownBleDev != null) _knownBleDev = knownBleDev;
if (bleInput != null) _bleInput = bleInput;
if (weightInput != null) _weightInput = weightInput;
+ if (weightUnit != null) _weightUnit = weightUnit;
_language = language; // No check here, as null is the default as well.
}
@@ -113,6 +116,8 @@ class Settings extends ChangeNotifier {
knownBleDev: ConvertUtil.parseList<String>(map['knownBleDev']),
bleInput: ConvertUtil.parseBool(map['bleInput']),
weightInput: ConvertUtil.parseBool(map['weightInput']),
+ preferredPressureUnit: PressureUnit.decode(ConvertUtil.parseInt(map['preferredPressureUnit'])),
+ weightUnit: WeightUnit.deserialize(ConvertUtil.parseInt(map['weightUnit'])),
);
// update
@@ -161,6 +166,7 @@ class Settings extends ChangeNotifier {
'knownBleDev': knownBleDev,
'bleInput': bleInput,
'weightInput': weightInput,
+ 'weightUnit': weightUnit.serialized,
};
/// Serialize the object to a restoreable string.
@@ -197,6 +203,7 @@ class Settings extends ChangeNotifier {
_medications.addAll(other._medications);
_highestMedIndex = other._highestMedIndex;
_weightInput = other._weightInput;
+ _weightUnit = other._weightUnit;
notifyListeners();
}
@@ -444,6 +451,14 @@ class Settings extends ChangeNotifier {
int _highestMedIndex = 0;
/// Total amount of medicines created.
int get highestMedIndex => _highestMedIndex;
+
+ WeightUnit _weightUnit = WeightUnit.kg;
+ /// Preferred unit for bodyweight.
+ WeightUnit get weightUnit => _weightUnit;
+ set weightUnit(WeightUnit value) {
+ _weightUnit = value;
+ notifyListeners();
+ }
// When adding fields notice the checklist at the top.
}
@@ -452,14 +467,9 @@ class Settings extends ChangeNotifier {
/// [ConvertUtil.parseThemeMode].
extension Serialization on ThemeMode {
/// Turns enum into a restoreable integer.
- int serialize() {
- switch(this) {
- case ThemeMode.system:
- return 0;
- case ThemeMode.dark:
- return 1;
- case ThemeMode.light:
- return 2;
- }
- }
+ int serialize() => switch(this) {
+ ThemeMode.system => 0,
+ ThemeMode.dark => 1,
+ ThemeMode.light => 2,
+ };
}
app/lib/model/weight_unit.dart
@@ -0,0 +1,42 @@
+import 'package:health_data_store/health_data_store.dart';
+
+/// A unit [Weight] can be in.
+enum WeightUnit {
+ /// Kilograms, SI unit
+ kg,
+
+ /// Pounds, Defined by the Units of Measurement Regulations 1994
+ lbs,
+
+ /// Stone, the imperial unit of mass
+ st;
+
+ /// Restore from [serialized].
+ static WeightUnit? deserialize(int? value) => switch(value) {
+ 0 => WeightUnit.kg,
+ 1 => WeightUnit.lbs,
+ 2 => WeightUnit.st,
+ _ => null,
+ };
+
+ /// Create a [WeightUnit.deserialize]able number.
+ int get serialized => switch(this) {
+ WeightUnit.kg => 0,
+ WeightUnit.lbs => 1,
+ WeightUnit.st => 2,
+ };
+
+ /// Create a [Weight] from a double in this unit.
+ Weight store(double value) => switch(this) {
+ WeightUnit.kg => Weight.kg(value),
+ WeightUnit.lbs => Weight.kg(value * 2.2046226218488),
+ WeightUnit.st => Weight.kg(value * 6.350),
+ };
+
+ /// Extract a weight to the preferred unit.
+ double extract(Weight w) => switch(this) {
+ WeightUnit.kg => w.kg,
+ WeightUnit.lbs => w.kg / 2.2046226218488,
+ WeightUnit.st => w.kg / 6.350,
+ };
+}
app/lib/screens/settings_screen.dart
@@ -25,6 +25,7 @@ import 'package:blood_pressure_app/model/storage/db/file_settings_loader.dart';
import 'package:blood_pressure_app/model/storage/db/settings_loader.dart';
import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
import 'package:blood_pressure_app/model/storage/storage.dart';
+import 'package:blood_pressure_app/model/weight_unit.dart';
import 'package:blood_pressure_app/platform_integration/platform_client.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
@@ -299,6 +300,22 @@ class SettingsPage extends StatelessWidget {
onChanged: (value) {
settings.weightInput = value;
},),
+ if (settings.weightInput)
+ DropDownListTile<WeightUnit?>(
+ leading: const Icon(Icons.language),
+ title: Text(localizations.preferredWeightUnit),
+ value: settings.weightUnit,
+ items: [
+ for (final u in WeightUnit.values)
+ DropdownMenuItem(
+ value: u,
+ child: Text(u.name),
+ ),
+ ],
+ onChanged: (WeightUnit? value) {
+ if (value != null) settings.weightUnit = value;
+ },
+ ),
],),
TitledColumn(
title: Text(localizations.data),
app/test/features/input/add_bodyweight_dialoge_test.dart
@@ -1,4 +1,6 @@
import 'package:blood_pressure_app/features/input/add_bodyweight_dialoge.dart';
+import 'package:blood_pressure_app/model/storage/settings_store.dart';
+import 'package:blood_pressure_app/model/weight_unit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -46,4 +48,21 @@ void main() {
expect(res, Weight.kg(123.45));
});
+ testWidgets('respects preferred weight unit', (tester) async {
+ Weight? res;
+ await tester.pumpWidget(materialApp(Builder(
+ builder: (context) => GestureDetector(
+ onTap: () async => res = await showDialog<Weight>(context: context, builder: (_) => const AddBodyweightDialoge()),
+ child: const Text('X'),
+ ),
+ ), settings: Settings(weightUnit: WeightUnit.st)));
+ await tester.tap(find.text('X'));
+ await tester.pumpAndSettle();
+
+ await tester.enterText(find.byType(TextFormField), '123.45');
+ await tester.testTextInput.receiveAction(TextInputAction.done);
+ await tester.pumpAndSettle();
+
+ expect(res, WeightUnit.st.store(123.45));
+ });
}
app/test/features/input/add_measurement_dialoge_test.dart
@@ -16,11 +16,7 @@ import '../settings/tiles/color_picker_list_tile_test.dart';
void main() {
group('AddEntryDialoge', () {
testWidgets('should show everything on initial page', (tester) async {
- await tester.pumpWidget(materialApp(
- AddEntryDialoge(
- availableMeds: [],
- ),
- ),);
+ await tester.pumpWidget(materialApp(const AddEntryDialoge(availableMeds: [])));
expect(tester.takeException(), isNull);
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
@@ -37,7 +33,7 @@ void main() {
initialRecord: mockEntryPos(
DateTime.now(), 123, 56, 43, 'Test note', Colors.teal,
),
- availableMeds: [],
+ availableMeds: const [],
),
),);
await tester.pumpAndSettle();
@@ -149,7 +145,7 @@ void main() {
);
await tester.pumpWidget(materialApp(
AddEntryDialoge(
- availableMeds: [],
+ availableMeds: const [],
),
settings: settings,
),);
app/test/features/measurement_list/weight_list_test.dart
@@ -1,6 +1,7 @@
import 'package:blood_pressure_app/features/measurement_list/weight_list.dart';
import 'package:blood_pressure_app/model/storage/interval_store.dart';
import 'package:blood_pressure_app/model/storage/settings_store.dart';
+import 'package:blood_pressure_app/model/weight_unit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -102,4 +103,21 @@ void main() {
expect(find.text(localizations.confirmDelete), findsNothing);
expect(find.text('123 kg'), findsNothing);
});
+ testWidgets('respects confirm weight unit setting', (tester) async {
+ final interval = IntervalStorage();
+ interval.changeStepSize(TimeStep.lifetime);
+ final repo = MockBodyweightRepository();
+ await repo.add(BodyweightRecord(time: DateTime(2001), weight: Weight.kg(123.0)));
+
+ await tester.pumpWidget(appBase(
+ weightRepo: repo,
+ intervallStoreManager: IntervalStoreManager(interval, IntervalStorage(), IntervalStorage()),
+ settings: Settings(weightUnit: WeightUnit.lbs),
+ const WeightList(rangeType: IntervalStoreManagerLocation.mainPage),
+ ));
+ await tester.pumpAndSettle();
+
+ expect(find.text('123 kg'), findsNothing);
+ expect(find.text('55.79 lbs'), findsOneWidget);
+ });
}
app/test/model/json_serialization_test.dart
@@ -1,4 +1,5 @@
+import 'package:blood_pressure_app/model/blood_pressure/pressure_unit.dart';
import 'package:blood_pressure_app/model/export_import/column.dart';
import 'package:blood_pressure_app/model/export_import/export_configuration.dart';
import 'package:blood_pressure_app/model/horizontal_graph_line.dart';
@@ -8,6 +9,7 @@ import 'package:blood_pressure_app/model/storage/export_pdf_settings_store.dart'
import 'package:blood_pressure_app/model/storage/export_settings_store.dart';
import 'package:blood_pressure_app/model/storage/interval_store.dart';
import 'package:blood_pressure_app/model/storage/settings_store.dart';
+import 'package:blood_pressure_app/model/weight_unit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:health_data_store/health_data_store.dart';
@@ -97,6 +99,8 @@ void main() {
knownBleDev: ['a', 'b'],
bleInput: false,
weightInput: true,
+ weightUnit: WeightUnit.st,
+ preferredPressureUnit: PressureUnit.kPa,
);
final fromJson = Settings.fromJson(initial.toJson());
@@ -126,6 +130,8 @@ void main() {
expect(initial.knownBleDev, fromJson.knownBleDev);
expect(initial.bleInput, fromJson.bleInput);
expect(initial.weightInput, fromJson.weightInput);
+ expect(initial.preferredPressureUnit, fromJson.preferredPressureUnit);
+ expect(initial.weightUnit, fromJson.weightUnit);
expect(initial.toJson(), fromJson.toJson());
});
app/test/model/weight_unit_test.dart
@@ -0,0 +1,16 @@
+import 'package:blood_pressure_app/model/weight_unit.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:health_data_store/health_data_store.dart';
+
+void main() {
+ test('converts all units to kg', () {
+ expect(WeightUnit.kg.store(72.34).kg, closeTo(72.34, 0.01));
+ expect(WeightUnit.lbs.store(32.812872).kg, closeTo(72.34, 0.01));
+ expect(WeightUnit.st.store(11.3916).kg, closeTo(72.34, 0.01));
+ });
+ test('converts kg to all units', () {
+ expect(WeightUnit.kg.extract(Weight.kg(72.34)), closeTo(72.34, 0.01));
+ expect(WeightUnit.lbs.extract(Weight.kg(72.34)), closeTo(32.812872, 0.00001));
+ expect(WeightUnit.st.extract(Weight.kg(72.34)), closeTo(11.3916, 0.001));
+ });
+}
app/pubspec.lock
@@ -5,23 +5,23 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
- sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77"
+ sha256: f6dbf021f4b214d85c79822912c5fcd142a2c4869f01222ad371bc51f9f1c356
url: "https://pub.dev"
source: hosted
- version: "73.0.0"
+ version: "74.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
- version: "0.3.2"
+ version: "0.3.3"
analyzer:
dependency: transitive
description:
name: analyzer
- sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a"
+ sha256: f7e8caf82f2d3190881d81012606effdf8a38e6c1ab9e30947149733065f817c
url: "https://pub.dev"
source: hosted
- version: "6.8.0"
+ version: "6.9.0"
app_settings:
dependency: "direct main"
description:
@@ -532,10 +532,10 @@ packages:
dependency: transitive
description:
name: macros
- sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
+ sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
- version: "0.1.2-main.4"
+ version: "0.1.3-main.0"
markdown:
dependency: transitive
description: