Commit 9a561ae
Changed files (3)
lib
components
dialoges
screens
subsettings
lib/components/dialoges/tree_selection_dialoge.dart
@@ -0,0 +1,116 @@
+
+import 'package:blood_pressure_app/components/dialoges/fullscreen_dialoge.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+
+/// Generic multilayered fullscreen dialoge for nesting string selections.
+class TreeSelectionDialoge extends StatefulWidget {
+ /// Create a multilayered string selection dialoge.
+ const TreeSelectionDialoge({super.key,
+ required this.buildTitle,
+ required this.buildOptions,
+ required this.bottomAppBars,
+ this.validator,
+ });
+
+ /// Builder for currently visible options.
+ ///
+ /// Should return currently visible options or close the dialoge. The
+ /// `madeSelections` parameter contains all selections the user already made
+ /// in the order in which they were made.
+ ///
+ /// **Tip:** use `madeSelections.length` to obtain the current depth.
+ ///
+ /// Guaranteed to be called after every selection.
+ final List<String> Function(List<String> madeSelections) buildOptions;
+
+ /// Builds a title that tells users what to this selection is about.
+ ///
+ /// Behaves like [buildOptions].
+ final String Function(List<String> madeSelections) buildTitle;
+
+ /// Validates selections and returns errors.
+ ///
+ /// When this function returns a string saving is not possible and the string
+ /// will be shown to the user. When this function returns null or is null the
+ /// selections will be returned popped to the underlying scope.
+ final String? Function(List<String> madeSelections)? validator;
+
+ /// Whether to move the app bar for saving and loading to the bottom of the
+ /// screen.
+ final bool bottomAppBars;
+
+ @override
+ State<TreeSelectionDialoge> createState() => _TreeSelectionDialogeState();
+}
+
+class _TreeSelectionDialogeState extends State<TreeSelectionDialoge> {
+ /// Selections the user already made.
+ final _selections = <String>[];
+
+ String? _error;
+
+ @override
+ Widget build(BuildContext context) {
+ final localizations = AppLocalizations.of(context)!;
+ final items = widget.buildOptions(_selections);
+ return PopScope(
+ canPop: _selections.isEmpty,
+ onPopInvoked: (didPop) {
+ if (!didPop) {
+ setState(_selections.removeLast);
+ }
+ },
+ child: FullscreenDialoge(
+ onActionButtonPressed: () {
+ setState(() {
+ _error = widget.validator?.call(_selections);
+ });
+ if (_error != null) {
+ return;
+ }
+ final selections = _selections.toList();
+ _selections.clear();
+ Navigator.pop(context, selections);
+ },
+ actionButtonText: localizations.btnSave,
+ bottomAppBar: widget.bottomAppBars,
+ body: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Expanded(
+ child: ListView.builder(
+ shrinkWrap: true,
+ itemCount: items.length + 1,
+ itemBuilder: (context, idx) => (idx == 0)
+ ? ListTile(
+ title: Text(widget.buildTitle(_selections)),
+ titleTextStyle: Theme.of(context).textTheme.headlineSmall,
+ )
+ : Padding(
+ padding: const EdgeInsets.only(top: 10),
+ child: ListTile(
+ title: Text(items[idx-1]),
+ onTap: () => setState(() {
+ _selections.add(items[idx-1]);
+ }),
+ tileColor: Theme.of(context).cardColor,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(10),
+ ),
+ ),
+ ),
+ ),
+ ),
+ if (_error != null)
+ ListTile(
+ title: Text(_error!,),
+ textColor: Theme.of(context).colorScheme.error,
+ titleTextStyle: Theme.of(context).textTheme.labelLarge,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
lib/screens/subsettings/foreign_db_import_screen.dart
@@ -1,4 +1,5 @@
import 'package:blood_pressure_app/components/consistent_future_builder.dart';
+import 'package:blood_pressure_app/components/dialoges/tree_selection_dialoge.dart';
import 'package:blood_pressure_app/model/export_import/import_field_type.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
@@ -18,91 +19,52 @@ class ForeignDBImportScreen extends StatefulWidget {
}
class _ForeignDBImportScreenState extends State<ForeignDBImportScreen> {
-
- /// The name of the table that contains the data.
- String? _selectedTableName;
-
- /// The name of the selected column that contains the timestamps.
- String? _activeTimeColumnName;
-
- /// The name of the column selected before selecting a datatype.
- ///
- /// Once a datatype is selected, this is reset to null.
- String? _lastSelectedColumnName;
-
@override
- Widget build(BuildContext context) => Scaffold(
- appBar: AppBar(
- title: (_selectedTableName == null)
- ? const Text('Table')
- : const Text('Time column'),
- ),
- body: ConsistentFutureBuilder(
- future: _ColumnImportData.loadFromDB(widget.db),
- onData: (BuildContext context, _ColumnImportData data) {
- final localizations = AppLocalizations.of(context)!;
-
- if (_selectedTableName == null) {
- return _buildTableSelection(data);
- }
- if (_activeTimeColumnName == null) {
- return _buildColumnSelection(data, (String columnName) => setState(() {
- _activeTimeColumnName = columnName;
- }),);
- }
-
- if (_lastSelectedColumnName == null) {
- return _buildColumnSelection(data, (columnName) => setState(() {
- _lastSelectedColumnName = columnName;
- }),);
- }
- return _buildCardList(
- RowDataFieldType.values.map((e) => e.localize(localizations)),
- (columnName) {
- setState(() {
- // TODO: add to columns
- _lastSelectedColumnName = null;
- });
- },
- );
-
- // TODO: add finalize button
-
-
- },
- ),
- );
-
- Widget _buildTableSelection(_ColumnImportData data) =>
- _buildCardList(data.tableNames, (tableName) => setState(() {
- _selectedTableName = tableName;
- }));
-
- Widget _buildColumnSelection(
- _ColumnImportData data,
- void Function(String columnName) onSelection,
- ) => _buildCardList(data.columns[_selectedTableName]!, onSelection);
-
- Widget _buildCardList(
- Iterable<String> allOptions,
- void Function(String columnName) onSelection,
- ) => ListView(
- children: [
- for (final option in allOptions)
- InkWell(
- onTap: () => onSelection(option),
- child: Card(
- child: Padding(
- padding: const EdgeInsets.all(14),
- child: Text(option),
- ),
- ),
- ),
- ],
+ Widget build(BuildContext context) => ConsistentFutureBuilder(
+ future: _ColumnImportData.loadFromDB(widget.db),
+ onData: (BuildContext context, _ColumnImportData data) {
+ final localizations = AppLocalizations.of(context)!;
+ return TreeSelectionDialoge(
+ buildOptions: (selections) {
+ if (selections.isEmpty) {
+ return data.tableNames.toList();
+ }
+ if (selections.length == 1) {
+ // TODO: don't show tables without columns
+ return data.columns[selections[0]]!;
+ }
+
+ if ((selections.length % 2 == 0)) {
+ final columns = data.columns[selections[0]]!;
+ columns.remove(selections[1]);
+ return columns;
+ } else {
+ return RowDataFieldType.values
+ .whereNot((element) => element == RowDataFieldType.timestamp)
+ .map((e) => e.localize(localizations))
+ .toList();
+ }
+ },
+ validator: (todo) { // TODO
+ return 'The schnibledumps doesn\'t schwibble!';
+ },
+ buildTitle: (selections) {
+ if (selections.isEmpty) return 'Select table';
+ if (selections.length == 1) return 'Select time column';
+ if ((selections.length % 2 == 0)) {
+ return 'Select data column';
+ } else {
+ return 'Select column type';
+ }
+ },
+ bottomAppBars: true, // TODO
+ );
+ // TODO: perform import
+ // TODO: localize everything
+ },
);
}
-
class _ColumnImportData {
_ColumnImportData._create(this.columns);
pubspec.lock
@@ -329,18 +329,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
- sha256: "04be76c4a4bb50f14904e64749237e541e7c7bcf7ec0b196907322ab5d2fc739"
+ sha256: f8cdf1383f5b4672a2693d875f1f239af6bd7e4a8925a17ef7219226db932624
url: "https://pub.dev"
source: hosted
- version: "9.0.16"
+ version: "10.0.1"
+ leak_tracker_flutter_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_flutter_testing
+ sha256: a2055640bf5bc903475e4bbdb34e04f8bf698542bee41edec47d337a5939e1ae
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.3"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
- sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff
+ sha256: e62042d479c4c139dd774125ed4dfbde646b8f07ac228e3c1b57a3d91d6d9df4
url: "https://pub.dev"
source: hosted
- version: "1.0.5"
+ version: "2.0.2"
lints:
dependency: transitive
description:
@@ -369,10 +377,10 @@ packages:
dependency: transitive
description:
name: matcher
- sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
+ sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
- version: "0.12.16"
+ version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
@@ -433,10 +441,10 @@ packages:
dependency: "direct main"
description:
name: path
- sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
+ sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
- version: "1.8.3"
+ version: "1.9.0"
path_parsing:
dependency: transitive
description:
@@ -844,4 +852,4 @@ packages:
version: "3.1.2"
sdks:
dart: ">=3.2.0 <4.0.0"
- flutter: ">=3.16.0"
+ flutter: ">=3.18.0-18.0.pre.54"