Commit 4984a5d
Changed files (8)
lib
components
l10n
screens
subsettings
lib/components/input_dialoge.dart
@@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
+// TODO: redo dialoges in flutter style
class InputDialoge extends StatefulWidget {
final String hintText;
final void Function(String text) onSubmit;
lib/components/measurement_graph.dart
@@ -3,6 +3,7 @@ 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/horizontal_graph_line.dart';
import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:collection/collection.dart';
import 'package:fl_chart/fl_chart.dart';
@@ -42,6 +43,8 @@ class _LineChartState extends State<_LineChart> {
// calculate lines for graph
List<FlSpot> pulSpots = [], diaSpots = [], sysSpots = [];
int maxValue = 0;
+ double? graphBegin;
+ double? graphEnd;
for (var e in data) {
final x = e.creationTime.millisecondsSinceEpoch.toDouble();
if (e.diastolic != null) {
@@ -56,9 +59,13 @@ class _LineChartState extends State<_LineChart> {
pulSpots.add(FlSpot(x, e.pulse!.toDouble()));
maxValue = max(maxValue, e.pulse!);
}
+ graphBegin ??= x;
+ graphEnd ??= x;
+ if (x < graphBegin) graphBegin = x;
+ if (x > graphEnd) graphEnd = x;
}
- if (diaSpots.length < 2 && sysSpots.length < 2 && pulSpots.length < 2) {
+ if (diaSpots.length < 2 && sysSpots.length < 2 && pulSpots.length < 2 || graphBegin == null || graphEnd == null) {
return Text(AppLocalizations.of(context)!.errNotEnoughDataToGraph);
}
@@ -86,6 +93,8 @@ class _LineChartState extends State<_LineChart> {
_buildRegressionLine(diaSpots),
if (settings.drawRegressionLines)
_buildRegressionLine(pulSpots),
+ for (final horizontalLine in settings.horizontalGraphLines)
+ _buildHorizontalLine(horizontalLine, graphBegin!, graphEnd!),
]
),
);
@@ -197,6 +206,21 @@ class _LineChartState extends State<_LineChart> {
),
);
}
+
+ LineChartBarData _buildHorizontalLine(HorizontalGraphLine line, double start, double end) {
+ return LineChartBarData(
+ color: line.color,
+ spots: [
+ FlSpot(start, line.height.toDouble()),
+ FlSpot(end, line.height.toDouble())
+ ],
+ barWidth: 1,
+ dotData: const FlDotData(
+ show: false,
+ ),
+ dashArray: [10,5,]
+ );
+ }
}
class MeasurementGraph extends StatelessWidget {
lib/l10n/app_en.arb
@@ -409,5 +409,11 @@
"startWithAddMeasurementPage": "Measurement on launch",
"@startWithAddMeasurementPage": {},
"startWithAddMeasurementPageDescription": "Upon app launch, measurement input screen shown.",
- "@startWithAddMeasurementPageDescription": {}
+ "@startWithAddMeasurementPageDescription": {},
+ "horizontalLines": "Horizontal lines",
+ "@horizontalLines": {},
+ "linePositionY": "Line position (y)",
+ "@linePositionY": {},
+ "customGraphMarkings": "Custom markings",
+ "@customGraphMarkings": {}
}
lib/model/horizontal_graph_line.dart
@@ -0,0 +1,17 @@
+import 'package:flutter/material.dart';
+
+class HorizontalGraphLine {
+ Color color;
+ int height;
+
+ HorizontalGraphLine(this.color, this.height);
+
+ HorizontalGraphLine.fromJson(Map<String, dynamic> json)
+ : color = Color(json['color']),
+ height = json['height'];
+
+ Map<String, dynamic> toJson() => {
+ 'color': color.value,
+ 'height': height,
+ };
+}
\ No newline at end of file
lib/model/ram_only_implementations.dart
@@ -508,15 +508,15 @@ class RamSettings extends ChangeNotifier implements Settings {
notifyListeners();
}
- Iterable<int> _horizontalGraphLines = [];
+ Iterable<HorizontalGraphLine> _horizontalGraphLines = [];
@override
- Iterable<int> get horizontalGraphLines {
+ Iterable<HorizontalGraphLine> get horizontalGraphLines {
return _horizontalGraphLines;
}
@override
- set horizontalGraphLines(Iterable<int> value) {
+ set horizontalGraphLines(Iterable<HorizontalGraphLine> value) {
_horizontalGraphLines = value;
notifyListeners();
}
lib/model/settings_store.dart
@@ -1,6 +1,9 @@
+import 'dart:convert';
+
import 'package:blood_pressure_app/model/blood_pressure.dart';
import 'package:blood_pressure_app/model/export_import.dart';
import 'package:blood_pressure_app/model/export_options.dart';
+import 'package:blood_pressure_app/model/horizontal_graph_line.dart';
import 'package:file_saver/file_saver.dart' show MimeType;
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -615,13 +618,13 @@ class Settings extends ChangeNotifier {
notifyListeners();
}
- Iterable<int> get horizontalGraphLines {
+ Iterable<HorizontalGraphLine> get horizontalGraphLines {
final linesStr = _prefs.getStringList('horizontalGraphLines') ?? [];
- return linesStr.map((e) => int.parse(e));
+ return linesStr.map((e) => HorizontalGraphLine.fromJson(jsonDecode(e)));
}
- set horizontalGraphLines(Iterable<int> value) {
- _prefs.setStringList('horizontalGraphLines', value.map((e) => e.toString()).toList());
+ set horizontalGraphLines(Iterable<HorizontalGraphLine> value) {
+ _prefs.setStringList('horizontalGraphLines', value.map((e) => jsonEncode(e)).toList());
notifyListeners();
}
}
lib/screens/subsettings/graph_markings.dart
@@ -1,58 +1,86 @@
import 'package:blood_pressure_app/components/input_dialoge.dart';
+import 'package:blood_pressure_app/model/horizontal_graph_line.dart';
+import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
import 'package:provider/provider.dart';
-import '../../model/settings_store.dart';
-
class GraphMarkingsScreen extends StatelessWidget {
const GraphMarkingsScreen({super.key});
@override
Widget build(BuildContext context) {
+ final localizations = AppLocalizations.of(context)!;
+ // IMPORTANT: When adding more option, like vertical lines, add navigation bar
return Scaffold(
appBar: AppBar(
+ title: Text(AppLocalizations.of(context)!.customGraphMarkings),
backgroundColor: Theme.of(context).primaryColor,
),
body: Center(child: Consumer<Settings>(
builder: (context, settings, child) {
final lines = settings.horizontalGraphLines.toList();
- return Container(
- padding: const EdgeInsets.all(20.0),
- child: ListView.builder(
- itemCount: lines.length+1,
- itemBuilder: (context, i) {
- if(i == 0) {
- return Row(
- children: [
- Text('Horizontal Lines'), // TODO localize, implement
- const Spacer(),
- IconButton(
- icon: const Icon(Icons.add),
- onPressed: () {
- showDialog(
- context: context,
- builder: (context) => NumberInputDialoge(
- hintText: 'hintText',
- onParsableSubmit: (value) {
- lines.add(value);
- Navigator.of(context).pop();
- }
- ),
- );
- },
- )
- ],
+ return ListView.builder(
+ itemCount: lines.length + 2, // support first and last row
+ itemBuilder: (context, i) {
+ if(i == 0) { // first row
+ return Container(
+ padding: const EdgeInsets.all(10),
+ child: DefaultTextStyle.merge(
+ child: Text(localizations.horizontalLines),
+ style: Theme.of(context).textTheme.headlineLarge
+ ),
);
- }
- return Row(
- children: [
- Text(lines[i-1].toString()),
- Spacer(),
- Icon(Icons.delete)
- ],
+ }
+ if (i > lines.length) { // last row
+ return ListTile(
+ leading: const Icon(Icons.add),
+ onTap: () async {
+ final color = await showDialog<Color>(context: context,
+ builder: (context) => SimpleDialog(
+ children: [MaterialColorPicker(
+ circleSize: 53,
+ onMainColorChange: (color) {
+ Navigator.of(context).pop(color);
+ },
+ )],));
+ if (!context.mounted) return;
+ final height = await showDialog<int>(context: context,
+ builder: (context) => NumberInputDialoge(
+ hintText: localizations.linePositionY,
+ onParsableSubmit: (value) {
+ Navigator.of(context).pop(value);
+ }
+ )
+ );
+
+ if (color == null || height == null) return;
+ lines.add(HorizontalGraphLine(color, height));
+ settings.horizontalGraphLines = lines;
+ },
+ title: Text(localizations.addEntry),
);
}
- )
+ return ListTile(
+ leading: Container(
+ width: 40.0,
+ height: 40.0,
+ decoration: BoxDecoration(
+ color: lines[i-1].color,
+ shape: BoxShape.circle,
+ ),
+ ),
+ title: Text(lines[i-1].height.toString()),
+ trailing: IconButton(
+ icon: Icon(Icons.delete),
+ onPressed: () {
+ lines.removeAt(i-1);
+ settings.horizontalGraphLines = lines;
+ },
+ ),
+ );
+ }
);
}),
),
lib/screens/settings.dart
@@ -258,8 +258,7 @@ class SettingsPage extends StatelessWidget {
),
SettingsTile(
key: const Key('GraphMarkingsScreen'),
- title: Text(AppLocalizations.of(context)!.text),
- description: Text(AppLocalizations.of(context)!.text), // TODO desc u title
+ title: Text(AppLocalizations.of(context)!.customGraphMarkings),
leading: const Icon(Icons.legend_toggle_outlined),
trailing: const Icon(Icons.arrow_forward_ios),
onPressed: (context) {