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}