main
1import 'package:blood_pressure_app/features/bluetooth/logic/characteristics/ble_measurement_data.dart';
2import 'package:blood_pressure_app/features/bluetooth/ui/input_card.dart';
3import 'package:flutter/material.dart';
4import 'package:blood_pressure_app/l10n/app_localizations.dart';
5
6/// Indication of a successful bluetooth read that returned multiple measurements.
7///
8/// TODO: Some devices can store up to 100 measurements which could cause a very long ListView. Maybe optimize UI for that?
9class MeasurementMultiple extends StatelessWidget {
10 /// Indicate a successful read while taking a bluetooth measurement.
11 const MeasurementMultiple({super.key,
12 required this.onClosed,
13 required this.onSelect,
14 required this.measurements,
15 });
16
17 /// All measurements decoded from bluetooth.
18 final List<BleMeasurementData> measurements;
19
20 /// Called when the user requests closing.
21 final void Function() onClosed;
22
23 /// Called when user selects a measurement
24 final void Function(BleMeasurementData data) onSelect;
25
26 Widget _buildMeasurementTile(BuildContext context, int index, BleMeasurementData data) {
27 final localizations = AppLocalizations.of(context)!;
28 return ListTile(
29 title: Text(data.timestamp?.toIso8601String() ?? localizations.measurementIndex(index + 1)),
30 subtitle: Text(() {
31 String str = '';
32 if (data.userID != null) {
33 str += '${localizations.userID}: ${data.userID}, ';
34 }
35 str += '${localizations.bloodPressure}: ${data.systolic.round()}/${data.diastolic.round()}';
36 if (data.pulse != null) {
37 str += ', ${localizations.pulLong}: ${data.pulse?.round()}';
38 }
39 return str;
40 }()),
41 trailing: FilledButton(
42 onPressed: () => onSelect(data),
43 child: Text(localizations.select),
44 ),
45 onTap: () => onSelect(data),
46 );
47 }
48
49 @override
50 Widget build(BuildContext context) {
51 // Sort measurements so latest measurement is on top of the list
52 measurements.sort((a, b) {
53 final aTimestamp = a.timestamp?.microsecondsSinceEpoch;
54 final bTimestamp = b.timestamp?.microsecondsSinceEpoch;
55
56 if (aTimestamp == bTimestamp) {
57 // don't sort when a & b are equal (either both null or equal value)
58 return 0;
59 }
60
61 if (aTimestamp == null) {
62 return 1;
63 }
64
65 if (bTimestamp == null) {
66 return -1;
67 }
68
69 return aTimestamp > bTimestamp ? -1 : 1;
70 });
71
72 return InputCard(
73 onClosed: onClosed,
74 title: Text(AppLocalizations.of(context)!.selectMeasurementTitle),
75 child: ListView(
76 shrinkWrap: true,
77 children: [
78 for (final (index, data) in measurements.indexed)
79 _buildMeasurementTile(context, index, data),
80 ]
81 ),
82 );
83 }
84}