Commit e67fdff

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2024-01-19 19:10:31
Rewrite statistics screen with new widgets
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 6d78d26
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());
-}