Skip to content

Commit

Permalink
Merge pull request #139 from NobodyForNothing/FEAT-horizontal-lines
Browse files Browse the repository at this point in the history
Add custom horizontal lines to graph
  • Loading branch information
derdilla authored Aug 28, 2023
2 parents 141854e + 5028bcc commit dc418ea
Show file tree
Hide file tree
Showing 9 changed files with 329 additions and 113 deletions.
80 changes: 80 additions & 0 deletions lib/components/input_dialoge.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:flutter/material.dart';
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;
final List<TextInputFormatter>? inputFormatters;
final TextInputType? keyboardType;

const InputDialoge({super.key, required this.hintText, required this.onSubmit, this.inputFormatters, this.keyboardType});

@override
State<InputDialoge> createState() => _InputDialogeState();
}

class _InputDialogeState extends State<InputDialoge> {
final formKey = GlobalKey<FormState>();
final controller = TextEditingController();
final inputFocusNode = FocusNode();

@override
void dispose() {
// Clean up the controller when the widget is disposed.
controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
inputFocusNode.requestFocus();
return AlertDialog(
content: TextFormField(
key: formKey,
focusNode: inputFocusNode,
controller: controller,
inputFormatters: widget.inputFormatters,
keyboardType: widget.keyboardType,
decoration: InputDecoration(hintText: widget.hintText),
),
actions: [
Consumer<Settings>(builder: (context, settings, child) {
return ElevatedButton(
onPressed: () {
widget.onSubmit(controller.text);
},
child: Text(AppLocalizations.of(context)!.btnConfirm)
);
}),

],
);
}
}

class NumberInputDialoge extends StatelessWidget {
final String hintText;
final void Function(int text) onParsableSubmit;

const NumberInputDialoge({super.key, required this.hintText, required this.onParsableSubmit});

@override
Widget build(BuildContext context) {
return InputDialoge(
hintText: hintText,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
keyboardType: TextInputType.number,
onSubmit: (text) {
if (text.isEmpty || (int.tryParse(text) == null)) {
return;
}
int value = int.parse(text);
onParsableSubmit(value);
}
);
}
}
30 changes: 28 additions & 2 deletions lib/components/measurement_graph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -42,6 +43,9 @@ class _LineChartState extends State<_LineChart> {
// calculate lines for graph
List<FlSpot> pulSpots = [], diaSpots = [], sysSpots = [];
int maxValue = 0;
int minValue = (settings.validateInputs ? 30 : 0);
double? graphBegin;
double? graphEnd;
for (var e in data) {
final x = e.creationTime.millisecondsSinceEpoch.toDouble();
if (e.diastolic != null) {
Expand All @@ -56,9 +60,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);
}

Expand All @@ -70,7 +78,7 @@ class _LineChartState extends State<_LineChart> {
return LineChart(
duration: const Duration(milliseconds: 200),
LineChartData(
minY: settings.validateInputs ? 30 : 0,
minY: minValue.toDouble(),
maxY: maxValue + 5,
titlesData: _buildFlTitlesData(settings),
lineTouchData: const LineTouchData(
Expand All @@ -86,6 +94,9 @@ class _LineChartState extends State<_LineChart> {
_buildRegressionLine(diaSpots),
if (settings.drawRegressionLines)
_buildRegressionLine(pulSpots),
for (final horizontalLine in settings.horizontalGraphLines)
if (horizontalLine.height < maxValue && horizontalLine.height > minValue)
_buildHorizontalLine(horizontalLine, graphBegin!, graphEnd!),
]
),
);
Expand Down Expand Up @@ -197,6 +208,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 {
Expand Down
10 changes: 9 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -409,5 +409,13 @@
"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": {},
"addLine": "Add line",
"@addLine": {}
}
17 changes: 17 additions & 0 deletions lib/model/horizontal_graph_line.dart
Original file line number Diff line number Diff line change
@@ -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,
};
}
15 changes: 15 additions & 0 deletions lib/model/ram_only_implementations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import 'package:blood_pressure_app/model/settings_store.dart';
import 'package:file_saver/file_saver.dart' show MimeType;
import 'package:flutter/material.dart';

import 'horizontal_graph_line.dart';

class RamBloodPressureModel extends ChangeNotifier implements BloodPressureModel {
final List<BloodPressureRecord> _records = [];

Expand Down Expand Up @@ -508,6 +510,19 @@ class RamSettings extends ChangeNotifier implements Settings {
notifyListeners();
}

Iterable<HorizontalGraphLine> _horizontalGraphLines = [];

@override
Iterable<HorizontalGraphLine> get horizontalGraphLines {
return _horizontalGraphLines;
}

@override
set horizontalGraphLines(Iterable<HorizontalGraphLine> value) {
_horizontalGraphLines = value;
notifyListeners();
}

@override
void changeStepSize(TimeStep value) {
graphStepSize = value;
Expand Down
13 changes: 13 additions & 0 deletions lib/model/settings_store.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -614,6 +617,16 @@ class Settings extends ChangeNotifier {
_prefs.setStringList('exportItemsPdf', value);
notifyListeners();
}

Iterable<HorizontalGraphLine> get horizontalGraphLines {
final linesStr = _prefs.getStringList('horizontalGraphLines') ?? [];
return linesStr.map((e) => HorizontalGraphLine.fromJson(jsonDecode(e)));
}

set horizontalGraphLines(Iterable<HorizontalGraphLine> value) {
_prefs.setStringList('horizontalGraphLines', value.map((e) => jsonEncode(e)).toList());
notifyListeners();
}
}

enum TimeStep {
Expand Down
4 changes: 2 additions & 2 deletions lib/screens/add_measurement.dart
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ class _AddMeasurementPageState extends State<AddMeasurementPage> {
ValueInput(
key: const Key('txtPul'),
initialValue: (_pulse ?? '').toString(),
minLines: 1,
maxLines: 4,
hintText: AppLocalizations.of(context)!.pulLong,
basicValidation: !settings.allowMissingValues,
preValidation: (v) => _pulse = int.tryParse(v ?? ''),
Expand All @@ -149,6 +147,8 @@ class _AddMeasurementPageState extends State<AddMeasurementPage> {
),
TextFormField(
initialValue: (_note ?? '').toString(),
minLines: 1,
maxLines: 4,
decoration: InputDecoration(hintText: AppLocalizations.of(context)?.addNote),
validator: (String? value) {
_note = value;
Expand Down
Loading

0 comments on commit dc418ea

Please sign in to comment.