Commit 0985b42
Changed files (21)
app
lib
components
model
screens
elements
subsettings
docs
app/lib/components/dialoges/add_export_column_dialoge.dart
@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:health_data_store/health_data_store.dart';
import 'package:intl/intl.dart';
+import 'package:provider/provider.dart';
/// Dialoge widget for creating and editing a [UserColumn].
///
@@ -16,7 +17,6 @@ class AddExportColumnDialoge extends StatefulWidget {
/// Create a widget for creating and editing a [UserColumn].
const AddExportColumnDialoge({super.key,
this.initialColumn,
- required this.settings,
});
/// Prefills the form to a submitted state.
@@ -24,9 +24,6 @@ class AddExportColumnDialoge extends StatefulWidget {
/// When this is null it is assumed creating a new column is intended.
final ExportColumn? initialColumn;
- /// Settings to determine general behavior.
- final Settings settings;
-
@override
State<AddExportColumnDialoge> createState() => _AddExportColumnDialogeState();
}
@@ -67,7 +64,7 @@ class _AddExportColumnDialogeState extends State<AddExportColumnDialoge>
_controller = AnimationController(
value: (type == _FormatterType.record) ? 1 : 0,
- duration: Duration(milliseconds: widget.settings.animationSpeed),
+ duration: Duration(milliseconds: context.watch<Settings>().animationSpeed),
vsync: this,
);
}
@@ -80,11 +77,12 @@ class _AddExportColumnDialogeState extends State<AddExportColumnDialoge>
@override
Widget build(BuildContext context) {
+ final settings = context.watch<Settings>();
final localizations = AppLocalizations.of(context)!;
return FullscreenDialoge(
actionButtonText: localizations.btnSave,
onActionButtonPressed: _saveForm,
- bottomAppBar: widget.settings.bottomAppBars,
+ bottomAppBar: settings.bottomAppBars,
body: GestureDetector(
onHorizontalDragEnd: (details) {
if (details.primaryVelocity == null) return;
@@ -163,8 +161,8 @@ class _AddExportColumnDialogeState extends State<AddExportColumnDialoge>
child: (){
final record = BloodPressureRecord(
time: DateTime.now(),
- sys: widget.settings.preferredPressureUnit.wrap(123),
- dia: widget.settings.preferredPressureUnit.wrap(78),
+ sys: settings.preferredPressureUnit.wrap(123),
+ dia: settings.preferredPressureUnit.wrap(78),
pul: 65,
);
final note = Note(
@@ -182,7 +180,6 @@ class _AddExportColumnDialogeState extends State<AddExportColumnDialoge>
if (type == _FormatterType.record)
MeasurementListRow(
data: (record, Note(time: record.time), []),
- settings: widget.settings,
onRequestEdit: () { }, // ignore
) else Text(
DateFormat('MMM d, y - h:m.s')
@@ -336,14 +333,12 @@ enum _FormatterType {
/// the CSV title. There is no check whether a userColumn
/// with the generated title exists.
Future<ExportColumn?> showAddExportColumnDialoge(
- BuildContext context,
- Settings settings, [
+ BuildContext context, [
ExportColumn? initialColumn,
]) => showDialog<ExportColumn?>(context: context,
builder: (context) => Dialog.fullscreen(
child: AddExportColumnDialoge(
initialColumn: initialColumn,
- settings: settings,
),
),
);
app/lib/components/dialoges/add_measurement_dialoge.dart
@@ -10,6 +10,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:health_data_store/health_data_store.dart';
+import 'package:provider/provider.dart';
/// Input mask for entering measurements.
class AddEntryDialoge extends StatefulWidget {
@@ -17,14 +18,10 @@ class AddEntryDialoge extends StatefulWidget {
///
/// This is usually created through the [showAddEntryDialoge] function.
const AddEntryDialoge({super.key,
- required this.settings,
required this.availableMeds,
this.initialRecord,
});
- /// Settings are followed by the dialoge.
- final Settings settings;
-
/// Values that are prefilled.
///
/// When this is null the timestamp is [DateTime.now] and the other fields
@@ -84,6 +81,7 @@ class _AddEntryDialogeState extends State<AddEntryDialoge> {
/// Prefilled with default dosis of selected medicine.
double? medicineDosis;
+ late Settings settings;
@override
void initState() {
@@ -94,6 +92,8 @@ class _AddEntryDialogeState extends State<AddEntryDialoge> {
noteController = TextEditingController();
_loadFields(widget.initialRecord);
+ settings = context.watch<Settings>();
+
sysFocusNode.requestFocus();
ServicesBinding.instance.keyboard.addHandler(_onKey);
}
@@ -118,11 +118,11 @@ class _AddEntryDialogeState extends State<AddEntryDialoge> {
void _loadFields(FullEntry? entry) {
time = entry?.time ?? DateTime.now();
final int? colorValue = entry?.color;
- final sysValue = switch(widget.settings.preferredPressureUnit) {
+ final sysValue = switch(settings.preferredPressureUnit) {
PressureUnit.mmHg => entry?.sys?.mmHg,
PressureUnit.kPa => entry?.sys?.kPa.round(),
};
- final diaValue = switch(widget.settings.preferredPressureUnit) {
+ final diaValue = switch(settings.preferredPressureUnit) {
PressureUnit.mmHg => entry?.dia?.mmHg,
PressureUnit.kPa => entry?.dia?.kPa.round(),
};
@@ -171,15 +171,15 @@ class _AddEntryDialogeState extends State<AddEntryDialoge> {
}
},
validator: (String? value) {
- if (!widget.settings.allowMissingValues
+ if (!settings.allowMissingValues
&& (value == null
|| value.isEmpty
|| int.tryParse(value) == null)) {
return localizations.errNaN;
- } else if (widget.settings.validateInputs
+ } else if (settings.validateInputs
&& (int.tryParse(value ?? '') ?? -1) <= 30) {
return localizations.errLt30;
- } else if (widget.settings.validateInputs
+ } else if (settings.validateInputs
&& (int.tryParse(value ?? '') ?? 0) >= 400) {
// https://pubmed.ncbi.nlm.nih.gov/7741618/
return localizations.errUnrealistic;
@@ -213,7 +213,7 @@ class _AddEntryDialogeState extends State<AddEntryDialoge> {
if (shouldHaveRecord && (recordFormKey.currentState?.validate() ?? false)) {
recordFormKey.currentState?.save();
if (systolic != null || diastolic != null || pulse != null) {
- final pressureUnit = widget.settings.preferredPressureUnit;
+ final pressureUnit = settings.preferredPressureUnit;
record = BloodPressureRecord(
time: time,
sys: systolic == null ? null : pressureUnit.wrap(systolic!),
@@ -253,24 +253,23 @@ class _AddEntryDialogeState extends State<AddEntryDialoge> {
}
},
actionButtonText: localizations.btnSave,
- bottomAppBar: widget.settings.bottomAppBars,
+ bottomAppBar: settings.bottomAppBars,
body: SizeChangedLayoutNotifier(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 8),
children: [
- if (widget.settings.bleInput)
+ if (settings.bleInput)
BluetoothInput(
- settings: widget.settings,
onMeasurement: (record) => setState(
() => _loadFields((record, Note(time: record.time, note: noteController.text, color: color?.value), [])),
),
),
- if (widget.settings.allowManualTimeInput)
+ if (settings.allowManualTimeInput)
ListTileTheme(
shape: _buildShapeBorder(),
child: DateTimeForm(
- validate: widget.settings.validateInputs,
- dateFormatString: widget.settings.dateFormatString,
+ validate: settings.validateInputs,
+ dateFormatString: settings.dateFormatString,
initialTime: time,
onTimeSelected: (newTime) => setState(() {
time = newTime;
@@ -300,7 +299,7 @@ class _AddEntryDialogeState extends State<AddEntryDialoge> {
setState(() => diastolic = int.tryParse(value ?? '')),
focusNode: diaFocusNode,
validator: (value) {
- if (widget.settings.validateInputs
+ if (settings.validateInputs
&& (int.tryParse(value ?? '') ?? 0)
>= (int.tryParse(sysController.text) ?? 1)
) {
@@ -422,7 +421,6 @@ class _AddEntryDialogeState extends State<AddEntryDialoge> {
/// Shows a dialoge to input a blood pressure measurement or a medication.
Future<FullEntry?> showAddEntryDialoge(
BuildContext context,
- Settings settings,
MedicineRepository medRepo,
[FullEntry? initialRecord,
]) async {
@@ -431,7 +429,6 @@ Future<FullEntry?> showAddEntryDialoge(
context: context, builder: (context) =>
Dialog.fullscreen(
child: AddEntryDialoge(
- settings: settings,
initialRecord: initialRecord,
availableMeds: meds,
),
app/lib/components/dialoges/add_medication_dialoge.dart
@@ -5,16 +5,12 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:health_data_store/health_data_store.dart';
+import 'package:provider/provider.dart';
/// Dialoge to enter values for a [Medicine].
class AddMedicationDialoge extends StatefulWidget {
/// Create a dialoge to enter values for a [Medicine].
- const AddMedicationDialoge({super.key,
- required this.settings,
- });
-
- /// Settings that determine general behavior.
- final Settings settings;
+ const AddMedicationDialoge({super.key});
@override
State<AddMedicationDialoge> createState() => _AddMedicationDialogeState();
@@ -38,6 +34,7 @@ class _AddMedicationDialogeState extends State<AddMedicationDialoge> {
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
+ final settings = context.watch<Settings>();
return FullscreenDialoge(
actionButtonText: localizations.btnSave,
onActionButtonPressed: () {
@@ -48,7 +45,7 @@ class _AddMedicationDialogeState extends State<AddMedicationDialoge> {
dosis: _defaultDosis == null ? null : Weight.mg(_defaultDosis!),
),);
},
- bottomAppBar: widget.settings.bottomAppBars,
+ bottomAppBar: settings.bottomAppBars,
body: Form(
key: formKey,
child: ListView(
@@ -101,7 +98,7 @@ class _AddMedicationDialogeState extends State<AddMedicationDialoge> {
/// Shows a full screen dialoge to input a medicine.
///
/// The created medicine gets an index that was never in settings.
-Future<Medicine?> showAddMedicineDialoge(BuildContext context, Settings settings) =>
+Future<Medicine?> showAddMedicineDialoge(BuildContext context) =>
showDialog<Medicine?>(context: context, builder: (context) => Dialog.fullscreen(
- child: AddMedicationDialoge(settings: settings),
+ child: AddMedicationDialoge(),
),);
app/lib/components/measurement_list/measurement_list.dart
@@ -4,6 +4,7 @@ import 'package:blood_pressure_app/model/storage/settings_store.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:health_data_store/health_data_store.dart';
+import 'package:provider/provider.dart';
/// List that renders measurements and medicine intakes.
///
@@ -11,19 +12,16 @@ import 'package:health_data_store/health_data_store.dart';
class MeasurementList extends StatelessWidget {
/// Create a list to display measurements and intakes.
const MeasurementList({super.key,
- required this.settings,
required this.entries,
});
- /// Settings that determine general behavior.
- final Settings settings;
-
/// Entries sorted with newest comming first.
final List<FullEntry> entries;
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
+ final settings = context.watch<Settings>();
return Column(
mainAxisSize: MainAxisSize.min,
children: [
@@ -72,7 +70,6 @@ class MeasurementList extends StatelessWidget {
itemCount: entries.length,
itemBuilder: (context, idx) => MeasurementListRow(
data: entries[idx],
- settings: settings,
onRequestEdit: () => context.createEntry(entries[idx]),
),
),
app/lib/components/measurement_list/measurement_list_entry.dart
@@ -14,22 +14,19 @@ class MeasurementListRow extends StatelessWidget {
/// Create a display of a measurements.
const MeasurementListRow({super.key,
required this.data,
- required this.settings,
required this.onRequestEdit,
});
/// The measurement to display.
final FullEntry data;
- /// Settings that determine general behavior.
- final Settings settings;
-
/// Called when the user taps on the edit icon.
final void Function() onRequestEdit; // TODO: consider removing in favor of context methods
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context)!;
+ final settings = context.watch<Settings>();
final formatter = DateFormat(settings.dateFormatString);
return ExpansionTile(
// Leading color possible
app/lib/components/statistics/blood_pressure_distribution.dart
@@ -4,6 +4,7 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:health_data_store/health_data_store.dart';
+import 'package:provider/provider.dart';
/// Viewer for [ValueDistribution]s from [BloodPressureRecord]s.
///
@@ -14,7 +15,6 @@ class BloodPressureDistribution extends StatefulWidget {
const BloodPressureDistribution({
super.key,
required this.records,
- required this.settings,
});
/// All records to include in statistics computations.
@@ -24,9 +24,6 @@ class BloodPressureDistribution extends StatefulWidget {
/// is required.
final Iterable<BloodPressureRecord> records;
- /// Settings used to determine colors in the distributions.
- final Settings settings;
-
@override
State<BloodPressureDistribution> createState() =>
_BloodPressureDistributionState();
@@ -83,17 +80,17 @@ class _BloodPressureDistributionState extends State<BloodPressureDistribution>
ValueDistribution(
key: const Key('sys-dist'),
values: widget.records.map((e) => e.sys?.mmHg).whereNotNull(),
- color: widget.settings.sysColor,
+ color: context.select<Settings, Color>((s) => s.sysColor),
),
ValueDistribution(
key: const Key('dia-dist'),
values: widget.records.map((e) => e.dia?.mmHg).whereNotNull(),
- color: widget.settings.diaColor,
+ color: context.select<Settings, Color>((s) => s.diaColor),
),
ValueDistribution(
key: const Key('pul-dist'),
values: widget.records.map((e) => e.pul).whereNotNull(),
- color: widget.settings.pulColor,
+ color: context.select<Settings, Color>((s) => s.pulColor),
),
],
),
app/lib/components/bluetooth_input.dart
@@ -22,7 +22,6 @@ import 'package:health_data_store/health_data_store.dart';
class BluetoothInput extends StatefulWidget {
/// Create a measurement input through bluetooth.
const BluetoothInput({super.key,
- required this.settings,
required this.onMeasurement,
this.bluetoothCubit,
this.deviceScanCubit,
@@ -30,9 +29,6 @@ class BluetoothInput extends StatefulWidget {
this.flutterBluePlus,
});
- /// Settings to store known devices.
- final Settings settings;
-
/// Called when a measurement was received through bluetooth.
final void Function(BloodPressureRecord data) onMeasurement;
@@ -108,9 +104,10 @@ class _BluetoothInputState extends State<BluetoothInput> {
_returnToIdle();
}
});
+ final settings = context.watch<Settings>();
_deviceScanCubit ??= widget.deviceScanCubit?.call() ?? DeviceScanCubit(
service: serviceUUID,
- settings: widget.settings,
+ settings: settings,
flutterBluePlus: widget.flutterBluePlus,
);
return BlocBuilder<DeviceScanCubit, DeviceScanState>(
app/lib/model/entry_context.dart
@@ -20,11 +20,9 @@ extension EntryUtils on BuildContext {
final recordRepo = RepositoryProvider.of<BloodPressureRepository>(this);
final noteRepo = RepositoryProvider.of<NoteRepository>(this);
final intakeRepo = RepositoryProvider.of<MedicineIntakeRepository>(this);
- final settings = Provider.of<Settings>(this, listen: false);
final exportSettings = Provider.of<ExportSettings>(this, listen: false);
final entry = await showAddEntryDialoge(this,
- settings,
RepositoryProvider.of<MedicineRepository>(this),
initial,
);
app/lib/screens/elements/legacy_measurement_list.dart
@@ -1,11 +1,11 @@
import 'package:blood_pressure_app/components/nullable_text.dart';
+import 'package:blood_pressure_app/components/pressure_text.dart';
import 'package:blood_pressure_app/model/storage/settings_store.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:health_data_store/health_data_store.dart';
import 'package:intl/intl.dart';
-
-import '../../components/pressure_text.dart';
+import 'package:provider/provider.dart';
/// A old more compact [BloodPressureRecord] list, that lacks some of the new
/// features.
@@ -13,15 +13,11 @@ class LegacyMeasurementsList extends StatefulWidget {
/// Create a more compact measurement list without all new features.
LegacyMeasurementsList({super.key,
required this.data,
- required this.settings,
});
/// Entries sorted with newest ordered first.
final List<FullEntry> data;
- /// Settings that determine general behavior.
- final Settings settings;
-
@override
State<LegacyMeasurementsList> createState() => _LegacyMeasurementsListState();
}
@@ -43,13 +39,16 @@ class _LegacyMeasurementsListState extends State<LegacyMeasurementsList> {
child: Text(AppLocalizations.of(context)!.time, style: const TextStyle(fontWeight: FontWeight.bold)),),
Expanded(
flex: _tableElementsSizes[1],
- child: Text(AppLocalizations.of(context)!.sysShort, style: TextStyle(fontWeight: FontWeight.bold, color: widget.settings.sysColor)),),
+ child: Text(AppLocalizations.of(context)!.sysShort,
+ style: TextStyle(fontWeight: FontWeight.bold, color: context.select<Settings, Color>((s) => s.sysColor))),),
Expanded(
flex: _tableElementsSizes[2],
- child: Text(AppLocalizations.of(context)!.diaShort, style: TextStyle(fontWeight: FontWeight.bold, color: widget.settings.diaColor)),),
+ child: Text(AppLocalizations.of(context)!.diaShort,
+ style: TextStyle(fontWeight: FontWeight.bold, color: context.select<Settings, Color>((s) => s.diaColor))),),
Expanded(
flex: _tableElementsSizes[3],
- child: Text(AppLocalizations.of(context)!.pulShort, style: TextStyle(fontWeight: FontWeight.bold, color: widget.settings.pulColor)),),
+ child: Text(AppLocalizations.of(context)!.pulShort,
+ style: TextStyle(fontWeight: FontWeight.bold, color: context.select<Settings, Color>((s) => s.pulColor))),),
Expanded(
flex: _tableElementsSizes[4],
child: Text(AppLocalizations.of(context)!.notes, style: const TextStyle(fontWeight: FontWeight.bold)),),
@@ -61,7 +60,7 @@ class _LegacyMeasurementsListState extends State<LegacyMeasurementsList> {
);
Widget _itemBuilder(context, int index) {
- final formatter = DateFormat(widget.settings.dateFormatString);
+ final formatter = DateFormat(context.select<Settings, String>((s) => s.dateFormatString));
return Column(
children: [
Dismissible(
app/lib/screens/subsettings/export_import/export_column_management_screen.dart
@@ -1,7 +1,6 @@
import 'package:blood_pressure_app/components/dialoges/add_export_column_dialoge.dart';
import 'package:blood_pressure_app/model/export_import/column.dart';
import 'package:blood_pressure_app/model/storage/export_columns_store.dart';
-import 'package:blood_pressure_app/model/storage/settings_store.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
@@ -48,8 +47,7 @@ class ExportColumnsManagementScreen extends StatelessWidget {
IconButton(
icon: const Icon(Icons.edit),
onPressed: () async {
- final settings = Provider.of<Settings>(context, listen: false);
- final editedColumn = await showAddExportColumnDialoge(context, settings, column);
+ final editedColumn = await showAddExportColumnDialoge(context, column);
if (editedColumn != null) {
columnsManager.addOrUpdate(editedColumn);
}
@@ -83,8 +81,7 @@ class ExportColumnsManagementScreen extends StatelessWidget {
leading: const Icon(Icons.add),
title: Text(localizations.addExportformat),
onTap: () async{
- final settings = Provider.of<Settings>(context, listen: false);
- ExportColumn? editedColumn = await showAddExportColumnDialoge(context, settings);
+ ExportColumn? editedColumn = await showAddExportColumnDialoge(context);
if (editedColumn != null) {
while (columnsManager.userColumns.containsKey(editedColumn!.internalIdentifier)) {
if (editedColumn is UserColumn) {
@@ -105,5 +102,4 @@ class ExportColumnsManagementScreen extends StatelessWidget {
),
);
}
-
-}
\ No newline at end of file
+}
app/lib/screens/subsettings/medicine_manager_screen.dart
@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:health_data_store/health_data_store.dart';
-import 'package:provider/provider.dart';
/// Screen to view and edit medications saved in [Settings].
///
@@ -45,8 +44,7 @@ class _MedicineManagerScreenState extends State<MedicineManagerScreen> {
title: Text(localizations.addMedication),
onTap: () async {
final medRepo = RepositoryProvider.of<MedicineRepository>(context);
- final settings = Provider.of<Settings>(context, listen: false);
- final medicine = await showAddMedicineDialoge(context, settings,);
+ final medicine = await showAddMedicineDialoge(context);
if (medicine != null) {
setState(() {
medicines.add(medicine);
app/lib/screens/home_screen.dart
@@ -47,39 +47,35 @@ class AppHome extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: Consumer<IntervallStoreManager>(builder: (context, intervalls, child) =>
- Consumer<Settings>(builder: (context, settings, child) =>
- Column(children: [
- /*MeasurementListRow(
- settings: Settings(), data: (BloodPressureRecord(time: DateTime(2023),
- sys:Pressure.mmHg(1), dia: Pressure.mmHg(2), pul: 3), Note(time: DateTime(2023), note: 'testTxt',), [])),*/
- const MeasurementGraph(),
- Expanded(
- child: BloodPressureBuilder(
+ Column(children: [
+ /*MeasurementListRow(
+ settings: Settings(), data: (BloodPressureRecord(time: DateTime(2023),
+ sys:Pressure.mmHg(1), dia: Pressure.mmHg(2), pul: 3), Note(time: DateTime(2023), note: 'testTxt',), [])),*/
+ const MeasurementGraph(),
+ Expanded(
+ child: BloodPressureBuilder(
+ rangeType: IntervallStoreManagerLocation.mainPage,
+ onData: (context, records) => RepositoryBuilder<MedicineIntake, MedicineIntakeRepository>(
rangeType: IntervallStoreManagerLocation.mainPage,
- onData: (context, records) => RepositoryBuilder<MedicineIntake, MedicineIntakeRepository>(
+ onData: (BuildContext context, List<MedicineIntake> intakes) => RepositoryBuilder<Note, NoteRepository>(
rangeType: IntervallStoreManagerLocation.mainPage,
- onData: (BuildContext context, List<MedicineIntake> intakes) => RepositoryBuilder<Note, NoteRepository>(
- rangeType: IntervallStoreManagerLocation.mainPage,
- onData: (BuildContext context, List<Note> notes) {
- final entries = FullEntryList.merged(records, notes, intakes);
- entries.sort((a, b) => b.time.compareTo(a.time)); // newest first
- if (settings.compactList) {
- return LegacyMeasurementsList(
- data: entries,
- settings: settings,
- );
- }
- return MeasurementList(
- settings: settings,
- entries: entries,
+ onData: (BuildContext context, List<Note> notes) {
+ final entries = FullEntryList.merged(records, notes, intakes);
+ entries.sort((a, b) => b.time.compareTo(a.time)); // newest first
+ if (context.select<Settings, bool>((s) => s.compactList)) {
+ return LegacyMeasurementsList(
+ data: entries,
);
- },
- ),
+ }
+ return MeasurementList(
+ entries: entries,
+ );
+ },
),
),
),
- ],),
- ),),
+ ),
+ ],),),
),
);
},
app/lib/screens/statistics_screen.dart
@@ -26,99 +26,97 @@ class _StatisticsScreenState extends State<StatisticsScreen> {
appBar: AppBar(
title: Text(localizations.statistics),
),
- body: Consumer<Settings>(
- builder: (context, settings, child) => BloodPressureBuilder(
- rangeType: IntervallStoreManagerLocation.statsPage,
- onData: (context, data) {
- final analyzer = BloodPressureAnalyser(data.toList());
- return ListView(
- children: [
- _buildSubTitle(localizations.statistics,),
- ListTile(
- title: Text(localizations.measurementCount),
- trailing: Text(
- data.length.toString(),
- style: Theme.of(context).textTheme.headlineSmall,
- ),
+ body: BloodPressureBuilder(
+ rangeType: IntervallStoreManagerLocation.statsPage,
+ onData: (context, data) {
+ final analyzer = BloodPressureAnalyser(data.toList());
+ return ListView(
+ children: [
+ _buildSubTitle(localizations.statistics,),
+ ListTile(
+ title: Text(localizations.measurementCount),
+ trailing: Text(
+ data.length.toString(),
+ style: Theme.of(context).textTheme.headlineSmall,
),
- ListTile(
- title: Text(localizations.measurementsPerDay),
- trailing: Text(
- analyzer.measurementsPerDay?.toString() ?? '-',
- style: Theme.of(context).textTheme.headlineSmall,
- ),
+ ),
+ ListTile(
+ title: Text(localizations.measurementsPerDay),
+ trailing: Text(
+ analyzer.measurementsPerDay?.toString() ?? '-',
+ style: Theme.of(context).textTheme.headlineSmall,
),
- _buildSubTitle(localizations.valueDistribution,),
- Container(
- height: 260,
- padding: const EdgeInsets.symmetric(horizontal: 16.0),
- child: BloodPressureDistribution(
- records: data,
- settings: settings,
- ),
+ ),
+ _buildSubTitle(localizations.valueDistribution,),
+ Container(
+ height: 260,
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: BloodPressureDistribution(
+ records: data,
),
- _buildSubTitle(localizations.timeResolvedMetrics),
- () {
- final data = analyzer.allAvgsRelativeToDaytime;
- const opacity = 0.5;
- final helperLinesStyle = BorderSide(
- color: Theme.of(context).dividerColor,
- width: 2,
- );
- return Container(
- padding: const EdgeInsets.symmetric(horizontal: 16.0),
- height: MediaQuery.of(context).size.width,
- child: RadarChart(
- RadarChartData(
- radarShape: RadarShape.circle,
- gridBorderData: helperLinesStyle,
- tickBorderData: helperLinesStyle,
- ticksTextStyle: const TextStyle(
- color: Colors.transparent,
- ),
- tickCount: 5,
- titleTextStyle: const TextStyle(fontSize: 25),
- getTitle: (pos, value) {
- if (pos % 2 == 0) {
- return RadarChartTitle(
- text: '$pos',
- positionPercentageOffset: 0.05,
- );
- }
- return const RadarChartTitle(text: '');
- },
- dataSets: [
- RadarDataSet( // FIXME
- dataEntries: _intListToRadarEntry(data[0]),
- borderColor: settings.diaColor,
- fillColor: settings.diaColor.withOpacity(opacity),
- entryRadius: 0,
- borderWidth: settings.graphLineThickness,
- ),
- RadarDataSet(
- dataEntries: _intListToRadarEntry(data[1]),
- borderColor: settings.sysColor,
- fillColor: settings.sysColor.withOpacity(opacity),
- entryRadius: 0,
- borderWidth: settings.graphLineThickness,
- ),
- RadarDataSet(
- dataEntries: _intListToRadarEntry(data[2]),
- borderColor: settings.pulColor,
- fillColor: settings.pulColor.withOpacity(opacity),
- entryRadius: 0,
- borderWidth: settings.graphLineThickness,
- ),
- ],
+ ),
+ _buildSubTitle(localizations.timeResolvedMetrics),
+ () {
+ final data = analyzer.allAvgsRelativeToDaytime;
+ const opacity = 0.5;
+ final helperLinesStyle = BorderSide(
+ color: Theme.of(context).dividerColor,
+ width: 2,
+ );
+ final settings = context.watch<Settings>();
+ return Container(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ height: MediaQuery.of(context).size.width,
+ child: RadarChart(
+ RadarChartData(
+ radarShape: RadarShape.circle,
+ gridBorderData: helperLinesStyle,
+ tickBorderData: helperLinesStyle,
+ ticksTextStyle: const TextStyle(
+ color: Colors.transparent,
),
+ tickCount: 5,
+ titleTextStyle: const TextStyle(fontSize: 25),
+ getTitle: (pos, value) {
+ if (pos % 2 == 0) {
+ return RadarChartTitle(
+ text: '$pos',
+ positionPercentageOffset: 0.05,
+ );
+ }
+ return const RadarChartTitle(text: '');
+ },
+ dataSets: [
+ RadarDataSet(
+ dataEntries: _intListToRadarEntry(data[0]),
+ borderColor: settings.diaColor,
+ fillColor: settings.diaColor.withOpacity(opacity),
+ entryRadius: 0,
+ borderWidth: settings.graphLineThickness,
+ ),
+ RadarDataSet(
+ dataEntries: _intListToRadarEntry(data[1]),
+ borderColor: settings.sysColor,
+ fillColor: settings.sysColor.withOpacity(opacity),
+ entryRadius: 0,
+ borderWidth: settings.graphLineThickness,
+ ),
+ RadarDataSet(
+ dataEntries: _intListToRadarEntry(data[2]),
+ borderColor: settings.pulColor,
+ fillColor: settings.pulColor.withOpacity(opacity),
+ entryRadius: 0,
+ borderWidth: settings.graphLineThickness,
+ ),
+ ],
),
- );
- }(),
- ],
- );
- },
- ),
- ),
+ ),
+ );
+ }(),
+ ],
+ );
+ },
+ ),
bottomNavigationBar: Container(
height: 70,
margin: const EdgeInsets.only(top: 15, bottom: 5),
app/test/ui/components/measurement_list/measurement_list_entry_test.dart
@@ -1,5 +1,4 @@
import 'package:blood_pressure_app/components/measurement_list/measurement_list_entry.dart';
-import 'package:blood_pressure_app/model/storage/settings_store.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:health_data_store/health_data_store.dart';
@@ -11,24 +10,20 @@ void main() {
testWidgets('should initialize without errors', (tester) async {
await tester.pumpWidget(materialApp(MeasurementListRow(
onRequestEdit: () => fail('should not request edit'),
- settings: Settings(),
data: mockEntryPos(DateTime(2023), 123, 80, 60, 'test'),),),);
expect(tester.takeException(), isNull);
await tester.pumpWidget(materialApp(MeasurementListRow(
onRequestEdit: () => fail('should not request edit'),
- settings: Settings(),
data: mockEntryPos(DateTime.fromMillisecondsSinceEpoch(31279811), null, null, null, 'null test'),),),);
expect(tester.takeException(), isNull);
await tester.pumpWidget(materialApp(MeasurementListRow(
- onRequestEdit: () => fail('should not request edit'),
- settings: Settings(),
+ onRequestEdit: () => fail('should not request edit'),
data: mockEntryPos(DateTime(2023), 124, 85, 63, 'color',Colors.cyan))));
expect(tester.takeException(), isNull);
});
testWidgets('should expand correctly', (tester) async {
await tester.pumpWidget(materialApp(MeasurementListRow(
onRequestEdit: () => fail('should not request edit'),
- settings: Settings(),
data: mockEntryPos(DateTime(2023), 123, 78, 56),),),);
expect(find.byIcon(Icons.medication), findsNothing);
expect(find.byIcon(Icons.expand_more), findsOneWidget);
@@ -41,7 +36,6 @@ void main() {
testWidgets('should display correct information', (tester) async {
await tester.pumpWidget(materialApp(MeasurementListRow(
onRequestEdit: () => fail('should not request edit'),
- settings: Settings(),
data: mockEntryPos(DateTime(2023), 123, 78, 56, 'Test text'),),),);
expect(find.text('123'), findsOneWidget);
expect(find.text('78'), findsOneWidget);
@@ -66,7 +60,7 @@ void main() {
testWidgets('should not display null values', (tester) async {
await tester.pumpWidget(materialApp(MeasurementListRow(
onRequestEdit: () => fail('should not request edit'),
- settings: Settings(), data: mockEntry(time: DateTime(2023)),),),);
+ data: mockEntry(time: DateTime(2023)),),),);
expect(find.text('null'), findsNothing);
expect(find.byIcon(Icons.medication), findsNothing);
expect(find.byIcon(Icons.expand_more), findsOneWidget);
@@ -77,7 +71,6 @@ void main() {
testWidgets('should propagate edit request', (tester) async {
int requestCount = 0;
await tester.pumpWidget(materialApp(MeasurementListRow(
- settings: Settings(),
data: mockEntry(
time: DateTime(2023),
sys:1,
@@ -109,7 +102,6 @@ void main() {
testWidgets('should indicate presence of intakes', (tester) async {
await tester.pumpWidget(materialApp(MeasurementListRow(
onRequestEdit: () => fail('should not request edit'),
- settings: Settings(),
data: mockEntry(
time: DateTime(2023),
intake: mockIntake(mockMedicine(designation: 'testMed', color: Colors.red), dosis: 12.0),
app/test/ui/components/measurement_list/measurement_list_test.dart
@@ -1,6 +1,8 @@
import 'package:blood_pressure_app/components/measurement_list/measurement_list.dart';
import 'package:blood_pressure_app/components/measurement_list/measurement_list_entry.dart';
import 'package:blood_pressure_app/model/storage/settings_store.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
import '../../../model/export_import/record_formatter_test.dart';
@@ -10,7 +12,6 @@ void main() {
testWidgets('contains all elements in time range', (tester) async {
await tester.pumpWidget(materialApp(
MeasurementList(
- settings: Settings(),
entries: [
mockEntry(time: DateTime(2020), sys: 2020),
mockEntry(time: DateTime(2021), sys: 2021),
@@ -25,22 +26,58 @@ void main() {
expect(find.text('2022'), findsOneWidget);
expect(find.text('2023'), findsOneWidget);
});
- testWidgets('entries are ordered in reversed chronological order', (tester) async {
+ testWidgets('entries are ordered in reversed passed order', (tester) async {
await tester.pumpWidget(materialApp(
MeasurementList(
- settings: Settings(),
entries: [
- mockEntry(time: DateTime.fromMillisecondsSinceEpoch(2000), sys: 2),
mockEntry(time: DateTime.fromMillisecondsSinceEpoch(4000), sys: 1),
+ mockEntry(time: DateTime.fromMillisecondsSinceEpoch(2000), sys: 2),
mockEntry(time: DateTime.fromMillisecondsSinceEpoch(1000), sys: 3),
],
),
));
expect(find.byType(MeasurementListRow), findsNWidgets(3));
+ // coordinates starting at top left
final top = await tester.getCenter(find.text('1')).dy;
final center = await tester.getCenter(find.text('2')).dy;
final bottom = await tester.getCenter(find.text('3')).dy;
expect(bottom, greaterThan(center));
- expect(center, greaterThan(top));
+ expect(top, lessThan(center));
+ });
+ testWidgets('entries are ordered in reversed chronological order', (tester) async {
+ final localizations = await AppLocalizations.delegate.load(const Locale('en'));
+ await tester.pumpWidget(materialApp(
+ MeasurementList(
+ entries: [
+ mockEntry(time: DateTime.fromMillisecondsSinceEpoch(2000), sys: 2),
+ ],
+ ),
+ settings: Settings(
+ sysColor: Colors.blue,
+ diaColor: Colors.purple,
+ pulColor: Colors.indigo,
+ )
+ ));
+ expect(
+ find.byWidgetPredicate((widget) =>
+ widget is Text
+ && widget.data == localizations.sysLong
+ && widget.style?.color == Colors.blue),
+ findsOneWidget,
+ );
+ expect(
+ find.byWidgetPredicate((widget) =>
+ widget is Text
+ && widget.data == localizations.diaLong
+ && widget.style?.color == Colors.purple),
+ findsOneWidget,
+ );
+ expect(
+ find.byWidgetPredicate((widget) =>
+ widget is Text
+ && widget.data == localizations.pulLong
+ && widget.style?.color == Colors.indigo),
+ findsOneWidget,
+ );
});
}
app/test/ui/components/statistics/blood_pressure_distribution_test.dart
@@ -10,10 +10,7 @@ import '../util.dart';
void main() {
testWidgets('should show allow navigation to view all widgets', (tester) async {
- await tester.pumpWidget(materialApp(BloodPressureDistribution(
- records: const [],
- settings: Settings(),
- ),),);
+ await tester.pumpWidget(materialApp(BloodPressureDistribution(records: [])));
final localizations = await AppLocalizations.delegate.load(const Locale('en'));
@@ -38,21 +35,23 @@ void main() {
expect(find.byKey(const Key('pul-dist')), findsOneWidget);
});
testWidgets('should report records to ValueDistribution', (tester) async {
- await tester.pumpWidget(materialApp(BloodPressureDistribution(
- records: [
- mockRecord(sys: 123),
- mockRecord(dia: 123),
- mockRecord(dia: 124),
- mockRecord(pul: 123),
- mockRecord(pul: 124),
- mockRecord(pul: 125),
- ],
+ await tester.pumpWidget(materialApp(
+ BloodPressureDistribution(
+ records: [
+ mockRecord(sys: 123),
+ mockRecord(dia: 123),
+ mockRecord(dia: 124),
+ mockRecord(pul: 123),
+ mockRecord(pul: 124),
+ mockRecord(pul: 125),
+ ],
+ ),
settings: Settings(
sysColor: Colors.red,
diaColor: Colors.green,
pulColor: Colors.blue,
),
- ),),);
+ ),);
final localizations = await AppLocalizations.delegate.load(const Locale('en'));
app/test/ui/components/add_export_column_dialoge_test.dart
@@ -1,7 +1,6 @@
import 'package:blood_pressure_app/components/dialoges/add_export_column_dialoge.dart';
import 'package:blood_pressure_app/components/measurement_list/measurement_list_entry.dart';
import 'package:blood_pressure_app/model/export_import/column.dart';
-import 'package:blood_pressure_app/model/storage/settings_store.dart';
import 'package:blood_pressure_app/screens/subsettings/export_import/export_field_format_documentation_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@@ -12,7 +11,7 @@ import 'util.dart';
void main() {
group('AddExportColumnDialoge', () {
testWidgets('should show everything on load', (tester) async {
- await tester.pumpWidget(materialApp(AddExportColumnDialoge(settings: Settings(),)));
+ await tester.pumpWidget(materialApp(AddExportColumnDialoge()));
expect(tester.takeException(), isNull);
expect(find.text('SAVE'), findsOneWidget);
@@ -27,7 +26,7 @@ void main() {
});
testWidgets('should prefill values', (tester) async {
await tester.pumpWidget(materialApp(
- AddExportColumnDialoge(initialColumn: UserColumn('id', 'csvTitle', r'formatPattern$SYS'), settings: Settings(),),
+ AddExportColumnDialoge(initialColumn: UserColumn('id', 'csvTitle', r'formatPattern$SYS'),),
),);
expect(tester.takeException(), isNull);
@@ -43,7 +42,7 @@ void main() {
});
testWidgets('should show preview', (tester) async {
await tester.pumpWidget(materialApp(
- AddExportColumnDialoge(initialColumn: UserColumn('id', 'csvTitle', r'formatPattern$SYS'), settings: Settings(),),
+ AddExportColumnDialoge(initialColumn: UserColumn('id', 'csvTitle', r'formatPattern$SYS'),),
),);
await tester.pumpAndSettle();
@@ -53,7 +52,7 @@ void main() {
expect(find.textContaining('RowDataFieldType.sys'), findsOneWidget);
});
testWidgets('should open format Info screen', (tester) async {
- await tester.pumpWidget(materialApp(AddExportColumnDialoge(settings: Settings(),)));
+ await tester.pumpWidget(materialApp(AddExportColumnDialoge()));
expect(find.byType(InformationScreen), findsNothing);
@@ -66,13 +65,13 @@ void main() {
});
group('showAddExportColumnDialoge', () {
testWidgets('should open AddExportColumnDialoge', (tester) async {
- await loadDialoge(tester, (context) => showAddExportColumnDialoge(context, Settings()));
+ await loadDialoge(tester, (context) => showAddExportColumnDialoge(context));
expect(find.byType(AddExportColumnDialoge), findsOneWidget);
});
testWidgets('should return null on cancel', (tester) async {
dynamic returnedValue = false;
- await loadDialoge(tester, (context) async => returnedValue = await showAddExportColumnDialoge(context, Settings()));
+ await loadDialoge(tester, (context) async => returnedValue = await showAddExportColumnDialoge(context));
expect(returnedValue, false);
expect(find.byIcon(Icons.close), findsOneWidget);
@@ -84,7 +83,7 @@ void main() {
});
testWidgets('should save entered values', (tester) async {
dynamic returnedValue = false;
- await loadDialoge(tester, (context) async => returnedValue = await showAddExportColumnDialoge(context, Settings()));
+ await loadDialoge(tester, (context) async => returnedValue = await showAddExportColumnDialoge(context));
final localizations = await AppLocalizations.delegate.load(const Locale('en'));
@@ -116,7 +115,7 @@ void main() {
dynamic returnedValue = false;
await loadDialoge(tester, (context) async => returnedValue =
- await showAddExportColumnDialoge(context, Settings(),
+ await showAddExportColumnDialoge(context,
UserColumn('initialInternalIdentifier', 'csvTitle', 'formatPattern'),
),);
app/test/ui/components/add_measurement_dialoge_test.dart
@@ -18,7 +18,6 @@ void main() {
await tester.pumpWidget(materialApp(
AddEntryDialoge(
availableMeds: [],
- settings: Settings(),
),
),);
expect(tester.takeException(), isNull);
@@ -34,7 +33,6 @@ void main() {
testWidgets('should prefill initialRecord values', (tester) async {
await tester.pumpWidget(materialApp(
AddEntryDialoge(
- settings: Settings(),
initialRecord: mockEntryPos(
DateTime.now(), 123, 56, 43, 'Test note', Colors.teal,
),
@@ -55,7 +53,6 @@ void main() {
testWidgets('should show medication picker when medications available', (tester) async {
await tester.pumpWidget(materialApp(
AddEntryDialoge(
- settings: Settings(),
availableMeds: [ mockMedicine(designation: 'testmed') ],
),
),);
@@ -74,7 +71,6 @@ void main() {
testWidgets('should reveal dosis on medication selection', (tester) async {
await tester.pumpWidget(materialApp(
AddEntryDialoge(
- settings: Settings(),
availableMeds: [ mockMedicine(designation: 'testmed') ],
),
),);
@@ -104,7 +100,6 @@ void main() {
testWidgets('should enter default dosis if available', (tester) async {
await tester.pumpWidget(materialApp(
AddEntryDialoge(
- settings: Settings(),
availableMeds: [ mockMedicine(designation: 'testmed', defaultDosis: 3.1415) ],
),
),);
@@ -121,7 +116,6 @@ void main() {
testWidgets('should not quit when the measurement field is incorrectly filled, but a intake is added', (tester) async {
await tester.pumpWidget(materialApp(
AddEntryDialoge(
- settings: Settings(),
availableMeds: [ mockMedicine(designation: 'testmed', defaultDosis: 3.1415) ],
),
),);
@@ -154,9 +148,9 @@ void main() {
);
await tester.pumpWidget(materialApp(
AddEntryDialoge(
- settings: settings,
availableMeds: [],
),
+ settings: settings,
),);
await tester.pumpAndSettle();
expect(find.byType(BluetoothInput, skipOffstage: false), findsOneWidget);
@@ -170,7 +164,7 @@ void main() {
testWidgets('should return null on cancel', (tester) async {
dynamic result = 'result before save';
await loadDialoge(tester, (context) async
- => result = await showAddEntryDialoge(context, Settings(),
+ => result = await showAddEntryDialoge(context,
medRepo(),
mockEntry(sys: 123, dia: 56, pul: 43, note: 'Test note', pin: Colors.teal),),);
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
@@ -186,7 +180,7 @@ void main() {
dynamic result = 'result before save';
final record = mockEntry(sys: 123, dia: 56, pul: 43, note: 'Test note', pin: Colors.teal);
await loadDialoge(tester, (context) async {
- result = await showAddEntryDialoge(context, Settings(), medRepo(), record);
+ result = await showAddEntryDialoge(context, medRepo(), record);
},);
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
@@ -207,7 +201,7 @@ void main() {
testWidgets('should be able to input records', (WidgetTester tester) async {
dynamic result = 'result before save';
await loadDialoge(tester, (context) async {
- result = await showAddEntryDialoge(context, Settings(), medRepo(),);
+ result = await showAddEntryDialoge(context, medRepo(),);
});
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
@@ -236,7 +230,7 @@ void main() {
testWidgets('should allow value only', (WidgetTester tester) async {
dynamic result = 'result before save';
await loadDialoge(tester, (context) async
- => result = await showAddEntryDialoge(context, Settings(), medRepo(),),);
+ => result = await showAddEntryDialoge(context, medRepo(),),);
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
final localizations = await AppLocalizations.delegate.load(const Locale('en'));
@@ -262,7 +256,7 @@ void main() {
testWidgets('should allow note only', (WidgetTester tester) async {
dynamic result = 'result before save';
await loadDialoge(tester, (context) async
- => result = await showAddEntryDialoge(context, Settings(), medRepo(),),);
+ => result = await showAddEntryDialoge(context, medRepo(),),);
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
final localizations = await AppLocalizations.delegate.load(const Locale('en'));
@@ -286,7 +280,7 @@ void main() {
dynamic result = 'result before save';
await loadDialoge(tester, (context) async
- => result = await showAddEntryDialoge(context, Settings(), medRepo([
+ => result = await showAddEntryDialoge(context, medRepo([
mockMedicine(designation: 'medication1'),
med2,
],),),);
@@ -330,7 +324,7 @@ void main() {
});
testWidgets('should not allow invalid values', (tester) async {
final mRep = medRepo();
- await loadDialoge(tester, (context) => showAddEntryDialoge(context, Settings(), mRep));
+ await loadDialoge(tester, (context) => showAddEntryDialoge(context, mRep));
final localizations = await AppLocalizations.delegate.load(const Locale('en'));
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
@@ -394,8 +388,9 @@ void main() {
});
testWidgets('should allow invalid values when setting is set', (tester) async {
final mRep = medRepo();
- await loadDialoge(tester, (context) =>
- showAddEntryDialoge(context, Settings(validateInputs: false, allowMissingValues: true), mRep),);
+ await loadDialoge(tester, (context) => showAddEntryDialoge(context, mRep),
+ settings: Settings(validateInputs: false, allowMissingValues: true),
+ );
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
await tester.enterText(find.ancestor(of: find.text('Systolic').first, matching: find.byType(TextFormField)), '2');
@@ -406,8 +401,9 @@ void main() {
});
testWidgets('should respect settings.allowManualTimeInput', (tester) async {
final mRep = medRepo();
- await loadDialoge(tester, (context) =>
- showAddEntryDialoge(context, Settings(allowManualTimeInput: false), mRep),);
+ await loadDialoge(tester, (context) => showAddEntryDialoge(context, mRep),
+ settings: Settings(allowManualTimeInput: false),
+ );
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
expect(find.byIcon(Icons.edit), findsNothing);
@@ -415,7 +411,7 @@ void main() {
testWidgets('should start with sys input focused', (tester) async {
final mRep = medRepo();
await loadDialoge(tester, (context) =>
- showAddEntryDialoge(context, Settings(), mRep, mockEntry(sys: 12)),);
+ showAddEntryDialoge(context, mRep, mockEntry(sys: 12)),);
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
final primaryFocus = FocusManager.instance.primaryFocus;
@@ -431,7 +427,7 @@ void main() {
testWidgets('should focus next on input finished', (tester) async {
final mRep = medRepo();
await loadDialoge(tester, (context) =>
- showAddEntryDialoge(context, Settings(), mRep, mockEntry(sys: 12, dia: 3, pul: 4, note: 'note')),);
+ showAddEntryDialoge(context, mRep, mockEntry(sys: 12, dia: 3, pul: 4, note: 'note')),);
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
await tester.enterText(find.ancestor(of: find.text('Systolic').first, matching: find.byType(TextFormField)), '123');
@@ -473,7 +469,7 @@ void main() {
testWidgets('should focus last input field on backspace pressed in empty input field', (tester) async {
final mRep = medRepo();
await loadDialoge(tester, (context) =>
- showAddEntryDialoge(context, Settings(), mRep, mockEntry(sys: 12, dia: 3, pul: 4, note: 'note')),);
+ showAddEntryDialoge(context, mRep, mockEntry(sys: 12, dia: 3, pul: 4, note: 'note')),);
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
await tester.enterText(find.ancestor(of: find.text('note').first, matching: find.byType(TextFormField)), '');
@@ -535,7 +531,7 @@ void main() {
final mRep = medRepo([mockMedicine(designation: 'testmed')]);
dynamic result;
await loadDialoge(tester, (context) async =>
- result = await showAddEntryDialoge(context, Settings(), mRep),
+ result = await showAddEntryDialoge(context, mRep),
);
await tester.tap(find.byType(DropdownButton<Medicine?>));
@@ -575,7 +571,7 @@ void main() {
final mRep = medRepo([mockMedicine(designation: 'testmed')]);
dynamic result;
await loadDialoge(tester, (context) async =>
- result = await showAddEntryDialoge(context, Settings(), mRep),
+ result = await showAddEntryDialoge(context, mRep),
);
final localizations = await AppLocalizations.delegate.load(const Locale('en'));
@@ -619,7 +615,7 @@ void main() {
testWidgets('should not go back to last field when the current field is still filled', (tester) async {
final mRep = medRepo([mockMedicine(designation: 'testmed')]);
await loadDialoge(tester, (context) =>
- showAddEntryDialoge(context, Settings(), mRep, mockEntry(sys: 12, dia: 3, pul: 4, note: 'note')),);
+ showAddEntryDialoge(context, mRep, mockEntry(sys: 12, dia: 3, pul: 4, note: 'note')),);
expect(find.byType(DropdownButton<Medicine?>), findsNothing, reason: 'No medication in settings.');
await tester.enterText(find.ancestor(
app/test/ui/components/bluetooth_input_test.dart
@@ -6,7 +6,6 @@ import 'package:blood_pressure_app/bluetooth/device_scan_cubit.dart';
import 'package:blood_pressure_app/components/bluetooth_input.dart';
import 'package:blood_pressure_app/components/bluetooth_input/closed_bluetooth_input.dart';
import 'package:blood_pressure_app/components/bluetooth_input/measurement_success.dart';
-import 'package:blood_pressure_app/model/storage/storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart' hide BluetoothState;
import 'package:flutter_test/flutter_test.dart';
@@ -47,7 +46,6 @@ void main() {
final List<BloodPressureRecord> reads = [];
await tester.pumpWidget(materialApp(BluetoothInput(
- settings: Settings(),
onMeasurement: reads.add,
bluetoothCubit: () => bluetoothCubit,
deviceScanCubit: () => deviceScanCubit,
@@ -59,11 +57,9 @@ void main() {
expect(find.byType(ClosedBluetoothInput), findsNothing);
expect(reads, hasLength(1));
- expect(reads, contains(isA<BloodPressureRecord>()
- .having((p) => p.sys, 'sys', 123)
- .having((p) => p.dia, 'dia', 45)
- .having((p) => p.pul, 'pul', isNull),
- ));
+ expect(reads.first.sys?.mmHg, 123);
+ expect(reads.first.dia?.mmHg, 45);
+ expect(reads.first.pul, null);
});
testWidgets('allows closing after successful read', (WidgetTester tester) async {
@@ -91,7 +87,6 @@ void main() {
final List<BloodPressureRecord> reads = [];
await tester.pumpWidget(materialApp(BluetoothInput(
- settings: Settings(),
onMeasurement: reads.add,
bluetoothCubit: () => bluetoothCubit,
deviceScanCubit: () => deviceScanCubit,
app/test/ui/components/util.dart
@@ -8,11 +8,33 @@ import 'package:provider/provider.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
/// Create a root material widget with localizations.
-Widget materialApp(Widget child) => MaterialApp(
- localizationsDelegates: AppLocalizations.localizationsDelegates,
- locale: const Locale('en'),
- home: Scaffold(body:child),
-);
+Widget materialApp(Widget child, {
+ Settings? settings,
+ ExportSettings? exportSettings,
+ CsvExportSettings? csvExportSettings,
+ PdfExportSettings? pdfExportSettings,
+ IntervallStoreManager? intervallStoreManager,
+}) {
+ settings ??= Settings();
+ exportSettings ??= ExportSettings();
+ csvExportSettings ??= CsvExportSettings();
+ pdfExportSettings ??= PdfExportSettings();
+ intervallStoreManager ??= IntervallStoreManager(IntervallStorage(), IntervallStorage(), IntervallStorage());
+ return MultiProvider(
+ providers: [
+ ChangeNotifierProvider.value(value: settings),
+ ChangeNotifierProvider.value(value: exportSettings),
+ ChangeNotifierProvider.value(value: csvExportSettings),
+ ChangeNotifierProvider.value(value: pdfExportSettings),
+ ChangeNotifierProvider.value(value: intervallStoreManager),
+ ],
+ child: MaterialApp(
+ localizationsDelegates: AppLocalizations.localizationsDelegates,
+ locale: const Locale('en'),
+ home: Scaffold(body:child),
+ ),
+ );
+}
/// Creates a the same App as the main method.
Future<Widget> appBase(Widget child, {
@@ -25,31 +47,25 @@ Future<Widget> appBase(Widget child, {
MedicineRepository? medRepo,
MedicineIntakeRepository? intakeRepo,
}) async {
- settings ??= Settings();
- exportSettings ??= ExportSettings();
- csvExportSettings ??= CsvExportSettings();
- pdfExportSettings ??= PdfExportSettings();
- intervallStoreManager ??= IntervallStoreManager(IntervallStorage(), IntervallStorage(), IntervallStorage());
-
HealthDataStore? db;
if (bpRepo == null || medRepo == null || intakeRepo == null) {
db = await _getHealthDateStore();
}
- return MultiProvider(providers: [
- ChangeNotifierProvider(create: (_) => settings),
- ChangeNotifierProvider(create: (_) => exportSettings),
- ChangeNotifierProvider(create: (_) => csvExportSettings),
- ChangeNotifierProvider(create: (_) => pdfExportSettings),
- ChangeNotifierProvider(create: (_) => intervallStoreManager),
- ], child: MultiRepositoryProvider(
+ return MultiRepositoryProvider(
providers: [
RepositoryProvider(create: (context) => bpRepo ?? db!.bpRepo),
RepositoryProvider(create: (context) => medRepo ?? db!.medRepo),
RepositoryProvider(create: (context) => intakeRepo ?? db!.intakeRepo),
],
- child: materialApp(child),
- ),);
+ child: materialApp(child,
+ settings: settings,
+ exportSettings: exportSettings,
+ csvExportSettings: csvExportSettings,
+ pdfExportSettings: pdfExportSettings,
+ intervallStoreManager: intervallStoreManager,
+ ),
+ );
}
/// Creates a the same App as the main method.
@@ -93,9 +109,14 @@ Future<Widget> appBaseWithData(Widget child, {
/// UserColumn('initialInternalIdentifier', 'csvTitle', 'formatPattern')
/// ));
/// ```
-Future<void> loadDialoge(WidgetTester tester, void Function(BuildContext context) dialogeStarter, { String dialogeStarterText = 'X' }) async {
- await tester.pumpWidget(materialApp(Builder(builder: (context) =>
- TextButton(onPressed: () => dialogeStarter(context), child: Text(dialogeStarterText)),),),);
+Future<void> loadDialoge(WidgetTester tester, void Function(BuildContext context) dialogeStarter, {
+ String dialogeStarterText = 'X',
+ Settings? settings,
+}) async {
+ await tester.pumpWidget(materialApp(
+ Builder(builder: (context) => TextButton(onPressed: () => dialogeStarter(context), child: Text(dialogeStarterText)),),
+ settings: settings,
+ ),);
await tester.tap(find.text(dialogeStarterText));
await tester.pumpAndSettle();
}
docs/codestyle.md
@@ -7,7 +7,7 @@ The goal of this style guideline is to make dart code maintainable and to reduce
### File structure
- One class per file (exceptions: `StatefulWidget` and `sealed` classes).
-- Widgets in the `components` directory don't require any `Provider` as an ancestor.
+- Widgets in the `components` directory don't require any data repository as an ancestor.
- Files with widgets that fill the whole screen are suffixed with either `_screen` or `_dialoge`. The corresponding widgets end in `Screen` or `Dialoge`
- Closely related files are grouped in a subdirectory