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}