Commit 70864b4
Changed files (1)
lib
screens
subsettings
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();
+ });
+ }
+ }
+