main
1import 'package:blood_pressure_app/features/input/forms/form_base.dart';
2import 'package:flutter/material.dart';
3import 'package:flutter/services.dart';
4import 'package:blood_pressure_app/l10n/app_localizations.dart';
5import 'package:health_data_store/health_data_store.dart';
6
7/// Form to enter medicine intakes.
8class MedicineIntakeForm extends FormBase<(Medicine, Weight)> {
9 /// Create form to enter medicine intakes.
10 MedicineIntakeForm({super.key,
11 super.initialValue,
12 required this.meds,
13 }) : assert(meds.isNotEmpty);
14
15 /// All selectable medicines.
16 final List<Medicine> meds;
17
18 @override
19 FormStateBase<(Medicine, Weight), MedicineIntakeForm> createState() =>
20 MedicineIntakeFormState();
21}
22
23/// State of form to enter medicine intakes.
24class MedicineIntakeFormState extends FormStateBase<(Medicine, Weight), MedicineIntakeForm> {
25 final _controller = TextEditingController();
26
27 Medicine? _leadingMed;
28 String? _error;
29
30 @override
31 void initState() {
32 super.initState();
33 _controller.text = _leadingMed?.dosis?.mg.toString() ?? '';
34
35 if (widget.initialValue != null) {
36 _leadingMed = widget.initialValue!.$1;
37 _controller.text = widget.initialValue!.$2.mg.toString();
38 }
39 }
40
41 @override
42 void dispose() {
43 _controller.dispose();
44 super.dispose();
45 }
46
47 @override
48 bool validate() {
49 if (_leadingMed != null && double.tryParse(_controller.text) == null) {
50 setState(() => _error = AppLocalizations.of(context)!.errNaN);
51 return false;
52 }
53 setState(() => _error = null);
54 return true;
55 }
56
57 @override
58 (Medicine, Weight)? save() {
59 if (_leadingMed == null || !validate()) return null;
60 return (_leadingMed!, Weight.mg(double.parse(_controller.text)));
61 }
62
63 @override
64 bool isEmptyInputFocused() => false;
65
66 @override
67 void fillForm((Medicine, Weight)? value) => setState(() {
68 if (value == null) {
69 _leadingMed = null;
70 _controller.text = '';
71 } else {
72 _leadingMed = value.$1;
73 _controller.text = value.$2.mg.toString();
74 }
75 });
76
77 @override
78 Widget build(BuildContext context) {
79 if (_leadingMed != null) {
80 return TextField(
81 decoration: InputDecoration(
82 helperText: _leadingMed!.designation,
83 labelText: AppLocalizations.of(context)!.dosis,
84 prefixIcon: Icon(Icons.medication,
85 color: _leadingMed!.color == null ? null : Color(_leadingMed!.color!)),
86 suffixIcon: IconButton(
87 onPressed: () => setState(() => _leadingMed = null),
88 icon: Icon(Icons.close),
89 ),
90 errorText: _error,
91 ),
92 controller: _controller,
93 inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9,.]'))],
94 keyboardType: const TextInputType.numberWithOptions(decimal: true),
95 );
96 }
97 return Column(
98 children: [
99 for (final m in widget.meds)
100 ListTile(
101 leading: Icon(Icons.medication, color: m.color == null ? null : Color(m.color!)),
102 title: Text(m.designation),
103 subtitle: (widget.meds.length == 1)
104 ? Text(AppLocalizations.of(context)!.tapToSelect)
105 : null,
106 onTap: () => setState(() {
107 _leadingMed = m;
108 _controller.text = _leadingMed?.dosis?.mg.toString() ?? '';
109 }),
110 ),
111 ],
112 );
113 }
114}