main
1import 'package:blood_pressure_app/components/nullable_text.dart';
2import 'package:blood_pressure_app/components/pressure_text.dart';
3import 'package:blood_pressure_app/data_util/entry_context.dart';
4import 'package:blood_pressure_app/features/input/forms/add_entry_form.dart';
5import 'package:blood_pressure_app/model/storage/settings_store.dart';
6import 'package:flutter/material.dart';
7import 'package:blood_pressure_app/l10n/app_localizations.dart';
8import 'package:health_data_store/health_data_store.dart';
9import 'package:intl/intl.dart';
10import 'package:provider/provider.dart';
11
12/// A more compact [BloodPressureRecord] list that is less verbose.
13class CompactMeasurementList extends StatefulWidget {
14 /// Create a more compact, less verbose measurement list.
15 const CompactMeasurementList({super.key,
16 required this.data,
17 });
18
19 /// Entries sorted with newest ordered first.
20 final List<FullEntry> data;
21
22 @override
23 State<CompactMeasurementList> createState() => _CompactMeasurementListState();
24}
25
26class _CompactMeasurementListState extends State<CompactMeasurementList> {
27 List<int> _tableElementsSizes = [33, 9, 9, 9, 30];
28 int _sideFlex = 1;
29 late DateFormat formatter;
30
31 @override
32 void initState() {
33 super.initState();
34 formatter = DateFormat('yyyy-MM-dd HH:mm');
35 }
36
37 Widget _buildHeader() => Row(
38 children: [
39 Expanded(
40 flex: _sideFlex,
41 child: const SizedBox(),
42 ),
43 Expanded(
44 flex: _tableElementsSizes[0],
45 child: Text(AppLocalizations.of(context)!.time, style: const TextStyle(fontWeight: FontWeight.bold)),),
46 Expanded(
47 flex: _tableElementsSizes[1],
48 child: Text(AppLocalizations.of(context)!.sysShort,
49 style: TextStyle(fontWeight: FontWeight.bold, color: context.select<Settings, Color>((s) => s.sysColor))),),
50 Expanded(
51 flex: _tableElementsSizes[2],
52 child: Text(AppLocalizations.of(context)!.diaShort,
53 style: TextStyle(fontWeight: FontWeight.bold, color: context.select<Settings, Color>((s) => s.diaColor))),),
54 Expanded(
55 flex: _tableElementsSizes[3],
56 child: Text(AppLocalizations.of(context)!.pulShort,
57 style: TextStyle(fontWeight: FontWeight.bold, color: context.select<Settings, Color>((s) => s.pulColor))),),
58 Expanded(
59 flex: _tableElementsSizes[4],
60 child: Text(AppLocalizations.of(context)!.notes, style: const TextStyle(fontWeight: FontWeight.bold)),),
61 Expanded(
62 flex: _sideFlex,
63 child: const SizedBox(),
64 ),
65 ],
66 );
67
68 Widget _itemBuilder(BuildContext context, int index) => Column(
69 children: [
70 Dismissible(
71 key: Key(widget.data[index].time.toIso8601String()),
72 confirmDismiss: (direction) async {
73 if (direction == DismissDirection.startToEnd) { // edit
74 await context.createEntry(widget.data[index].asAddEntry);
75 return false;
76 } else { // delete
77 await context.deleteEntry(widget.data[index]);
78 return false;
79 }
80 },
81 onDismissed: (direction) {},
82 background: Container(
83 width: 10,
84 decoration:
85 BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(5)),
86 child: const Align(alignment: Alignment(-0.95, 0), child: Icon(Icons.edit)),
87 ),
88 secondaryBackground: Container(
89 width: 10,
90 decoration:
91 BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(5)),
92 child: const Align(alignment: Alignment(0.95, 0), child: Icon(Icons.delete)),
93 ),
94 child: Container(
95 constraints: const BoxConstraints(minHeight: 40),
96 child: Row(children: [
97 Expanded(
98 flex: _sideFlex,
99 child: const SizedBox(),
100 ),
101 Expanded(
102 flex: _tableElementsSizes[0],
103 child: Text(formatter.format(widget.data[index].time)),),
104 Expanded(
105 flex: _tableElementsSizes[1],
106 child: PressureText(widget.data[index].sys)),
107 Expanded(
108 flex: _tableElementsSizes[2],
109 child: PressureText(widget.data[index].dia),),
110 Expanded(
111 flex: _tableElementsSizes[3],
112 child: NullableText(widget.data[index].pul?.toString()),),
113 Expanded(
114 flex: _tableElementsSizes[4],
115 child: NullableText(() {
116 String note = widget.data[index].note ?? '';
117 for (final i in widget.data[index].intakes) {
118 note += '${i.medicine.designation}(${i.dosis.mg}mg)';
119 }
120 return note.isEmpty ? null : note;
121 }()),
122 ),
123 Expanded(
124 flex: _sideFlex,
125 child: const SizedBox(),
126 ),
127 ],),
128 ),
129 ),
130 const Divider(
131 thickness: 1,
132 height: 1,
133 ),
134 ],
135 );
136
137 @override
138 Widget build(BuildContext context) {
139 formatter = DateFormat(context.select<Settings, String>((s) => s.dateFormatString));
140 if (MediaQuery.of(context).size.width < 1000) {
141 _tableElementsSizes = [33, 9, 9, 9, 30];
142 _sideFlex = 1;
143 } else {
144 _tableElementsSizes = [20, 5, 5, 5, 60];
145 _sideFlex = 5;
146 }
147 return Column(
148 children: [
149 _buildHeader(),
150 const SizedBox(
151 height: 10,
152 ),
153 Divider(
154 height: 0,
155 thickness: 2,
156 color: Theme.of(context).colorScheme.primaryContainer,
157 ),
158 Expanded(
159 child: Builder(
160 builder: (BuildContext context) {
161 if (widget.data.isEmpty) return Text(AppLocalizations.of(context)!.errNoData);
162 return ListView.builder(
163 itemCount: widget.data.length,
164 shrinkWrap: true,
165 padding: const EdgeInsets.all(2),
166 itemBuilder: _itemBuilder,
167 );
168 },
169 ),
170 ),
171 ],
172 );
173 }
174}