Commit 8c2c0fa

derdilla <82763757+NobodyForNothing@users.noreply.github.com>
2023-12-11 16:44:39
update ExportFieldCustomisationSettings
Signed-off-by: derdilla <82763757+NobodyForNothing@users.noreply.github.com>
1 parent 2850e2e
lib/components/export_item_order.dart
@@ -1,227 +0,0 @@
-
-import 'dart:async';
-
-import 'package:blood_pressure_app/model/export_import/column.dart';
-import 'package:flutter/material.dart';
-
-@Deprecated("TODO: repalace")
-class ExportItemsCustomizer extends StatefulWidget {
-  final List<ExportColumn> shownItems;
-  final List<ExportColumn> disabledItems;
-  final FutureOr<void> Function(List<ExportColumn> exportItems, List<ExportColumn> exportAddableItems) onReorder;
-
-  const ExportItemsCustomizer({super.key, required this.shownItems, required this.disabledItems,
-      required this.onReorder});
-
-  @override
-  State<ExportItemsCustomizer> createState() => _ExportItemsCustomizerState();
-}
-
-class _ExportItemsCustomizerState extends State<ExportItemsCustomizer> {
-  @override
-  Widget build(BuildContext context) => Text('TODO - ExportItemsCustomizer');
-/* => Consumer<ExportColumnsManager>(
-    builder: (context, manager, child) => _buildAddItemBadge(context,
-        child: _buildManagePresetsBadge(context, manager,
-            child: _buildList(context, manager)
-        )
-    )
-  );
-
-
-  Container _buildList(BuildContext context, ExportColumnsManager manager) {
-    return Container(
-        margin: const EdgeInsets.all(25),
-        padding: const EdgeInsets.all(20),
-        height: 420,
-        decoration: BoxDecoration(
-          border: Border.all(color: Theme.of(context).textTheme.labelLarge?.color ?? Colors.teal),
-          borderRadius: const BorderRadius.all(Radius.circular(10)),
-        ),
-        clipBehavior: Clip.hardEdge,
-        child: ReorderableListView(
-          shrinkWrap: true,
-          onReorder: _onReorderList,
-          children: <Widget>[
-            for (int i = 0; i < manger.shownItems.length; i += 1)
-              ListTile(
-                  key: Key('l_${widget.shownItems[i].internalName}'),
-                  title: Text(widget.shownItems[i].columnTitle),
-                  trailing: _buildListItemTrailing( context, widget.shownItems[i], exportConfigModel),
-                  contentPadding: EdgeInsets.zero
-              ),
-            _buildListSectionDivider(context),
-            for (int i = 0; i < widget.disabledItems.length; i += 1)
-              (widget.disabledItems[i].hidden) ? SizedBox.shrink(key: Key('ul_${widget.disabledItems[i].internalName}'),)
-              : ListTile(
-                key: Key('ul_${widget.disabledItems[i].internalName}'),
-                title: Opacity(
-                  opacity: 0.7,
-                  child: Text(widget.disabledItems[i].columnTitle),
-                ),
-                trailing: _buildListItemTrailing(context, widget.disabledItems[i], exportConfigModel),
-                contentPadding: EdgeInsets.zero
-              ),
-          ],
-        ),
-      );
-  }
-
-  Widget _buildAddItemBadge(BuildContext context, {required Widget child}) {
-    return badges.Badge(
-      badgeStyle: badges.BadgeStyle(
-        badgeColor: Theme.of(context).colorScheme.background,
-        padding: const EdgeInsets.all(10)
-      ),
-      position: badges.BadgePosition.bottomEnd(bottom: 3, end: 3),
-      badgeContent: Container(
-        decoration: BoxDecoration(
-          border: Border.all(color: Theme.of(context).colorScheme.onBackground),
-          shape: BoxShape.circle
-        ),
-        child: IconButton(
-          tooltip: AppLocalizations.of(context)!.addExportformat,
-          onPressed:() {
-            Navigator.of(context).push(MaterialPageRoute(builder: (context) => const EditExportColumnPage()));
-          },
-          icon: const Icon(Icons.add),
-        ),
-      ),
-      child: child,
-    );
-  }
-
-  Widget _buildManagePresetsBadge(BuildContext context, ExportColumnsManager manager, {required Widget child}) {
-    final exportConfigurations = result.exportConfigurations;
-    return badges.Badge(
-      position: badges.BadgePosition.topEnd(top: 3, end: 3),
-      badgeStyle: badges.BadgeStyle(
-        badgeColor: Theme.of(context).colorScheme.background,
-        padding: const EdgeInsets.all(5)
-      ),
-      badgeContent: PopupMenuButton<int>(
-        icon: const Icon(Icons.collections_bookmark),
-        itemBuilder: (BuildContext context) {
-          return [
-            for (var i = 0; i< exportConfigurations.length; i++)
-              PopupMenuItem<int>(value: i, child: Text(exportConfigurations[i].$1)),
-          ];
-        },
-        onSelected: (value) {
-          final exportSettings = Provider.of<ExportSettings>(context, listen: false);
-          if (exportSettings.exportFormat == ExportFormat.csv) {
-            Provider.of<CsvExportSettings>(context, listen: false).customFields = exportConfigurations[value].$2;
-          } else {
-            assert(exportSettings.exportFormat == ExportFormat.pdf);
-            Provider.of<PdfExportSettings>(context, listen: false).customFields = exportConfigurations[value].$2;
-          }
-        },
-      ),
-      child: child,
-    );
-  }
-
-  Widget _buildListItemTrailing(BuildContext context, ExportColumn data, ExportConfigurationModel config) {
-    return Row(
-      mainAxisSize: MainAxisSize.min,
-      children: [
-        if (data.editable)
-          IconButton(
-            onPressed: () async {
-              setState(() {
-                config.delete(data);
-              });
-            },
-            tooltip: AppLocalizations.of(context)!.delete,
-            icon: const Icon(Icons.delete),
-            color: Colors.red,
-          ),
-        IconButton(
-          onPressed: () {
-            Navigator.of(context).push(MaterialPageRoute(builder: (context) =>
-              EditExportColumnPage(
-                initialDisplayName: data.columnTitle,
-                initialInternalName: data.internalName,
-                initialFormatPattern: data.formatPattern,
-                editable: data.editable,
-              )
-            ));
-          },
-          tooltip: AppLocalizations.of(context)!.edit,
-          icon: const Icon(Icons.edit)
-        ),
-        const Icon(Icons.drag_handle),
-      ],
-    );
-  }
-
-  Widget _buildListSectionDivider(BuildContext context) {
-    return IgnorePointer(
-      key: UniqueKey(),
-      child: Opacity(
-        opacity: 0.7,
-        child: Row(
-            children: <Widget>[
-              const Expanded(
-                  child: Divider()
-              ),
-              Container(
-                  padding: const EdgeInsets.all(10),
-                  child: const Icon(Icons.arrow_downward)
-              ),
-              Text(AppLocalizations.of(context)!.exportHiddenFields),
-              Container(
-                  padding: const EdgeInsets.all(10),
-                  child: const Icon(Icons.arrow_downward)
-              ),
-              const Expanded(
-                  child: Divider()
-              ),
-            ]
-        ),
-      ),
-    );
-  }
-
-  _onReorderList(oldIndex, newIndex) {
-    /**
-     * We have a list of items that is structured like the following:
-     * [ exportItems.length, 1, exportAddableItems.length ]
-     *
-     * So oldIndex is either (0 <= oldIndex < exportItems.length) or
-     * ((exportItems.length + 1) <= oldIndex < (exportItems.length + 1 + exportAddableItems.length))
-     * newIndex is in the range (0 <= newIndex < (exportItems.length + 1 + exportAddableItems.length))
-     *
-     * In case the entry is moved upwards on the list the new position needs to have 1 subtracted because there
-     * is an entry missing above it now.
-     *
-     * If the newIndex is (0 <= newIndex < (exportItems.length + 1)) the Item got moved above the divider.
-     * The + 1 is needed to compensate for moving the item one position above the divider and thereby replacing
-     * its index.
-     */
-    if (oldIndex < newIndex) {
-      newIndex -= 1;
-    }
-
-    final ExportColumn item;
-    if (0 <= oldIndex && oldIndex < widget.shownItems.length) {
-      item = widget.shownItems.removeAt(oldIndex);
-    } else if ((widget.shownItems.length + 1) <= oldIndex && oldIndex < (widget.shownItems.length + 1 + widget.disabledItems.length)) {
-      item = widget.disabledItems.removeAt(oldIndex - (widget.shownItems.length + 1));
-    } else {
-      assert(false, 'oldIndex outside expected boundaries');
-      return;
-    }
-
-    if (newIndex < (widget.shownItems.length + 1)) {
-      widget.shownItems.insert(newIndex, item);
-    } else {
-      newIndex -= (widget.shownItems.length + 1);
-      widget.disabledItems.insert(newIndex, item);
-    }
-
-    widget.onReorder(widget.shownItems, widget.disabledItems);
-  }
-
-   */
-}
lib/l10n/app_en.arb
@@ -470,5 +470,7 @@
         "type": "String"
       }
     }
