Commit 162fd62
Changed files (10)
app
lib
components
l10n
model
screens
test
app/lib/components/measurement_list/measurement_list.dart
@@ -12,28 +12,18 @@ class MeasurementList extends StatelessWidget {
/// Create a list to display measurements and intakes.
const MeasurementList({super.key,
required this.settings,
- required this.records,
- required this.notes,
- required this.intakes,
+ required this.entries,
});
/// Settings that determine general behavior.
final Settings settings;
- /// Records to display.
- final List<BloodPressureRecord> records;
-
- /// Complementary notes info to show.
- final List<Note> notes;
-
- /// Medicine intake info to show.
- final List<MedicineIntake> intakes;
+ /// Entries sorted with newest comming first.
+ final List<FullEntry> entries;
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
- final entries = FullEntryList.merged(records, notes, intakes);
- entries.sort((a, b) => b.time.compareTo(a.time)); // newest first
return Column(
mainAxisSize: MainAxisSize.min,
children: [
app/lib/components/measurement_list/measurement_list_entry.dart
@@ -7,6 +7,7 @@ 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';
import 'package:intl/intl.dart';
+import 'package:provider/provider.dart';
/// Display of a blood pressure measurement data.
class MeasurementListRow extends StatelessWidget {
@@ -24,7 +25,7 @@ class MeasurementListRow extends StatelessWidget {
final Settings settings;
/// Called when the user taps on the edit icon.
- final void Function() onRequestEdit;
+ final void Function() onRequestEdit; // TODO: consider removing in favor of context methods
@override
Widget build(BuildContext context) {
@@ -51,7 +52,7 @@ class MeasurementListRow extends StatelessWidget {
tooltip: localizations.edit,
),
IconButton(
- onPressed: () => _deleteEntry(settings, context, localizations),
+ onPressed: () => context.deleteEntry(data),
icon: const Icon(Icons.delete),
tooltip: localizations.delete,
),
@@ -112,27 +113,33 @@ class MeasurementListRow extends StatelessWidget {
),
],
);
+}
+
+extension DeleteEntry on BuildContext {
+ /// Delete record and note of an entry from the repositories.
+ Future<void> deleteEntry(FullEntry entry) async {
+ final localizations = AppLocalizations.of(this)!;
+ final settings = Provider.of<Settings>(this, listen: false);
+ final bpRepo = RepositoryProvider.of<BloodPressureRepository>(this);
+ final noteRepo = RepositoryProvider.of<NoteRepository>(this);
+ final messenger = ScaffoldMessenger.of(this);
- void _deleteEntry(Settings settings, BuildContext context, AppLocalizations localizations) async {
- final bpRepo = RepositoryProvider.of<BloodPressureRepository>(context); // TODO: extract
- final noteRepo = RepositoryProvider.of<NoteRepository>(context);
- final messenger = ScaffoldMessenger.of(context);
bool confirmedDeletion = true;
if (settings.confirmDeletion) {
- confirmedDeletion = await showConfirmDeletionDialoge(context);
+ confirmedDeletion = await showConfirmDeletionDialoge(this);
}
if (confirmedDeletion) {
- await bpRepo.remove(data.$1);
- await noteRepo.remove(data.$2);
+ await bpRepo.remove(entry.$1);
+ await noteRepo.remove(entry.$2);
messenger.removeCurrentSnackBar();
messenger.showSnackBar(SnackBar(
content: Text(localizations.deletionConfirmed),
action: SnackBarAction(
label: localizations.btnUndo,
onPressed: () async {
- await bpRepo.add(data.$1);
- await noteRepo.add(data.$2);
+ await bpRepo.add(entry.$1);
+ await noteRepo.add(entry.$2);
},
),
),);
app/lib/components/pressure_text.dart
@@ -20,6 +20,6 @@ class PressureText extends StatelessWidget {
switch (context.watch<Settings>().preferredPressureUnit) {
PressureUnit.mmHg => pressure?.mmHg,
PressureUnit.kPa => pressure?.kPa,
- }.toString(),
+ }?.toString(),
);
}
app/lib/l10n/app_en.arb
@@ -506,5 +506,7 @@
"titleInCsv": "Title in CSV",
"@titleInCsv": {},
"preferredPressureUnit": "Preferred pressure unit",
- "@preferredPressureUnit": {}
+ "@preferredPressureUnit": {},
+ "compactList": "Compact measurement list",
+ "@compactList": {}
}
app/lib/model/storage/settings_store.dart
@@ -139,7 +139,7 @@ class Settings extends ChangeNotifier {
'allowMissingValues': allowMissingValues,
'drawRegressionLines': drawRegressionLines,
'startWithAddMeasurementPage': startWithAddMeasurementPage,
- 'useLegacyList': useLegacyList,
+ 'useLegacyList': compactList,
'language': ConvertUtil.serializeLocale(language),
'horizontalGraphLines': horizontalGraphLines.map(jsonEncode).toList(),
'needlePinBarWidth': _needlePinBarWidth,
@@ -322,8 +322,8 @@ class Settings extends ChangeNotifier {
bool _useLegacyList = false;
/// Whether to use the compact list with swipe deletion.
- bool get useLegacyList => _useLegacyList;
- set useLegacyList(bool value) {
+ bool get compactList => _useLegacyList;
+ set compactList(bool value) {
_useLegacyList = value;
notifyListeners();
}
app/lib/model/storage/update_legacy_settings.dart
@@ -181,7 +181,7 @@ Future<void> updateLegacySettings(Settings settings, ExportSettings exportSettin
HorizontalGraphLine.fromJson(jsonDecode(e)),).toList();
break;
case 'useLegacyList':
- settings.useLegacyList = sharedPreferences.getBool(key)!;
+ settings.compactList = sharedPreferences.getBool(key)!;
break;
case 'lastAppVersion':
break;
app/lib/screens/elements/legacy_measurement_list.dart
@@ -1,67 +1,149 @@
-import 'dart:collection';
-
-import 'package:blood_pressure_app/components/dialoges/add_measurement_dialoge.dart';
-import 'package:blood_pressure_app/components/dialoges/confirm_deletion_dialoge.dart';
import 'package:blood_pressure_app/components/nullable_text.dart';
-import 'package:blood_pressure_app/model/storage/intervall_store.dart';
import 'package:blood_pressure_app/model/storage/settings_store.dart';
-import 'package:blood_pressure_app/screens/elements/blood_pressure_builder.dart';
import 'package:flutter/material.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';
import 'package:intl/intl.dart';
-import 'package:provider/provider.dart';
import '../../components/pressure_text.dart';
/// A old more compact [BloodPressureRecord] list, that lacks some of the new
/// features.
-class LegacyMeasurementsList extends StatelessWidget {
+class LegacyMeasurementsList extends StatefulWidget {
/// Create a more compact measurement list without all new features.
- LegacyMeasurementsList(BuildContext context, {super.key}) {
- if (MediaQuery.of(context).size.width < 1000) {
- _tableElementsSizes = [33, 9, 9, 9, 30];
- _sideFlex = 1;
- } else {
- _tableElementsSizes = [20, 5, 5, 5, 60];
- _sideFlex = 5;
- }
- }
+ LegacyMeasurementsList({super.key,
+ required this.data,
+ required this.settings,
+ });
+
+ /// Entries sorted with newest ordered first.
+ final List<FullEntry> data;
+
+ /// Settings that determine general behavior.
+ final Settings settings;
+
+ @override
+ State<LegacyMeasurementsList> createState() => _LegacyMeasurementsListState();
+}
+
+class _LegacyMeasurementsListState extends State<LegacyMeasurementsList> {
+
late final List<int> _tableElementsSizes;
+
late final int _sideFlex;
- @override
- Widget build(BuildContext context) => Consumer<Settings>(builder: (context, settings, child) =>
- Column(
+ Widget _buildHeader() => Row(
+ children: [
+ Expanded(
+ flex: _sideFlex,
+ child: const SizedBox(),
+ ),
+ Expanded(
+ flex: _tableElementsSizes[0],
+ child: Text(AppLocalizations.of(context)!.time, style: const TextStyle(fontWeight: FontWeight.bold)),),
+ Expanded(
+ flex: _tableElementsSizes[1],
+ child: Text(AppLocalizations.of(context)!.sysShort, style: TextStyle(fontWeight: FontWeight.bold, color: widget.settings.sysColor)),),
+ Expanded(
+ flex: _tableElementsSizes[2],
+ child: Text(AppLocalizations.of(context)!.diaShort, style: TextStyle(fontWeight: FontWeight.bold, color: widget.settings.diaColor)),),
+ Expanded(
+ flex: _tableElementsSizes[3],
+ child: Text(AppLocalizations.of(context)!.pulShort, style: TextStyle(fontWeight: FontWeight.bold, color: widget.settings.pulColor)),),
+ Expanded(
+ flex: _tableElementsSizes[4],
+ child: Text(AppLocalizations.of(context)!.notes, style: const TextStyle(fontWeight: FontWeight.bold)),),
+ Expanded(
+ flex: _sideFlex,
+ child: const SizedBox(),
+ ),
+ ],
+ );
+
+ Widget _itemBuilder(context, int index) {
+ final formatter = DateFormat(widget.settings.dateFormatString);
+ return Column(
children: [
- Row(
- children: [
- Expanded(
- flex: _sideFlex,
- child: const SizedBox(),
- ),
- Expanded(
+ Dismissible(
+ key: Key(widget.data[index].time.toIso8601String()),
+ confirmDismiss: (direction) async {
+ if (direction == DismissDirection.startToEnd) { // edit
+ await context.createEntry(widget.data[index]);
+ return false;
+ } else { // delete
+ await context.deleteEntry(widget.data[index]);
+ return false;
+ }
+ },
+ onDismissed: (direction) {},
+ background: Container(
+ width: 10,
+ decoration:
+ BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(5)),
+ child: const Align(alignment: Alignment(-0.95, 0), child: Icon(Icons.edit)),
+ ),
+ secondaryBackground: Container(
+ width: 10,
+ decoration:
+ BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(5)),
+ child: const Align(alignment: Alignment(0.95, 0), child: Icon(Icons.delete)),
+ ),
+ child: Container(
+ constraints: const BoxConstraints(minHeight: 40),
+ child: Row(children: [
+ Expanded(
+ flex: _sideFlex,
+ child: const SizedBox(),
+ ),
+ Expanded(
flex: _tableElementsSizes[0],
- child: Text(AppLocalizations.of(context)!.time, style: const TextStyle(fontWeight: FontWeight.bold)),),
- Expanded(
- flex: _tableElementsSizes[1],
- child: Text(AppLocalizations.of(context)!.sysShort, style: TextStyle(fontWeight: FontWeight.bold, color: settings.sysColor)),),
- Expanded(
+ child: Text(formatter.format(widget.data[index].time)),),
+ Expanded(
+ flex: _tableElementsSizes[1],
+ child: PressureText(widget.data[index].sys)),
+ Expanded(
flex: _tableElementsSizes[2],
- child: Text(AppLocalizations.of(context)!.diaShort, style: TextStyle(fontWeight: FontWeight.bold, color: settings.diaColor)),),
- Expanded(
+ child: PressureText(widget.data[index].dia),),
+ Expanded(
flex: _tableElementsSizes[3],
- child: Text(AppLocalizations.of(context)!.pulShort, style: TextStyle(fontWeight: FontWeight.bold, color: settings.pulColor)),),
- Expanded(
+ child: NullableText(widget.data[index].pul?.toString()),),
+ Expanded(
flex: _tableElementsSizes[4],
- child: Text(AppLocalizations.of(context)!.notes, style: const TextStyle(fontWeight: FontWeight.bold)),),
- Expanded(
- flex: _sideFlex,
- child: const SizedBox(),
- ),
- ],
+ child: NullableText(() {
+ String note = widget.data[index].note ?? '';
+ for (final i in widget.data[index].intakes) {
+ note += '${i.medicine.designation}(${i.dosis.mg}mg)';
+ }
+ return note.isEmpty ? null : note;
+ }()),
+ ),
+ Expanded(
+ flex: _sideFlex,
+ child: const SizedBox(),
+ ),
+ ],),
+ ),
+ ),
+ const Divider(
+ thickness: 1,
+ height: 1,
),
+ ],
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (MediaQuery.of(context).size.width < 1000) {
+ _tableElementsSizes = [33, 9, 9, 9, 30];
+ _sideFlex = 1;
+ } else {
+ _tableElementsSizes = [20, 5, 5, 5, 60];
+ _sideFlex = 5;
+ }
+ return Column(
+ children: [
+ _buildHeader(),
const SizedBox(
height: 10,
),
@@ -70,99 +152,20 @@ class LegacyMeasurementsList extends StatelessWidget {
thickness: 2,
color: Theme.of(context).colorScheme.primaryContainer,
),
- Expanded(// TODO: intakes
- child: BloodPressureBuilder(
- rangeType: IntervallStoreManagerLocation.mainPage,
- onData: (BuildContext context, UnmodifiableListView<BloodPressureRecord> records) {
- if (records.isEmpty) return Text(AppLocalizations.of(context)!.errNoData);
+ Expanded(
+ child: Builder(
+ builder: (BuildContext context) {
+ if (widget.data.isEmpty) return Text(AppLocalizations.of(context)!.errNoData);
return ListView.builder(
- itemCount: records.length,
+ itemCount: widget.data.length,
shrinkWrap: true,
padding: const EdgeInsets.all(2),
- itemBuilder: (context, index) {
- final formatter = DateFormat(settings.dateFormatString);
- return Column(
- children: [
- Dismissible(
- key: Key(records[index].time.toIso8601String()),
- confirmDismiss: (direction) async {
- final repo = RepositoryProvider.of<BloodPressureRepository>(context);
- if (direction == DismissDirection.startToEnd) {
- // edit
- await context.createEntry((records[index], Note(time: records[index].time), []));
- return false;
- } else { // delete
- if (!settings.confirmDeletion || await showConfirmDeletionDialoge(context)) {
- await repo.remove(records[index]);
- if (!context.mounted) return true;
- ScaffoldMessenger.of(context).removeCurrentSnackBar();
- ScaffoldMessenger.of(context).showSnackBar(SnackBar(
- content: Text(AppLocalizations.of(context)!.deletionConfirmed),
- action: SnackBarAction(
- label: AppLocalizations.of(context)!.btnUndo,
- onPressed: () async {
- await repo.add(records[index]);
- },
- ),
- ),);
- return true;
- }
- return false;
- }
- },
- onDismissed: (direction) {},
- background: Container(
- width: 10,
- decoration:
- BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(5)),
- child: const Align(alignment: Alignment(-0.95, 0), child: Icon(Icons.edit)),
- ),
- secondaryBackground: Container(
- width: 10,
- decoration:
- BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(5)),
- child: const Align(alignment: Alignment(0.95, 0), child: Icon(Icons.delete)),
- ),
- child: Container(
- constraints: const BoxConstraints(minHeight: 40),
- child: Row(children: [
- Expanded(
- flex: _sideFlex,
- child: const SizedBox(),
- ),
- Expanded(
- flex: _tableElementsSizes[0],
- child: Text(formatter.format(records[index].time)),),
- Expanded(
- flex: _tableElementsSizes[1],
- child: PressureText(records[index].sys)),
- Expanded(
- flex: _tableElementsSizes[2],
- child: PressureText(records[index].dia),),
- Expanded(
- flex: _tableElementsSizes[3],
- child: NullableText(records[index].pul?.toString()),),
- // FIXME: reimplement notes
- /*Expanded(
- flex: _tableElementsSizes[4],
- child: Text(data[index].),),*/
- Expanded(
- flex: _sideFlex,
- child: const SizedBox(),
- ),
- ],),
- ),
- ),
- const Divider(
- thickness: 1,
- height: 1,
- ),
- ],
- );
- },);
+ itemBuilder: _itemBuilder,
+ );
},
),
),
],
- ),);
+ );
+ }
}
app/lib/screens/home_screen.dart
@@ -54,23 +54,29 @@ class AppHome extends StatelessWidget {
sys:Pressure.mmHg(1), dia: Pressure.mmHg(2), pul: 3), Note(time: DateTime(2023), note: 'testTxt',), [])),*/
const MeasurementGraph(),
Expanded(
- child: (settings.useLegacyList) ?
- LegacyMeasurementsList(context) :
- BloodPressureBuilder(
+ child: BloodPressureBuilder(
+ rangeType: IntervallStoreManagerLocation.mainPage,
+ onData: (context, records) => RepositoryBuilder<MedicineIntake, MedicineIntakeRepository>(
rangeType: IntervallStoreManagerLocation.mainPage,
- onData: (context, records) => RepositoryBuilder<MedicineIntake, MedicineIntakeRepository>(
+ onData: (BuildContext context, List<MedicineIntake> intakes) => RepositoryBuilder<Note, NoteRepository>(
rangeType: IntervallStoreManagerLocation.mainPage,
- onData: (BuildContext context, List<MedicineIntake> intakes) => RepositoryBuilder<Note, NoteRepository>(
- rangeType: IntervallStoreManagerLocation.mainPage,
- onData: (BuildContext context, List<Note> notes) => MeasurementList(
+ onData: (BuildContext context, List<Note> notes) {
+ final entries = FullEntryList.merged(records, notes, intakes);
+ entries.sort((a, b) => b.time.compareTo(a.time)); // newest first
+ if (settings.compactList) {
+ return LegacyMeasurementsList(
+ data: entries,
+ settings: settings,
+ );
+ }
+ return MeasurementList(
settings: settings,
- records: records,
- notes: notes.cast(),
- intakes: intakes.cast(),
- ),
- ),
+ entries: entries,
+ );
+ },
),
),
+ ),
),
],),
),),
app/lib/screens/settings_screen.dart
@@ -133,12 +133,12 @@ class SettingsPage extends StatelessWidget {
initialColor: settings.pulColor,
title: Text(localizations.pulColor),),
SwitchListTile(
- value: settings.useLegacyList,
+ value: settings.compactList,
onChanged: (value) {
- settings.useLegacyList = value;
+ settings.compactList = value;
},
secondary: const Icon(Icons.list_alt_outlined),
- title: Text(localizations.useLegacyList),),
+ title: Text(localizations.compactList),),
],),
TitledColumn(title: Text(localizations.behavior), children: [
app/test/model/json_serialization_test.dart
@@ -114,7 +114,7 @@ void main() {
expect(initial.allowMissingValues, fromJson.allowMissingValues);
expect(initial.drawRegressionLines, fromJson.drawRegressionLines);
expect(initial.startWithAddMeasurementPage, fromJson.startWithAddMeasurementPage);
- expect(initial.useLegacyList, fromJson.useLegacyList);
+ expect(initial.compactList, fromJson.compactList);
expect(initial.horizontalGraphLines.length, fromJson.horizontalGraphLines.length);
expect(initial.horizontalGraphLines.first.color.value, fromJson.horizontalGraphLines.first.color.value);
expect(initial.horizontalGraphLines.first.height, fromJson.horizontalGraphLines.first.height);
@@ -140,7 +140,7 @@ void main() {
expect(v1.pulColor.value, Settings().pulColor.value);
expect(v2.validateInputs, Settings().validateInputs);
- expect(v3.useLegacyList, Settings().useLegacyList);
+ expect(v3.compactList, Settings().compactList);
});
});