Commit ae9ccfa
Changed files (4)
lib
screens
lib/components/measurement_graph.dart
@@ -1,6 +1,7 @@
import 'dart:collection';
import 'dart:math';
+import 'package:blood_pressure_app/components/consistent_future_builder.dart';
import 'package:blood_pressure_app/components/display_interval_picker.dart';
import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/settings_store.dart';
@@ -34,139 +35,127 @@ class _LineChartState extends State<_LineChart> {
builder: (context, settings, child) {
return Consumer<BloodPressureModel>(builder: (context, model, child) {
var end = settings.displayDataEnd;
+ return ConsistentFutureBuilder<UnmodifiableListView<BloodPressureRecord>>(
+ future: (settings.graphStepSize == TimeStep.lifetime)
+ ? model.all
+ : model.getInTimeRange(settings.displayDataStart, end),
+ onData: (context, fetchedData) {
+ if (fetchedData.length < 2) {
+ return Text(AppLocalizations.of(context)!.errNotEnoughDataToGraph);
+ }
+ List<BloodPressureRecord> data = fetchedData.toList();
+ data.sort((a, b) => a.creationTime.compareTo(b.creationTime));
- return FutureBuilder<UnmodifiableListView<BloodPressureRecord>>(
- future: (settings.graphStepSize == TimeStep.lifetime)
- ? model.all
- : model.getInTimeRange(settings.displayDataStart, end),
- builder:
- (BuildContext context, AsyncSnapshot<UnmodifiableListView<BloodPressureRecord>> snapshot) {
- switch (snapshot.connectionState) {
- case ConnectionState.none:
- return Text(AppLocalizations.of(context)!.errNotStarted);
- case ConnectionState.waiting:
- return Text(AppLocalizations.of(context)!.loading);
- default:
- if (snapshot.hasError) {
- return Text(AppLocalizations.of(context)!.error(snapshot.error.toString()));
- } else if (snapshot.hasData && snapshot.data!.length < 2) {
- return Text(AppLocalizations.of(context)!.errNotEnoughDataToGraph);
- } else {
- assert(snapshot.hasData);
- List<BloodPressureRecord> data = snapshot.data?.toList() ?? [];
- data.sort((a, b) => a.creationTime.compareTo(b.creationTime));
+ 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);
+ }
- 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);
- }
+ const noTitels = AxisTitles(sideTitles: SideTitles(reservedSize: 40, showTitles: false));
+ return LineChart(
+ LineChartData(
+ minY: settings.validateInputs ? 30 : 0,
+ maxY: max(pulMax.toDouble(), max(diaMax.toDouble(), sysMax.toDouble())) + 5,
+ titlesData: FlTitlesData(
+ topTitles: noTitels,
+ rightTitles: noTitels,
+ bottomTitles: AxisTitles(
+ sideTitles: SideTitles(
+ showTitles: true,
+ interval: _lineChartTitleIntervall,
+ getTitlesWidget: (double pos, TitleMeta meta) {
+ // calculate new intervall
+ double graphWidth = meta.max - meta.min;
+ assert(graphWidth > 0);
+ if (((graphWidth - 2) / settings.graphTitlesCount) !=
+ _lineChartTitleIntervall) {
+ // simple hack needed to change the state during build
+ // https://stackoverflow.com/a/63607696/21489239
+ Future.delayed(Duration.zero, () async {
+ setState(() {
+ _lineChartTitleIntervall =
+ (graphWidth - 2) / settings.graphTitlesCount;
+ });
+ });
+ }
- const noTitels = AxisTitles(sideTitles: SideTitles(reservedSize: 40, showTitles: false));
- return LineChart(
- LineChartData(
- minY: settings.validateInputs ? 30 : 0,
- maxY: max(pulMax.toDouble(), max(diaMax.toDouble(), sysMax.toDouble())) + 5,
- titlesData: FlTitlesData(
- topTitles: noTitels,
- rightTitles: noTitels,
- bottomTitles: AxisTitles(
- sideTitles: SideTitles(
- showTitles: true,
- interval: _lineChartTitleIntervall,
- getTitlesWidget: (double pos, TitleMeta meta) {
- // calculate new intervall
- double graphWidth = meta.max - meta.min;
- assert(graphWidth > 0);
- if (((graphWidth - 2) / settings.graphTitlesCount) !=
- _lineChartTitleIntervall) {
- // simple hack needed to change the state during build
- // https://stackoverflow.com/a/63607696/21489239
- Future.delayed(Duration.zero, () async {
- setState(() {
- _lineChartTitleIntervall =
- (graphWidth - 2) / settings.graphTitlesCount;
- });
- });
- }
+ // don't show fixed titles, as they are replaced by long dates below
+ if (meta.axisPosition <= 1 || pos >= meta.max) {
+ return const SizedBox.shrink();
+ }
- // don't show fixed titles, as they are replaced by long dates below
- if (meta.axisPosition <= 1 || pos >= meta.max) {
- return const SizedBox.shrink();
- }
-
- late final DateFormat formatter;
- switch (settings.graphStepSize) {
- case TimeStep.day:
- formatter = DateFormat('H:m');
- break;
- case TimeStep.month:
- formatter = DateFormat('d');
- break;
- case TimeStep.week:
- formatter = DateFormat('E');
- break;
- case TimeStep.year:
- formatter = DateFormat('MMM');
- break;
- case TimeStep.lifetime:
- formatter = DateFormat('yyyy');
- }
- return Text(formatter
- .format(DateTime.fromMillisecondsSinceEpoch(pos.toInt())));
- }),
- ),
- ),
- lineTouchData: LineTouchData(
- touchTooltipData:
- LineTouchTooltipData(tooltipMargin: -200, tooltipRoundedRadius: 20)),
- lineBarsData: [
- LineChartBarData(
- spots: pulseSpots,
- dotData: FlDotData(
- show: false,
- ),
- color: settings.pulColor,
- barWidth: settings.graphLineThickness,
- ),
- LineChartBarData(
- spots: diastolicSpots,
- color: settings.diaColor,
- barWidth: settings.graphLineThickness,
- dotData: FlDotData(
- show: false,
- ),
- belowBarData: BarAreaData(
- show: true,
- color: Colors.red.shade400.withAlpha(100),
- cutOffY: settings.diaWarn,
- applyCutOffY: true)),
- LineChartBarData(
- spots: systolicSpots,
- color: settings.sysColor,
- barWidth: settings.graphLineThickness,
- dotData: FlDotData(
- show: false,
- ),
- belowBarData: BarAreaData(
- show: true,
- color: Colors.red.shade400.withAlpha(100),
- cutOffY: settings.sysWarn,
- applyCutOffY: true))
- ]));
- }
- }
- });
+ late final DateFormat formatter;
+ switch (settings.graphStepSize) {
+ case TimeStep.day:
+ formatter = DateFormat('H:m');
+ break;
+ case TimeStep.month:
+ formatter = DateFormat('d');
+ break;
+ case TimeStep.week:
+ formatter = DateFormat('E');
+ break;
+ case TimeStep.year:
+ formatter = DateFormat('MMM');
+ break;
+ case TimeStep.lifetime:
+ formatter = DateFormat('yyyy');
+ }
+ return Text(formatter
+ .format(DateTime.fromMillisecondsSinceEpoch(pos.toInt())));
+ }),
+ ),
+ ),
+ lineTouchData: const LineTouchData(
+ touchTooltipData:
+ LineTouchTooltipData(tooltipMargin: -200, tooltipRoundedRadius: 20)),
+ lineBarsData: [
+ LineChartBarData(
+ spots: pulseSpots,
+ dotData: const FlDotData(
+ show: false,
+ ),
+ color: settings.pulColor,
+ barWidth: settings.graphLineThickness,
+ ),
+ LineChartBarData(
+ spots: diastolicSpots,
+ color: settings.diaColor,
+ barWidth: settings.graphLineThickness,
+ dotData: const FlDotData(
+ show: false,
+ ),
+ belowBarData: BarAreaData(
+ show: true,
+ color: Colors.red.shade400.withAlpha(100),
+ cutOffY: settings.diaWarn,
+ applyCutOffY: true)),
+ LineChartBarData(
+ spots: systolicSpots,
+ color: settings.sysColor,
+ barWidth: settings.graphLineThickness,
+ dotData: const FlDotData(
+ show: false,
+ ),
+ belowBarData: BarAreaData(
+ show: true,
+ color: Colors.red.shade400.withAlpha(100),
+ cutOffY: settings.sysWarn,
+ applyCutOffY: true))
+ ]));
+ }
+ );
});
},
)),
lib/components/measurement_list.dart
@@ -1,5 +1,6 @@
import 'dart:collection';
+import 'package:blood_pressure_app/components/consistent_future_builder.dart';
import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:blood_pressure_app/screens/add_measurement.dart';
@@ -69,147 +70,138 @@ class MeasurementList extends StatelessWidget {
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);
+ return ConsistentFutureBuilder<UnmodifiableListView<BloodPressureRecord>>(
+ future: items,
+ onData: (context, data) {
+ if (data.isNotEmpty && data.first.diastolic > 0) {
+ return ListView.builder(
+ itemCount: data.length,
+ shrinkWrap: true,
+ padding: const EdgeInsets.all(2),
+ itemBuilder: (context, index) {
+ final formatter = DateFormat(settings.dateFormatString);
+ return Column(
+ children: [
+ Dismissible(
+ key: Key(data[index].creationTime.toIso8601String()),
+ confirmDismiss: (direction) async {
+ final model = Provider.of<BloodPressureModel>(context, listen: false);
+ if (direction == DismissDirection.startToEnd) { // edit
+ model.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,
+ )),
+ );
+ return false;
+ } else { // delete
+ bool dialogeDeletionConfirmed = false;
+ if (settings.confirmDeletion) {
+ await showDialog(
+ context: context,
+ builder: (context) {
+ return AlertDialog(
+ title: Text(AppLocalizations.of(context)!.confirmDelete),
+ content: Text(AppLocalizations.of(context)!.confirmDeleteDesc),
+ actions: [
+ ElevatedButton(
+ onPressed: () => Navigator.of(context).pop(),
+ child: Text(AppLocalizations.of(context)!.btnCancel)),
+ ElevatedButton(
+ onPressed: () {
+ model.delete(data[index].creationTime);
- if (recordsSnapshot.connectionState == ConnectionState.waiting) {
- return Text(AppLocalizations.of(context)!.loading);
- } else if (recordsSnapshot.hasError) {
- return Text(AppLocalizations.of(context)!.error(recordsSnapshot.error.toString()));
- } 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),
- itemBuilder: (context, index) {
- final formatter = DateFormat(settings.dateFormatString);
- return Column(
- children: [
- Dismissible(
- key: Key(data[index].creationTime.toIso8601String()),
- confirmDismiss: (direction) async {
- final model = Provider.of<BloodPressureModel>(context, listen: false);
- if (direction == DismissDirection.startToEnd) { // edit
- model.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,
- )),
- );
- return false;
- } else { // delete
- bool dialogeDeletionConfirmed = false;
- if (settings.confirmDeletion) {
- await showDialog(
- context: context,
- builder: (context) {
- return AlertDialog(
- title: Text(AppLocalizations.of(context)!.confirmDelete),
- content: Text(AppLocalizations.of(context)!.confirmDeleteDesc),
- actions: [
- ElevatedButton(
- onPressed: () => Navigator.of(context).pop(),
- child: Text(AppLocalizations.of(context)!.btnCancel)),
- ElevatedButton(
- onPressed: () {
- model.delete(data[index].creationTime);
+ dialogeDeletionConfirmed = true;
+ Navigator.of(context).pop();
+ },
+ child: Text(AppLocalizations.of(context)!.btnConfirm)),
+ ],
+ );
+ });
+ } else {
+ model.delete(data[index].creationTime);
+ dialogeDeletionConfirmed = true;
+ }
- dialogeDeletionConfirmed = true;
- Navigator.of(context).pop();
- },
- child: Text(AppLocalizations.of(context)!.btnConfirm)),
- ],
- );
- });
- } else {
- model.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: Text(AppLocalizations.of(context)!.deletionConfirmed),
- action: SnackBarAction(
- label: AppLocalizations.of(context)!.btnUndo,
- onPressed: () async {
- model.add(BloodPressureRecord(
- data[index].creationTime,
- data[index].systolic,
- data[index].diastolic,
- data[index].pulse,
- data[index].notes));
- },
- ),
- ));
- }
- return dialogeDeletionConfirmed;
- }
- },
- onDismissed: (direction) {},
- background: Container(
- width: 10,
- decoration:
- BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(5)),
- child: const Align(alignment: Alignment(-0.95, 0), child: Icon(Icons.edit)),
- ),
- secondaryBackground: Container(
- width: 10,
- decoration:
- BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(5)),
- child: const Align(alignment: Alignment(0.95, 0), child: Icon(Icons.delete)),
- ),
- child: Container(
- constraints: const BoxConstraints(minHeight: 40),
- child: Row(children: [
- Expanded(
- flex: _sideFlex,
- child: const SizedBox(),
+ if (dialogeDeletionConfirmed) {
+ if (!context.mounted) return true;
+ ScaffoldMessenger.of(context).removeCurrentSnackBar();
+ ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+ duration: const Duration(seconds: 5),
+ content: Text(AppLocalizations.of(context)!.deletionConfirmed),
+ action: SnackBarAction(
+ label: AppLocalizations.of(context)!.btnUndo,
+ onPressed: () async {
+ model.add(BloodPressureRecord(
+ data[index].creationTime,
+ data[index].systolic,
+ data[index].diastolic,
+ data[index].pulse,
+ data[index].notes));
+ },
),
- 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(),
- ),
- ]),
+ ));
+ }
+ return dialogeDeletionConfirmed;
+ }
+ },
+ onDismissed: (direction) {},
+ background: Container(
+ width: 10,
+ decoration:
+ BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(5)),
+ child: const Align(alignment: Alignment(-0.95, 0), child: Icon(Icons.edit)),
+ ),
+ secondaryBackground: Container(
+ width: 10,
+ decoration:
+ BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(5)),
+ child: const Align(alignment: Alignment(0.95, 0), child: Icon(Icons.delete)),
+ ),
+ child: Container(
+ constraints: const BoxConstraints(minHeight: 40),
+ child: Row(children: [
+ Expanded(
+ flex: _sideFlex,
+ child: const SizedBox(),
+ ),
+ Expanded(
+ flex: _tableElementsSizes[0],
+ child: Text(formatter.format(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,
- )
- ],
- );
- });
- } else {
- return Text(AppLocalizations.of(context)!.errNoData);
- }
- }
- });
+ ]),
+ ),
+ ),
+ const Divider(
+ thickness: 1,
+ height: 1,
+ )
+ ],
+ );
+ });
+ } else {
+ return Text(AppLocalizations.of(context)!.errNoData);
+ }
+ },
+ );
});
}),
)
lib/screens/settings.dart
@@ -1,3 +1,4 @@
+import 'package:blood_pressure_app/components/consistent_future_builder.dart';
import 'package:blood_pressure_app/components/settings_widgets.dart';
import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:blood_pressure_app/screens/subsettings/enter_timeformat.dart';
@@ -259,34 +260,20 @@ class SettingsPage extends StatelessWidget {
],
),
SettingsSection(title: const Text('about'), children: [
- FutureBuilder<PackageInfo>(
- future: PackageInfo.fromPlatform(),
- builder: (context, snapshot) {
- String description = AppLocalizations.of(context)!.errNotStarted;
- switch (snapshot.connectionState) {
- case ConnectionState.waiting:
- description = AppLocalizations.of(context)!.loading;
- break;
- default:
- if (snapshot.hasError) {
- description = (AppLocalizations.of(context)!.error(snapshot.error.toString()));
- } else if (snapshot.hasData && snapshot.data != null) {
- description = snapshot.data!.version;
- }
+ SettingsTile(
+ key: const Key('version'),
+ title: Text(AppLocalizations.of(context)!.version),
+ leading: const Icon(Icons.info_outline),
+ description: ConsistentFutureBuilder<PackageInfo>(
+ future: PackageInfo.fromPlatform(),
+ onData: (context, info) => Text(info.version)
+ ),
+ onPressed: (context) {
+ Navigator.push(
+ context,
+ MaterialPageRoute(builder: (context) => const VersionScreen()),
+ );
}
- return SettingsTile(
- key: const Key('version'),
- title: Text(AppLocalizations.of(context)!.version),
- leading: const Icon(Icons.info_outline),
- description: Text(description),
- onPressed: (context) {
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => const VersionScreen()),
- );
- }
- );
- },
),
SettingsTile(
key: const Key('sourceCode'),
lib/screens/statistics.dart
@@ -1,3 +1,4 @@
+import 'package:blood_pressure_app/components/consistent_future_builder.dart';
import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/blood_pressure_analyzer.dart';
import 'package:blood_pressure_app/model/settings_store.dart';
@@ -82,64 +83,51 @@ class StatisticsPage extends StatelessWidget {
// Time-Resolved Metrics
Statistic(
caption: Text(AppLocalizations.of(context)!.timeResolvedMetrics),
- child: FutureBuilder<List<List<int>>>(
+ child: ConsistentFutureBuilder<List<List<int>>>(
future: BloodPressureAnalyser(model).allAvgsRelativeToDaytime,
- builder: (BuildContext context, AsyncSnapshot<List<List<int>>> snapshot) {
- switch (snapshot.connectionState) {
- case ConnectionState.none:
- return Text(AppLocalizations.of(context)!.errNotStarted);
- case ConnectionState.waiting:
- return Text(AppLocalizations.of(context)!.loading);
- default:
- if (snapshot.hasError) {
- return Text(AppLocalizations.of(context)!.error(snapshot.error.toString()));
- }
- 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),
- ],
- ),
- ),
- );
- }
+ onData: (context, 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(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),
+ ],
+ ),
+ ),
+ );
}),
),
],
@@ -267,23 +255,13 @@ class StatisticsRow extends StatelessWidget {
}
Widget futureInt(Future<int> value) {
- return FutureBuilder<int>(
+ return ConsistentFutureBuilder<int>(
future: value,
- builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
- switch (snapshot.connectionState) {
- case ConnectionState.none:
- return Text(AppLocalizations.of(context)!.errNotStarted);
- case ConnectionState.waiting:
- return Text(AppLocalizations.of(context)!.loading);
- default:
- if (snapshot.hasError) {
- return Text(AppLocalizations.of(context)!.error(snapshot.error.toString()));
- }
- assert(snapshot.hasData);
- if ((snapshot.data ?? -1) < 0) {
- return const Text('-');
- }
- return Text(snapshot.data.toString());
+ onData: (context, data) {
+ if (data < 0) {
+ return const Text('-');
}
- });
+ return Text(data.toString());
+ }
+ );
}