-  }
+  },
+  "exportFieldsPreset": "Export fields preset",
+  "@exportFieldsPreset": {}
 }
lib/model/export_import/export_configuration.dart
@@ -37,7 +37,9 @@ class ActiveExportColumnConfiguration extends ChangeNotifier {
     'preset': _activePreset.encode()
   });
 
-  /// The last selected columns, different from [getActiveColumns].
+  /// The [UserColumn.internalIdentifier] of columns currently selected by user.
+  ///
+  /// Note that this is persistent different from [getActiveColumns].
   final List<String> _userSelectedColumns;
 
   ExportImportPreset _activePreset;
@@ -47,6 +49,26 @@ class ActiveExportColumnConfiguration extends ChangeNotifier {
     notifyListeners();
   }
 
+  /// Put the user column at [oldIndex] to [newIndex].
+  void reorderUserColumns(int oldIndex, int newIndex) {
+    assert(_activePreset == ExportImportPreset.none, 'user columns are not modifiable while another configuration is active');
+    assert(oldIndex >= 0 && oldIndex < _userSelectedColumns.length);
+    assert(newIndex >= 0 && newIndex < _userSelectedColumns.length);
+    if (oldIndex < newIndex) {
+      newIndex -= 1;
+    }
+    final item = _userSelectedColumns.removeAt(oldIndex);
+    _userSelectedColumns.insert(newIndex, item);
+    notifyListeners();
+  }
+
+  /// Add a export column to the end of user columns.
+  void addUserColumn(ExportColumn column) {
+    assert(_activePreset == ExportImportPreset.none, 'user columns are not modifiable while another configuration is active');
+    _userSelectedColumns.add(column.internalIdentifier);
+    notifyListeners();
+  }
+
   /// Columns to respect for export.
   UnmodifiableListView<ExportColumn> getActiveColumns(ExportColumnsManager availableColumns) => UnmodifiableListView(
     switch (_activePreset) {
lib/model/export_import/legacy_column.dart
@@ -4,11 +4,11 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
 
 /// Convert [BloodPressureRecord]s from and to strings and provide metadata about the conversion.
 @Deprecated("repaced by class in column.dart")
-class ExportColumn { // TODO: change this class so it implements the interface.
+class LegacyExportColumn { // TODO: change this class so it implements the interface.
   /// Create object that turns data into strings.
   ///
   /// Example: ExportColumn(internalColumnName: 'pulsePressure', columnTitle: 'Pulse pressure', formatPattern: '{{$SYS-$DIA}}')
-  ExportColumn({required this.internalName, required this.columnTitle, required String formatPattern, this.editable = true, this.hidden = false}) {
+  LegacyExportColumn({required this.internalName, required this.columnTitle, required String formatPattern, this.editable = true, this.hidden = false}) {
     this.formatPattern = formatPattern.replaceAll('{{}}', '');
     _formatter = ScriptedFormatter(formatPattern);
   }
@@ -44,8 +44,8 @@ class ExportColumn { // TODO: change this class so it implements the interface.
   /// doesn't show up as unused / hidden field in list
   final bool hidden;
 
-  factory ExportColumn.fromJson(Map<String, dynamic> json, [editable = true, hidden = false]) =>
-    ExportColumn(
+  factory LegacyExportColumn.fromJson(Map<String, dynamic> json, [editable = true, hidden = false]) =>
+    LegacyExportColumn(
       internalName: json['internalColumnName'],
       columnTitle: json['columnTitle'],
       formatPattern: json['formatPattern'],
@@ -82,7 +82,7 @@ class ExportColumn { // TODO: change this class so it implements the interface.
 
 /// Type a [Formatter] can uses to indicate the kind of data returned.
 ///
-/// The data types returned from the deprecated [ExportColumn] may differ from the guarantees.
+/// The data types returned from the deprecated [LegacyExportColumn] may differ from the guarantees.
 enum RowDataFieldType {
   /// Guarantees [DateTime] is returned.
   timestamp,
lib/model/storage/db/config_dao.dart
@@ -313,14 +313,14 @@ class ConfigDao {
   /// Loads the current export columns from the database.
   ///
   /// Changes will *not* be saved automatically, see [updateExportColumn].
-  Future<List<ExportColumn>> loadExportColumns() async {
+  Future<List<LegacyExportColumn>> loadExportColumns() async {
     final existingDbEntries = await _configDB.database.query(
       ConfigDB.exportStringsTable,
       columns: ['internalColumnName', 'columnTitle', 'formatPattern']
     );
     return [
       for (final e in existingDbEntries)
-        ExportColumn(
+        LegacyExportColumn(
             internalName: e['internalColumnName'].toString(),
             columnTitle: e['columnTitle'].toString(),
             formatPattern: e['formatPattern'].toString()
@@ -328,10 +328,10 @@ class ConfigDao {
     ];
   }
 
-  /// Saves a [ExportColumn] to the database.
+  /// Saves a [LegacyExportColumn] to the database.
   ///
-  /// If one with the same [ExportColumn.internalName] exists, it will get replaced by the new one regardless of content.
-  Future<void> updateExportColumn(ExportColumn exportColumn) async {
+  /// If one with the same [LegacyExportColumn.internalName] exists, it will get replaced by the new one regardless of content.
+  Future<void> updateExportColumn(LegacyExportColumn exportColumn) async {
     if (!_configDB.database.isOpen) return;
     await _configDB.database.insert(
         ConfigDB.exportStringsTable,
@@ -344,7 +344,7 @@ class ConfigDao {
     );
   }
 
-  /// Deletes the [ExportColumn] where [ExportColumn.internalName] matches [internalName] from the database.
+  /// Deletes the [LegacyExportColumn] where [LegacyExportColumn.internalName] matches [internalName] from the database.
   Future<void> deleteExportColumn(String internalName) async {
     if (!_configDB.database.isOpen) return;
     await _configDB.database.delete('exportStrings', where: 'internalColumnName = ?', whereArgs: [internalName]);
lib/model/storage/export_columns_store.dart
@@ -58,6 +58,17 @@ class ExportColumnsManager extends ChangeNotifier { // TODO: separate ExportColu
         // ?? ...
   }
 
+  /// Returns a list of all userColumns, NativeColumns and BuildInColumns defined.
+  ///
+  /// Prefer using other methods like [firstWhere] when possible.
+  UnmodifiableListView<ExportColumn> getAllColumns() {
+    final columns = <ExportColumn>[];
+    columns.addAll(NativeColumn.allColumns);
+    columns.addAll(userColumns.values);
+    columns.addAll(BuildInColumn.allColumns);
+    return UnmodifiableListView(columns);
+  }
+
   String toJson() { // TODO: update from and TO json to new style
     final columns = [];
     for (final c in _userColumns.values) {
lib/model/export_options.dart
@@ -21,7 +21,7 @@ class ExportConfigurationModel {
   final AppLocalizations localizations;
   final ConfigDao _configDao; // TODO: remove after #181 is complete
   
-  final List<ExportColumn> _availableFormats = [];
+  final List<LegacyExportColumn> _availableFormats = [];
 
   /// Format: (title, List<internalNameOfExportFormat>)
   List<(String, List<String>)> get exportConfigurations => [
@@ -48,56 +48,56 @@ class ExportConfigurationModel {
   ///
   /// The [fieldSettings] parameter describes the settings of the current export format and should be set accordingly.
   @Deprecated('not implemented anymore')
-  List<ExportColumn> getActiveExportColumns(ExportFormat format, CustomFieldsSettings fieldsSettings) {
+  List<LegacyExportColumn> getActiveExportColumns(ExportFormat format, CustomFieldsSettings fieldsSettings) {
     return [];
   }
   
-  List<ExportColumn> getDefaultFormates() => [
-    ExportColumn(internalName: 'timestampUnixMs', columnTitle: localizations.unixTimestamp, formatPattern: r'$TIMESTAMP', editable: false),
-    ExportColumn(internalName: 'formattedTimestamp', columnTitle: localizations.time, formatPattern: '\$FORMAT{\$TIMESTAMP,yyyy-MM-dd HH:mm:ss}', editable: false),
-    ExportColumn(internalName: 'systolic', columnTitle: localizations.sysLong, formatPattern: r'$SYS', editable: false),
-    ExportColumn(internalName: 'diastolic', columnTitle: localizations.diaLong, formatPattern: r'$DIA', editable: false),
-    ExportColumn(internalName: 'pulse', columnTitle: localizations.pulLong, formatPattern: r'$PUL', editable: false),
-    ExportColumn(internalName: 'notes', columnTitle: localizations.notes, formatPattern: r'$NOTE', editable: false),
-    ExportColumn(internalName: 'pulsePressure', columnTitle: localizations.pulsePressure, formatPattern: r'{{$SYS-$DIA}}', editable: false),
-    ExportColumn(internalName: 'color', columnTitle: localizations.color, formatPattern: r'$COLOR', editable: false),
+  List<LegacyExportColumn> getDefaultFormates() => [
+    LegacyExportColumn(internalName: 'timestampUnixMs', columnTitle: localizations.unixTimestamp, formatPattern: r'$TIMESTAMP', editable: false),
+    LegacyExportColumn(internalName: 'formattedTimestamp', columnTitle: localizations.time, formatPattern: '\$FORMAT{\$TIMESTAMP,yyyy-MM-dd HH:mm:ss}', editable: false),
+    LegacyExportColumn(internalName: 'systolic', columnTitle: localizations.sysLong, formatPattern: r'$SYS', editable: false),
+    LegacyExportColumn(internalName: 'diastolic', columnTitle: localizations.diaLong, formatPattern: r'$DIA', editable: false),
+    LegacyExportColumn(internalName: 'pulse', columnTitle: localizations.pulLong, formatPattern: r'$PUL', editable: false),
+    LegacyExportColumn(internalName: 'notes', columnTitle: localizations.notes, formatPattern: r'$NOTE', editable: false),
+    LegacyExportColumn(internalName: 'pulsePressure', columnTitle: localizations.pulsePressure, formatPattern: r'{{$SYS-$DIA}}', editable: false),
+    LegacyExportColumn(internalName: 'color', columnTitle: localizations.color, formatPattern: r'$COLOR', editable: false),
 
-    ExportColumn(internalName: 'DATUM', columnTitle: '"My Heart" export time', formatPattern: r'$FORMAT{$TIMESTAMP,yyyy-MM-dd HH:mm:ss}', editable: false, hidden: true),
-    ExportColumn(internalName: 'SYSTOLE', columnTitle: '"My Heart" export sys', formatPattern: r'$SYS', editable: false, hidden: true),
-    ExportColumn(internalName: 'DIASTOLE', columnTitle: '"My Heart" export dia', formatPattern: r'$DIA', editable: false, hidden: true),
-    ExportColumn(internalName: 'PULS', columnTitle: '"My Heart" export pul', formatPattern: r'$PUL', editable: false, hidden: true),
-    ExportColumn(internalName: 'Beschreibung', columnTitle: '"My Heart" export description', formatPattern: r'null', editable: false, hidden: true),
-    ExportColumn(internalName: 'Tags', columnTitle: '"My Heart" export tags', formatPattern: r'', editable: false, hidden: true),
-    ExportColumn(internalName: 'Gewicht', columnTitle: '"My Heart" export weight', formatPattern: r'0.0', editable: false, hidden: true),
-    ExportColumn(internalName: 'Sauerstoffsättigung', columnTitle: '"My Heart" export oxygen', formatPattern: r'0', editable: false, hidden: true),
+    LegacyExportColumn(internalName: 'DATUM', columnTitle: '"My Heart" export time', formatPattern: r'$FORMAT{$TIMESTAMP,yyyy-MM-dd HH:mm:ss}', editable: false, hidden: true),
+    LegacyExportColumn(internalName: 'SYSTOLE', columnTitle: '"My Heart" export sys', formatPattern: r'$SYS', editable: false, hidden: true),
+    LegacyExportColumn(internalName: 'DIASTOLE', columnTitle: '"My Heart" export dia', formatPattern: r'$DIA', editable: false, hidden: true),
+    LegacyExportColumn(internalName: 'PULS', columnTitle: '"My Heart" export pul', formatPattern: r'$PUL', editable: false, hidden: true),
+    LegacyExportColumn(internalName: 'Beschreibung', columnTitle: '"My Heart" export description', formatPattern: r'null', editable: false, hidden: true),
+    LegacyExportColumn(internalName: 'Tags', columnTitle: '"My Heart" export tags', formatPattern: r'', editable: false, hidden: true),
+    LegacyExportColumn(internalName: 'Gewicht', columnTitle: '"My Heart" export weight', formatPattern: r'0.0', editable: false, hidden: true),
+    LegacyExportColumn(internalName: 'Sauerstoffsättigung', columnTitle: '"My Heart" export oxygen', formatPattern: r'0', editable: false, hidden: true),
   ];
 
-  /// Saves a new [ExportColumn] to the list of the available columns.
+  /// Saves a new [LegacyExportColumn] to the list of the available columns.
   ///
   /// In case one with the same internal name exists it gets updated with the new values
-  void addOrUpdate(ExportColumn format) {
+  void addOrUpdate(LegacyExportColumn format) {
     _availableFormats.removeWhere((e) => e.internalName == format.internalName);
     _availableFormats.add(format);
     _configDao.updateExportColumn(format);
   }
 
-  void delete(ExportColumn format) {
+  void delete(LegacyExportColumn format) {
     final existingEntries = _availableFormats.where((element) => (element.internalName == format.internalName) && element.editable);
     assert(existingEntries.isNotEmpty, r"Tried to delete entry that doesn't exist or is not editable.");
     _availableFormats.removeWhere((element) => element.internalName == format.internalName);
     _configDao.deleteExportColumn(format.internalName);
   }
 
-  UnmodifiableListView<ExportColumn> get availableFormats => UnmodifiableListView(_availableFormats);
-  UnmodifiableMapView<String, ExportColumn> get availableFormatsMap =>
+  UnmodifiableListView<LegacyExportColumn> get availableFormats => UnmodifiableListView(_availableFormats);
+  UnmodifiableMapView<String, LegacyExportColumn> get availableFormatsMap =>
       UnmodifiableMapView(Map.fromIterable(_availableFormats, key: (e) => e.internalName));
 
 
   /// Creates list of rows with that follow the order and format described by [activeExportColumns].
   ///
-  /// The [createHeadline] option will create put a row at the start that contains [ExportColumn.internalName] of the
+  /// The [createHeadline] option will create put a row at the start that contains [LegacyExportColumn.internalName] of the
   /// given [activeExportColumns].
-  List<List<String>> createTable(Iterable<BloodPressureRecord> data, List<ExportColumn> activeExportColumns,
+  List<List<String>> createTable(Iterable<BloodPressureRecord> data, List<LegacyExportColumn> activeExportColumns,
       {bool createHeadline = true,}) {
     List<List<String>> items = [];
     if (createHeadline) {
lib/screens/subsettings/export_column_data.dart
@@ -127,7 +127,7 @@ class _EditExportColumnPageState extends State<EditExportColumnPage> {
                                   return localizations.errNoValue;
                                 } else if (_internalName != null && _displayName != null) {
                                   try {
-                                    final column = ExportColumn(internalName: _internalName!, columnTitle: _displayName!, formatPattern: value);
+                                    final column = LegacyExportColumn(internalName: _internalName!, columnTitle: _displayName!, formatPattern: value);
                                     column.formatRecord(BloodPressureRecord(DateTime.now(), 100, 80, 60, ''));
                                     _formatPattern = value;
                                   } catch (e) {
@@ -142,7 +142,7 @@ class _EditExportColumnPageState extends State<EditExportColumnPage> {
                             const SizedBox(height: 12,),
                             Text(localizations.result),
                             Text(((){try {
-                              final column = ExportColumn(internalName: _internalName!, columnTitle: _displayName!, formatPattern: _formatPattern!);
+                              final column = LegacyExportColumn(internalName: _internalName!, columnTitle: _displayName!, formatPattern: _formatPattern!);
                               return column.formatRecord(BloodPressureRecord(DateTime.now(), 100, 80, 60, 'test'));
                             } catch (e) {
                               return '-';
@@ -170,7 +170,7 @@ class _EditExportColumnPageState extends State<EditExportColumnPage> {
                           onPressed: (widget.editable) ? (() async {
                             if (_formKey.currentState?.validate() ?? false) {
                               final navigator = Navigator.of(context);
-                              exportConfigurationModel.addOrUpdate(ExportColumn(
+                              exportConfigurationModel.addOrUpdate(LegacyExportColumn(
                                   internalName: _internalName!,
                                   columnTitle: _displayName!,
                                   formatPattern: _formatPattern!
lib/screens/subsettings/export_import_screen.dart
@@ -7,6 +7,7 @@ import 'package:blood_pressure_app/components/diabled.dart';
 import 'package:blood_pressure_app/components/display_interval_picker.dart';
 import 'package:blood_pressure_app/components/settings/settings_widgets.dart';
 import 'package:blood_pressure_app/model/blood_pressure.dart';
+import 'package:blood_pressure_app/model/export_import/column.dart';
 import 'package:blood_pressure_app/model/export_import/csv_converter.dart';
 import 'package:blood_pressure_app/model/export_import/export_configuration.dart';
 import 'package:blood_pressure_app/model/export_import/legacy_column.dart';
@@ -106,9 +107,6 @@ class ExportImportScreen extends StatelessWidget {
                           csvExportSettings.exportHeadline = value;
                         }
                       ),
-                      ExportFieldCustomisationSetting(
-                        fieldsSettings: csvExportSettings,
-                      ),
                     ],
                   )
                 ),
@@ -165,14 +163,14 @@ class ExportImportScreen extends StatelessWidget {
                                 pdfExportSettings.cellFontSize = value;
                               },
                             ),
-                            ExportFieldCustomisationSetting(
-                              fieldsSettings: pdfExportSettings,
-                            ),
                           ],
                         ),
                     ]
                   )
                 ),
+              ExportFieldCustomisationSetting(
+                format: settings.exportFormat,
+              ),
             ],
           ),
         );
@@ -182,52 +180,96 @@ class ExportImportScreen extends StatelessWidget {
   }
 }
 
-class ExportFieldCustomisationSetting extends StatelessWidget {
-  const ExportFieldCustomisationSetting({super.key, required this.fieldsSettings});
+class ExportFieldCustomisationSetting extends StatelessWidget { // TODO: consider extracting class into file
+  const ExportFieldCustomisationSetting({super.key,
+    required this.format,});
 
-  final CustomFieldsSettings fieldsSettings;
+  final ExportFormat format;
 
   @override
-  Widget build(BuildContext context) {
+  Widget build(BuildContext context) => switch (format) {
+      ExportFormat.csv => Consumer<CsvExportSettings>(builder: _builder),
+      ExportFormat.pdf => Consumer<PdfExportSettings>(builder: _builder),
+      ExportFormat.db => const SizedBox.shrink()
+    };
+
+  Widget _builder(BuildContext context, CustomFieldsSettings settings, Widget? child) {
     final localizations = AppLocalizations.of(context)!;
-    return ConsistentFutureBuilder(
-      future: ExportConfigurationModel.get(localizations),
-      lastChildWhileWaiting: true,
-      onData: (context, configurationModel) {
-        return const Text('TODO - ExportFieldCustomisationSetting');
-        /* TODO: rewrite with dropdown and reorderable list view, consider extracting this class into file
-        return Consumer<ExportSettings>(builder: (context, settings, child) {
-          final formats = configurationModel.availableFormats.toSet();
-          List<ExportColumn> activeFields = configurationModel
-              .getActiveExportColumns(settings.exportFormat, fieldsSettings);
-          List<ExportColumn> hiddenFields = [];
-          for (final internalName in fieldsSettings.customFields) {
-            formats.removeWhere((e) => e.internalName == internalName);
-          }
-          hiddenFields = formats.toList();
+    final fieldsConfig = settings.exportFieldsConfiguration;
+    final dropdown = DropDownListTile(
+      title: Text(localizations.exportFieldsPreset),
+      value: fieldsConfig.activePreset,
+      items: ExportImportPreset.values.map(
+              (e) => DropdownMenuItem(
+            value: e,
+            child: Text(e.localize(localizations)),
+          )
+      ).toList(),
+      onChanged: (selectedPreset) {
+        if (selectedPreset != null) {
+          fieldsConfig.activePreset = selectedPreset;
+        }
+      },
+    );
 
-          return Column(
-            children: [
-              SwitchListTile(
-                title: Text(localizations.exportCustomEntries),
-                value: fieldsSettings.exportCustomFields,
-                onChanged: (value) {
-                  fieldsSettings.exportCustomFields = value;
-                }
-              ),
-              if (fieldsSettings.exportCustomFields)
-                ExportItemsCustomizer(
-                  shownItems: activeFields,
-                  disabledItems: hiddenFields,
-                  onReorder: (exportItems, exportAddableItems) {
-                    fieldsSettings.customFields = exportItems.map((e) => e.internalName).toList();
-                  },
-                ),
-            ],
-          );
-        });
-       */
-      }
+    if (fieldsConfig.activePreset != ExportImportPreset.none) {
+      return dropdown;
+    }
+    return Column(
+      mainAxisSize: MainAxisSize.min,
+        children: [
+          dropdown,
+          Container(
+            margin: const EdgeInsets.all(16),
+            height: 400,
+            decoration: BoxDecoration(
+              border: Border.all(color: Theme.of(context).textTheme.labelLarge?.color ?? Colors.teal),
+              borderRadius: const BorderRadius.all(Radius.circular(10)),
+            ),
+            child: Consumer<ExportColumnsManager>(
+              builder: (context, availableColumns, child) {
+                final activeColumns = fieldsConfig.getActiveColumns(availableColumns);
+                return ReorderableListView.builder(
+                    itemBuilder: (context, idx) {
+                      if (idx >= activeColumns.length) {
+                        return ListTile(
+                          key: const Key('add field'),
+                          leading: const Icon(Icons.add),
+                          title: Text(localizations.addEntry),
+                          onTap: () async {
+                            final column = await showDialog<ExportColumn?>(context: context, builder: (context) =>
+                              SimpleDialog(
+                                title: Text(localizations.addEntry),
+                                insetPadding: EdgeInsets.symmetric(
+                                  vertical: 64,
+                                ),
+                                children: availableColumns.getAllColumns().map((column) =>
+                                  ListTile(
+                                    title: Text(column.userTitle(localizations)),
+                                    onTap: () => Navigator.of(context).pop(column),
+                                  )
+                                ).toList(),
+                              )
+                            );
+                            if (column != null) fieldsConfig.addUserColumn(column);
+                          },
+                        );
+                      }
+                      return ListTile(
+                        key: Key(activeColumns[idx].internalIdentifier + idx.toString()),
+                        title: Text(activeColumns[idx].userTitle(localizations)),
+                        trailing: const Icon(Icons.drag_handle),
+                        // TODO: removing columns
+                      );
+                    },
+                    itemCount: activeColumns.length + 1,
+                    onReorder: fieldsConfig.reorderUserColumns
+                );
+              }
+            ),
+          )
+          // TODO implement adding / editing columns => separate ColumnsManagerScreen ?
+        ],
     );
   }
 }
test/ui/navigation_test.dart
@@ -91,7 +91,7 @@ Future<void> pumpAppRoot(WidgetTester widgetTester, {
 }
 
 class MockConfigDao implements ConfigDao {
-  Map<String, ExportColumn> columns = {};
+  Map<String, LegacyExportColumn> columns = {};
 
   @override
   Future<void> deleteExportColumn(String internalName) async => columns.remove(internalName);
@@ -100,7 +100,7 @@ class MockConfigDao implements ConfigDao {
   Future<CsvExportSettings> loadCsvExportSettings(int profileID) async => CsvExportSettings();
 
   @override
-  Future<List<ExportColumn>> loadExportColumns() async => columns.values.toList();
+  Future<List<LegacyExportColumn>> loadExportColumns() async => columns.values.toList();
 
   @override
   Future<ExportSettings> loadExportSettings(int profileID) async => ExportSettings();
@@ -115,7 +115,7 @@ class MockConfigDao implements ConfigDao {
   Future<Settings> loadSettings(int profileID) async => Settings();
 
   @override
-  Future<void> updateExportColumn(ExportColumn exportColumn) async => columns.update(exportColumn.internalName, (value) => exportColumn);
+  Future<void> updateExportColumn(LegacyExportColumn exportColumn) async => columns.update(exportColumn.internalName, (value) => exportColumn);
 
   void reset() {
     columns = {};
pubspec.lock
@@ -41,14 +41,6 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.11.0"
-  badges:
-    dependency: "direct main"
-    description:
-      name: badges
-      sha256: a7b6bbd60dce418df0db3058b53f9d083c22cdb5132a052145dc267494df0b84
-      url: "https://pub.dev"
-    source: hosted
-    version: "3.1.2"
   barcode:
     dependency: transitive
     description:
pubspec.yaml
@@ -24,7 +24,6 @@ dependencies:
   pdf: ^3.10.4
   package_info_plus: ^4.0.2
   function_tree: ^0.9.0
-  badges: ^3.1.1
   flutter_markdown: ^0.6.17
   collection: ^1.17.1