Commit 9c763f7
Changed files (5)
lib/components/measurement_graph.dart
@@ -1,4 +1,174 @@
+import 'dart:collection';
+
+import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:flutter/material.dart';
+import 'package:fl_chart/fl_chart.dart';
+import 'package:provider/provider.dart';
+import 'dart:math';
+import 'package:intl/intl.dart';
+
+class _LineChart extends StatefulWidget {
+
+ @override
+ State<StatefulWidget> createState() {
+ return _LineChartState();
+ }
+
+}
+
+class _LineChartState extends State<_LineChart> {
+ static const _recordsCount = 50;
+ var _displayMode = DisplayModes.day;
+
+ @override
+ Widget build(BuildContext context) {
+ const _pulseColor = Colors.red;
+ const _diaColor = Colors.green;
+ const _sysColor = Colors.teal;
+
+
+ return Consumer<BloodPressureModel>(
+ builder: (context, model, child) {
+ late final _dataFuture;
+ DateTime now = DateTime.now();
+ switch (_displayMode) {
+ case DisplayModes.day:
+ _dataFuture = model.getInTimeRange(DateTime(now.year, now.month, now.day), now);
+ break;
+ case DisplayModes.month:
+ _dataFuture = model.getInTimeRange(DateTime(now.year, now.month), now);
+ break;
+ case DisplayModes.year:
+ _dataFuture = model.getInTimeRange(DateTime(now.year), now);
+ break;
+ case DisplayModes.lifetime:
+ _dataFuture = model.getInTimeRange(DateTime.fromMillisecondsSinceEpoch(0), now);
+ break;
+ }
+
+
+ return FutureBuilder<UnmodifiableListView<BloodPressureRecord>>(
+ future: _dataFuture,
+ builder: (BuildContext context, AsyncSnapshot<UnmodifiableListView<BloodPressureRecord>> snapshot) {
+ Widget res;
+ switch (snapshot.connectionState) {
+ case ConnectionState.none:
+ res = const Text('not started');
+ break;
+ case ConnectionState.waiting:
+ res = const Text('loading...');
+ break;
+ default:
+ if (snapshot.hasError) {
+ res = Text('ERROR: ${snapshot.error}');
+ } else {
+ assert(snapshot.hasData);
+ final data = snapshot.data ?? [];
+
+ List<FlSpot> pulseSpots = [];
+ List<FlSpot> diastolicSpots = [];
+ List<FlSpot> systolicSpots = [];
+ int pulMax = 0;
+ int diaMax = 0;
+ int sysMax = 0;
+ for (var element in data) {
+ final x = element.creationTime.millisecondsSinceEpoch.toDouble();
+ diastolicSpots.add(FlSpot(x, element.diastolic.toDouble()));
+ systolicSpots.add(FlSpot(x, element.systolic.toDouble()));
+ pulseSpots.add(FlSpot(x, element.pulse.toDouble()));
+ pulMax = max(pulMax, element.pulse);
+ diaMax = max(diaMax, element.diastolic);
+ sysMax = max(sysMax, element.systolic);
+ }
+
+
+ final noTitels = AxisTitles(sideTitles: SideTitles(reservedSize: 40, showTitles: false));
+ res = LineChart(
+ swapAnimationDuration: const Duration(milliseconds: 250),
+ LineChartData(
+ minY: 30,
+ maxY: max(pulMax.toDouble(), max(diaMax.toDouble(), sysMax.toDouble())) + 5,
+ titlesData: FlTitlesData(topTitles: noTitels, rightTitles: noTitels,
+ bottomTitles: AxisTitles(
+ sideTitles: SideTitles(
+ showTitles: true,
+ getTitlesWidget: (double pos, TitleMeta meta) {
+ late final DateFormat formater;
+ switch (_displayMode) {
+ case DisplayModes.day:
+ formater = DateFormat('H:mm');
+ break;
+ case DisplayModes.month:
+ formater = DateFormat('d');
+ break;
+ case DisplayModes.year:
+ formater = DateFormat('MMM');
+ break;
+ case DisplayModes.lifetime:
+ formater = DateFormat('yyyy');
+ }
+ return Text(
+ formater.format(DateTime.fromMillisecondsSinceEpoch(pos.toInt()))
+ );
+ }
+ ),
+ )
+ ),
+ lineBarsData: [
+ // high blood pressure marking acordning to https://www.texasheart.org/heart-health/heart-information-center/topics/high-blood-pressure-hypertension/
+ LineChartBarData(
+ spots: pulseSpots,
+ color: _pulseColor,
+ barWidth: 4,
+ isCurved: true,
+ preventCurveOverShooting: true
+ ),
+ LineChartBarData(
+ spots: diastolicSpots,
+ color: _diaColor,
+ barWidth: 4,
+ isCurved: true,
+ preventCurveOverShooting: true,
+ belowBarData: BarAreaData(
+ show: true,
+ color: Colors.red.shade400.withAlpha(100),
+ cutOffY: 80,
+ applyCutOffY: true
+ )
+ ),
+ LineChartBarData(
+ spots: systolicSpots,
+ color: _sysColor,
+ barWidth: 4,
+ isCurved: true,
+ preventCurveOverShooting: true,
+ belowBarData: BarAreaData(
+ show: true,
+ color: Colors.red.shade400.withAlpha(100),
+ cutOffY: 130,
+ applyCutOffY: true
+ )
+ )
+ ]
+ )
+ );
+ }
+ }
+ return res;
+ }
+ );
+ },
+ );
+ }
+
+}
+
+class DisplayModes {
+ static const day = 0;
+ static const month = 1;
+ static const year = 2;
+ static const lifetime = 3;
+}
class MeasurementGraph extends StatelessWidget {
const MeasurementGraph({super.key});
@@ -6,6 +176,12 @@ class MeasurementGraph extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
- return Container();
+ return Container(
+ height: 100,
+ child: Padding(
+ padding: const EdgeInsets.only(right: 16, left: 6),
+ child: _LineChart(),
+ ),
+ );
}
}
\ No newline at end of file
lib/components/measurement_list.dart
@@ -93,6 +93,7 @@ class MeasurementList extends StatelessWidget {
return Container(
child: Column (
children: [
+ const SizedBox(height: 15 ),
Row(
children: [
Expanded(
@@ -105,15 +106,18 @@ class MeasurementList extends StatelessWidget {
),
Expanded(
flex: _tableElementsSizes[1],
- child: Text("sys", style: TextStyle(fontWeight: FontWeight.bold))
+ child: const Text("sys",
+ style: TextStyle(fontWeight: FontWeight.bold, color: Colors.teal))
),
Expanded(
flex: _tableElementsSizes[2],
- child: Text("dia", style: TextStyle(fontWeight: FontWeight.bold))
+ child: const Text("dia",
+ style: TextStyle(fontWeight: FontWeight.bold, color: Colors.green))
),
Expanded(
flex: _tableElementsSizes[3],
- child: Text("pul", style: TextStyle(fontWeight: FontWeight.bold))
+ child: const Text("pul",
+ style: TextStyle(fontWeight: FontWeight.bold, color: Colors.red))
),
Expanded(
flex: _tableElementsSizes[4],
lib/model/blood_pressure.dart
@@ -29,9 +29,6 @@ class BloodPressureModel extends ChangeNotifier {
return component;
}
- // All measurements (might get slow after some time)
- UnmodifiableListView<BloodPressureRecord> get allMeasurements => UnmodifiableListView(_allMeasurements);
-
Future<void> _cacheLast() async {
var dbEntries = await _database.query('bloodPressureModel',
orderBy: 'timestamp DESC', limit: _cacheCount); // descending
@@ -63,8 +60,8 @@ class BloodPressureModel extends ChangeNotifier {
_cacheLast();
}
- /// Returns the last x BloodPressureRecords from new to old
- /// caches new ones if necessary
+ /// Returns the last x BloodPressureRecords from new to old.
+ /// Caches new ones if necessary
UnmodifiableListView<BloodPressureRecord> getLastX(int count) {
List<BloodPressureRecord> lastMeasurements = [];
@@ -82,6 +79,26 @@ class BloodPressureModel extends ChangeNotifier {
return UnmodifiableListView(lastMeasurements);
}
+
+ /// Returns all recordings in saved in a range in ascending order
+ Future<UnmodifiableListView<BloodPressureRecord>> getInTimeRange(DateTime from, DateTime to) async {
+ var dbEntries = await _database.query('bloodPressureModel',
+ orderBy: 'timestamp DESC',
+ where: 'timestamp BETWEEN ? AND ?',
+ whereArgs: [from.millisecondsSinceEpoch, to.millisecondsSinceEpoch]
+ ); // descending
+ // syncronous part
+ List<BloodPressureRecord> recordsInRange = [];
+ for (var e in dbEntries) {
+ recordsInRange.add(BloodPressureRecord(
+ DateTime.fromMillisecondsSinceEpoch(e['timestamp']as int),
+ e['systolic'] as int,
+ e['diastolic'] as int,
+ e['pulse'] as int,
+ e['notes'] as String));
+ }
+ return UnmodifiableListView(recordsInRange);
+ }
/* TODO:
pubspec.lock
@@ -49,6 +49,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.5"
+ equatable:
+ dependency: transitive
+ description:
+ name: equatable
+ sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.5"
fake_async:
dependency: transitive
description:
@@ -65,6 +73,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
+ fl_chart:
+ dependency: "direct main"
+ description:
+ name: fl_chart
+ sha256: "48a1b69be9544e2b03d9a8e843affd89e43f3194c9248776222efcb4206bb1ec"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.62.0"
flutter:
dependency: "direct main"
description: flutter
pubspec.yaml
@@ -39,6 +39,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
intl: ^0.18.1
+ fl_chart: ^0.62.0
dev_dependencies:
flutter_test: