Commit 70864b4

derdilla <derdilla06@gmail.com>
2023-06-22 15:01:33
refactor ExportImportScreen to be more manageable
1 parent 57c41b4
Changed files (1)
lib
screens
lib/screens/subsettings/export_import_screen.dart
@@ -14,16 +14,9 @@ import 'package:intl/intl.dart';
 import 'package:provider/provider.dart';
 import 'package:share_plus/share_plus.dart';
 
-class ExportImportScreen extends StatefulWidget {
+class ExportImportScreen extends StatelessWidget {
   const ExportImportScreen({super.key});
 
-  @override
-  State<ExportImportScreen> createState() => _ExportImportScreenState();
-}
-
-class _ExportImportScreenState extends State<ExportImportScreen> {
-  bool _showWarnBanner = true;
-
   @override
   Widget build(BuildContext context) {
     return Scaffold(
@@ -34,266 +27,134 @@ class _ExportImportScreenState extends State<ExportImportScreen> {
       body: Container(
         margin: const EdgeInsets.only(bottom: 80),
         child: Consumer<Settings>(builder: (context, settings, child) {
-           // export range
-          var exportRange = settings.exportDataRange;
-          String exportRangeText;
-          if (exportRange.start.millisecondsSinceEpoch != 0 && exportRange.end.millisecondsSinceEpoch != 0) {
-            var formatter = DateFormat.yMMMd(AppLocalizations.of(context)!.localeName);
-            exportRangeText = '${formatter.format(exportRange.start)} - ${formatter.format(exportRange.end)}';
-          } else {
-            exportRangeText = AppLocalizations.of(context)!.errPleaseSelect;
-          }
-
-          // default options
-          List<Widget> options = [
-            SwitchSettingsTile(
-                title: Text(AppLocalizations.of(context)!.exportLimitDataRange),
-                initialValue: settings.exportLimitDataRange,
-                onToggle: (value) {
-                  settings.exportLimitDataRange = value;
-                }
-            ),
-            (settings.exportLimitDataRange) ? SettingsTile(
-                title: Text(AppLocalizations.of(context)!.exportInterval),
-                description: Text(exportRangeText),
-                onPressed: (context) async {
-                  var model = Provider.of<BloodPressureModel>(context, listen: false);
-                  var newRange = await showDateRangePicker(context: context, firstDate: await model.firstDay, lastDate: await model.lastDay);
-                  if (newRange == null && context.mounted) {
-                    ScaffoldMessenger.of(context)
-                        .showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.errNoRangeForExport)));
-                    return;
-                  }
-                  settings.exportDataRange = newRange ?? DateTimeRange(start: DateTime.fromMillisecondsSinceEpoch(0), end: DateTime.fromMillisecondsSinceEpoch(0));
-
-                }
-            ) : const SizedBox.shrink(),
-            DropDownSettingsTile<ExportFormat>(
-              key: const Key('exportFormat'),
-              title: Text(AppLocalizations.of(context)!.exportFormat),
-              value: settings.exportFormat,
-              items: [
-                DropdownMenuItem(value: ExportFormat.csv, child: Text(AppLocalizations.of(context)!.csv)),
-                DropdownMenuItem(value: ExportFormat.pdf, child: Text(AppLocalizations.of(context)!.pdf)),
-                DropdownMenuItem(value: ExportFormat.db, child: Text(AppLocalizations.of(context)!.db)),
-              ],
-              onChanged: (ExportFormat? value) {
-                if (value != null) {
-                  settings.exportFormat = value;
-                }
-              },
-            ),
-            /*
-            DropDownSettingsTile<MimeType>(
-              key: const Key('exportMimeType'),
-              title: Text(AppLocalizations.of(context)!.exportMimeType),
-              description: Text(AppLocalizations.of(context)!.exportMimeTypeDesc),
-              value: settings.exportMimeType,
-              items: [
-                DropdownMenuItem(value: MimeType.csv, child: Text(AppLocalizations.of(context)!.csv)),
-                DropdownMenuItem(value: MimeType.text, child: Text(AppLocalizations.of(context)!.text)),
-                DropdownMenuItem(value: MimeType.pdf, child: Text(AppLocalizations.of(context)!.pdf)),
-                DropdownMenuItem(value: MimeType.other, child: Text(AppLocalizations.of(context)!.other)),
+          return SingleChildScrollView(
+            child: Column(
+              children: [
+                const ExportWarnBanner(),
+                DropDownSettingsTile<ExportFormat>(
+                  key: const Key('exportFormat'),
+                  title: Text(AppLocalizations.of(context)!.exportFormat),
+                  value: settings.exportFormat,
+                  items: [
+                    DropdownMenuItem(value: ExportFormat.csv, child: Text(AppLocalizations.of(context)!.csv)),
+                    DropdownMenuItem(value: ExportFormat.pdf, child: Text(AppLocalizations.of(context)!.pdf)),
+                    DropdownMenuItem(value: ExportFormat.db, child: Text(AppLocalizations.of(context)!.db)),
+                  ],
+                  onChanged: (ExportFormat? value) {
+                    if (value != null) {
+                      settings.exportFormat = value;
+                    }
+                  },
+                ),
+                const ExportDataRangeSettings(),
+                InputSettingsTile(
+                  title: Text(AppLocalizations.of(context)!.fieldDelimiter),
+                  inputWidth: 40,
+                  initialValue: settings.csvFieldDelimiter,
+                  disabled: !(settings.exportFormat == ExportFormat.csv),
+                  onEditingComplete: (value) {
+                    if (value != null) {
+                      settings.csvFieldDelimiter = value;
+                    }
+                  },
+                ),
+                InputSettingsTile(
+                  title: Text(AppLocalizations.of(context)!.textDelimiter),
+                  inputWidth: 40,
+                  initialValue: settings.csvTextDelimiter,
+                  disabled: !(settings.exportFormat == ExportFormat.csv),
+                  onEditingComplete: (value) {
+                    if (value != null) {
+                      settings.csvTextDelimiter = value;
+                    }
+                  },
+                ),
+                SwitchSettingsTile(
+                    title: Text(AppLocalizations.of(context)!.exportCsvHeadline),
+                    description: Text(AppLocalizations.of(context)!.exportCsvHeadlineDesc),
+                    initialValue: settings.exportCsvHeadline,
+                    disabled: !(settings.exportFormat == ExportFormat.csv),
+                    onToggle: (value) {
+                      settings.exportCsvHeadline = value;
+                    }
+                ),
+                const ExportFieldCustomisationSetting(),
               ],
-              onChanged: (MimeType? value) {
-                if (value != null) {
-                  settings.exportMimeType = value;
-                }
-              },
             ),
-             */
-          ];
-
-          // mode specifics
-          List<Widget> modeSpecificSettings = [];
-          if (settings.exportFormat == ExportFormat.csv) {
-            modeSpecificSettings = [
-              InputSettingsTile(
-                title: Text(AppLocalizations.of(context)!.fieldDelimiter),
-                inputWidth: 40,
-                initialValue: settings.csvFieldDelimiter,
-                onEditingComplete: (value) {
-                  if (value != null) {
-                    settings.csvFieldDelimiter = value;
-                  }
-                },
-              ),
-              InputSettingsTile(
-                title: Text(AppLocalizations.of(context)!.textDelimiter),
-                inputWidth: 40,
-                initialValue: settings.csvTextDelimiter,
-                onEditingComplete: (value) {
-                  if (value != null) {
-                    settings.csvTextDelimiter = value;
-                  }
-                },
-              ),
-              SwitchSettingsTile(
-                  title: Text(AppLocalizations.of(context)!.exportCsvHeadline),
-                  description: Text(AppLocalizations.of(context)!.exportCsvHeadlineDesc),
-                  initialValue: settings.exportCsvHeadline,
-                  onToggle: (value) {
-                    settings.exportCsvHeadline = value;
-                  }
-              ),
-              SwitchSettingsTile(
-                title: Text(AppLocalizations.of(context)!.exportCustomEntries),
-                initialValue: settings.exportCustomEntries,
-                onToggle: (value) {
-                  settings.exportCustomEntries = value;
-                }
-              ),
-              (settings.exportCustomEntries) ? const CsvItemsOrderCreator(): const SizedBox.shrink()
-            ];
-          }
-
-          // warn banner when the exported data can't be imported
-          if (_showWarnBanner && ![ExportFormat.csv, ExportFormat.db].contains(settings.exportFormat) ||
-              settings.exportCsvHeadline == false ||
-              !(
-                  (settings.exportItems.contains('timestampUnixMs') || settings.exportItems.contains('isoUTCTime')) &&
-                  settings.exportItems.contains('systolic') &&
-                  settings.exportItems.contains('diastolic') &&
-                  settings.exportItems.contains('pulse') &&
-                  settings.exportItems.contains('notes')
-              )
-          ) {
-            options.insert(0, MaterialBanner(
-              padding: const EdgeInsets.all(20),
-              content: Text(AppLocalizations.of(context)!.exportWarnConfigNotImportable),
-              actions: [
-                TextButton(
-                    onPressed: () {
-                      setState(() {
-                        _showWarnBanner = false;
-                      });
-                    },
-                    child: Text(AppLocalizations.of(context)!.btnConfirm))
-              ]
-            ));
-          }
-
-          // create view
-          options.addAll(modeSpecificSettings);
-          options.add(const SizedBox(height: 20,));
-          return ListView(
-            children: options,
           );
-        }),
+        })
       ),
-      floatingActionButton: Container(
-        height: 60,
-        color: Theme.of(context).colorScheme.onBackground,
-        child: Center(
-          child: Row(
-            children: [
-              Expanded(
-                  flex: 50,
-                  child: MaterialButton(
-                    height: 60,
-                    child:  Text(AppLocalizations.of(context)!.export),
-                    onPressed: () async {
-                      var settings = Provider.of<Settings>(context, listen: false);
+      floatingActionButton: const ExportImportButtons(),
+    );
+  }
+}
 
-                      final UnmodifiableListView<BloodPressureRecord> entries;
-                      if (settings.exportLimitDataRange) {
-                        var range = settings.exportDataRange;
-                        if (range.start.millisecondsSinceEpoch == 0 || range.end.millisecondsSinceEpoch == 0) {
-                          ScaffoldMessenger.of(context)
-                              .showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.errNoRangeForExport)));
-                          return;
-                        }
-                        entries = await Provider.of<BloodPressureModel>(context, listen: false).getInTimeRange(settings.exportDataRange.start, settings.exportDataRange.end);
-                      } else {
-                        entries = await Provider.of<BloodPressureModel>(context, listen: false).all;
-                      }
-                      var fileContents = await DataExporter(settings).createFile(entries);
+class ExportDataRangeSettings extends StatelessWidget {
+  const ExportDataRangeSettings({super.key});
 
-                      String filename = 'blood_press_${DateTime.now().toIso8601String()}';
-                      switch(settings.exportFormat) {
-                        case ExportFormat.csv:
-                          filename += '.csv';
-                          break;
-                        case ExportFormat.pdf:
-                          filename += '.pdf';
-                          break;
-                        case ExportFormat.db:
-                          filename += '.db';
-                          break;
-                      }
-                      String path = await FileSaver.instance.saveFile(name: filename, bytes: fileContents);
+  @override
+  Widget build(BuildContext context) {
+    return Consumer<Settings>(builder: (context, settings, child) {
+      var exportRange = settings.exportDataRange;
+      String exportRangeText;
+      if (exportRange.start.millisecondsSinceEpoch != 0 && exportRange.end.millisecondsSinceEpoch != 0) {
+        var formatter = DateFormat.yMMMd(AppLocalizations.of(context)!.localeName);
+        exportRangeText = '${formatter.format(exportRange.start)} - ${formatter.format(exportRange.end)}';
+      } else {
+        exportRangeText = AppLocalizations.of(context)!.errPleaseSelect;
+      }
+      return Column(
+        children: [
+          SwitchSettingsTile(
+            title: Text(AppLocalizations.of(context)!.exportLimitDataRange),
+            initialValue: settings.exportLimitDataRange,
+            onToggle: (value) {
+              settings.exportLimitDataRange = value;
+            },
+            disabled: settings.exportFormat == ExportFormat.db,
+          ),
+          SettingsTile(
+            title: Text(AppLocalizations.of(context)!.exportInterval),
+            description: Text(exportRangeText),
+            disabled: !settings.exportLimitDataRange,
+            onPressed: (context) async {
+              var model = Provider.of<BloodPressureModel>(context, listen: false);
+              var newRange = await showDateRangePicker(context: context, firstDate: await model.firstDay, lastDate: await model.lastDay);
+              if (newRange == null && context.mounted) {
+                ScaffoldMessenger.of(context)
+                    .showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.errNoRangeForExport)));
+                return;
+              }
+              settings.exportDataRange = newRange ?? DateTimeRange(start: DateTime.fromMillisecondsSinceEpoch(0), end: DateTime.fromMillisecondsSinceEpoch(0));
+            }
+          ),
+        ],
+      );
+    });
+  }
 
-                      if ((Platform.isLinux || Platform.isWindows || Platform.isMacOS) && context.mounted) {
-                        ScaffoldMessenger.of(context)
-                            .showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.success(path))));
-                      } else if (Platform.isAndroid || Platform.isIOS) {
-                        Share.shareXFiles([
-                          XFile(
-                            path,
-                            mimeType: MimeType.csv.type
-                          )
-                        ]);
-                      } else {
-                        ScaffoldMessenger.of(context)
-                            .showSnackBar(const SnackBar(content: Text('UNSUPPORTED PLATFORM')));
-                      }
-                    },
-                  )
-              ),
-              const VerticalDivider(),
-              Expanded(
-                flex: 50,
-                child: MaterialButton(
-                  height: 60,
-                  child: Text(AppLocalizations.of(context)!.import),
-                  onPressed: () async {
-                    final settings = Provider.of<Settings>(context, listen: false);
-                    if (!(settings.exportFormat == ExportFormat.csv)) {
-                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
-                          content: Text(AppLocalizations.of(context)!.errNotCsvFormat)));
-                    }
-                    if (!settings.exportCsvHeadline) {
-                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
-                          content: Text(AppLocalizations.of(context)!.errNeedHeadline)));
-                    }
+}
 
-                    var result = await FilePicker.platform.pickFiles(
-                      allowMultiple: false,
-                      withData: true,
-                    );
-                    if (!context.mounted) return;
-                    if (result == null) {
-                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
-                          content: Text(AppLocalizations.of(context)!.errNoFileOpened)));
-                      return;
-                    }
-                    var binaryContent = result.files.single.bytes;
-                    if (binaryContent == null) {
-                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
-                          content: Text(AppLocalizations.of(context)!.errCantReadFile)));
-                      return;
-                    }
+class ExportFieldCustomisationSetting extends StatelessWidget {
+  const ExportFieldCustomisationSetting({super.key});
 
-                    var fileContents = DataExporter(settings).parseFile(binaryContent);
-                    if (fileContents == null) {
-                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
-                          content: Text(AppLocalizations.of(context)!.errNotImportable)));
-                      return;
-                    }
-                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
-                        content: Text(AppLocalizations.of(context)!.importSuccess(fileContents.length))));
-                    var model = Provider.of<BloodPressureModel>(context, listen: false);
-                    for (final e in fileContents) {
-                      model.add(e);
-                    }
-                  },
-                )
-              ),
-            ],
+  @override
+  Widget build(BuildContext context) {
+    return Consumer<Settings>(builder: (context, settings, child) {
+      return Column(
+        children: [
+          SwitchSettingsTile(
+              title: Text(AppLocalizations.of(context)!.exportCustomEntries),
+              initialValue: settings.exportCustomEntries,
+              disabled: settings.exportFormat != ExportFormat.csv,
+              onToggle: (value) {
+                settings.exportCustomEntries = value;
+              }
           ),
-        ),
-      ),
-    );
+          (settings.exportFormat == ExportFormat.csv && settings.exportCustomEntries) ? const CsvItemsOrderCreator() : const SizedBox.shrink()
+        ],
+      );
+    });
   }
 }
 
@@ -398,6 +259,166 @@ class CsvItemsOrderCreator extends StatelessWidget {
       );
     });
   }
+}
 
+class ExportImportButtons extends StatelessWidget {
+  const ExportImportButtons({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      height: 60,
+      color: Theme.of(context).colorScheme.onBackground,
+      child: Center(
+        child: Row(
+          children: [
+            Expanded(
+                flex: 50,
+                child: MaterialButton(
+                  height: 60,
+                  child:  Text(AppLocalizations.of(context)!.export),
+                  onPressed: () async {
+                    var settings = Provider.of<Settings>(context, listen: false);
+
+                    final UnmodifiableListView<BloodPressureRecord> entries;
+                    if (settings.exportLimitDataRange) {
+                      var range = settings.exportDataRange;
+                      if (range.start.millisecondsSinceEpoch == 0 || range.end.millisecondsSinceEpoch == 0) {
+                        ScaffoldMessenger.of(context)
+                            .showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.errNoRangeForExport)));
+                        return;
+                      }
+                      entries = await Provider.of<BloodPressureModel>(context, listen: false).getInTimeRange(settings.exportDataRange.start, settings.exportDataRange.end);
+                    } else {
+                      entries = await Provider.of<BloodPressureModel>(context, listen: false).all;
+                    }
+                    var fileContents = await DataExporter(settings).createFile(entries);
+
+                    String filename = 'blood_press_${DateTime.now().toIso8601String()}';
+                    switch(settings.exportFormat) {
+                      case ExportFormat.csv:
+                        filename += '.csv';
+                        break;
+                      case ExportFormat.pdf:
+                        filename += '.pdf';
+                        break;
+                      case ExportFormat.db:
+                        filename += '.db';
+                        break;
+                    }
+                    String path = await FileSaver.instance.saveFile(name: filename, bytes: fileContents);
+
+                    if ((Platform.isLinux || Platform.isWindows || Platform.isMacOS) && context.mounted) {
+                      ScaffoldMessenger.of(context)
+                          .showSnackBar(SnackBar(content: Text(AppLocalizations.of(context)!.success(path))));
+                    } else if (Platform.isAndroid || Platform.isIOS) {
+                      Share.shareXFiles([
+                        XFile(
+                            path,
+                            mimeType: MimeType.csv.type
+                        )
+                      ]);
+                    } else {
+                      ScaffoldMessenger.of(context)
+                          .showSnackBar(const SnackBar(content: Text('UNSUPPORTED PLATFORM')));
+                    }
+                  },
+                )
+            ),
+            const VerticalDivider(),
+            Expanded(
+                flex: 50,
+                child: MaterialButton(
+                  height: 60,
+                  child: Text(AppLocalizations.of(context)!.import),
+                  onPressed: () async {
+                    final settings = Provider.of<Settings>(context, listen: false);
+                    if (!(settings.exportFormat == ExportFormat.csv)) {
+                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                          content: Text(AppLocalizations.of(context)!.errNotCsvFormat)));
+                    }
+                    if (!settings.exportCsvHeadline) {
+                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                          content: Text(AppLocalizations.of(context)!.errNeedHeadline)));
+                    }
+
+                    var result = await FilePicker.platform.pickFiles(
+                      allowMultiple: false,
+                      withData: true,
+                    );
+                    if (!context.mounted) return;
+                    if (result == null) {
+                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                          content: Text(AppLocalizations.of(context)!.errNoFileOpened)));
+                      return;
+                    }
+                    var binaryContent = result.files.single.bytes;
+                    if (binaryContent == null) {
+                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                          content: Text(AppLocalizations.of(context)!.errCantReadFile)));
+                      return;
+                    }
+
+                    var fileContents = DataExporter(settings).parseFile(binaryContent);
+                    if (fileContents == null) {
+                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                          content: Text(AppLocalizations.of(context)!.errNotImportable)));
+                      return;
+                    }
+                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
+                        content: Text(AppLocalizations.of(context)!.importSuccess(fileContents.length))));
+                    var model = Provider.of<BloodPressureModel>(context, listen: false);
+                    for (final e in fileContents) {
+                      model.add(e);
+                    }
+                  },
+                )
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}
+
+class ExportWarnBanner extends StatefulWidget {
+  const ExportWarnBanner({super.key});
+
+  @override
+  State<StatefulWidget> createState() => _ExportWarnBannerState();
 }
 
+class _ExportWarnBannerState extends State<ExportWarnBanner> {
+  bool _showWarnBanner = true;
+  @override
+  Widget build(BuildContext context) {
+    return Consumer<Settings>(builder: (context, settings, child) {
+      if (_showWarnBanner && ![ExportFormat.csv, ExportFormat.db].contains(settings.exportFormat) ||
+          settings.exportCsvHeadline == false ||
+          !(
+              (settings.exportItems.contains('timestampUnixMs') || settings.exportItems.contains('isoUTCTime')) &&
+                  settings.exportItems.contains('systolic') &&
+                  settings.exportItems.contains('diastolic') &&
+                  settings.exportItems.contains('pulse') &&
+                  settings.exportItems.contains('notes')
+          )
+      ) {
+        return MaterialBanner(
+            padding: const EdgeInsets.all(20),
+            content: Text(AppLocalizations.of(context)!.exportWarnConfigNotImportable),
+            actions: [
+              TextButton(
+                  onPressed: () {
+                    setState(() {
+                      _showWarnBanner = false;
+                    });
+                  },
+                  child: Text(AppLocalizations.of(context)!.btnConfirm))
+            ]
+        );
+      }
+      return const SizedBox.shrink();
+    });
+    }
+  }
+