Commit 5518078
Changed files (20)
lib
components
model
screens
lib/components/date_time_picker.dart
@@ -11,12 +11,7 @@ Future<DateTime?> showDateTimePicker({
lastDate ??= firstDate.add(const Duration(days: 365 * 200));
final DateTime? selectedDate = await showDatePicker(
- context: context,
- initialDate: initialDate,
- firstDate: firstDate,
- lastDate: lastDate,
- confirmText: 'NEXT'
- );
+ context: context, initialDate: initialDate, firstDate: firstDate, lastDate: lastDate, confirmText: 'NEXT');
if (selectedDate == null) return null;
if (!context.mounted) return null;
@@ -29,10 +24,10 @@ Future<DateTime?> showDateTimePicker({
return selectedTime == null
? selectedDate
: DateTime(
- selectedDate.year,
- selectedDate.month,
- selectedDate.day,
- selectedTime.hour,
- selectedTime.minute,
- );
-}
\ No newline at end of file
+ selectedDate.year,
+ selectedDate.month,
+ selectedDate.day,
+ selectedTime.hour,
+ selectedTime.minute,
+ );
+}
lib/components/display_interval_picker.dart
@@ -2,67 +2,53 @@ import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
-
-class IntervalPicker extends StatelessWidget{
+class IntervalPicker extends StatelessWidget {
const IntervalPicker({super.key});
@override
Widget build(BuildContext context) {
- return Consumer<Settings>(
- builder: (context, settings, child) {
- return Row(
- children: [
- Expanded(
- flex: 30,
- child: MaterialButton(
- onPressed: () {
- settings.moveDisplayDataByStep(-1);
- },
- child: const Icon(
- Icons.chevron_left,
- size: 48,
- ),
- ),
- ),
-
- Expanded(
- flex: 40,
- child: DropdownButton<int>(
- value: settings.graphStepSize,
- isExpanded: true,
- onChanged: (int? value) {
- if (value != null) {
- settings.changeStepSize(value);
- }
- },
- items: TimeStep.options.map<DropdownMenuItem<int>>((v) {
- return DropdownMenuItem(
- value: v,
- child: Text(
- TimeStep.getName(v)
- )
- );
- }).toList(),
- ),
- ),
-
-
- Expanded(
- flex: 30,
- child: MaterialButton(
- onPressed: () {
- settings.moveDisplayDataByStep(1);
- },
- child: const Icon(
- Icons.chevron_right,
- size: 48,
- ),
- ),
- ),
- ]
- );
- }
- );
+ return Consumer<Settings>(builder: (context, settings, child) {
+ return Row(children: [
+ Expanded(
+ flex: 30,
+ child: MaterialButton(
+ onPressed: () {
+ settings.moveDisplayDataByStep(-1);
+ },
+ child: const Icon(
+ Icons.chevron_left,
+ size: 48,
+ ),
+ ),
+ ),
+ Expanded(
+ flex: 40,
+ child: DropdownButton<int>(
+ value: settings.graphStepSize,
+ isExpanded: true,
+ onChanged: (int? value) {
+ if (value != null) {
+ settings.changeStepSize(value);
+ }
+ },
+ items: TimeStep.options.map<DropdownMenuItem<int>>((v) {
+ return DropdownMenuItem(value: v, child: Text(TimeStep.getName(v)));
+ }).toList(),
+ ),
+ ),
+ Expanded(
+ flex: 30,
+ child: MaterialButton(
+ onPressed: () {
+ settings.moveDisplayDataByStep(1);
+ },
+ child: const Icon(
+ Icons.chevron_right,
+ size: 48,
+ ),
+ ),
+ ),
+ ]);
+ });
}
-
-}
\ No newline at end of file
+}
lib/components/measurement_graph.dart
@@ -189,30 +189,34 @@ class MeasurementGraph extends StatelessWidget {
padding: const EdgeInsets.only(right: 16, left: 6, top: 2),
child: Column(
children: [
- const SizedBox(height: 20,),
- _LineChart(height: height-100),
- const SizedBox(height: 2,),
- Consumer<Settings>(
- builder: (context, settings, child) {
- final formatter = DateFormat(settings.dateFormatString);
- return Row(
- children: [
- (settings.graphStepSize == TimeStep.lifetime) ?
- const Text('-') :
- Text(formatter.format(settings.displayDataStart)),
- const Spacer(),
- (settings.graphStepSize == TimeStep.lifetime) ?
- const Text('now') :
- Text(formatter.format(settings.displayDataEnd)),
- ],
- );
- }
+ const SizedBox(
+ height: 20,
+ ),
+ _LineChart(height: height - 100),
+ const SizedBox(
+ height: 2,
+ ),
+ Consumer<Settings>(builder: (context, settings, child) {
+ final formatter = DateFormat(settings.dateFormatString);
+ return Row(
+ children: [
+ (settings.graphStepSize == TimeStep.lifetime)
+ ? const Text('-')
+ : Text(formatter.format(settings.displayDataStart)),
+ const Spacer(),
+ (settings.graphStepSize == TimeStep.lifetime)
+ ? const Text('now')
+ : Text(formatter.format(settings.displayDataEnd)),
+ ],
+ );
+ }),
+ const SizedBox(
+ height: 2,
),
- const SizedBox(height: 2,),
const IntervalPicker()
],
),
),
);
}
-}
\ No newline at end of file
+}
lib/components/measurement_list.dart
@@ -3,8 +3,8 @@ import 'dart:collection';
import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:flutter/material.dart';
-import 'package:provider/provider.dart';
import 'package:intl/intl.dart';
+import 'package:provider/provider.dart';
import '../screens/add_measurement.dart';
@@ -14,87 +14,75 @@ class MeasurementList extends StatelessWidget {
MeasurementList(BuildContext context, {super.key}) {
if (MediaQuery.of(context).size.width < 1000) {
- _tableElementsSizes = [33,9,9,9,30];
+ _tableElementsSizes = [33, 9, 9, 9, 30];
_sideFlex = 1;
} else {
- _tableElementsSizes = [20,5,5,5,60];
+ _tableElementsSizes = [20, 5, 5, 5, 60];
_sideFlex = 5;
}
}
@override
Widget build(BuildContext context) {
-
return Column(
children: [
- Consumer<Settings>(
- builder: (context, settings, child) {
- return Column (
+ Consumer<Settings>(builder: (context, settings, child) {
+ return Column(children: [
+ Row(
children: [
- Row(
- children: [
- Expanded(
- flex: _sideFlex,
- child: const SizedBox(),
- ),
- Expanded(
- flex: _tableElementsSizes[0],
- child: const Text("time", style: TextStyle(fontWeight: FontWeight.bold))
- ),
- Expanded(
- flex: _tableElementsSizes[1],
- child: Text("sys",
- style: TextStyle(fontWeight: FontWeight.bold, color: settings.sysColor))
- ),
- Expanded(
- flex: _tableElementsSizes[2],
- child: Text("dia",
- style: TextStyle(fontWeight: FontWeight.bold, color: settings.diaColor))
- ),
- Expanded(
- flex: _tableElementsSizes[3],
- child: Text("pul",
- style: TextStyle(fontWeight: FontWeight.bold, color: settings.pulColor))
- ),
- Expanded(
- flex: _tableElementsSizes[4],
- child: const Text("notes", style: TextStyle(fontWeight: FontWeight.bold))
- ),
- Expanded(
- flex: _sideFlex,
- child: const SizedBox(),
- ),
- ],
+ Expanded(
+ flex: _sideFlex,
+ child: const SizedBox(),
+ ),
+ Expanded(
+ flex: _tableElementsSizes[0],
+ child: const Text("time", style: TextStyle(fontWeight: FontWeight.bold))),
+ Expanded(
+ flex: _tableElementsSizes[1],
+ child: Text("sys", style: TextStyle(fontWeight: FontWeight.bold, color: settings.sysColor))),
+ Expanded(
+ flex: _tableElementsSizes[2],
+ child: Text("dia", style: TextStyle(fontWeight: FontWeight.bold, color: settings.diaColor))),
+ Expanded(
+ flex: _tableElementsSizes[3],
+ child: Text("pul", style: TextStyle(fontWeight: FontWeight.bold, color: settings.pulColor))),
+ Expanded(
+ flex: _tableElementsSizes[4],
+ child: const Text("notes", style: TextStyle(fontWeight: FontWeight.bold))),
+ Expanded(
+ flex: _sideFlex,
+ child: const SizedBox(),
),
- const SizedBox(height: 10,),
- Divider(
- height: 0,
- thickness: 2,
- color: Theme.of(context).primaryColor,
- )
- ]
- );
- }
- ),
+ ],
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Divider(
+ height: 0,
+ thickness: 2,
+ color: Theme.of(context).primaryColor,
+ )
+ ]);
+ }),
Expanded(
- child: Consumer<BloodPressureModel>(
- builder: (context, model, child) {
- return Consumer<Settings>(
- builder: (context, settings, child) {
- final items = model.getInTimeRange(settings.displayDataStart, settings.displayDataEnd);
- return FutureBuilder<UnmodifiableListView<BloodPressureRecord>>(
- future: items,
- builder: (BuildContext context, AsyncSnapshot<UnmodifiableListView<BloodPressureRecord>> recordsSnapshot) {
- assert(recordsSnapshot.connectionState != ConnectionState.none);
+ child: Consumer<BloodPressureModel>(builder: (context, model, child) {
+ return Consumer<Settings>(builder: (context, settings, child) {
+ final items = model.getInTimeRange(settings.displayDataStart, settings.displayDataEnd);
+ return FutureBuilder<UnmodifiableListView<BloodPressureRecord>>(
+ future: items,
+ builder:
+ (BuildContext context, AsyncSnapshot<UnmodifiableListView<BloodPressureRecord>> recordsSnapshot) {
+ assert(recordsSnapshot.connectionState != ConnectionState.none);
- if (recordsSnapshot.connectionState == ConnectionState.waiting) {
- return const Text('loading...');
- } else if (recordsSnapshot.hasError) {
- return Text('Error loading data:\n${recordsSnapshot.error}');
- } else {
- final data = recordsSnapshot.data ?? [];
- if (data.isNotEmpty && data.first.diastolic > 0) {
- return ListView.builder(
+ if (recordsSnapshot.connectionState == ConnectionState.waiting) {
+ return const Text('loading...');
+ } else if (recordsSnapshot.hasError) {
+ return Text('Error loading data:\n${recordsSnapshot.error}');
+ } else {
+ final data = recordsSnapshot.data ?? [];
+ if (data.isNotEmpty && data.first.diastolic > 0) {
+ return ListView.builder(
itemCount: data.length,
shrinkWrap: true,
padding: const EdgeInsets.all(2),
@@ -105,149 +93,127 @@ class MeasurementList extends StatelessWidget {
Dismissible(
key: Key(data[index].creationTime.toIso8601String()),
confirmDismiss: (direction) async {
- if (direction == DismissDirection.startToEnd) { // edit
- Provider.of<BloodPressureModel>(context, listen: false).delete(data[index].creationTime);
+ if (direction == DismissDirection.startToEnd) {
+ // edit
+ Provider.of<BloodPressureModel>(context, listen: false)
+ .delete(data[index].creationTime);
Navigator.push(
context,
- MaterialPageRoute(builder: (context) => AddMeasurementPage(
- initTime: data[index].creationTime,
- initSys: data[index].systolic,
- initDia: data[index].diastolic,
- initPul: data[index].pulse,
- initNote: data[index].notes,
- isEdit: true,
- )),
+ MaterialPageRoute(
+ builder: (context) => AddMeasurementPage(
+ initTime: data[index].creationTime,
+ initSys: data[index].systolic,
+ initDia: data[index].diastolic,
+ initPul: data[index].pulse,
+ initNote: data[index].notes,
+ isEdit: true,
+ )),
);
return false;
- } else { // delete
+ } else {
+ // delete
bool dialogeDeletionConfirmed = false;
if (settings.confirmDeletion) {
- await showDialog(context: context,
- builder: (context) {
- return AlertDialog(
- title: const Text("Confirm deletion"),
- content: const Text("Do you really want to delete this entry? You can turn of this question in the settings."),
- actions: [
- ElevatedButton(
- onPressed: () => Navigator.of(context).pop(),
- child: const Text('CANCEL')
- ),
- ElevatedButton(
- onPressed: () {
- Provider.of<BloodPressureModel>(context, listen: false).delete(data[index].creationTime);
- dialogeDeletionConfirmed = true;
- Navigator.of(context).pop();
- },
- child: const Text('CONFIRM')
- ),
- ],
- );
- }
- );
+ await showDialog(
+ context: context,
+ builder: (context) {
+ return AlertDialog(
+ title: const Text("Confirm deletion"),
+ content: const Text(
+ "Do you really want to delete this entry? You can turn of this question in the settings."),
+ actions: [
+ ElevatedButton(
+ onPressed: () => Navigator.of(context).pop(),
+ child: const Text('CANCEL')),
+ ElevatedButton(
+ onPressed: () {
+ Provider.of<BloodPressureModel>(context, listen: false)
+ .delete(data[index].creationTime);
+ dialogeDeletionConfirmed = true;
+ Navigator.of(context).pop();
+ },
+ child: const Text('CONFIRM')),
+ ],
+ );
+ });
} else {
- Provider.of<BloodPressureModel>(context, listen: false).delete(data[index].creationTime);
+ Provider.of<BloodPressureModel>(context, listen: false)
+ .delete(data[index].creationTime);
dialogeDeletionConfirmed = true;
}
if (dialogeDeletionConfirmed) {
if (!context.mounted) return true;
ScaffoldMessenger.of(context).removeCurrentSnackBar();
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(
- duration: const Duration(seconds: 5),
- content: const Text('entry has been deleted'),
- action: SnackBarAction(
- label: "UNDO",
- onPressed: () => model.add(BloodPressureRecord(
- data[index].creationTime,
- data[index].systolic,
- data[index].diastolic,
- data[index].pulse,
- data[index].notes)),
- ),
- )
- );
+ ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+ duration: const Duration(seconds: 5),
+ content: const Text('entry has been deleted'),
+ action: SnackBarAction(
+ label: "UNDO",
+ onPressed: () => model.add(BloodPressureRecord(
+ data[index].creationTime,
+ data[index].systolic,
+ data[index].diastolic,
+ data[index].pulse,
+ data[index].notes)),
+ ),
+ ));
}
return dialogeDeletionConfirmed;
}
},
- onDismissed: (direction) {
- },
+ 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)
- ),
+ 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)
- ),
+ 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(data[index].creationTime))
- ),
- Expanded(
- flex: _tableElementsSizes[1],
- child: Text(data[index].systolic.toString())
- ),
- Expanded(
- flex: _tableElementsSizes[2],
- child: Text(data[index].diastolic.toString())
- ),
- Expanded(
- flex: _tableElementsSizes[3],
- child: Text(data[index].pulse.toString())
- ),
- Expanded(
- flex: _tableElementsSizes[4],
- child: Text(data[index].notes)
- ),
- Expanded(
- flex: _sideFlex,
- child: const SizedBox(),
- ),
- ]
- ),
+ constraints: const BoxConstraints(minHeight: 40),
+ child: Row(children: [
+ Expanded(
+ flex: _sideFlex,
+ child: const SizedBox(),
+ ),
+ Expanded(
+ flex: _tableElementsSizes[0],
+ child: Text(formatter.format(data[index].creationTime))),
+ Expanded(
+ flex: _tableElementsSizes[1], child: Text(data[index].systolic.toString())),
+ Expanded(
+ flex: _tableElementsSizes[2],
+ child: Text(data[index].diastolic.toString())),
+ Expanded(
+ flex: _tableElementsSizes[3], child: Text(data[index].pulse.toString())),
+ Expanded(flex: _tableElementsSizes[4], child: Text(data[index].notes)),
+ Expanded(
+ flex: _sideFlex,
+ child: const SizedBox(),
+ ),
+ ]),
),
),
- const Divider(thickness: 1, height: 1,)
+ const Divider(
+ thickness: 1,
+ height: 1,
+ )
],
);
- }
- );
- } else {
- return const Text('no data');
- }
+ });
+ } else {
+ return const Text('no data');
}
}
- );
- }
- );
- }
- ),
+ });
+ });
+ }),
)
],
);
lib/components/settings_widgets.dart
@@ -10,7 +10,14 @@ class SettingsTile extends StatelessWidget {
final Widget? trailing;
final bool disabled;
- const SettingsTile({super.key, required this.title, this.leading, this.trailing, required this.onPressed, this.description, this.disabled = false});
+ const SettingsTile(
+ {super.key,
+ required this.title,
+ this.leading,
+ this.trailing,
+ required this.onPressed,
+ this.description,
+ this.disabled = false});
@override
Widget build(BuildContext context) {
@@ -29,27 +36,28 @@ class SettingsTile extends StatelessWidget {
child: Row(
children: [
lead,
- const SizedBox(width: 15,),
+ const SizedBox(
+ width: 15,
+ ),
SizedBox(
child: (() {
- if (description != null) {
- return Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- title,
- Flexible(
- child: DefaultTextStyle(
- style: const TextStyle(color: Colors.grey),
- overflow: TextOverflow.visible,
- child: description ?? const SizedBox.shrink()
- )
- )
- ],
- );
- }
- return title;
- })(),),
+ if (description != null) {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ title,
+ Flexible(
+ child: DefaultTextStyle(
+ style: const TextStyle(color: Colors.grey),
+ overflow: TextOverflow.visible,
+ child: description ?? const SizedBox.shrink()))
+ ],
+ );
+ }
+ return title;
+ })(),
+ ),
const Expanded(child: SizedBox.shrink()),
trail
],
@@ -68,7 +76,15 @@ class ColorSelectionSettingsTile extends StatelessWidget {
final Widget? description;
final bool disabled;
- const ColorSelectionSettingsTile({super.key, required this.title, required this.onMainColorChanged, required this.initialColor, this.leading, this.trailing, this.description, this.disabled = false});
+ const ColorSelectionSettingsTile(
+ {super.key,
+ required this.title,
+ required this.onMainColorChanged,
+ required this.initialColor,
+ this.leading,
+ this.trailing,
+ this.description,
+ this.disabled = false});
@override
Widget build(BuildContext context) {
@@ -122,14 +138,20 @@ class SwitchSettingsTile extends StatelessWidget {
final bool? initialValue;
final bool disabled;
- const SwitchSettingsTile({super.key, required this.title, required this.onToggle,
- this.leading, this.description, this.initialValue, this.disabled = false});
+ const SwitchSettingsTile(
+ {super.key,
+ required this.title,
+ required this.onToggle,
+ this.leading,
+ this.description,
+ this.initialValue,
+ this.disabled = false});
@override
Widget build(BuildContext context) {
var s = Switch(
value: initialValue ?? false,
- onChanged: onToggle,
+ onChanged: onToggle,
);
return SettingsTile(
title: title,
@@ -156,18 +178,18 @@ class SliderSettingsTile extends StatefulWidget {
final double stepSize;
final double initialValue;
- const SliderSettingsTile({
- super.key,
- required this.title,
- required this.onChanged,
- required this.initialValue,
- required this.start,
- required this.end,
- this.stepSize = 1,
- this.description,
- this.leading,
- this.trailing,
- this.disabled = false});
+ const SliderSettingsTile(
+ {super.key,
+ required this.title,
+ required this.onChanged,
+ required this.initialValue,
+ required this.start,
+ required this.end,
+ this.stepSize = 1,
+ this.description,
+ this.leading,
+ this.trailing,
+ this.disabled = false});
@override
State<StatefulWidget> createState() => _SliderSettingsTileState();
@@ -199,7 +221,9 @@ class _SliderSettingsTileState extends State<SliderSettingsTile> {
child: Row(
children: [
lead,
- const SizedBox(width: 15,),
+ const SizedBox(
+ width: 15,
+ ),
SizedBox(
width: MediaQuery.of(context).size.width - 150,
child: Column(
@@ -211,24 +235,23 @@ class _SliderSettingsTileState extends State<SliderSettingsTile> {
child: DefaultTextStyle(
style: const TextStyle(color: Colors.grey),
overflow: TextOverflow.visible,
- child: widget.description ?? const SizedBox.shrink()
- )
+ child: widget.description ?? const SizedBox.shrink())),
+ const SizedBox(
+ height: 7,
),
- const SizedBox(height: 7,),
Expanded(
child: Slider(
- value: _value,
- onChanged: (newValue) {
- setState(() {
- _value = newValue;
- });
- widget.onChanged(newValue);
- },
- min: widget.start,
- max: widget.end,
- divisions: (widget.end-widget.start)~/widget.stepSize,
- )
- )
+ value: _value,
+ onChanged: (newValue) {
+ setState(() {
+ _value = newValue;
+ });
+ widget.onChanged(newValue);
+ },
+ min: widget.start,
+ max: widget.end,
+ divisions: (widget.end - widget.start) ~/ widget.stepSize,
+ ))
],
),
),
@@ -253,7 +276,18 @@ class InputSettingsTile extends StatefulWidget {
final TextInputType? keyboardType;
final List<TextInputFormatter>? inputFormatters;
- const InputSettingsTile({super.key, required this.title, required this.inputWidth, this.leading, this.description, this.disabled = false, this.initialValue, this.decoration, this.onEditingComplete, this.keyboardType, this.inputFormatters});
+ const InputSettingsTile(
+ {super.key,
+ required this.title,
+ required this.inputWidth,
+ this.leading,
+ this.description,
+ this.disabled = false,
+ this.initialValue,
+ this.decoration,
+ this.onEditingComplete,
+ this.keyboardType,
+ this.inputFormatters});
@override
State<StatefulWidget> createState() => _InputSettingsTileState();
@@ -261,7 +295,7 @@ class InputSettingsTile extends StatefulWidget {
class _InputSettingsTileState extends State<InputSettingsTile> {
late String _value;
-
+
@override
void initState() {
super.initState();
@@ -297,7 +331,9 @@ class _InputSettingsTileState extends State<InputSettingsTile> {
focusNode: focusNode,
),
),
- const SizedBox(width: 20,),
+ const SizedBox(
+ width: 20,
+ ),
],
),
);
@@ -321,8 +357,7 @@ class SettingsSection extends StatelessWidget {
child: DefaultTextStyle(
style: (Theme.of(context).textTheme.bodyMedium ?? const TextStyle())
.copyWith(color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold),
- child: title
- ),
+ child: title),
),
),
Container(
lib/model/blood_pressure.dart
@@ -1,16 +1,14 @@
-import 'package:collection/collection.dart';
+import 'dart:convert' show utf8;
import 'dart:io';
+
+import 'package:collection/collection.dart';
import 'package:csv/csv.dart';
import 'package:file_picker/file_picker.dart';
+import 'package:file_saver/file_saver.dart';
import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
import 'package:path/path.dart';
-import 'package:sqflite_common_ffi/sqflite_ffi.dart';
-import 'package:sqflite/sqflite.dart';
-import 'package:file_saver/file_saver.dart';
import 'package:share_plus/share_plus.dart';
-import 'package:cross_file/cross_file.dart' show XFile;
-import 'dart:convert' show utf8;
+import 'package:sqflite_common_ffi/sqflite_ffi.dart';
class BloodPressureModel extends ChangeNotifier {
static const maxEntries = 2E64; // https://www.sqlite.org/limits.html Nr.13
@@ -28,11 +26,13 @@ class BloodPressureModel extends ChangeNotifier {
dbPath,
// runs when the database is first created
onCreate: (db, version) {
- return db.execute('CREATE TABLE bloodPressureModel(timestamp INTEGER(14) PRIMARY KEY, systolic INTEGER, diastolic INTEGER, pulse INTEGER, notes STRING)');
+ return db.execute(
+ 'CREATE TABLE bloodPressureModel(timestamp INTEGER(14) PRIMARY KEY, systolic INTEGER, diastolic INTEGER, pulse INTEGER, notes STRING)');
},
version: 1,
);
}
+
// factory method, to allow for async constructor
static Future<BloodPressureModel> create({String? dbPath}) async {
if (Platform.isWindows || Platform.isLinux) {
@@ -49,16 +49,19 @@ class BloodPressureModel extends ChangeNotifier {
/// Adds a new measurement at the correct chronological position in the List.
Future<void> add(BloodPressureRecord measurement) async {
- final existing = await _database.query('bloodPressureModel', where: 'timestamp = ?',
- whereArgs: [measurement.creationTime.millisecondsSinceEpoch]);
+ final existing = await _database.query('bloodPressureModel',
+ where: 'timestamp = ?', whereArgs: [measurement.creationTime.millisecondsSinceEpoch]);
if (existing.isNotEmpty) {
- await _database.update('bloodPressureModel', {
- 'systolic': measurement.systolic,
- 'diastolic': measurement.diastolic,
- 'pulse': measurement.pulse,
- 'notes': measurement.notes
- }, where: 'timestamp = ?',
- whereArgs: [measurement.creationTime.millisecondsSinceEpoch]);
+ await _database.update(
+ 'bloodPressureModel',
+ {
+ 'systolic': measurement.systolic,
+ 'diastolic': measurement.diastolic,
+ 'pulse': measurement.pulse,
+ 'notes': measurement.notes
+ },
+ where: 'timestamp = ?',
+ whereArgs: [measurement.creationTime.millisecondsSinceEpoch]);
} else {
await _database.insert('bloodPressureModel', {
'timestamp': measurement.creationTime.millisecondsSinceEpoch,
@@ -79,10 +82,9 @@ class BloodPressureModel extends ChangeNotifier {
/// Returns all recordings in saved in a range in ascending order
Future<UnmodifiableListView<BloodPressureRecord>> getInTimeRange(DateTime from, DateTime to) async {
final dbEntries = await _database.query('bloodPressureModel',
- orderBy: 'timestamp DESC',
- where: 'timestamp BETWEEN ? AND ?',
- whereArgs: [from.millisecondsSinceEpoch, to.millisecondsSinceEpoch]
- ); // descending
+ orderBy: 'timestamp DESC',
+ where: 'timestamp BETWEEN ? AND ?',
+ whereArgs: [from.millisecondsSinceEpoch, to.millisecondsSinceEpoch]); // descending
List<BloodPressureRecord> recordsInRange = _convert(dbEntries);
return UnmodifiableListView(recordsInRange);
}
@@ -90,26 +92,35 @@ class BloodPressureModel extends ChangeNotifier {
Future<UnmodifiableListView<BloodPressureRecord>> get all async {
return UnmodifiableListView(_convert(await _database.query('bloodPressureModel', columns: ['*'])));
}
-
+
Future<int> get count async {
return (await _database.rawQuery('SELECT COUNT(*) FROM bloodPressureModel'))[0]['COUNT(*)'] as int? ?? -1;
}
Future<DateTime> get firstDay async {
- return DateTime.fromMillisecondsSinceEpoch((await _database.rawQuery('SELECT timestamp FROM bloodPressureModel ORDER BY timestamp ASC LIMIT 1'))[0]['timestamp'] as int? ?? -1);
+ return DateTime.fromMillisecondsSinceEpoch(
+ (await _database.rawQuery('SELECT timestamp FROM bloodPressureModel ORDER BY timestamp ASC LIMIT 1'))[0]
+ ['timestamp'] as int? ??
+ -1);
}
+
Future<DateTime> get lastDay async {
- return DateTime.fromMillisecondsSinceEpoch((await _database.rawQuery('SELECT timestamp FROM bloodPressureModel ORDER BY timestamp DESC LIMIT 1'))[0]['timestamp'] as int? ?? -1);
+ return DateTime.fromMillisecondsSinceEpoch(
+ (await _database.rawQuery('SELECT timestamp FROM bloodPressureModel ORDER BY timestamp DESC LIMIT 1'))[0]
+ ['timestamp'] as int? ??
+ -1);
}
Future<int> get avgDia async {
var res = _toInt((await _database.rawQuery('SELECT AVG(diastolic) as dia FROM bloodPressureModel'))[0]['dia']);
return res ?? -1;
}
+
Future<int> get avgSys async {
var res = _toInt((await _database.rawQuery('SELECT AVG(systolic) as sys FROM bloodPressureModel'))[0]['sys']);
return res ?? -1;
}
+
Future<int> get avgPul async {
var res = _toInt((await _database.rawQuery('SELECT AVG(pulse) as pul FROM bloodPressureModel'))[0]['pul']);
return res ?? -1;
@@ -119,10 +130,12 @@ class BloodPressureModel extends ChangeNotifier {
var res = (await _database.rawQuery('SELECT MAX(diastolic) as dia FROM bloodPressureModel'))[0]['dia'];
return (res as int?) ?? -1;
}
+
Future<int> get maxSys async {
var res = (await _database.rawQuery('SELECT MAX(systolic) as sys FROM bloodPressureModel'))[0]['sys'];
return (res as int?) ?? -1;
}
+
Future<int> get maxPul async {
var res = (await _database.rawQuery('SELECT MAX(pulse) as pul FROM bloodPressureModel'))[0]['pul'];
return (res as int?) ?? -1;
@@ -132,10 +145,12 @@ class BloodPressureModel extends ChangeNotifier {
var res = (await _database.rawQuery('SELECT MIN(diastolic) as dia FROM bloodPressureModel'))[0]['dia'];
return (res as int?) ?? -1;
}
+
Future<int> get minSys async {
var res = (await _database.rawQuery('SELECT MIN(systolic) as sys FROM bloodPressureModel'))[0]['sys'];
return (res as int?) ?? -1;
}
+
Future<int> get minPul async {
var res = (await _database.rawQuery('SELECT MIN(pulse) as pul FROM bloodPressureModel'))[0]['pul'];
return (res as int?) ?? -1;
@@ -144,20 +159,15 @@ class BloodPressureModel extends ChangeNotifier {
Future<void> save(void Function(bool success, String? msg) callback, {bool exportAsText = false}) async {
// create csv
String csvData = 'timestampUnixMs, systolic, diastolic, pulse, notes\n';
- List<Map<String, Object?>> allEntries = await _database.query('bloodPressureModel',
- orderBy: 'timestamp DESC');
+ List<Map<String, Object?>> allEntries = await _database.query('bloodPressureModel', orderBy: 'timestamp DESC');
for (var e in allEntries) {
csvData += '${e['timestamp']}, ${e['systolic']}, ${e['diastolic']}, ${e['pulse']}, "${e['notes']}"\n';
}
// save data
String filename = 'blood_press_${DateTime.now().toIso8601String()}';
- String path = await FileSaver.instance.saveFile(
- name: filename,
- bytes: Uint8List.fromList(utf8.encode(csvData)),
- ext: 'csv',
- mimeType: MimeType.csv
- );
+ String path = await FileSaver.instance
+ .saveFile(name: filename, bytes: Uint8List.fromList(utf8.encode(csvData)), ext: 'csv', mimeType: MimeType.csv);
// notify user about location
if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
@@ -167,40 +177,34 @@ class BloodPressureModel extends ChangeNotifier {
if (exportAsText) {
mimeType = MimeType.text;
}
- Share.shareXFiles([XFile(path, mimeType: mimeType.type,)]);
+ Share.shareXFiles([
+ XFile(
+ path,
+ mimeType: mimeType.type,
+ )
+ ]);
callback(true, null);
- } else {
-
- }
+ } else {}
}
Future<void> import(void Function(bool, String?) callback) async {
var result = await FilePicker.platform.pickFiles(
- allowMultiple: false,
- withData: true,
+ allowMultiple: false,
+ withData: true,
);
if (result != null) {
var binaryContent = result.files.single.bytes;
if (binaryContent != null) {
- final csvContents = const CsvToListConverter().convert(
- utf8.decode(binaryContent),
- fieldDelimiter: ',',
- textDelimiter: '"',
- eol: '\n'
- );
+ final csvContents = const CsvToListConverter()
+ .convert(utf8.decode(binaryContent), fieldDelimiter: ',', textDelimiter: '"', eol: '\n');
for (var i = 1; i < csvContents.length; i++) {
var line = csvContents[i];
- BloodPressureRecord record = BloodPressureRecord(
- DateTime.fromMillisecondsSinceEpoch(line[0] as int),
- (line[1] as int),
- (line[2] as int),
- (line[3] as int),
- line[4].toString());
+ BloodPressureRecord record = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(line[0] as int),
+ (line[1] as int), (line[2] as int), (line[3] as int), line[4].toString());
add(record);
}
return callback(true, null);
-
} else {
return callback(false, 'empty file');
}
@@ -224,13 +228,8 @@ class BloodPressureModel extends ChangeNotifier {
List<BloodPressureRecord> _convert(List<Map<String, Object?>> dbResult) {
List<BloodPressureRecord> records = [];
for (var e in dbResult) {
- records.add(BloodPressureRecord(
- DateTime.fromMillisecondsSinceEpoch(e['timestamp']as int),
- e['systolic'] as int,
- e['diastolic'] as int,
- e['pulse'] as int,
- e['notes'].toString())
- );
+ records.add(BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(e['timestamp'] as int), e['systolic'] as int,
+ e['diastolic'] as int, e['pulse'] as int, e['notes'].toString()));
}
return records;
}
@@ -269,6 +268,7 @@ class BloodPressureWarnValues {
return 90;
}
}
+
static int getUpperSysWarnValue(int age) {
if (age <= 2) {
return 100;
lib/model/blood_pressure_analyzer.dart
@@ -3,16 +3,15 @@ import 'package:collection/collection.dart';
class BloodPressureAnalyser {
final BloodPressureModel _model;
-
+
BloodPressureAnalyser(this._model);
-
+
Future<int> get measurementsPerDay async {
final c = await _model.count;
final firstDay = await _model.firstDay;
final lastDay = await _model.lastDay;
- if (c <= 1 || firstDay.millisecondsSinceEpoch == -1 ||
- lastDay.millisecondsSinceEpoch == -1) {
+ if (c <= 1 || firstDay.millisecondsSinceEpoch == -1 || lastDay.millisecondsSinceEpoch == -1) {
return -1;
}
if (lastDay.difference(firstDay).inDays <= 0) {
@@ -22,14 +21,88 @@ class BloodPressureAnalyser {
return c ~/ lastDay.difference(firstDay).inDays;
}
-
/// outer list is type (0 -> diastolic, 1 -> systolic, 2 -> pulse)
/// inner list index is hour of day ([0] -> 00:00-00:59; [1] -> ...)
Future<List<List<int>>> get allAvgsRelativeToDaytime async {
// setup vars
- List<List<int>> allDiaValuesRelativeToTime = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]];
- List<List<int>> allSysValuesRelativeToTime = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]];
- List<List<int>> allPulValuesRelativeToTime = [[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]];
+ List<List<int>> allDiaValuesRelativeToTime = [
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ []
+ ];
+ List<List<int>> allSysValuesRelativeToTime = [
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ []
+ ];
+ List<List<int>> allPulValuesRelativeToTime = [
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ []
+ ];
// sort all data
final dbRes = await _model.all;
@@ -39,7 +112,7 @@ class BloodPressureAnalyser {
allSysValuesRelativeToTime[ts.hour].add(entry.systolic);
allPulValuesRelativeToTime[ts.hour].add(entry.pulse);
}
- for(int i = 0; i < 24; i++) {
+ for (int i = 0; i < 24; i++) {
if (allDiaValuesRelativeToTime[i].isEmpty) {
allDiaValuesRelativeToTime[i].add(await _model.avgDia);
}
@@ -52,12 +125,12 @@ class BloodPressureAnalyser {
}
// make avgs
- List<List<int>> res = [[],[],[]];
- for(int i = 0; i < 24; i++) {
+ List<List<int>> res = [[], [], []];
+ for (int i = 0; i < 24; i++) {
res[0].add(allDiaValuesRelativeToTime[i].average.toInt());
res[1].add(allSysValuesRelativeToTime[i].average.toInt());
res[2].add(allPulValuesRelativeToTime[i].average.toInt());
}
return res;
}
-}
\ No newline at end of file
+}
lib/model/ram_only_implementations.dart
@@ -21,7 +21,7 @@ class RamBloodPressureModel extends ChangeNotifier implements BloodPressureModel
@override
Future<UnmodifiableListView<BloodPressureRecord>> getInTimeRange(DateTime from, DateTime to) async {
List<BloodPressureRecord> recordsInTime = [];
- for(final e in _records) {
+ for (final e in _records) {
if (e.creationTime.isAfter(from) && e.creationTime.isBefore(to)) {
recordsInTime.add(e);
}
@@ -36,31 +36,31 @@ class RamBloodPressureModel extends ChangeNotifier implements BloodPressureModel
Future<int> get count async => _records.length;
@override
- Future<int> get avgDia async => _records.map((e) => e.diastolic).reduce((a, b) => a+b) ~/ _records.length;
+ Future<int> get avgDia async => _records.map((e) => e.diastolic).reduce((a, b) => a + b) ~/ _records.length;
@override
- Future<int> get avgPul async => _records.map((e) => e.pulse).reduce((a, b) => a+b) ~/ _records.length;
+ Future<int> get avgPul async => _records.map((e) => e.pulse).reduce((a, b) => a + b) ~/ _records.length;
@override
- Future<int> get avgSys async => _records.map((e) => e.systolic).reduce((a, b) => a+b) ~/ _records.length;
+ Future<int> get avgSys async => _records.map((e) => e.systolic).reduce((a, b) => a + b) ~/ _records.length;
@override
- Future<int> get maxDia async => _records.reduce((a,b) => (a.diastolic>=b.diastolic) ? a : b).diastolic;
+ Future<int> get maxDia async => _records.reduce((a, b) => (a.diastolic >= b.diastolic) ? a : b).diastolic;
@override
- Future<int> get maxPul async => _records.reduce((a,b) => (a.pulse>=b.pulse) ? a : b).pulse;
+ Future<int> get maxPul async => _records.reduce((a, b) => (a.pulse >= b.pulse) ? a : b).pulse;
@override
- Future<int> get maxSys async => _records.reduce((a,b) => (a.systolic>=b.systolic) ? a : b).systolic;
+ Future<int> get maxSys async => _records.reduce((a, b) => (a.systolic >= b.systolic) ? a : b).systolic;
@override
- Future<int> get minDia async => _records.reduce((a,b) => (a.diastolic<=b.diastolic) ? a : b).diastolic;
+ Future<int> get minDia async => _records.reduce((a, b) => (a.diastolic <= b.diastolic) ? a : b).diastolic;
@override
- Future<int> get minPul async => _records.reduce((a,b) => (a.pulse<=b.pulse) ? a : b).pulse;
+ Future<int> get minPul async => _records.reduce((a, b) => (a.pulse <= b.pulse) ? a : b).pulse;
@override
- Future<int> get minSys async => _records.reduce((a,b) => (a.systolic<=b.systolic) ? a : b).systolic;
+ Future<int> get minSys async => _records.reduce((a, b) => (a.systolic <= b.systolic) ? a : b).systolic;
@override
Future<DateTime> get firstDay async {
@@ -128,6 +128,7 @@ class RamSettings extends ChangeNotifier implements Settings {
}
return _diaWarn;
}
+
@override
set diaWarn(double newWarn) {
_diaWarn = newWarn;
@@ -138,6 +139,7 @@ class RamSettings extends ChangeNotifier implements Settings {
DateTime get displayDataStart {
return _displayDataStart ?? getMostRecentDisplayIntervall()[0];
}
+
@override
set displayDataStart(DateTime newGraphStart) {
_displayDataStart = newGraphStart;
@@ -148,6 +150,7 @@ class RamSettings extends ChangeNotifier implements Settings {
DateTime get displayDataEnd {
return _displayDataEnd ?? getMostRecentDisplayIntervall()[1];
}
+
@override
set displayDataEnd(DateTime newGraphEnd) {
_displayDataEnd = newGraphEnd;
@@ -161,6 +164,7 @@ class RamSettings extends ChangeNotifier implements Settings {
}
return _sysWarn;
}
+
@override
set sysWarn(double newWarn) {
_sysWarn = newWarn;
@@ -340,7 +344,7 @@ class RamSettings extends ChangeNotifier implements Settings {
@override
void moveDisplayDataByStep(int directionalStep) {
final oldStart = displayDataStart;
- final oldEnd =displayDataEnd;
+ final oldEnd = displayDataEnd;
switch (graphStepSize) {
case TimeStep.day:
displayDataStart = oldStart.copyWith(day: oldStart.day + directionalStep);
@@ -377,17 +381,17 @@ class RamSettings extends ChangeNotifier implements Settings {
return [start, start.copyWith(day: start.day + DateTime.sunday)]; // end of sunday
case TimeStep.month:
final start = DateTime(now.year, now.month);
- return [start, start.copyWith(month: now.month + 1)];
+ return [start, start.copyWith(month: now.month + 1)];
case TimeStep.year:
final start = DateTime(now.year);
- return [start, start.copyWith(year: now.year + 1)];
+ return [start, start.copyWith(year: now.year + 1)];
case TimeStep.lifetime:
final start = DateTime.fromMillisecondsSinceEpoch(0);
- return [start, now];
+ return [start, now];
default:
assert(false);
final start = DateTime.fromMillisecondsSinceEpoch(0);
- return [start, now];
+ return [start, now];
}
}
-}
\ No newline at end of file
+}
lib/model/settings_store.dart
@@ -19,6 +19,7 @@ class Settings extends ChangeNotifier {
int get graphStepSize {
return _prefs.getInt('graphStepSize') ?? TimeStep.day;
}
+
set graphStepSize(int newStepSize) {
_prefs.setInt('graphStepSize', newStepSize);
notifyListeners();
@@ -33,7 +34,7 @@ class Settings extends ChangeNotifier {
void moveDisplayDataByStep(int directionalStep) {
final oldStart = displayDataStart;
- final oldEnd =displayDataEnd;
+ final oldEnd = displayDataEnd;
switch (graphStepSize) {
case TimeStep.day:
displayDataStart = oldStart.copyWith(day: oldStart.day + directionalStep);
@@ -69,23 +70,24 @@ class Settings extends ChangeNotifier {
return [start, start.copyWith(day: start.day + DateTime.sunday)]; // end of sunday
case TimeStep.month:
final start = DateTime(now.year, now.month);
- return [start, start.copyWith(month: now.month + 1)];
+ return [start, start.copyWith(month: now.month + 1)];
case TimeStep.year:
final start = DateTime(now.year);
- return [start, start.copyWith(year: now.year + 1)];
+ return [start, start.copyWith(year: now.year + 1)];
case TimeStep.lifetime:
final start = DateTime.fromMillisecondsSinceEpoch(0);
- return [start, now];
+ return [start, now];
default:
assert(false);
final start = DateTime.fromMillisecondsSinceEpoch(0);
- return [start, now];
+ return [start, now];
}
}
DateTime get displayDataStart {
return _displayDataStart ?? getMostRecentDisplayIntervall()[0];
}
+
set displayDataStart(DateTime newGraphStart) {
_displayDataStart = newGraphStart;
notifyListeners();
@@ -94,76 +96,97 @@ class Settings extends ChangeNotifier {
DateTime get displayDataEnd {
return _displayDataEnd ?? getMostRecentDisplayIntervall()[1];
}
+
set displayDataEnd(DateTime newGraphEnd) {
_displayDataEnd = newGraphEnd;
notifyListeners();
}
+
bool get followSystemDarkMode {
return _prefs.getBool('followSystemDarkMode') ?? true;
}
+
set followSystemDarkMode(bool newSetting) {
_prefs.setBool('followSystemDarkMode', newSetting);
notifyListeners();
}
+
bool get darkMode {
return _prefs.getBool('darkMode') ?? true;
}
+
set darkMode(bool newSetting) {
_prefs.setBool('darkMode', newSetting);
notifyListeners();
}
+
MaterialColor get accentColor {
return createMaterialColor(_prefs.getInt('accentColor') ?? 0xFF009688);
}
+
set accentColor(MaterialColor newColor) {
_prefs.setInt('accentColor', newColor.value);
notifyListeners();
}
+
MaterialColor get diaColor {
return createMaterialColor(_prefs.getInt('diaColor') ?? 0xFF4CAF50);
}
+
set diaColor(MaterialColor newColor) {
_prefs.setInt('diaColor', newColor.value);
notifyListeners();
}
+
MaterialColor get sysColor {
return createMaterialColor(_prefs.getInt('sysColor') ?? 0xFF009688);
}
+
set sysColor(MaterialColor newColor) {
_prefs.setInt('sysColor', newColor.value);
notifyListeners();
}
+
MaterialColor get pulColor {
return createMaterialColor(_prefs.getInt('pulColor') ?? 0xFFF44336);
}
+
set pulColor(MaterialColor newColor) {
_prefs.setInt('pulColor', newColor.value);
notifyListeners();
}
+
bool get allowManualTimeInput {
return _prefs.getBool('allowManualTimeInput') ?? true;
}
+
set allowManualTimeInput(bool newSetting) {
_prefs.setBool('allowManualTimeInput', newSetting);
notifyListeners();
}
+
String get dateFormatString {
return _prefs.getString('dateFormatString') ?? 'yyyy-MM-dd HH:mm';
}
+
set dateFormatString(String newFormatString) {
_prefs.setString('dateFormatString', newFormatString);
notifyListeners();
}
+
bool get useExportCompatability {
return _prefs.getBool('useExportCompatability') ?? false;
}
+
set useExportCompatability(bool useExportCompatability) {
_prefs.setBool('useExportCompatability', useExportCompatability);
notifyListeners();
}
+
double get iconSize {
return _prefs.getInt('iconSize')?.toDouble() ?? 30;
}
+
set iconSize(double newSize) {
_prefs.setInt('iconSize', newSize.toInt());
notifyListeners();
@@ -175,6 +198,7 @@ class Settings extends ChangeNotifier {
}
return _prefs.getInt('sysWarn')?.toDouble() ?? 120;
}
+
set sysWarn(double newWarn) {
_prefs.setInt('sysWarn', newWarn.toInt());
notifyListeners();
@@ -186,6 +210,7 @@ class Settings extends ChangeNotifier {
}
return _prefs.getInt('diaWarn')?.toDouble() ?? 80;
}
+
set diaWarn(double newWarn) {
_prefs.setInt('diaWarn', newWarn.toInt());
notifyListeners();
@@ -194,6 +219,7 @@ class Settings extends ChangeNotifier {
int get age {
return _prefs.getInt('age') ?? 30;
}
+
set age(int newAge) {
_prefs.setInt('age', newAge.toInt());
notifyListeners();
@@ -202,6 +228,7 @@ class Settings extends ChangeNotifier {
bool get overrideWarnValues {
return _prefs.getBool('overrideWarnValues') ?? false;
}
+
set overrideWarnValues(bool overrideWarnValues) {
_prefs.setBool('overrideWarnValues', overrideWarnValues);
notifyListeners();
@@ -210,6 +237,7 @@ class Settings extends ChangeNotifier {
bool get validateInputs {
return _prefs.getBool('validateInputs') ?? true;
}
+
set validateInputs(bool validateInputs) {
_prefs.setBool('validateInputs', validateInputs);
notifyListeners();
@@ -218,6 +246,7 @@ class Settings extends ChangeNotifier {
double get graphLineThickness {
return _prefs.getDouble('graphLineThickness') ?? 3;
}
+
set graphLineThickness(double newThickness) {
_prefs.setDouble('graphLineThickness', newThickness);
notifyListeners();
@@ -226,6 +255,7 @@ class Settings extends ChangeNotifier {
int get animationSpeed {
return _prefs.getInt('animationSpeed') ?? 150;
}
+
set animationSpeed(int newSpeed) {
_prefs.setInt('animationSpeed', newSpeed);
notifyListeners();
@@ -297,4 +327,4 @@ MaterialColor createMaterialColor(int value) {
);
}
return MaterialColor(value, swatch);
-}
\ No newline at end of file
+}
lib/screens/subsettings/enter_timeformat.dart
@@ -1,8 +1,7 @@
import 'package:blood_pressure_app/model/settings_store.dart';
+import 'package:blood_pressure_app/screens/subsettings/time_formats_explainer.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
-import 'package:blood_pressure_app/screens/subsettings/time_formats_explainer.dart';
-
class EnterTimeFormatScreen extends StatefulWidget {
const EnterTimeFormatScreen({super.key});
@@ -30,13 +29,11 @@ class _EnterTimeFormatScreenState extends State<EnterTimeFormatScreen> {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- const Text('A formatter String consists of a mixture of predefined ICU/Skeleton Strings and any other text you want to include.'),
+ const Text(
+ 'A formatter String consists of a mixture of predefined ICU/Skeleton Strings and any other text you want to include.'),
InkWell(
onTap: () async {
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => const TimeFormattingHelp())
- );
+ Navigator.push(context, MaterialPageRoute(builder: (context) => const TimeFormattingHelp()));
},
child: const SizedBox(
height: 48,
@@ -48,40 +45,40 @@ class _EnterTimeFormatScreenState extends State<EnterTimeFormatScreen> {
),
),
),
- const Text('Please note that having longer/shorter format Strings wont magically change the width of the table columns, so it might come to awkward line breaks.'),
- const SizedBox(height: 7,),
+ const Text(
+ 'Please note that having longer/shorter format Strings wont magically change the width of the table columns, so it might come to awkward line breaks.'),
+ const SizedBox(
+ height: 7,
+ ),
const Text('default: "yy-MM-dd H:mm"'),
-
- const SizedBox(height: 10,),
- Consumer<Settings>(
- builder: (context, settings, child) {
- _newVal = settings.dateFormatString;
- return TextFormField(
- initialValue: _newVal,
- decoration: const InputDecoration(
- hintText: 'format string'
- ),
- validator: (String? value) {
- if (value == null || value.isEmpty) {
- return 'Please enter a value';
- } else {
- _newVal = value;
- }
- return null;
- },
- );
- }
+ const SizedBox(
+ height: 10,
+ ),
+ Consumer<Settings>(builder: (context, settings, child) {
+ _newVal = settings.dateFormatString;
+ return TextFormField(
+ initialValue: _newVal,
+ decoration: const InputDecoration(hintText: 'format string'),
+ validator: (String? value) {
+ if (value == null || value.isEmpty) {
+ return 'Please enter a value';
+ } else {
+ _newVal = value;
+ }
+ return null;
+ },
+ );
+ }),
+ const SizedBox(
+ height: 25,
),
- const SizedBox(height: 25,),
Row(
children: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
- style: ElevatedButton.styleFrom(
- backgroundColor: Theme.of(context).unselectedWidgetColor
- ),
+ style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).unselectedWidgetColor),
child: const Text('CANCEL')),
const Spacer(),
ElevatedButton(
@@ -91,9 +88,7 @@ class _EnterTimeFormatScreenState extends State<EnterTimeFormatScreen> {
Navigator.of(context).pop();
}
},
- style: ElevatedButton.styleFrom(
- backgroundColor: Theme.of(context).primaryColor
- ),
+ style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).primaryColor),
child: const Text('SAVE'))
],
)
@@ -105,6 +100,4 @@ class _EnterTimeFormatScreenState extends State<EnterTimeFormatScreen> {
),
);
}
-
-
-}
\ No newline at end of file
+}
lib/screens/subsettings/time_formats_explainer.dart
@@ -44,23 +44,19 @@ class TimeFormattingHelp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
- appBar: AppBar(
- title: const Text('Date formatting'),
- backgroundColor: Theme.of(context).primaryColor,
- ),
- body: Container(
- padding: const EdgeInsets.all(20),
- child: SingleChildScrollView(
- child: Table(
- columnWidths: const {
- 0: FlexColumnWidth(0.71),
- 1: FlexColumnWidth(0.29)
- },
- children: getRows(),
- ),
+ appBar: AppBar(
+ title: const Text('Date formatting'),
+ backgroundColor: Theme.of(context).primaryColor,
),
- )
- );
+ body: Container(
+ padding: const EdgeInsets.all(20),
+ child: SingleChildScrollView(
+ child: Table(
+ columnWidths: const {0: FlexColumnWidth(0.71), 1: FlexColumnWidth(0.29)},
+ children: getRows(),
+ ),
+ ),
+ ));
}
List<TableRow> getRows() {
@@ -69,14 +65,8 @@ class TimeFormattingHelp extends StatelessWidget {
for (int i = 1; i < lines.length; i++) {
List<String> values = lines[i].trim().split(RegExp(r'\s{2,}'));
- rowsOut.add(TableRow(
- children: [
- Text(values[0]),
- Text(values[1])
- ]
- ));
+ rowsOut.add(TableRow(children: [Text(values[0]), Text(values[1])]));
}
return rowsOut;
}
-
-}
\ No newline at end of file
+}
lib/screens/subsettings/warn_about.dart
@@ -1,6 +1,4 @@
-
import 'package:blood_pressure_app/model/blood_pressure.dart';
-import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -22,14 +20,17 @@ class AboutWarnValuesScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('The warn values are a pure suggestions and no medical advice.'),
- const SizedBox(height: 5,),
+ const SizedBox(
+ height: 5,
+ ),
InkWell(
onTap: () async {
final url = Uri.parse(BloodPressureWarnValues.source);
if (await canLaunchUrl(url)) {
await launchUrl(url, mode: LaunchMode.externalApplication);
} else {
- ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Can\'t open URL:\n${BloodPressureWarnValues.source}')));
+ ScaffoldMessenger.of(context)
+ .showSnackBar(SnackBar(content: Text('Can\'t open URL:\n${BloodPressureWarnValues.source}')));
}
},
child: const SizedBox(
@@ -42,13 +43,15 @@ class AboutWarnValuesScreen extends StatelessWidget {
),
),
),
- const SizedBox(height: 5,),
- const Text('Feel free to change the values to suit your needs and follow the recommendations of your doctor.'),
+ const SizedBox(
+ height: 5,
+ ),
+ const Text(
+ 'Feel free to change the values to suit your needs and follow the recommendations of your doctor.'),
],
),
),
),
);
}
-
-}
\ No newline at end of file
+}
lib/screens/add_measurement.dart
@@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
-
class AddMeasurementPage extends StatefulWidget {
final DateTime? initTime;
final int initSys;
@@ -15,13 +14,14 @@ class AddMeasurementPage extends StatefulWidget {
final String initNote;
final bool isEdit;
- const AddMeasurementPage({super.key,
- this.initTime,
- this.initSys = -1,
- this.initDia = -1,
- this.initPul = -1,
- this.initNote = '',
- this.isEdit = false});
+ const AddMeasurementPage(
+ {super.key,
+ this.initTime,
+ this.initSys = -1,
+ this.initDia = -1,
+ this.initPul = -1,
+ this.initNote = '',
+ this.isEdit = false});
@override
State<AddMeasurementPage> createState() => _AddMeasurementPageState();
@@ -61,156 +61,128 @@ class _AddMeasurementPageState extends State<AddMeasurementPage> {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
- Consumer<Settings>(
- builder: (context, settings, child) {
- final formatter = DateFormat(settings.dateFormatString);
- if(settings.allowManualTimeInput) {
- return GestureDetector(
- onTap: () async {
- var selectedTime = await showDateTimePicker(
- context: context,
- firstDate: DateTime.fromMillisecondsSinceEpoch(0),
- lastDate: DateTime.now().copyWith(second: DateTime.now().second+1)
- );
- if (selectedTime != null) {
- setState(() {
- _time = selectedTime;
- });
- }
- },
- child: Column(
- children: [
- Row(
- children: [
- Text(formatter.format(_time)),
- const Spacer(),
- const Icon(Icons.edit)
- ],
- ),
- const SizedBox(height: 3,),
- Divider(
- color: Theme.of(context).disabledColor,
- thickness: 1,
- )
- ],
- ),
- );
- } else {
- return const SizedBox.shrink();
- }
- }
- ),
- Consumer<Settings>(
- builder: (context, settings, child) {
- return TextFormField(
- key: const Key('txtSys'),
- initialValue: widget.isEdit ? _systolic.toString() : '',
- decoration: const InputDecoration(
- hintText: 'systolic'
- ),
- keyboardType: TextInputType.number,
- inputFormatters: <TextInputFormatter>[
- FilteringTextInputFormatter.digitsOnly
- ],
- focusNode: _sysFocusNode,
- onChanged: (String? value) {
- // to next field
- if ( value != null && value.isNotEmpty &&
- (int.tryParse(value) ?? -1) > 40) {
- FocusScope.of(context).nextFocus();
- }
- },
- validator: (String? value) {
- if (value == null || value.isEmpty
- || (int.tryParse(value) == null)) {
- return 'Please enter a Number';
- } else if (settings.validateInputs && (int.tryParse(value) ?? -1) <= 30) {
- return 'Number < 30? Turn off validation in settings!';
- } else if (settings.validateInputs && (int.tryParse(value) ?? 1000) >= 400) { // exceeding this value is unlikely: https://pubmed.ncbi.nlm.nih.gov/7741618/
- return 'Unrealistic value? Turn off validation in settings!';
- } else {
- _systolic = int.tryParse(value) ?? -1;
- }
- return null;
- },
- );
- }
- ),
- Consumer<Settings>(
- builder: (context, settings, child) {
- return TextFormField(
- key: const Key('txtDia'),
- initialValue: widget.isEdit ? _diastolic.toString() : '',
- decoration: const InputDecoration(
- hintText: 'diastolic'
- ),
- keyboardType: TextInputType.number,
- inputFormatters: <TextInputFormatter>[
- FilteringTextInputFormatter.digitsOnly
- ],
- onChanged: (String? value) {
- // to next field
- if ( value != null && value.isNotEmpty &&
- (int.tryParse(value) ?? -1) > 40) {
- FocusScope.of(context).nextFocus();
+ Consumer<Settings>(builder: (context, settings, child) {
+ final formatter = DateFormat(settings.dateFormatString);
+ if (settings.allowManualTimeInput) {
+ return GestureDetector(
+ onTap: () async {
+ var selectedTime = await showDateTimePicker(
+ context: context,
+ firstDate: DateTime.fromMillisecondsSinceEpoch(0),
+ lastDate: DateTime.now().copyWith(second: DateTime.now().second + 1));
+ if (selectedTime != null) {
+ setState(() {
+ _time = selectedTime;
+ });
}
},
- validator: (String? value) {
- if (value == null || value.isEmpty
- || (int.tryParse(value) == null)) {
- return 'Please enter a Number';
- } else if (settings.validateInputs && (int.tryParse(value) ?? -1) <= 30) {
- return 'Number < 30? Turn off validation in settings!';
- } else if (settings.validateInputs && (int.tryParse(value) ?? 1000) >= 400) { // exceeding this value is unlikely: https://pubmed.ncbi.nlm.nih.gov/7741618/
- return 'Unrealistic value? Turn off validation in settings!';
- } else {
- _diastolic = int.tryParse(value) ?? -1;
- }
- return null;
- },
- );
- }
- ),
- Consumer<Settings>(
- builder: (context, settings, child) {
- return TextFormField(
- key: const Key('txtPul'),
- initialValue: widget.isEdit ? _pulse.toString() : '',
- decoration: const InputDecoration(
- hintText: 'pulse'
+ child: Column(
+ children: [
+ Row(
+ children: [Text(formatter.format(_time)), const Spacer(), const Icon(Icons.edit)],
+ ),
+ const SizedBox(
+ height: 3,
+ ),
+ Divider(
+ color: Theme.of(context).disabledColor,
+ thickness: 1,
+ )
+ ],
),
- keyboardType: TextInputType.number,
- inputFormatters: <TextInputFormatter>[
- FilteringTextInputFormatter.digitsOnly
- ],
- onChanged: (String? value) {
- // to next field
- if ( value != null && value.isNotEmpty &&
- (int.tryParse(value) ?? -1) > 35) {
- FocusScope.of(context).nextFocus();
- }
- },
- validator: (String? value) {
- if (value == null || value.isEmpty
- || (int.tryParse(value) == null)) {
- return 'Please enter a Number';
- } else if (settings.validateInputs && (int.tryParse(value) ?? -1) <= 30) {
- return 'Number < 30? Turn off validation in settings!';
- } else if (settings.validateInputs && (int.tryParse(value) ?? 1000) >= 600) { // exceeding this value is unlikely: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3273956/
- return 'Unrealistic value? Turn off validation in settings!';
- } else {
- _pulse = int.tryParse(value) ?? -1;
- }
- return null;
- },
);
+ } else {
+ return const SizedBox.shrink();
}
- ),
+ }),
+ Consumer<Settings>(builder: (context, settings, child) {
+ return TextFormField(
+ key: const Key('txtSys'),
+ initialValue: widget.isEdit ? _systolic.toString() : '',
+ decoration: const InputDecoration(hintText: 'systolic'),
+ keyboardType: TextInputType.number,
+ inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+ focusNode: _sysFocusNode,
+ onChanged: (String? value) {
+ // to next field
+ if (value != null && value.isNotEmpty && (int.tryParse(value) ?? -1) > 40) {
+ FocusScope.of(context).nextFocus();
+ }
+ },
+ validator: (String? value) {
+ if (value == null || value.isEmpty || (int.tryParse(value) == null)) {
+ return 'Please enter a Number';
+ } else if (settings.validateInputs && (int.tryParse(value) ?? -1) <= 30) {
+ return 'Number < 30? Turn off validation in settings!';
+ } else if (settings.validateInputs && (int.tryParse(value) ?? 1000) >= 400) {
+ // exceeding this value is unlikely: https://pubmed.ncbi.nlm.nih.gov/7741618/
+ return 'Unrealistic value? Turn off validation in settings!';
+ } else {
+ _systolic = int.tryParse(value) ?? -1;
+ }
+ return null;
+ },
+ );
+ }),
+ Consumer<Settings>(builder: (context, settings, child) {
+ return TextFormField(
+ key: const Key('txtDia'),
+ initialValue: widget.isEdit ? _diastolic.toString() : '',
+ decoration: const InputDecoration(hintText: 'diastolic'),
+ keyboardType: TextInputType.number,
+ inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+ onChanged: (String? value) {
+ // to next field
+ if (value != null && value.isNotEmpty && (int.tryParse(value) ?? -1) > 40) {
+ FocusScope.of(context).nextFocus();
+ }
+ },
+ validator: (String? value) {
+ if (value == null || value.isEmpty || (int.tryParse(value) == null)) {
+ return 'Please enter a Number';
+ } else if (settings.validateInputs && (int.tryParse(value) ?? -1) <= 30) {
+ return 'Number < 30? Turn off validation in settings!';
+ } else if (settings.validateInputs && (int.tryParse(value) ?? 1000) >= 400) {
+ // exceeding this value is unlikely: https://pubmed.ncbi.nlm.nih.gov/7741618/
+ return 'Unrealistic value? Turn off validation in settings!';
+ } else {
+ _diastolic = int.tryParse(value) ?? -1;
+ }
+ return null;
+ },
+ );
+ }),
+ Consumer<Settings>(builder: (context, settings, child) {
+ return TextFormField(
+ key: const Key('txtPul'),
+ initialValue: widget.isEdit ? _pulse.toString() : '',
+ decoration: const InputDecoration(hintText: 'pulse'),
+ keyboardType: TextInputType.number,
+ inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+ onChanged: (String? value) {
+ // to next field
+ if (value != null && value.isNotEmpty && (int.tryParse(value) ?? -1) > 35) {
+ FocusScope.of(context).nextFocus();
+ }
+ },
+ validator: (String? value) {
+ if (value == null || value.isEmpty || (int.tryParse(value) == null)) {
+ return 'Please enter a Number';
+ } else if (settings.validateInputs && (int.tryParse(value) ?? -1) <= 30) {
+ return 'Number < 30? Turn off validation in settings!';
+ } else if (settings.validateInputs && (int.tryParse(value) ?? 1000) >= 600) {
+ // exceeding this value is unlikely: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3273956/
+ return 'Unrealistic value? Turn off validation in settings!';
+ } else {
+ _pulse = int.tryParse(value) ?? -1;
+ }
+ return null;
+ },
+ );
+ }),
TextFormField(
initialValue: widget.isEdit ? _note.toString() : '',
- decoration: const InputDecoration(
- hintText: 'note (optional)'
- ),
+ decoration: const InputDecoration(hintText: 'note (optional)'),
validator: (String? value) {
_note = value ?? "";
return null;
@@ -222,36 +194,32 @@ class _AddMeasurementPageState extends State<AddMeasurementPage> {
Row(
children: [
ElevatedButton(
- key: const Key('btnCancel'),
- onPressed: () {
- if (widget.isEdit) {
- Provider.of<BloodPressureModel>(context, listen: false).add(
- BloodPressureRecord(widget.initTime ?? DateTime.now(), widget.initSys, widget.initDia, widget.initPul, widget.initNote)
- );
- }
- Navigator.of(context).pop();
- },
- style: ElevatedButton.styleFrom(
- backgroundColor: Theme.of(context).unselectedWidgetColor
- ),
- child: const Text('CANCEL')
- ),
+ key: const Key('btnCancel'),
+ onPressed: () {
+ if (widget.isEdit) {
+ Provider.of<BloodPressureModel>(context, listen: false).add(BloodPressureRecord(
+ widget.initTime ?? DateTime.now(),
+ widget.initSys,
+ widget.initDia,
+ widget.initPul,
+ widget.initNote));
+ }
+ Navigator.of(context).pop();
+ },
+ style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).unselectedWidgetColor),
+ child: const Text('CANCEL')),
const Spacer(),
ElevatedButton(
- key: const Key('btnSave'),
- onPressed: () {
- if (_formKey.currentState!.validate()) {
- Provider.of<BloodPressureModel>(context, listen: false).add(
- BloodPressureRecord(_time, _systolic, _diastolic, _pulse, _note)
- );
- Navigator.of(context).pop();
- }
- },
- style: ElevatedButton.styleFrom(
- backgroundColor: Theme.of(context).primaryColor
- ),
- child: const Text('SAVE')
- )
+ key: const Key('btnSave'),
+ onPressed: () {
+ if (_formKey.currentState!.validate()) {
+ Provider.of<BloodPressureModel>(context, listen: false)
+ .add(BloodPressureRecord(_time, _systolic, _diastolic, _pulse, _note));
+ Navigator.of(context).pop();
+ }
+ },
+ style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).primaryColor),
+ child: const Text('SAVE'))
],
)
],
@@ -262,4 +230,4 @@ class _AddMeasurementPageState extends State<AddMeasurementPage> {
),
);
}
-}
\ No newline at end of file
+}
lib/screens/home.dart
@@ -1,14 +1,13 @@
+import 'package:blood_pressure_app/components/measurement_graph.dart';
+import 'package:blood_pressure_app/components/measurement_list.dart';
import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:blood_pressure_app/screens/add_measurement.dart';
import 'package:blood_pressure_app/screens/settings.dart';
import 'package:blood_pressure_app/screens/statistics.dart';
import 'package:flutter/material.dart';
-import 'package:blood_pressure_app/components/measurement_graph.dart';
-import 'package:blood_pressure_app/components/measurement_list.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
-
class AppHome extends StatelessWidget {
const AppHome({super.key});
@@ -21,105 +20,79 @@ class AppHome extends StatelessWidget {
padding = const EdgeInsets.all(80);
}
- return Scaffold(
- body: OrientationBuilder(
- builder: (context, orientation) {
- if (orientation == Orientation.landscape && MediaQuery.of(context).size.height < 500) {
- return MeasurementGraph(height: MediaQuery.of(context).size.height,);
- }
- return Center(
- child: Container(
- padding: padding,
- child: Column(
- children: [
- const MeasurementGraph(),
- Expanded(
- flex: 50,
- child: MeasurementList(context)
- ),
- ]
+ return Scaffold(body: OrientationBuilder(
+ builder: (context, orientation) {
+ if (orientation == Orientation.landscape && MediaQuery.of(context).size.height < 500) {
+ return MeasurementGraph(
+ height: MediaQuery.of(context).size.height,
+ );
+ }
+ return Center(
+ child: Container(
+ padding: padding,
+ child: Column(children: [
+ const MeasurementGraph(),
+ Expanded(flex: 50, child: MeasurementList(context)),
+ ]),
+ ),
+ );
+ },
+ ), floatingActionButton: OrientationBuilder(builder: (context, orientation) {
+ if (orientation == Orientation.landscape && MediaQuery.of(context).size.height < 500) {
+ SystemChrome.setEnabledSystemUIMode(SystemUiMode.leanBack);
+ return const SizedBox.shrink();
+ }
+ SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values);
+ return Consumer<Settings>(builder: (context, settings, child) {
+ return Column(
+ verticalDirection: VerticalDirection.up,
+ children: [
+ Ink(
+ decoration: ShapeDecoration(shape: const CircleBorder(), color: Theme.of(context).primaryColor),
+ child: IconButton(
+ iconSize: settings.iconSize,
+ icon: const Icon(
+ Icons.add,
+ color: Colors.black,
+ ),
+ onPressed: () {
+ Navigator.push(
+ context,
+ _buildTransition(const AddMeasurementPage(), settings.animationSpeed),
+ );
+ },
),
),
- );
- },
- ),
- floatingActionButton: OrientationBuilder(
- builder: (context, orientation) {
- if (orientation == Orientation.landscape && MediaQuery.of(context).size.height < 500) {
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.leanBack);
- return const SizedBox.shrink();
- }
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values);
- return Consumer<Settings>(
- builder: (context, settings, child) {
- return Column(
- verticalDirection: VerticalDirection.up,
- children: [
- Ink(
- decoration: ShapeDecoration(
- shape: const CircleBorder(),
- color: Theme.of(context).primaryColor
- ),
- child: IconButton(
- iconSize: settings.iconSize,
- icon: const Icon(
- Icons.add,
- color: Colors.black,
- ),
- onPressed: () {
- Navigator.push(
- context,
- _buildTransition(const AddMeasurementPage(), settings.animationSpeed),
- );
- },
- ),
- ),
- const SizedBox(height: 10,),
- Ink(
- decoration: ShapeDecoration(
- shape: const CircleBorder(),
- color: Theme.of(context).unselectedWidgetColor
- ),
- child: IconButton(
- iconSize: settings.iconSize,
- icon: const Icon(
- Icons.insights,
- color: Colors.black
- ),
- onPressed: () {
- Navigator.push(
- context,
- _buildTransition(const StatisticsPage(), settings.animationSpeed)
- );
- },
- ),
- ),
- const SizedBox(height: 10,),
- Ink(
- decoration: ShapeDecoration(
- shape: const CircleBorder(),
- color: Theme.of(context).unselectedWidgetColor
- ),
- child: IconButton(
- iconSize: settings.iconSize,
- icon: const Icon(
- Icons.settings,
- color: Colors.black
- ),
- onPressed: () {
- Navigator.push(
- context,
- _buildTransition(const SettingsPage(), settings.animationSpeed)
- );
- },
- ),
- ),
- ],
- );
- }
- );
- })
- );
+ const SizedBox(
+ height: 10,
+ ),
+ Ink(
+ decoration: ShapeDecoration(shape: const CircleBorder(), color: Theme.of(context).unselectedWidgetColor),
+ child: IconButton(
+ iconSize: settings.iconSize,
+ icon: const Icon(Icons.insights, color: Colors.black),
+ onPressed: () {
+ Navigator.push(context, _buildTransition(const StatisticsPage(), settings.animationSpeed));
+ },
+ ),
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Ink(
+ decoration: ShapeDecoration(shape: const CircleBorder(), color: Theme.of(context).unselectedWidgetColor),
+ child: IconButton(
+ iconSize: settings.iconSize,
+ icon: const Icon(Icons.settings, color: Colors.black),
+ onPressed: () {
+ Navigator.push(context, _buildTransition(const SettingsPage(), settings.animationSpeed));
+ },
+ ),
+ ),
+ ],
+ );
+ });
+ }));
}
}
@@ -129,11 +102,11 @@ PageRoute _buildTransition(Widget page, int duration) {
class TimedMaterialPageRouter extends MaterialPageRoute {
Duration _duration = Duration.zero;
-
+
TimedMaterialPageRouter({required WidgetBuilder builder, required Duration duration}) : super(builder: builder) {
_duration = duration;
}
@override
Duration get transitionDuration => _duration;
-}
\ No newline at end of file
+}
lib/screens/settings.dart
@@ -18,85 +18,83 @@ class SettingsPage extends StatelessWidget {
title: const Text('Settings'),
backgroundColor: Theme.of(context).primaryColor,
),
- body: Consumer<Settings>(
- builder: (context, settings, child) {
- return ListView(
- children: [
- SettingsSection(
- title: const Text('layout'),
- children: [
- SwitchSettingsTile(
- key: const Key('allowManualTimeInput'),
- initialValue: settings.allowManualTimeInput,
- onToggle: (value) {
- settings.allowManualTimeInput = value;
- },
- leading: const Icon(Icons.details),
- title: const Text('allow manual time input')
- ),
- SettingsTile(
- key: const Key('EnterTimeFormatScreen'),
- title: const Text('time format'),
- leading: const Icon(Icons.schedule),
- trailing: Icon(Icons.arrow_forward_ios, color: Theme.of(context).highlightColor,),
- description: Text(settings.dateFormatString),
- onPressed: (context) {
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => const EnterTimeFormatScreen()),
- );
- },
- ),
- SwitchSettingsTile(
- key: const Key('followSystemDarkMode'),
- initialValue: settings.followSystemDarkMode,
- onToggle: (value) {
- settings.followSystemDarkMode = value;
- },
- leading: const Icon(Icons.auto_mode),
- title: const Text('follow system dark mode')
- ),
- SwitchSettingsTile(
- key: const Key('darkMode'),
- initialValue: (() {
- if (settings.followSystemDarkMode) {
- return MediaQuery.of(context).platformBrightness == Brightness.dark;
- }
- return settings.darkMode;
- })(),
- onToggle: (value) {
- settings.darkMode = value;
- },
- leading: const Icon(Icons.dark_mode),
- title: const Text('enable dark mode'),
- disabled: settings.followSystemDarkMode,
- ),
- SliderSettingsTile(
- key: const Key('iconSize'),
- title: const Text('icon size'),
- leading: const Icon(Icons.zoom_in),
- onChanged: (double value) {
- settings.iconSize = value;
- },
- initialValue: settings.iconSize,
- start: 15,
- end: 70,
- stepSize: 5,
- ),
- SliderSettingsTile(
- key: const Key('graphLineThickness'),
- title: const Text('line thickness'),
- leading: const Icon(Icons.line_weight),
- onChanged: (double value) {
- settings.graphLineThickness = value;
- },
- initialValue: settings.graphLineThickness,
- start: 1,
- end: 5,
- stepSize: 1,
- ),
- SliderSettingsTile(
- key: const Key('animationSpeed'),
+ body: Consumer<Settings>(builder: (context, settings, child) {
+ return ListView(
+ children: [
+ SettingsSection(title: const Text('layout'), children: [
+ SwitchSettingsTile(
+ key: const Key('allowManualTimeInput'),
+ initialValue: settings.allowManualTimeInput,
+ onToggle: (value) {
+ settings.allowManualTimeInput = value;
+ },
+ leading: const Icon(Icons.details),
+ title: const Text('allow manual time input')),
+ SettingsTile(
+ key: const Key('EnterTimeFormatScreen'),
+ title: const Text('time format'),
+ leading: const Icon(Icons.schedule),
+ trailing: Icon(
+ Icons.arrow_forward_ios,
+ color: Theme.of(context).highlightColor,
+ ),
+ description: Text(settings.dateFormatString),
+ onPressed: (context) {
+ Navigator.push(
+ context,
+ MaterialPageRoute(builder: (context) => const EnterTimeFormatScreen()),
+ );
+ },
+ ),
+ SwitchSettingsTile(
+ key: const Key('followSystemDarkMode'),
+ initialValue: settings.followSystemDarkMode,
+ onToggle: (value) {
+ settings.followSystemDarkMode = value;
+ },
+ leading: const Icon(Icons.auto_mode),
+ title: const Text('follow system dark mode')),
+ SwitchSettingsTile(
+ key: const Key('darkMode'),
+ initialValue: (() {
+ if (settings.followSystemDarkMode) {
+ return MediaQuery.of(context).platformBrightness == Brightness.dark;
+ }
+ return settings.darkMode;
+ })(),
+ onToggle: (value) {
+ settings.darkMode = value;
+ },
+ leading: const Icon(Icons.dark_mode),
+ title: const Text('enable dark mode'),
+ disabled: settings.followSystemDarkMode,
+ ),
+ SliderSettingsTile(
+ key: const Key('iconSize'),
+ title: const Text('icon size'),
+ leading: const Icon(Icons.zoom_in),
+ onChanged: (double value) {
+ settings.iconSize = value;
+ },
+ initialValue: settings.iconSize,
+ start: 15,
+ end: 70,
+ stepSize: 5,
+ ),
+ SliderSettingsTile(
+ key: const Key('graphLineThickness'),
+ title: const Text('line thickness'),
+ leading: const Icon(Icons.line_weight),
+ onChanged: (double value) {
+ settings.graphLineThickness = value;
+ },
+ initialValue: settings.graphLineThickness,
+ start: 1,
+ end: 5,
+ stepSize: 1,
+ ),
+ SliderSettingsTile(
+ key: const Key('animationSpeed'),
title: const Text('animation duration'),
leading: const Icon(Icons.speed),
onChanged: (double value) {
@@ -129,227 +127,192 @@ class SettingsPage extends StatelessWidget {
key: const Key('sysColor'),
onMainColorChanged: (color) => settings.sysColor = createMaterialColor((color ?? Colors.green).value),
initialColor: settings.sysColor,
- title: const Text('systolic color')
- ),
- ColorSelectionSettingsTile(
- key: const Key('diaColor'),
- onMainColorChanged: (color) => settings.diaColor = createMaterialColor((color ?? Colors.teal).value),
- initialColor: settings.diaColor,
- title: const Text('diastolic color')
- ),
- ColorSelectionSettingsTile(
- key: const Key('pulColor'),
- onMainColorChanged: (color) => settings.pulColor = createMaterialColor((color ?? Colors.red).value),
- initialColor: settings.pulColor,
- title: const Text('pulse color')
- ),
- ]
+ title: const Text('systolic color')),
+ ColorSelectionSettingsTile(
+ key: const Key('diaColor'),
+ onMainColorChanged: (color) => settings.diaColor = createMaterialColor((color ?? Colors.teal).value),
+ initialColor: settings.diaColor,
+ title: const Text('diastolic color')),
+ ColorSelectionSettingsTile(
+ key: const Key('pulColor'),
+ onMainColorChanged: (color) => settings.pulColor = createMaterialColor((color ?? Colors.red).value),
+ initialColor: settings.pulColor,
+ title: const Text('pulse color')),
+ ]),
+ SettingsSection(title: const Text('behavior'), children: [
+ SwitchSettingsTile(
+ key: const Key('validateInputs'),
+ initialValue: settings.validateInputs,
+ title: const Text('validate inputs'),
+ leading: const Icon(Icons.edit),
+ onToggle: (value) {
+ settings.validateInputs = value;
+ }),
+ SwitchSettingsTile(
+ key: const Key('confirmDeletion'),
+ initialValue: settings.confirmDeletion,
+ title: const Text('confirm deletion'),
+ leading: const Icon(Icons.check),
+ onToggle: (value) {
+ settings.confirmDeletion = value;
+ }),
+ InputSettingsTile(
+ key: const Key('age'),
+ title: const Text('age'),
+ description: const Text('determines warn values'),
+ leading: const Icon(Icons.manage_accounts_outlined),
+ keyboardType: TextInputType.number,
+ inputFormatters: [FilteringTextInputFormatter.digitsOnly],
+ initialValue: settings.age.toString(),
+ onEditingComplete: (String? value) {
+ if (value == null || value.isEmpty || (int.tryParse(value) == null)) {
+ return;
+ }
+ settings.age = int.tryParse(value) as int; // no error possible as per above's condition
+ },
+ decoration: const InputDecoration(hintText: 'age'),
+ inputWidth: 80,
+ disabled: false,
+ // although no function provided, when overriding warn values,
+ // this field intentionally doesn't get disabled, as this
+ // would cause unexpected jumps in layout
),
- SettingsSection(
- title: const Text('behavior'),
- children: [
- SwitchSettingsTile(
- key: const Key('validateInputs'),
- initialValue: settings.validateInputs,
- title: const Text('validate inputs'),
- leading: const Icon(Icons.edit),
- onToggle: (value) {
- settings.validateInputs = value;
- }
- ),
- SwitchSettingsTile(
- key: const Key('confirmDeletion'),
- initialValue: settings.confirmDeletion,
- title: const Text('confirm deletion'),
- leading: const Icon(Icons.check),
- onToggle: (value) {
- settings.confirmDeletion = value;
- }
- ),
- InputSettingsTile(
- key: const Key('age'),
- title: const Text('age'),
- description: const Text('determines warn values'),
- leading: const Icon(Icons.manage_accounts_outlined),
- keyboardType: TextInputType.number,
- inputFormatters: [
- FilteringTextInputFormatter.digitsOnly
- ],
- initialValue: settings.age.toString(),
- onEditingComplete: (String? value) {
- if (value == null || value.isEmpty
- || (int.tryParse(value) == null)) {
- return;
- }
- settings.age = int.tryParse(value) as int; // no error possible as per above's condition
- },
- decoration: const InputDecoration(
- hintText: 'age'
- ),
- inputWidth: 80,
- disabled: false,
- // although no function provided, when overriding warn values,
- // this field intentionally doesn't get disabled, as this
- // would cause unexpected jumps in layout
- ),
- SettingsTile(
- key: const Key('AboutWarnValuesScreen'),
- title: const Text('about'),
- description: const Text('more information on warn values'),
- leading: const Icon(Icons.info_outline),
- onPressed: (context) {
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => const AboutWarnValuesScreen()),
- );
- }
- ),
- SwitchSettingsTile(
- key: const Key('overrideWarnValues'),
- initialValue: settings.overrideWarnValues,
- onToggle: (value) {
- settings.overrideWarnValues = value;
- },
- leading: const Icon(Icons.tune),
- title: const Text('override warn values'),
- ),
- InputSettingsTile(
- key: const Key('sysWarn'),
- title: const Text('systolic warn'),
- leading: const Icon(Icons.settings_applications_outlined),
- keyboardType: TextInputType.number,
- inputFormatters: [
- FilteringTextInputFormatter.digitsOnly
- ],
- initialValue: settings.sysWarn.toInt().toString(),
- onEditingComplete: (String? value) {
- if (value == null || value.isEmpty
- || (double.tryParse(value) == null)) {
- return;
- }
- // no error possible as per above's condition
- settings.sysWarn = double.tryParse(value) as double;
- },
- decoration: const InputDecoration(
- hintText: 'systolic warn'
- ),
- inputWidth: 120,
- disabled: !settings.overrideWarnValues,
- ),
- InputSettingsTile(
- key: const Key('diaWarn'),
- title: const Text('diastolic warn'),
- leading: const Icon(Icons.settings_applications_outlined),
- keyboardType: TextInputType.number,
- inputFormatters: [
- FilteringTextInputFormatter.digitsOnly
- ],
- initialValue: settings.diaWarn.toInt().toString(),
- onEditingComplete: (String? value) {
- if (value == null || value.isEmpty
- || (double.tryParse(value) == null)) {
- return;
- }
- // no error possible as per above's condition
- settings.diaWarn = double.tryParse(value) as double;
- },
- decoration: const InputDecoration(
- hintText: 'diastolic warn'
- ),
- inputWidth: 120,
- disabled: !settings.overrideWarnValues,
- ),
- ]
+ SettingsTile(
+ key: const Key('AboutWarnValuesScreen'),
+ title: const Text('about'),
+ description: const Text('more information on warn values'),
+ leading: const Icon(Icons.info_outline),
+ onPressed: (context) {
+ Navigator.push(
+ context,
+ MaterialPageRoute(builder: (context) => const AboutWarnValuesScreen()),
+ );
+ }),
+ SwitchSettingsTile(
+ key: const Key('overrideWarnValues'),
+ initialValue: settings.overrideWarnValues,
+ onToggle: (value) {
+ settings.overrideWarnValues = value;
+ },
+ leading: const Icon(Icons.tune),
+ title: const Text('override warn values'),
),
- SettingsSection(
- title: const Text('data'),
- children: [
- SwitchSettingsTile(
- key: const Key('useExportCompatability'),
- initialValue: settings.useExportCompatability,
- title: const Text('compatability export'),
- description: const Text('sets export mime type to text'),
- leading: const Icon(Icons.support),
- onToggle: (value) {
- settings.useExportCompatability = value;
- }
- ),
- SettingsTile(
- key: const Key('export'),
- title: const Text('export'),
- leading: const Icon(Icons.save),
- onPressed: (context) => Provider.of<BloodPressureModel>(context, listen: false).save((success, msg) {
- if (success && msg != null) {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text(msg)));
- } else if (!success && msg != null) {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text('Error: $msg')));
- }
- }
- , exportAsText: settings.useExportCompatability),
- ),
- SettingsTile(
- key: const Key('import'),
- title: const Text('import'),
- leading: const Icon(Icons.file_upload),
- onPressed: (context) {
- try {
- Provider.of<BloodPressureModel>(context, listen: false)
- .import((res, String? err) {
- if (res) {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(
- content: Text('import successful')));
- } else {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text(
- 'Error: ${err ?? 'unknown error'}')));
- }
- });
- } on Exception catch (e) {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text('error: ${e.toString()}')));
- }
- },
- ),
- ],
+ InputSettingsTile(
+ key: const Key('sysWarn'),
+ title: const Text('systolic warn'),
+ leading: const Icon(Icons.settings_applications_outlined),
+ keyboardType: TextInputType.number,
+ inputFormatters: [FilteringTextInputFormatter.digitsOnly],
+ initialValue: settings.sysWarn.toInt().toString(),
+ onEditingComplete: (String? value) {
+ if (value == null || value.isEmpty || (double.tryParse(value) == null)) {
+ return;
+ }
+ // no error possible as per above's condition
+ settings.sysWarn = double.tryParse(value) as double;
+ },
+ decoration: const InputDecoration(hintText: 'systolic warn'),
+ inputWidth: 120,
+ disabled: !settings.overrideWarnValues,
),
- SettingsSection(
- title: const Text('about'),
- children: [
- SettingsTile(
- key: const Key('sourceCode'),
- title: const Text('source code'),
- leading: const Icon(Icons.merge),
- onPressed: (context) async {
- var url = Uri.parse('https://github.com/NobodyForNothing/blood-pressure-monitor-fl');
- if (await canLaunchUrl(url)) {
- await launchUrl(url, mode: LaunchMode.externalApplication);
+ InputSettingsTile(
+ key: const Key('diaWarn'),
+ title: const Text('diastolic warn'),
+ leading: const Icon(Icons.settings_applications_outlined),
+ keyboardType: TextInputType.number,
+ inputFormatters: [FilteringTextInputFormatter.digitsOnly],
+ initialValue: settings.diaWarn.toInt().toString(),
+ onEditingComplete: (String? value) {
+ if (value == null || value.isEmpty || (double.tryParse(value) == null)) {
+ return;
+ }
+ // no error possible as per above's condition
+ settings.diaWarn = double.tryParse(value) as double;
+ },
+ decoration: const InputDecoration(hintText: 'diastolic warn'),
+ inputWidth: 120,
+ disabled: !settings.overrideWarnValues,
+ ),
+ ]),
+ SettingsSection(
+ title: const Text('data'),
+ children: [
+ SwitchSettingsTile(
+ key: const Key('useExportCompatability'),
+ initialValue: settings.useExportCompatability,
+ title: const Text('compatability export'),
+ description: const Text('sets export mime type to text'),
+ leading: const Icon(Icons.support),
+ onToggle: (value) {
+ settings.useExportCompatability = value;
+ }),
+ SettingsTile(
+ key: const Key('export'),
+ title: const Text('export'),
+ leading: const Icon(Icons.save),
+ onPressed: (context) => Provider.of<BloodPressureModel>(context, listen: false).save((success, msg) {
+ if (success && msg != null) {
+ ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg)));
+ } else if (!success && msg != null) {
+ ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error: $msg')));
+ }
+ }, exportAsText: settings.useExportCompatability),
+ ),
+ SettingsTile(
+ key: const Key('import'),
+ title: const Text('import'),
+ leading: const Icon(Icons.file_upload),
+ onPressed: (context) {
+ try {
+ Provider.of<BloodPressureModel>(context, listen: false).import((res, String? err) {
+ if (res) {
+ ScaffoldMessenger.of(context)
+ .showSnackBar(const SnackBar(content: Text('import successful')));
} else {
- if (!context.mounted) return;
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(content: Text('Can\'t open URL:\nhttps://github.com/NobodyForNothing/blood-pressure-monitor-fl')));
+ ScaffoldMessenger.of(context)
+ .showSnackBar(SnackBar(content: Text('Error: ${err ?? 'unknown error'}')));
}
- },
- ),
- SettingsTile(
- key: const Key('licenses'),
- title: const Text('3rd party licenses'),
- leading: const Icon(Icons.policy_outlined),
- trailing: Icon(Icons.arrow_forward_ios, color: Theme.of(context).highlightColor,),
- onPressed: (context) {
- showLicensePage(context: context);
- },
- ),
- ]
- )
- ],
- );
- }
- ),
+ });
+ } on Exception catch (e) {
+ ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('error: ${e.toString()}')));
+ }
+ },
+ ),
+ ],
+ ),
+ SettingsSection(title: const Text('about'), children: [
+ SettingsTile(
+ key: const Key('sourceCode'),
+ title: const Text('source code'),
+ leading: const Icon(Icons.merge),
+ onPressed: (context) async {
+ var url = Uri.parse('https://github.com/NobodyForNothing/blood-pressure-monitor-fl');
+ if (await canLaunchUrl(url)) {
+ await launchUrl(url, mode: LaunchMode.externalApplication);
+ } else {
+ if (!context.mounted) return;
+ ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
+ content:
+ Text('Can\'t open URL:\nhttps://github.com/NobodyForNothing/blood-pressure-monitor-fl')));
+ }
+ },
+ ),
+ SettingsTile(
+ key: const Key('licenses'),
+ title: const Text('3rd party licenses'),
+ leading: const Icon(Icons.policy_outlined),
+ trailing: Icon(
+ Icons.arrow_forward_ios,
+ color: Theme.of(context).highlightColor,
+ ),
+ onPressed: (context) {
+ showLicensePage(context: context);
+ },
+ ),
+ ])
+ ],
+ );
+ }),
);
}
}
-
-
lib/screens/statistics.dart
@@ -15,140 +15,136 @@ class StatisticsPage extends StatelessWidget {
title: const Text('Statistics'),
backgroundColor: Theme.of(context).primaryColor,
),
- body: SingleChildScrollView(
- child: Consumer<BloodPressureModel>(
- builder: (context, model, child) {
- return Consumer<Settings>(
- builder: (context, settings, child) {
- return Column(
- children: [
- Statistic(
- caption: const Text('Measurement count'),
- child: futureInt(model.count)
- ),
- // special measurements
- StatisticsRow(
- caption1: Text('Systolic avg.',
- style: TextStyle(color: settings.sysColor, fontWeight: FontWeight.w700),),
- child1: futureInt(model.avgSys),
- caption2: Text('Diastolic avg.', style: TextStyle(color: settings.diaColor, fontWeight: FontWeight.w700),),
- child2: futureInt(model.avgDia),
- caption3: Text('Pulse avg.',
- style: TextStyle(color: settings.pulColor, fontWeight: FontWeight.w700),),
- child3: futureInt(model.avgPul),
- ),
- Statistic(
- caption: const Text('Measurements per Day'),
- child: futureInt(BloodPressureAnalyser(model).measurementsPerDay)
- ),
- StatisticsRow(
- caption2: Text('Diastolic min.',
- style: TextStyle(color: settings.diaColor, fontWeight: FontWeight.w700),),
- child2: futureInt(model.minDia),
- caption1: Text('Systolic min.',
- style: TextStyle(color: settings.sysColor, fontWeight: FontWeight.w700),),
- child1: futureInt(model.minSys),
- caption3: Text('Pulse min.',
- style: TextStyle(color: settings.pulColor, fontWeight: FontWeight.w700),),
- child3: futureInt(model.minPul),
- ),
- StatisticsRow(
- caption2: Text('Diastolic max.',
- style: TextStyle(color: settings.diaColor, fontWeight: FontWeight.w700),),
- child2: futureInt(model.maxDia),
- caption1: Text('Systolic max.',
- style: TextStyle(color: settings.sysColor, fontWeight: FontWeight.w700),),
- child1: futureInt(model.maxSys),
- caption3: Text('Pulse max.',
- style: TextStyle(color: settings.pulColor, fontWeight: FontWeight.w700),),
- child3: futureInt(model.maxPul),
- ),
- // Time-Resolved Metrics
- Statistic(
- caption: const Text('Time-Resolved Metrics'),
- child: FutureBuilder<List<List<int>>>(
- future: BloodPressureAnalyser(model).allAvgsRelativeToDaytime,
- builder: (BuildContext context, AsyncSnapshot<List<
- List<int>>> snapshot) {
- switch (snapshot.connectionState) {
- case ConnectionState.none:
- return const Text('not started');
- case ConnectionState.waiting:
- return const Text('loading...');
- default:
- if (snapshot.hasError) {
- return Text('ERROR: ${snapshot.error}');
- }
- assert(snapshot.hasData);
- assert(snapshot.data != null);
- final daytimeAvgs = snapshot.data ?? [];
- const opacity = 0.5;
- return SizedBox(
- width: 500,
- height: 270,
- child: RadarChart(
- RadarChartData(
- radarShape: RadarShape.circle,
- radarBorderData: const BorderSide(color: Colors.transparent),
- gridBorderData: BorderSide(
- color: Theme.of(context).dividerColor,
- width: 2
- ),
- tickBorderData: BorderSide(
- color: Theme.of(context).dividerColor,
- width: 2),
- ticksTextStyle: const TextStyle(color: Colors.transparent),
- tickCount: 5,
- titleTextStyle: const TextStyle(
- fontSize: 25
- ),
- getTitle: (pos, value) {
- if (pos % 2 == 0) {
- return RadarChartTitle(
- text: '$pos',
- positionPercentageOffset: 0.05
- );
- }
- return const RadarChartTitle(text: '');
- },
- dataSets: [
- RadarDataSet(
- dataEntries: intListToRadarEntry(daytimeAvgs[0]),
- borderColor: settings.diaColor,
- fillColor: settings.diaColor.withOpacity(opacity),
- entryRadius: 0,
- borderWidth: settings.graphLineThickness
- ),
- RadarDataSet(
- dataEntries: intListToRadarEntry(daytimeAvgs[1]),
- borderColor: settings.sysColor,
- fillColor: settings.sysColor.withOpacity(opacity),
- entryRadius: 0,
- borderWidth: settings.graphLineThickness
- ),
- RadarDataSet(
- dataEntries: intListToRadarEntry(daytimeAvgs[2]),
- borderColor: settings.pulColor,
- fillColor: settings.pulColor.withOpacity(opacity),
- entryRadius: 0,
- borderWidth: settings.graphLineThickness
- ),
- ],
- ),
- ),
- );
+ body: SingleChildScrollView(child: Consumer<BloodPressureModel>(
+ builder: (context, model, child) {
+ return Consumer<Settings>(builder: (context, settings, child) {
+ return Column(
+ children: [
+ Statistic(caption: const Text('Measurement count'), child: futureInt(model.count)),
+ // special measurements
+ StatisticsRow(
+ caption1: Text(
+ 'Systolic avg.',
+ style: TextStyle(color: settings.sysColor, fontWeight: FontWeight.w700),
+ ),
+ child1: futureInt(model.avgSys),
+ caption2: Text(
+ 'Diastolic avg.',
+ style: TextStyle(color: settings.diaColor, fontWeight: FontWeight.w700),
+ ),
+ child2: futureInt(model.avgDia),
+ caption3: Text(
+ 'Pulse avg.',
+ style: TextStyle(color: settings.pulColor, fontWeight: FontWeight.w700),
+ ),
+ child3: futureInt(model.avgPul),
+ ),
+ Statistic(
+ caption: const Text('Measurements per Day'),
+ child: futureInt(BloodPressureAnalyser(model).measurementsPerDay)),
+ StatisticsRow(
+ caption2: Text(
+ 'Diastolic min.',
+ style: TextStyle(color: settings.diaColor, fontWeight: FontWeight.w700),
+ ),
+ child2: futureInt(model.minDia),
+ caption1: Text(
+ 'Systolic min.',
+ style: TextStyle(color: settings.sysColor, fontWeight: FontWeight.w700),
+ ),
+ child1: futureInt(model.minSys),
+ caption3: Text(
+ 'Pulse min.',
+ style: TextStyle(color: settings.pulColor, fontWeight: FontWeight.w700),
+ ),
+ child3: futureInt(model.minPul),
+ ),
+ StatisticsRow(
+ caption2: Text(
+ 'Diastolic max.',
+ style: TextStyle(color: settings.diaColor, fontWeight: FontWeight.w700),
+ ),
+ child2: futureInt(model.maxDia),
+ caption1: Text(
+ 'Systolic max.',
+ style: TextStyle(color: settings.sysColor, fontWeight: FontWeight.w700),
+ ),
+ child1: futureInt(model.maxSys),
+ caption3: Text(
+ 'Pulse max.',
+ style: TextStyle(color: settings.pulColor, fontWeight: FontWeight.w700),
+ ),
+ child3: futureInt(model.maxPul),
+ ),
+ // Time-Resolved Metrics
+ Statistic(
+ caption: const Text('Time-Resolved Metrics'),
+ child: FutureBuilder<List<List<int>>>(
+ future: BloodPressureAnalyser(model).allAvgsRelativeToDaytime,
+ builder: (BuildContext context, AsyncSnapshot<List<List<int>>> snapshot) {
+ switch (snapshot.connectionState) {
+ case ConnectionState.none:
+ return const Text('not started');
+ case ConnectionState.waiting:
+ return const Text('loading...');
+ default:
+ if (snapshot.hasError) {
+ return Text('ERROR: ${snapshot.error}');
}
- }
- ),
- ),
- // TODO: Weekdays / Weekends
- ],
- );
- }
+ assert(snapshot.hasData);
+ assert(snapshot.data != null);
+ final daytimeAvgs = snapshot.data ?? [];
+ const opacity = 0.5;
+ return SizedBox(
+ width: 500,
+ height: 270,
+ child: RadarChart(
+ RadarChartData(
+ radarShape: RadarShape.circle,
+ radarBorderData: const BorderSide(color: Colors.transparent),
+ gridBorderData: BorderSide(color: Theme.of(context).dividerColor, width: 2),
+ tickBorderData: BorderSide(color: Theme.of(context).dividerColor, width: 2),
+ ticksTextStyle: const TextStyle(color: Colors.transparent),
+ tickCount: 5,
+ titleTextStyle: const TextStyle(fontSize: 25),
+ getTitle: (pos, value) {
+ if (pos % 2 == 0) {
+ return RadarChartTitle(text: '$pos', positionPercentageOffset: 0.05);
+ }
+ return const RadarChartTitle(text: '');
+ },
+ dataSets: [
+ RadarDataSet(
+ dataEntries: intListToRadarEntry(daytimeAvgs[0]),
+ borderColor: settings.diaColor,
+ fillColor: settings.diaColor.withOpacity(opacity),
+ entryRadius: 0,
+ borderWidth: settings.graphLineThickness),
+ RadarDataSet(
+ dataEntries: intListToRadarEntry(daytimeAvgs[1]),
+ borderColor: settings.sysColor,
+ fillColor: settings.sysColor.withOpacity(opacity),
+ entryRadius: 0,
+ borderWidth: settings.graphLineThickness),
+ RadarDataSet(
+ dataEntries: intListToRadarEntry(daytimeAvgs[2]),
+ borderColor: settings.pulColor,
+ fillColor: settings.pulColor.withOpacity(opacity),
+ entryRadius: 0,
+ borderWidth: settings.graphLineThickness),
+ ],
+ ),
+ ),
+ );
+ }
+ }),
+ ),
+ // TODO: Weekdays / Weekends
+ ],
);
- },
- )
- ),
+ });
+ },
+ )),
);
}
@@ -166,7 +162,7 @@ class Statistic extends StatelessWidget {
final Widget child;
final bool smallEdges;
- const Statistic({super.key, required this.caption, required this.child, this.smallEdges=false});
+ const Statistic({super.key, required this.caption, required this.child, this.smallEdges = false});
@override
Widget build(BuildContext context) {
@@ -179,15 +175,9 @@ class Statistic extends StatelessWidget {
}
return Container(
margin: EdgeInsets.only(left: sides, right: sides, top: top),
- constraints: const BoxConstraints(
- minHeight: 50,
- minWidth: 110
- ),
+ constraints: const BoxConstraints(minHeight: 50, minWidth: 110),
decoration: BoxDecoration(
- border: Border.all(
- width: 3,
- color: Theme.of(context).textTheme.bodyMedium?.color ?? Colors.white38
- ),
+ border: Border.all(width: 3, color: Theme.of(context).textTheme.bodyMedium?.color ?? Colors.white38),
borderRadius: const BorderRadius.all(Radius.circular(25)),
),
child: Stack(
@@ -196,20 +186,15 @@ class Statistic extends StatelessWidget {
top: 6,
left: 12,
child: DefaultTextStyle(
- style: TextStyle(color: Theme.of(context).textTheme.bodyMedium?.color ?? Colors.white38),
- child: caption
- ),
+ style: TextStyle(color: Theme.of(context).textTheme.bodyMedium?.color ?? Colors.white38),
+ child: caption),
),
Container(
- padding: EdgeInsets.only(left: padding, right: padding, bottom: padding, top: padding+5),
+ padding: EdgeInsets.only(left: padding, right: padding, bottom: padding, top: padding + 5),
child: Align(
alignment: Alignment.center,
child: DefaultTextStyle(
- style: TextStyle(
- color: Theme.of(context).primaryColor,
- fontWeight: FontWeight.bold,
- fontSize: 40
- ),
+ style: TextStyle(color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold, fontSize: 40),
child: child,
),
),
@@ -228,13 +213,22 @@ class StatisticsRow extends StatelessWidget {
final Widget child2;
final Widget child3;
- const StatisticsRow({super.key, required this.caption1, required this.caption2, required this.caption3, required this.child1, required this.child2, required this.child3});
+ const StatisticsRow(
+ {super.key,
+ required this.caption1,
+ required this.caption2,
+ required this.caption3,
+ required this.child1,
+ required this.child2,
+ required this.child3});
@override
Widget build(BuildContext context) {
return Row(
children: [
- const SizedBox(width: 20,),
+ const SizedBox(
+ width: 20,
+ ),
Expanded(
child: Statistic(
smallEdges: true,
@@ -242,7 +236,9 @@ class StatisticsRow extends StatelessWidget {
child: child1,
),
),
- const SizedBox(width: 10,),
+ const SizedBox(
+ width: 10,
+ ),
Expanded(
child: Statistic(
smallEdges: true,
@@ -250,7 +246,9 @@ class StatisticsRow extends StatelessWidget {
child: child2,
),
),
- const SizedBox(width: 10,),
+ const SizedBox(
+ width: 10,
+ ),
Expanded(
child: Statistic(
smallEdges: true,
@@ -258,7 +256,9 @@ class StatisticsRow extends StatelessWidget {
child: child3,
),
),
- const SizedBox(width: 20,),
+ const SizedBox(
+ width: 20,
+ ),
],
);
}
@@ -283,7 +283,5 @@ Widget futureInt(Future<int> value) {
}
return Text(snapshot.data?.toString() ?? 'error');
}
- }
- );
+ });
}
-
lib/main.dart
@@ -1,8 +1,8 @@
+import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/settings_store.dart';
+import 'package:blood_pressure_app/screens/home.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
-import 'package:blood_pressure_app/model/blood_pressure.dart';
-import 'package:blood_pressure_app/screens/home.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -10,15 +10,10 @@ void main() async {
final dataModel = await BloodPressureModel.create();
final settingsModel = await Settings.create();
- runApp(
- MultiProvider(
- providers: [
- ChangeNotifierProvider(create: (context) => dataModel),
- ChangeNotifierProvider(create: (context) => settingsModel),
- ],
- child: const AppRoot()
- )
- );
+ runApp(MultiProvider(providers: [
+ ChangeNotifierProvider(create: (context) => dataModel),
+ ChangeNotifierProvider(create: (context) => settingsModel),
+ ], child: const AppRoot()));
}
class AppRoot extends StatelessWidget {
@@ -26,35 +21,27 @@ class AppRoot extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return Consumer<Settings>(
- builder: (context, settings, child) {
- late final ThemeMode mode;
- if (settings.followSystemDarkMode) {
- mode = ThemeMode.system;
- } else if (settings.darkMode) {
- mode = ThemeMode.dark;
- } else {
- mode = ThemeMode.light;
- }
-
- return MaterialApp(
- title: 'Blood Pressure App',
- theme: ThemeData(
- primaryColor: settings.accentColor
- ),
- darkTheme: ThemeData(
- brightness: Brightness.dark,
- canvasColor: Colors.black,
- primaryColor: settings.accentColor.shade400,
- ),
- themeMode: mode,
- home: const AppHome(),
- );
- });
+ return Consumer<Settings>(builder: (context, settings, child) {
+ late final ThemeMode mode;
+ if (settings.followSystemDarkMode) {
+ mode = ThemeMode.system;
+ } else if (settings.darkMode) {
+ mode = ThemeMode.dark;
+ } else {
+ mode = ThemeMode.light;
+ }
+
+ return MaterialApp(
+ title: 'Blood Pressure App',
+ theme: ThemeData(primaryColor: settings.accentColor),
+ darkTheme: ThemeData(
+ brightness: Brightness.dark,
+ canvasColor: Colors.black,
+ primaryColor: settings.accentColor.shade400,
+ ),
+ themeMode: mode,
+ home: const AppHome(),
+ );
+ });
}
}
-
-
-
-
-
test/model/bood_pressure_test.dart
@@ -1,40 +1,36 @@
import 'package:blood_pressure_app/model/blood_pressure.dart';
+import 'package:blood_pressure_app/model/ram_only_implementations.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
-import 'package:blood_pressure_app/model/ram_only_implementations.dart';
-
void main() {
group('BloodPressureRecord', () {
test('should initialize with all values supported by dart', () {
- BloodPressureRecord record = BloodPressureRecord(
- DateTime.fromMicrosecondsSinceEpoch(0),
- 0,
- -50,
- 1000,
+ BloodPressureRecord record = BloodPressureRecord(DateTime.fromMicrosecondsSinceEpoch(0), 0, -50, 1000,
"((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
expect(record.creationTime, DateTime.fromMicrosecondsSinceEpoch(0));
expect(record.systolic, 0);
expect(record.diastolic, -50);
expect(record.pulse, 1000);
- expect(record.notes, "((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
+ expect(record.notes,
+ "((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
});
});
- group('BloodPressureModel',() {
+ group('BloodPressureModel', () {
// setup db path
databaseFactory = databaseFactoryFfi;
test('should initialize', () async {
- expect(() async { await BloodPressureModel.create(dbPath: '/tmp/bp_test/should_init');
- }, returnsNormally);
+ expect(() async {
+ await BloodPressureModel.create(dbPath: '/tmp/bp_test/should_init');
+ }, returnsNormally);
});
test('should start empty', () async {
var m = await BloodPressureModel.create(dbPath: '/tmp/bp_test/should_start_empty');
expect((await m.getInTimeRange(DateTime.fromMillisecondsSinceEpoch(0), DateTime.now())).length, 0);
-
});
test('should notify when adding entries', () async {
@@ -55,7 +51,8 @@ void main() {
test('should return entries as added', () async {
var m = await BloodPressureModel.create(dbPath: '/tmp/bp_test/should_return_as_added');
- var r = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), -172, 10000, 0, "((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
+ var r = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), -172, 10000, 0,
+ "((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
m.addListener(() async {
var res = (await m.getInTimeRange(DateTime.fromMillisecondsSinceEpoch(0), DateTime.now())).first;
expect(res, isNotNull);
@@ -72,7 +69,8 @@ void main() {
test('should save and load between objects/sessions', () async {
var m = await BloodPressureModel.create(dbPath: '/tmp/bp_test/should_store_between_sessions');
- var r = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), -172, 10000, 0, "((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
+ var r = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), -172, 10000, 0,
+ "((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
await m.add(r);
var m2 = await BloodPressureModel.create(dbPath: '/tmp/bp_test/should_store_between_sessions');
@@ -87,7 +85,8 @@ void main() {
test('should import exported values', () async {
var m = await BloodPressureModel.create(dbPath: '/tmp/bp_test/should_import_exported');
- var r = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), -172, 10000, 0, "((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
+ var r = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), -172, 10000, 0,
+ "((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
await m.add(r);
m.save((success, msg) {
@@ -114,7 +113,7 @@ void main() {
await m.add(BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(1), 122, 87, 65, ''));
await m.add(BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(2), 100, 60, 62, ''));
await m.add(BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(3), 111, 73, 73, ''));
-
+
expect(await m.avgSys, 111); // 111 // gets 116
expect(await m.avgDia, 73); // 73.3333...
expect(await m.avgPul, 66); // 66.6666...
@@ -197,7 +196,8 @@ void main() {
test('should return entries as added', () async {
var m = RamBloodPressureModel();
- var r = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), -172, 10000, 0, "((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
+ var r = BloodPressureRecord(DateTime.fromMillisecondsSinceEpoch(31415926), -172, 10000, 0,
+ "((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈๏ แผ่นดินฮั่นเABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвг, \n \t д∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi�⑀₂ἠḂӥẄɐː⍎אԱა");
m.addListener(() async {
var res = (await m.getInTimeRange(DateTime.fromMillisecondsSinceEpoch(0), DateTime.now())).first;
expect(res, isNotNull);
@@ -212,7 +212,6 @@ void main() {
m.add(r);
});
-
test('should return averages', () async {
var m = RamBloodPressureModel();
@@ -273,4 +272,4 @@ void main() {
expect((await m.lastDay), DateTime.fromMillisecondsSinceEpoch(9000000));
});
});
-}
\ No newline at end of file
+}
test/model/settings_test.dart
@@ -18,12 +18,14 @@ void main() {
});
});
- group('Settings model',() {
+ group('Settings model', () {
// setup db path
databaseFactory = databaseFactoryFfi;
test('should initialize', () async {
- expect(() async { await Settings.create(); }, returnsNormally);
+ expect(() async {
+ await Settings.create();
+ }, returnsNormally);
});
test('fields defaults should be set after initialization', () async {
@@ -147,12 +149,14 @@ void main() {
});
});
- group('RamSettings model',() {
+ group('RamSettings model', () {
// setup db path
databaseFactory = databaseFactoryFfi;
test('should initialize', () async {
- expect(() async { RamSettings(); }, returnsNormally);
+ expect(() async {
+ RamSettings();
+ }, returnsNormally);
});
test('fields defaults should be set after initialization', () async {
@@ -275,4 +279,4 @@ void main() {
expect(i, 22);
});
});
-}
\ No newline at end of file
+}