Commit e67fdff
Changed files (4)
lib
components
screens
lib/components/statistics/blood_pressure_distribution.dart
@@ -52,7 +52,7 @@ class _BloodPressureDistributionState extends State<BloodPressureDistribution>
children: [
DecoratedBox(
decoration: BoxDecoration(
- color: Theme.of(context).primaryColor,
+ color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(50),
),
child: TabBar.secondary(
lib/components/statistics/value_distribution.dart
@@ -8,9 +8,9 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
///
/// Shows in form of a graph that has centered columns of different height and
/// labels that indicate min, max and average.
-class LinearBarDistribution extends StatelessWidget {
+class ValueDistribution extends StatelessWidget {
/// Create a statistic to show how often values occur.
- const LinearBarDistribution({
+ const ValueDistribution({
super.key,
required this.values,
required this.color,
@@ -49,7 +49,7 @@ class LinearBarDistribution extends StatelessWidget {
assert(distribution[distribution.keys.max]! > 0);
assert(distribution[distribution.keys.min]! > 0);
return CustomPaint(
- painter: _LinearBarDistributionPainter(
+ painter: _ValueDistributionPainter(
distribution,
AppLocalizations.of(context)!,
color,
@@ -60,9 +60,9 @@ class LinearBarDistribution extends StatelessWidget {
}
/// Painter of a horizontal array vertical bars.
-class _LinearBarDistributionPainter extends CustomPainter {
+class _ValueDistributionPainter extends CustomPainter {
/// Painter of a horizontal array vertical bars.
- _LinearBarDistributionPainter(
+ _ValueDistributionPainter(
this.distribution,
this.localizations,
this.barColor,
@@ -134,7 +134,7 @@ class _LinearBarDistributionPainter extends CustomPainter {
final style = TextStyle(
color: _kDecorationColor,
backgroundColor: Colors.black.withOpacity(0.5),
- fontSize: 12,
+ fontSize: 16,
);
final textSpan = TextSpan(
text: text,
@@ -167,7 +167,9 @@ class _LinearBarDistributionPainter extends CustomPainter {
}
@override
- bool shouldRepaint(covariant _LinearBarDistributionPainter oldDelegate) =>
+ bool shouldRepaint(covariant _ValueDistributionPainter oldDelegate) =>
distribution == oldDelegate.distribution;
}
+
+ // TODO: Consider adding semantics
lib/model/blood_pressure_analyzer.dart
@@ -3,6 +3,8 @@ import 'dart:math';
import 'package:blood_pressure_app/model/blood_pressure/record.dart';
import 'package:collection/collection.dart';
+// TODO: consider removing avg methods
+
/// Analysis utils for a list of blood pressure records.
class BloodPressureAnalyser {
/// Create a analyzer for a list of records.
lib/screens/statistics_screen.dart
@@ -1,4 +1,6 @@
+import 'package:blood_pressure_app/components/settings/settings_widgets.dart';
+import 'package:blood_pressure_app/components/statistics/blood_pressure_distribution.dart';
import 'package:blood_pressure_app/model/blood_pressure_analyzer.dart';
import 'package:blood_pressure_app/model/storage/intervall_store.dart';
import 'package:blood_pressure_app/model/storage/settings_store.dart';
@@ -9,143 +11,107 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
-
+/// A page that shows statistics about stored blood pressure values.
class StatisticsPage extends StatelessWidget {
const StatisticsPage({super.key});
@override
- Widget build(BuildContext context) => Scaffold(
+ Widget build(BuildContext context) {
+ final localizations = AppLocalizations.of(context)!;
+ return Scaffold(
appBar: AppBar(
- title: Text(AppLocalizations.of(context)!.statistics),
- backgroundColor: Theme.of(context).primaryColor,
+ title: Text(localizations.statistics),
),
- body: SingleChildScrollView(
- child: Consumer<Settings>(builder: (context, settings, child) => BloodPressureBuilder(
- rangeType: IntervallStoreManagerLocation.statsPage,
- onData: (context, data) {
- final analyzer = BloodPressureAnalyser(data.toList());
- return Column(
- children: [
- Statistic(
- key: const Key('measurementCount'),
- caption: Text(AppLocalizations.of(context)!.measurementCount), child: displayInt(analyzer.count),),
- // special measurements
- StatisticsRow(
- caption1: Text(
- AppLocalizations.of(context)!.avgOf(AppLocalizations.of(context)!.sysLong),
- style: TextStyle(color: settings.sysColor, fontWeight: FontWeight.w700),
- ),
- child1: displayInt(analyzer.avgSys),
- caption2: Text(
- AppLocalizations.of(context)!.avgOf(AppLocalizations.of(context)!.diaLong),
- style: TextStyle(color: settings.diaColor, fontWeight: FontWeight.w700),
- ),
- child2: displayInt(analyzer.avgDia),
- caption3: Text(
- AppLocalizations.of(context)!.avgOf(AppLocalizations.of(context)!.pulLong),
- style: TextStyle(color: settings.pulColor, fontWeight: FontWeight.w700),
- ),
- child3: displayInt(analyzer.avgPul),
+ body: Consumer<Settings>(
+ builder: (context, settings, child) => BloodPressureBuilder(
+ rangeType: IntervallStoreManagerLocation.statsPage,
+ onData: (context, data) {
+ final analyzer = BloodPressureAnalyser(data.toList());
+ return ListView(
+ children: [
+ ListTile(
+ title: Text(localizations.measurementCount),
+ trailing: Text(
+ data.length.toString(),
+ style: Theme.of(context).textTheme.headlineSmall,
),
- Statistic(
- caption: Text(AppLocalizations.of(context)!.measurementsPerDay),
- child: displayInt(analyzer.measurementsPerDay),),
- StatisticsRow(
- caption1: Text(
- AppLocalizations.of(context)!.minOf(AppLocalizations.of(context)!.sysLong),
- style: TextStyle(color: settings.sysColor, fontWeight: FontWeight.w700),
- ),
- child1: displayInt(analyzer.minSys),
- caption2: Text(
- AppLocalizations.of(context)!.minOf(AppLocalizations.of(context)!.diaLong),
- style: TextStyle(color: settings.diaColor, fontWeight: FontWeight.w700),
- ),
- child2: displayInt(analyzer.minDia),
- caption3: Text(
- AppLocalizations.of(context)!.minOf(AppLocalizations.of(context)!.pulLong),
- style: TextStyle(color: settings.pulColor, fontWeight: FontWeight.w700),
- ),
- child3: displayInt(analyzer.minPul),
+ ),
+ ListTile(
+ title: Text(localizations.measurementsPerDay),
+ trailing: Text(
+ analyzer.measurementsPerDay.toString(),
+ style: Theme.of(context).textTheme.headlineSmall,
),
- StatisticsRow(
- caption2: Text(
- AppLocalizations.of(context)!.maxOf(AppLocalizations.of(context)!.diaLong),
- style: TextStyle(color: settings.diaColor, fontWeight: FontWeight.w700),
- ),
- child2: displayInt(analyzer.maxDia),
- caption1: Text(
- AppLocalizations.of(context)!.maxOf(AppLocalizations.of(context)!.sysLong),
- style: TextStyle(color: settings.sysColor, fontWeight: FontWeight.w700),
- ),
- child1: displayInt(analyzer.maxSys),
- caption3: Text(
- AppLocalizations.of(context)!.maxOf(AppLocalizations.of(context)!.pulLong),
- style: TextStyle(color: settings.pulColor, fontWeight: FontWeight.w700),
- ),
- child3: displayInt(analyzer.maxPul),
+ ),
+ SizedBox(
+ height: 260,
+ child: BloodPressureDistribution(
+ records: data,
+ settings: settings,
),
- // Time-Resolved Metrics
- Statistic(
- caption: Text(AppLocalizations.of(context)!.timeResolvedMetrics),
- child: () {
- final data = analyzer.allAvgsRelativeToDaytime;
- const opacity = 0.5;
- return SizedBox(
- width: 500,
- height: 500,
- 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(data[0]),
- borderColor: settings.diaColor,
- fillColor: settings.diaColor.withOpacity(opacity),
- entryRadius: 0,
- borderWidth: settings.graphLineThickness,),
- RadarDataSet(
- dataEntries: intListToRadarEntry(data[1]),
- borderColor: settings.sysColor,
- fillColor: settings.sysColor.withOpacity(opacity),
- entryRadius: 0,
- borderWidth: settings.graphLineThickness,),
- RadarDataSet(
- dataEntries: intListToRadarEntry(data[2]),
- borderColor: settings.pulColor,
- fillColor: settings.pulColor.withOpacity(opacity),
- entryRadius: 0,
- borderWidth: settings.graphLineThickness,),
- ],
- ),
+ ),
+ TitledColumn( // TODO: rewrite this one, maybe another tab bar (layout wise)
+ title: Text(AppLocalizations.of(context)!.timeResolvedMetrics),
+ children: [() {
+ final data = analyzer.allAvgsRelativeToDaytime;
+ const opacity = 0.5;
+ return SizedBox(
+ width: 500,
+ height: 500,
+ 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(data[0]),
+ borderColor: settings.diaColor,
+ fillColor: settings.diaColor.withOpacity(opacity),
+ entryRadius: 0,
+ borderWidth: settings.graphLineThickness,),
+ RadarDataSet(
+ dataEntries: _intListToRadarEntry(data[1]),
+ borderColor: settings.sysColor,
+ fillColor: settings.sysColor.withOpacity(opacity),
+ entryRadius: 0,
+ borderWidth: settings.graphLineThickness,),
+ RadarDataSet(
+ dataEntries: _intListToRadarEntry(data[2]),
+ borderColor: settings.pulColor,
+ fillColor: settings.pulColor.withOpacity(opacity),
+ entryRadius: 0,
+ borderWidth: settings.graphLineThickness,),
+ ],
),
- );
- }(),
- ),
- ],
- );
- },
- ),
- ),),
+ ),
+ );
+ }()],
+ ),
+ ],
+ );
+ },
+ ),
+ ),
bottomNavigationBar: Container(
height: 70,
margin: const EdgeInsets.only(top: 15, bottom: 5),
child: const IntervalPicker(type: IntervallStoreManagerLocation.statsPage,),
),
);
+ }
- List<RadarEntry> intListToRadarEntry(List<int> data) {
+ List<RadarEntry> _intListToRadarEntry(List<int> data) {
final res = <RadarEntry>[];
for (final v in data) {
res.add(RadarEntry(value: v.toDouble()));
@@ -153,118 +119,3 @@ class StatisticsPage extends StatelessWidget {
return res;
}
}
-
-class Statistic extends StatelessWidget {
-
- const Statistic({super.key, required this.caption, required this.child, this.smallEdges = false});
- final Widget caption;
- final Widget child;
- /// Reduces the padding at the sites to allow packing [Statistic] widgets tighter together.
- final bool smallEdges;
-
- @override
- Widget build(BuildContext context) {
- const double top = 20;
- double sides = 20;
- double padding = 20;
- if (smallEdges) {
- sides = 0;
- padding = 10;
- }
- return Container(
- margin: EdgeInsets.only(left: sides, right: sides, top: top),
- constraints: const BoxConstraints(minHeight: 50, minWidth: 110),
- decoration: BoxDecoration(
- border: Border.all(width: 3, color: Theme.of(context).textTheme.bodyMedium?.color ?? Colors.white38),
- borderRadius: const BorderRadius.all(Radius.circular(25)),
- ),
- child: Stack(
- children: [
- Positioned(
- top: 6,
- left: 12,
- child: DefaultTextStyle(
- 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),
- child: Align(
- child: DefaultTextStyle(
- style: Theme.of(context).textTheme.displaySmall!,
- child: FittedBox(
- fit: BoxFit.fitHeight,
- child: child,
- ),
- ),
- ),
- ),
- ],
- ),
- );
- }
-}
-
-class StatisticsRow extends StatelessWidget {
-
- const StatisticsRow(
- {super.key,
- required this.caption1,
- required this.caption2,
- required this.caption3,
- required this.child1,
- required this.child2,
- required this.child3,});
- final Widget caption1;
- final Widget caption2;
- final Widget caption3;
- final Widget child1;
- final Widget child2;
- final Widget child3;
-
- @override
- Widget build(BuildContext context) => Row(
- children: [
- const SizedBox(
- width: 20,
- ),
- Expanded(
- child: Statistic(
- smallEdges: true,
- caption: caption1,
- child: child1,
- ),
- ),
- const SizedBox(
- width: 10,
- ),
- Expanded(
- child: Statistic(
- smallEdges: true,
- caption: caption2,
- child: child2,
- ),
- ),
- const SizedBox(
- width: 10,
- ),
- Expanded(
- child: Statistic(
- smallEdges: true,
- caption: caption3,
- child: child3,
- ),
- ),
- const SizedBox(
- width: 20,
- ),
- ],
- );
-}
-
-Widget displayInt(int value) {
- if (value < 0) {
- return const Text('-');
- }
- return Text(value.toString());
-}