]> gitweb.hamatoma.de Git - flutter_bones.git/commitdiff
daily work: role.create works
authorHamatoma <author@hamatoma.de>
Wed, 21 Oct 2020 12:05:31 +0000 (14:05 +0200)
committerHamatoma <author@hamatoma.de>
Fri, 23 Oct 2020 09:41:33 +0000 (11:41 +0200)
58 files changed:
CreateModule [new file with mode: 0755]
lib/app.dart
lib/src/controller/base_controller.dart [deleted file]
lib/src/controller/base_controller.dart.01 [new file with mode: 0644]
lib/src/controller/button_controller.dart [deleted file]
lib/src/controller/button_controller.dart.01 [new file with mode: 0644]
lib/src/controller/combobox_controller.dart [deleted file]
lib/src/controller/combobox_controller.dart.01 [new file with mode: 0644]
lib/src/controller/text_form_controller.dart [deleted file]
lib/src/controller/text_form_controller.dart.01 [new file with mode: 0644]
lib/src/helper/settings.dart
lib/src/helper/string_helper.dart
lib/src/model/all_db_fields_model.dart
lib/src/model/combobox_model.dart
lib/src/model/db_reference_model.dart
lib/src/model/field_model.dart
lib/src/model/module_model.dart
lib/src/model/page_model.dart
lib/src/model/section_model.dart
lib/src/model/standard/role_model.dart
lib/src/page/application_data.dart
lib/src/page/configuration/configuration_change_page.dart
lib/src/page/configuration/configuration_controller.dart
lib/src/page/configuration/configuration_create_page.dart
lib/src/page/configuration/configuration_list_page.dart
lib/src/page/role/role_change_page.dart
lib/src/page/role/role_controller.dart
lib/src/page/role/role_create_page.dart
lib/src/page/role/role_list_page.dart
lib/src/page/user/user_change_page.dart
lib/src/page/user/user_controller.dart
lib/src/page/user/user_create_page.dart
lib/src/page/user/user_list_page.dart
lib/src/page/user_page.dart
lib/src/persistence/persistence.dart
lib/src/persistence/rest_persistence.dart
lib/src/private/bdrawer.dart
lib/src/private/bsettings.dart
lib/src/widget/callback_controller_bones.dart [new file with mode: 0644]
lib/src/widget/checkbox_list_tile_bone.dart [new file with mode: 0644]
lib/src/widget/dropdown_button_form_bone.dart
lib/src/widget/edit_form.dart
lib/src/widget/filter_fields.dart [deleted file]
lib/src/widget/filter_set.dart [new file with mode: 0644]
lib/src/widget/list_form.dart
lib/src/widget/module_controller.dart [deleted file]
lib/src/widget/page_controller_bones.dart [new file with mode: 0644]
lib/src/widget/raised_button_bone.dart
lib/src/widget/text_form_field_bone.dart
lib/src/widget/view.dart
lib/src/widget/widget_list.dart
test/helpers/settings_test.dart
test/helpers/string_helper_test.dart
test/model/db_model_test.dart
test/model/model_test.dart
test/model/standard_test.dart
test/rest_persistence_test.dart
test/widget/widget_test.dart

diff --git a/CreateModule b/CreateModule
new file mode 100755 (executable)
index 0000000..baf291f
--- /dev/null
@@ -0,0 +1,21 @@
+#! /bin/bash
+MODULE=$1
+if [ -z "$MODULE" ]; then
+  echo "Missing module name"
+else
+  cd lib/src/page
+  if [ -d $MODULE ]; then
+    FN_NEW=/tmp/$MODULE.$(date "+%s")
+    echo "$MODULE already exists, moving to $FN_NEW"
+    mv $MODULE $FN_NEW
+  fi
+  mkdir $MODULE
+  cp -av role/* $MODULE/
+  FN_REPL=/tmp/create_module.repl
+  MODULE3=$(echo $MODULE | tr a-z A-Z)
+  MODULE2=${MODULE3:0:1}${MODULE:1}
+  # echo -e "role\t$MODULE\nRole\t$MODULE2" >$FN_REPL
+  cd $MODULE
+  perl -pi -e"s/role/$MODULE/g;s/Role/$MODULE2/g;" *.dart
+  rename -v "s/role(.*)/${MODULE}\$1/;" *.dart
+fi
\ No newline at end of file
index 51fc23d9f6766a5205b8d9253799b79bb3e9b068..6c5f4a31af2732870b7f2c82ff0f18dffa112dd5 100644 (file)
@@ -6,12 +6,6 @@ import 'src/page/login_page.dart';
 import 'src/page/role/role_change_page.dart';
 import 'src/page/role/role_create_page.dart';
 import 'src/page/role/role_list_page.dart';
-import 'src/page/user/user_change_page.dart';
-import 'src/page/user/user_create_page.dart';
-import 'src/page/user/user_list_page.dart';
-import 'src/page/configuration/configuration_change_page.dart';
-import 'src/page/configuration/configuration_create_page.dart';
-import 'src/page/configuration/configuration_list_page.dart';
 import 'src/private/bsettings.dart';
 
 class BoneApp extends StatefulWidget {
@@ -39,7 +33,7 @@ class BoneAppState extends State<BoneApp> {
         primarySwatch: Colors.blue,
         visualDensity: VisualDensity.adaptivePlatformDensity,
       ),
-      initialRoute: '/role/list',
+      initialRoute: '/role/create',
       onGenerateRoute: _getRoute,
     );
   }
@@ -58,7 +52,7 @@ Route<dynamic> _getRoute(RouteSettings settings) {
     case '/role/create':
       page = RoleCreatePage(BSettings.lastInstance.pageData);
       break;
-      /*
+    /*
     case '/user/list':
       page = UserListPage(BSettings.lastInstance.pageData);
       break;
diff --git a/lib/src/controller/base_controller.dart b/lib/src/controller/base_controller.dart
deleted file mode 100644 (file)
index 46b9533..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-import 'package:flutter_bones/flutter_bones.dart';
-
-/// Base class of all controller classes in this package.
-abstract class BaseController {
-  /// Returns the assigned model.
-  WidgetModel getModel();
-
-  /// Returns a unique name of the controller inside the module, mostly the
-  /// widget name of the assigned model.
-  String getName();
-}
diff --git a/lib/src/controller/base_controller.dart.01 b/lib/src/controller/base_controller.dart.01
new file mode 100644 (file)
index 0000000..46b9533
--- /dev/null
@@ -0,0 +1,11 @@
+import 'package:flutter_bones/flutter_bones.dart';
+
+/// Base class of all controller classes in this package.
+abstract class BaseController {
+  /// Returns the assigned model.
+  WidgetModel getModel();
+
+  /// Returns a unique name of the controller inside the module, mostly the
+  /// widget name of the assigned model.
+  String getName();
+}
diff --git a/lib/src/controller/button_controller.dart b/lib/src/controller/button_controller.dart
deleted file mode 100644 (file)
index 7d5347c..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-import 'package:flutter_bones/flutter_bones.dart';
-import 'package:flutter_bones/src/controller/base_controller.dart';
-
-import '../widget/raised_button_bone.dart';
-
-class ButtonController extends BaseController
-    implements ButtonCallbackController {
-  ButtonModel model;
-
-  ButtonController(this.model);
-
-  @override
-  WidgetModel getModel() {
-    return model;
-  }
-
-  @override
-  String getName() {
-    return model.widgetName();
-  }
-
-  @override
-  getOnHighlightChanged(
-      String customString, ButtonCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnLongPressed(String customString, ButtonCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnPressed(String customString, ButtonCallbackController controller) {
-    var rc;
-    switch (model.buttonModelType) {
-      case ButtonModelType.cancel:
-        break;
-      case ButtonModelType.custom:
-        break;
-      case ButtonModelType.search:
-        break;
-      case ButtonModelType.store:
-        break;
-    }
-    return rc;
-  }
-}
diff --git a/lib/src/controller/button_controller.dart.01 b/lib/src/controller/button_controller.dart.01
new file mode 100644 (file)
index 0000000..e608ab7
--- /dev/null
@@ -0,0 +1,53 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_bones/src/controller/base_controller.dart';
+
+import '../widget/raised_button_bone.dart';
+
+class ButtonController extends BaseController
+    implements ButtonCallbackController {
+  ButtonModel model;
+
+  ButtonController(this.model);
+
+  @override
+  WidgetModel getModel() {
+    return model;
+  }
+
+  @override
+  String getName() {
+    return model.widgetName();
+  }
+
+  @override
+  getOnHighlightChanged(String customString, ButtonCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnLongPressed(String customString, ButtonCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnPressed(String customString, ButtonCallbackController controller) {
+    var rc;
+    switch (model.buttonModelType) {
+      case ButtonModelType.cancel:
+        break;
+      case ButtonModelType.custom:
+        break;
+      case ButtonModelType.search:
+        break;
+      case ButtonModelType.store:
+        break;
+    }
+    return rc;
+  }
+
+  @override
+  BuildContext getContext() {
+    return null;
+  }
+}
diff --git a/lib/src/controller/combobox_controller.dart b/lib/src/controller/combobox_controller.dart
deleted file mode 100644 (file)
index 322f642..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-import 'package:flutter_bones/flutter_bones.dart';
-
-import '../widget/dropdown_button_form_bone.dart';
-import 'base_controller.dart';
-
-class ComboboxController<T> extends BaseController
-    implements ComboboxCallbackController {
-  ComboboxModel model;
-
-  ComboboxController(this.model);
-
-  @override
-  WidgetModel getModel() {
-    return model;
-  }
-
-  @override
-  String getName() {
-    return model.widgetName();
-  }
-
-  @override
-  getOnChanged(String customString, ComboboxCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnSaved(String customString, ComboboxCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnSelectedItemBuilder(
-      String customString, ComboboxCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnTap(String customString, ComboboxCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnValidator(String customString, ComboboxCallbackController controller) {
-    return null;
-  }
-
-  @override
-  List<String> texts() {
-    return model.texts;
-  }
-
-  @override
-  List<dynamic> values() {
-    return model.values;
-  }
-}
diff --git a/lib/src/controller/combobox_controller.dart.01 b/lib/src/controller/combobox_controller.dart.01
new file mode 100644 (file)
index 0000000..322f642
--- /dev/null
@@ -0,0 +1,57 @@
+import 'package:flutter_bones/flutter_bones.dart';
+
+import '../widget/dropdown_button_form_bone.dart';
+import 'base_controller.dart';
+
+class ComboboxController<T> extends BaseController
+    implements ComboboxCallbackController {
+  ComboboxModel model;
+
+  ComboboxController(this.model);
+
+  @override
+  WidgetModel getModel() {
+    return model;
+  }
+
+  @override
+  String getName() {
+    return model.widgetName();
+  }
+
+  @override
+  getOnChanged(String customString, ComboboxCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnSaved(String customString, ComboboxCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnSelectedItemBuilder(
+      String customString, ComboboxCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnTap(String customString, ComboboxCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnValidator(String customString, ComboboxCallbackController controller) {
+    return null;
+  }
+
+  @override
+  List<String> texts() {
+    return model.texts;
+  }
+
+  @override
+  List<dynamic> values() {
+    return model.values;
+  }
+}
diff --git a/lib/src/controller/text_form_controller.dart b/lib/src/controller/text_form_controller.dart
deleted file mode 100644 (file)
index d7cd775..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-import 'package:flutter_bones/flutter_bones.dart';
-
-import '../widget/text_form_field_bone.dart';
-import 'base_controller.dart';
-
-class TextFormController extends BaseController
-    implements TextFormCallbackController {
-  ComboboxModel model;
-
-  TextFormController(this.model);
-
-  @override
-  WidgetModel getModel() {
-    return model;
-  }
-
-  @override
-  String getName() {
-    return model.widgetName();
-  }
-
-  @override
-  getOnChanged(String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnEditingComplete(
-      String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnFieldSubmitted(
-      String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnSaved(String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnTap(String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getValidator(String customString, TextFormCallbackController controller) {
-    return null;
-  }
-}
diff --git a/lib/src/controller/text_form_controller.dart.01 b/lib/src/controller/text_form_controller.dart.01
new file mode 100644 (file)
index 0000000..d7cd775
--- /dev/null
@@ -0,0 +1,53 @@
+import 'package:flutter_bones/flutter_bones.dart';
+
+import '../widget/text_form_field_bone.dart';
+import 'base_controller.dart';
+
+class TextFormController extends BaseController
+    implements TextFormCallbackController {
+  ComboboxModel model;
+
+  TextFormController(this.model);
+
+  @override
+  WidgetModel getModel() {
+    return model;
+  }
+
+  @override
+  String getName() {
+    return model.widgetName();
+  }
+
+  @override
+  getOnChanged(String customString, TextFormCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnEditingComplete(
+      String customString, TextFormCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnFieldSubmitted(
+      String customString, TextFormCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnSaved(String customString, TextFormCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnTap(String customString, TextFormCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getValidator(String customString, TextFormCallbackController controller) {
+    return null;
+  }
+}
index 7e814f65f9a8fb18b2bc702bfa7eec246760ae35..dfe23efbd0f2e16215d3463af98fdac437d69935 100644 (file)
@@ -10,6 +10,7 @@ class SettingLocale {
   String countryCode;
   SettingLocale({this.languageCode, this.countryCode});
 }
+
 class BaseSettings {
   static var locale = SettingLocale(countryCode: 'US', languageCode: 'en');
 
index a2cca4e34f18fc9f24caf431563c0299d26fe5cf..298f70c9fcf26c1f4f2ef754cff8bd54d5537901 100644 (file)
@@ -111,4 +111,27 @@ class StringHelper {
     }
     return rc;
   }
+
+  /// Convert [value] into a string for storing in a database respecting [dataType].
+  static String asDatabaseString(dynamic value, DataType dataType) {
+    if (value == null) {
+      value = 'NULL';
+    } else {
+      switch (dataType) {
+        case DataType.dateTime:
+          value = DateFormat('yyyy-MM-dd HH:mm:ss').format(value);
+          break;
+        case DataType.date:
+          value = DateFormat('yyyy-MM-dd').format(value);
+          break;
+        case DataType.bool:
+          value = value ? 'T' : 'F';
+          break;
+        default:
+          value = value.toString();
+          break;
+      }
+    }
+    return value;
+  }
 }
index 72128b36b61801971583955e547860e29ebd2da6..361395df3d779cc7788c468527d32ab35c99a06f 100644 (file)
@@ -1,7 +1,9 @@
 import 'package:dart_bones/dart_bones.dart';
 
+import 'db_reference_model.dart';
 import 'page_model.dart';
 import 'section_model.dart';
+import 'table_model.dart';
 import 'widget_model.dart';
 
 /// Describes a text widget without user interaction.
@@ -14,7 +16,7 @@ class AllDbFieldsModel extends WidgetModel {
 
   AllDbFieldsModel(
       SectionModel section, PageModel page, this.map, BaseLogger logger)
-      : super(section, page, WidgetModelType.text, logger);
+      : super(section, page, WidgetModelType.allDbFields, logger);
 
   /// Returns the name including the names of the parent
   @override
@@ -22,8 +24,7 @@ class AllDbFieldsModel extends WidgetModel {
 
   /// Dumps the internal structure into a [stringBuffer]
   StringBuffer dump(StringBuffer stringBuffer) {
-    stringBuffer
-        .write('    allDbFields $id text: options: ${options.join(' ')}\n');
+    stringBuffer.write('    allDbFields $id options: ${options.join(' ')}\n');
     return stringBuffer;
   }
 
@@ -31,9 +32,23 @@ class AllDbFieldsModel extends WidgetModel {
   void parse() {
     checkSuperfluousAttributes(map, 'options widgetType'.split(' '));
     options = parseOptions('options', map);
+    addFieldsOfTable(page.module.mainTable());
     checkOptionsByRegExpr(options, regExprOptions);
   }
 
   @override
   String widgetName() => 'allDbFields$id';
+
+  void addFieldsOfTable(TableModel table) {
+    bool isCreatePage = page.pageModelType == PageModelType.create;
+    table.columns.forEach((col) {
+      if (col.hasOption('doStore') ||
+          (!col.hasOption('hidden') &&
+              (!isCreatePage || !col.hasOption('primary')))) {
+        final field = DbReferenceModel.direct(section, page, col, logger);
+        page.addField(field);
+        page.widgets.add(field);
+      }
+    });
+  }
 }
index 159bd3c80fec0d8ea29d86571f5d0544b5051182..5a3dc477d8a13672624897de0d0676f0091da1d9 100644 (file)
@@ -26,11 +26,11 @@ class ComboboxModel extends FieldModel {
 
   /// Parses the map and stores the data in the instance.
   void parse() {
-    super.parse();
     checkSuperfluousAttributes(
         map,
         'name label dataType filterType options texts toolTip widgetType values'
             .split(' '));
+    super.parse();
     texts = parseStringList('texts', map);
     values = parseValueList('values', map, dataType);
     options = parseOptions('options', map);
index 83f8081fcb22f03de3dc86887005f6fd31746282..f93fabacd5b451acf321d6edd70495faa6829cea 100644 (file)
@@ -9,16 +9,35 @@ import 'widget_model.dart';
 /// Describes a form text field widget.
 class DbReferenceModel extends FieldModel {
   static final regExprOptions =
-      RegExp(r'^(readonly|disabled|required|password|unique)$');
+      RegExp(r'^(readonly|disabled|required|password|unique|combobox|undef)$');
   int maxSize;
   int rows;
   var value;
   ColumnModel column;
-  final Map<String, dynamic> map;
 
+  /// A constructor fetching the properties by parsing the [map].
   DbReferenceModel(
-      SectionModel section, PageModel page, this.map, BaseLogger logger)
-      : super(section, page, map, WidgetModelType.textField, logger);
+      SectionModel section, PageModel page, Map map, BaseLogger logger)
+      : super(section, page, map, WidgetModelType.dbReference, logger);
+
+  /// A constructor without map parsing.
+  DbReferenceModel.direct(SectionModel section, PageModel page,
+      ColumnModel column, BaseLogger logger)
+      : super.direct(
+            section,
+            page,
+            null,
+            WidgetModelType.dbReference,
+            column.name,
+            column.label,
+            column.toolTip,
+            column.dataType,
+            column.options,
+            logger) {
+    this.column = column;
+    maxSize = column.size;
+    rows = column.rows;
+  }
 
   /// Dumps the internal structure into a [stringBuffer]
   StringBuffer dump(StringBuffer stringBuffer) {
index a833b027aa71080e17f607915759479afe5669ec..d388646de5c92919c154c17557212eaf101a1bd7 100644 (file)
@@ -22,6 +22,27 @@ abstract class FieldModel extends WidgetModel {
       WidgetModelType fieldModelType, BaseLogger logger)
       : super(section, page, fieldModelType, logger);
 
+  FieldModel.direct(
+      SectionModel section,
+      PageModel page,
+      this.map,
+      WidgetModelType fieldModelType,
+      String name,
+      String label,
+      String toolTip,
+      DataType dataType,
+      List<String> options,
+      BaseLogger logger,
+      [FilterType filterType])
+      : super(section, page, fieldModelType, logger) {
+    this.name = name;
+    this.label = label;
+    this.toolTip = toolTip;
+    this.options = options;
+    this.dataType = dataType;
+    this.filterType = filterType;
+  }
+
   get value => _value;
 
   /// Stores the value of the field.
index e93e6e56c7548c84b835a443fb6620cc9e3753f6..13acc33ce89de539807f4daf32981780ef016118 100644 (file)
@@ -76,15 +76,15 @@ class ModuleModel extends ModelBase {
     addColumnIfMissing(columns, table.columns, '${table.name}_createdby');
     buffer.write(columns.fold(
         '',
-            (prev, col) =>
-        (prev as String) +
+        (prev, col) =>
+            (prev as String) +
             ((prev as String).isEmpty ? col.name : ',' + col.name)));
     buffer.write(')\n');
     buffer.write('          VALUES(');
     buffer.write(columns.fold(
         '',
-            (prev, col) =>
-        (prev as String) +
+        (prev, col) =>
+            (prev as String) +
             ((prev as String).isEmpty ? '' : ',') +
             (col.name.endsWith('createdat') ? 'NOW()' : (':' + col.name))));
     buffer.write(');"\n');
@@ -93,14 +93,14 @@ class ModuleModel extends ModelBase {
   /// Writes the list SQL part into the [buffer].
   void exportList(StringBuffer buffer, TableModel table) {
     final page = pages.firstWhere(
-            (element) => element.pageModelType == PageModelType.list,
+        (element) => element.pageModelType == PageModelType.list,
         orElse: () => null);
     if (page != null) {
       buffer.write('''      - name: list
         type: list
         sql: "SELECT * from ${table.name}\n''');
       // @ToDo: joins
-      final fields = page.fields.values
+      final fields = page.fields
           .where((item) => item is FieldModel && item.filterType != null);
       if (fields.isEmpty) {
         buffer.write('          WHERE 1;"');
@@ -143,14 +143,15 @@ class ModuleModel extends ModelBase {
         .forEach((col) {
       exportRecordOne(
           'by_${col.name}',
-          '${col.name}=:${col.name}&&${table.name}_id!=:excluded', buffer,
+          '${col.name}=:${col.name}&&${table.name}_id!=:excluded',
+          buffer,
           table);
     });
   }
 
   /// Writes the record SQL part into the [buffer].
-  void exportRecordOne(String sqlName, String condition, StringBuffer buffer,
-      TableModel table) {
+  void exportRecordOne(
+      String sqlName, String condition, StringBuffer buffer, TableModel table) {
     buffer.write('      - name: $sqlName\n');
     buffer.write('        type: record\n');
     buffer.write(
@@ -259,8 +260,8 @@ modules:
     buffer.write('        sql: "UPDATE ${table.name} SET\n          ');
     final columns = table.columns
         .where((col) =>
-    col.hasOption('doStore') ||
-        (!col.hasOption('primary') && !col.hasOption('hidden')))
+            col.hasOption('doStore') ||
+            (!col.hasOption('primary') && !col.hasOption('hidden')))
         .toList();
     addColumnIfMissing(columns, table.columns, '${table.name}_changedat');
     addColumnIfMissing(columns, table.columns, '${table.name}_changedby');
@@ -310,7 +311,7 @@ modules:
   /// Returns a child page given by [name], null otherwise.
   PageModel pageByName(String name) {
     final found =
-    pages.firstWhere((element) => element.name == name, orElse: () => null);
+        pages.firstWhere((element) => element.name == name, orElse: () => null);
     return found;
   }
 
index cc1fc91624dbb8cd990e4ccec901eaef6469631b..ccd7475f8c86ea53c7e3716dafb3ed02d74ea5e2 100644 (file)
@@ -18,7 +18,7 @@ class PageModel extends ModelBase {
   final List<SectionModel> sections = [];
   PageModelType pageModelType;
   List<String> options;
-  final fields = <String, FieldModel>{};
+  final fields = <FieldModel>[];
   final buttons = <String, ButtonModel>{};
   final widgets = <WidgetModel>[];
 
@@ -36,15 +36,23 @@ class PageModel extends ModelBase {
     }
   }
 
+  /// Tests whether the field with [name] is part of the page.
+  bool hasField(String name) {
+    final first = fields.firstWhere((element) => element.name == name,
+        orElse: () => null);
+    final rc = first != null;
+    return rc;
+  }
+
   /// Adds a [field] to the [this.fields].
   /// If already defined and error is logged.
   void addField(FieldModel field) {
     final name = field.name;
-    if (fields.containsKey(name)) {
+    if (hasField(name)) {
       logger.error('field ${field.fullName()} already defined: ' +
-          fields[name].fullName());
+          getField(name).fullName());
     } else {
-      fields[name] = field;
+      fields.add(field);
     }
   }
 
@@ -77,13 +85,10 @@ class PageModel extends ModelBase {
 
   /// Returns a field by [name] or null on error.
   FieldModel getField(String name, {bool required = true}) {
-    FieldModel rc;
-    if (!fields.containsKey(name)) {
-      if (required) {
-        logger.error('missing field $name in page ${fullName()}');
-      }
-    } else {
-      rc = fields[name];
+    final rc = fields.firstWhere((element) => element.name == name,
+        orElse: () => null);
+    if (required && rc == null) {
+      logger.error('missing field $name in page ${fullName()}');
     }
     return rc;
   }
index 05f49f6a8b4d344c3d725ff93ca2826421d02406..0e06a8877c209abd79e79a73f809e09b20baea53 100644 (file)
@@ -1,9 +1,10 @@
 import 'package:dart_bones/dart_bones.dart';
+
 import 'all_db_fields_model.dart';
-import 'db_reference_model.dart';
 import 'button_model.dart';
 import 'checkbox_model.dart';
 import 'combobox_model.dart';
+import 'db_reference_model.dart';
 import 'empty_line_model.dart';
 import 'model_base.dart';
 import 'page_model.dart';
@@ -67,7 +68,7 @@ class SectionModel extends WidgetModel {
           if (!ModelBase.isMap(child)) {
             logger
                 .error('child $no of "children" is not a map in ${fullName()}: '
-                '${StringUtils.limitString(child.toString(), 80)}');
+                    '${StringUtils.limitString(child.toString(), 80)}');
           } else {
             if (!child.containsKey('widgetType')) {
               logger.error(
@@ -86,7 +87,6 @@ class SectionModel extends WidgetModel {
                   break;
                 case WidgetModelType.combobox:
                   widget = ComboboxModel(this, page, child, logger);
-                  page.addField(widget);
                   break;
                 case WidgetModelType.textField:
                   widget = TextFieldModel(this, page, child, logger);
@@ -120,6 +120,7 @@ class SectionModel extends WidgetModel {
                   case WidgetModelType.textField:
                   case WidgetModelType.combobox:
                   case WidgetModelType.checkbox:
+                  case WidgetModelType.dbReference:
                     page.addField(widget);
                     break;
                   default:
index 1d3e28384a56503375f7d8958c4f9cc1d5e29077..32af803997dc2cd9ff31bc1edd660b8013d35b7f 100644 (file)
@@ -1,4 +1,5 @@
 import 'package:dart_bones/dart_bones.dart';
+
 import '../module_model.dart';
 
 class RoleModel extends ModuleModel {
@@ -75,7 +76,8 @@ class RoleModel extends ModuleModel {
                 "filterType": "pattern",
                 "name": "role_name",
                 "label": "Name",
-                "toolTip": "Suchmuster des Rollennamens: Joker: '*' (beliebiger Text), z.B. '*min*'"
+                "toolTip":
+                    "Suchmuster des Rollennamens: Joker: '*' (beliebiger Text), z.B. '*min*'"
               }
             ]
           }
index 3cc7b5c25b242e7701f55ba72a7529d433961e33..99b1dc1078d75ba95f04ff6aeb10ee219816ddcc 100644 (file)
@@ -5,14 +5,22 @@ import 'package:flutter_bones/flutter_bones.dart';
 /// Data class for storing parameter to build a page.
 class ApplicationData {
   final BaseConfiguration configuration;
+  final BaseLogger logger;
 
   /// Signature: AppBar func(String title)
   final AppBar Function(String title) appBarBuilder;
   final Drawer Function(dynamic context) drawerBuilder;
   final Persistence persistence;
+  String currentUser;
+  int currentRoleId;
+
   /// Constructor.
   /// [configuration] is a map with the widget data (e.g. padding)
   /// [appBarBuilder] is a factory of a function returning a outside designed AppBar
   /// [drawerBuilder] is a factory of a function returning a Drawer which handles the "Hamburger menu"
-  ApplicationData(this.configuration, this.appBarBuilder, this.drawerBuilder, this.persistence);
+  ApplicationData(this.configuration, this.appBarBuilder, this.drawerBuilder,
+      this.persistence, this.logger) {
+    currentUser = 'Gast';
+    currentRoleId = 100;
+  }
 }
index eff159472a02d5007191ce978c49846f560fb047..55c0e60a7bf88406fb5c55ad8e8e0f93e205c4c3 100644 (file)
@@ -5,39 +5,43 @@ import '../../widget/edit_form.dart';
 import 'configuration_controller.dart';
 
 class ConfigurationChangePage extends StatefulWidget {
-  final ApplicationData pageData;
+  final ApplicationData applicationData;
   final logger = Settings().logger;
-  ConfigurationChangePageState lastState;
 
-  ConfigurationChangePage(this.pageData, {Key key}) : super(key: key);
+  //ConfigurationChangePageState lastState;
+
+  ConfigurationChangePage(this.applicationData, {Key key}) : super(key: key);
 
   @override
   ConfigurationChangePageState createState() {
-    final rc = lastState = ConfigurationChangePageState(pageData);
+    final rc = ConfigurationChangePageState(applicationData);
+    // lastState = rc;
     return rc;
   }
 }
 
 class ConfigurationChangePageState extends State<ConfigurationChangePage> {
-  final ApplicationData pageData;
+  final ApplicationData applicationData;
 
-  final GlobalKey<FormState> _formKey = GlobalKey<FormState>(debugLabel: 'configuration');
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'configuration_change');
 
   ConfigurationController controller;
 
-  ConfigurationChangePageState(this.pageData);
+  ConfigurationChangePageState(this.applicationData);
 
   @override
   Widget build(BuildContext context) {
-    controller = controller ?? ConfigurationController(_formKey, this);
+    controller = controller ??
+        ConfigurationController(
+            _formKey, this, 'change', context, applicationData);
     return Scaffold(
-        appBar: pageData.appBarBuilder('Rolle ändern'),
-        drawer: pageData.drawerBuilder(context),
+        appBar: applicationData.appBarBuilder('Rolle ändern'),
+        drawer: applicationData.drawerBuilder(context),
         body: EditForm.editForm(
           key: _formKey,
-          isCreateForm: false,
-          moduleController: controller,
-          configuration: pageData.configuration,
+          pageController: controller,
+          configuration: applicationData.configuration,
         ));
   }
 }
index 26453692b2d9979e87dfe82e3996469cfb68d26c..9049089740c48d278d54153d3aefa7ecf5aa76a4 100644 (file)
@@ -2,11 +2,24 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bones/flutter_bones.dart';
 
 import '../../model/standard/configuration_model.dart';
-import '../../widget/module_controller.dart';
+import '../../widget/page_controller_bones.dart';
 
-class ConfigurationController extends ModuleController {
-  ConfigurationController(GlobalKey<FormState> formKey, State<StatefulWidget> parent)
-      : super(formKey, parent, ConfigurationModel(Settings().logger)) {
+class ConfigurationController extends PageControllerBones {
+  /// Controller for a page named [pageName].
+  ConfigurationController(
+      GlobalKey<FormState> formKey,
+      State<StatefulWidget> parent,
+      String pageName,
+      BuildContext context,
+      ApplicationData applicationData)
+      : super(
+          formKey,
+          parent,
+          ConfigurationModel(Settings().logger),
+          pageName,
+          context,
+          applicationData,
+        ) {
     moduleModel.parse();
   }
 }
index a77611628f32b89e6b11eaef21da0ff87c7c5af6..6e6b0d341e4e9114b52b33a4862208437bb5f03a 100644 (file)
@@ -7,37 +7,38 @@ import 'configuration_controller.dart';
 class ConfigurationCreatePage extends StatefulWidget {
   final ApplicationData pageData;
   final logger = Settings().logger;
-  ConfigurationCreatePageState lastState;
 
   ConfigurationCreatePage(this.pageData, {Key key}) : super(key: key);
 
   @override
   ConfigurationCreatePageState createState() {
-    final rc = lastState = ConfigurationCreatePageState(pageData);
+    final rc = ConfigurationCreatePageState(pageData);
     return rc;
   }
 }
 
 class ConfigurationCreatePageState extends State<ConfigurationCreatePage> {
-  final ApplicationData pageData;
+  final ApplicationData applicationData;
 
-  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'configuration_create');
 
   ConfigurationController controller;
 
-  ConfigurationCreatePageState(this.pageData);
+  ConfigurationCreatePageState(this.applicationData);
 
   @override
   Widget build(BuildContext context) {
-    controller = controller ?? ConfigurationController(_formKey, this);
+    controller = controller ??
+        ConfigurationController(
+            _formKey, this, 'create', context, applicationData);
     return Scaffold(
-        appBar: pageData.appBarBuilder('Neue Rolle'),
-        drawer: pageData.drawerBuilder(context),
+        appBar: applicationData.appBarBuilder('Neue Rolle'),
+        drawer: applicationData.drawerBuilder(context),
         body: EditForm.editForm(
           key: _formKey,
-          isCreateForm: true,
-          moduleController: controller,
-          configuration: pageData.configuration,
+          pageController: controller,
+          configuration: applicationData.configuration,
         ));
   }
 }
index b9b0332a86a259181658ebfbb1af29682a370370..4cae1cbe0d71fd1542ae6613ac12f446aa733446 100644 (file)
@@ -1,11 +1,9 @@
 import 'package:flutter/material.dart';
 
-import '../../helper/settings.dart';
-import '../../model/model_types.dart';
-import '../../widget/filter_fields.dart';
+import '../../widget/filter_set.dart';
 import '../../widget/list_form.dart';
-import '../../widget/raised_button_bone.dart';
 import '../application_data.dart';
+import 'configuration_controller.dart';
 
 class ConfigurationListPage extends StatefulWidget {
   final ApplicationData pageData;
@@ -22,82 +20,72 @@ class ConfigurationListPage extends StatefulWidget {
 }
 
 class ConfigurationListPageState extends State<ConfigurationListPage> {
-  ConfigurationListPageState(this.pageData);
-
-  final ApplicationData pageData;
-
-  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
-  static Configuration currentConfiguration = Configuration();
-
-  Iterable<dynamic> getRows(FilterSet filters) {
-    final rows = <Map<String, dynamic>>[
-      {
-        'configuration_id': '1',
-        'configuration_name': 'Administrator',
-        'configuration_priority': '10',
-      },
-      {
-        'configuration_id': '2',
-        'configuration_name': 'Verwalter',
-        'configuration_priority': '20',
-      },
-      {
-        'configuration_id': '3',
-        'configuration_name': 'Benutzer',
-        'configuration_priority': '30',
-      },
-      {
-        'configuration_id': '4',
-        'configuration_name': 'Gast',
-        'configuration_priority': '40',
-      },
-    ];
-    final rc = rows.where((row) => filters.isValid(row)).toList();
-    return rc;
-  }
+  final ApplicationData applicationData;
 
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'configuration_list');
+  Iterable<dynamic> rows;
+  ConfigurationController controller;
   FilterSet filters;
 
+  ConfigurationListPageState(this.applicationData);
+
   @override
   Widget build(BuildContext context) {
-    final logger = Settings().logger;
-    if (filters == null) {
-      filters = FilterSet.ready(
-          _formKey,
-          this,
-          [
-            FilterItem(
-                name: 'configuration_name',
-                filterType: FilterType.pattern,
-                label: 'Name'),
-          ],
-          logger);
-    }
+    controller ??= ConfigurationController(
+        _formKey, this, 'list', context, applicationData);
+    filters ??= controller.filterSet(page: 'list');
+    getRows();
     return Scaffold(
-      appBar: pageData.appBarBuilder('Rollen'),
-      drawer: pageData.drawerBuilder(context),
+      appBar: applicationData.appBarBuilder('Rollen'),
+      drawer: applicationData.drawerBuilder(context),
       body: ListForm.listForm(
         key: _formKey,
-        configuration: pageData.configuration,
+        configuration: applicationData.configuration,
         titles: ListForm.stringsToTitles(';Id;Name;Priorität'),
-        columnNames: 'configuration_id configuration_name configuration_priority'.split(' '),
-        rows: getRows(filters),
+        columnNames:
+            'configuration_id configuration_name configuration_priority'
+                .split(' '),
+        rows: rows ?? [],
         showEditIcon: true,
         buttons: <Widget>[
-          RaisedButtonBone(
-            'search',
-            filters,
-            child: Text('Suchen'),
-          ),
+          ButtonBar(alignment: MainAxisAlignment.center, children: [
+            RaisedButton(
+              child: Text('Suchen'),
+              onPressed: () {
+                rows = null;
+                if (_formKey.currentState.validate()) {
+                  _formKey.currentState.save();
+                  getRows();
+                }
+              },
+            ),
+            RaisedButton(
+              child: Text('Neue Rolle'),
+              onPressed: () {
+                Navigator.pushNamed(context, '/configuration/create');
+              },
+            ),
+          ]),
         ],
         filters: filters,
       ),
     );
   }
-}
 
-class Configuration {
-  int id;
-  String priority;
-  String name;
+  void getRows() {
+    final persistence = applicationData.persistence;
+    final namePattern = filters.valueOf('configuration_name') ?? '';
+    persistence.list(module: 'configuration', params: {
+      ':configuration_name': namePattern.replaceAll('*', '%') + '%'
+    }).then((list) {
+      if (rows == null) {
+        rows = list;
+        setState(() {});
+      }
+    }, onError: (error) {
+      applicationData.logger
+          .error('cannot retrieve configuration list: $error');
+    });
+  }
 }
index c0e2e2baf92db395953a6be7547d49389edf7c4f..e1c56331f851fc56baf24217f316923a23f32de0 100644 (file)
@@ -5,39 +5,42 @@ import '../../widget/edit_form.dart';
 import 'role_controller.dart';
 
 class RoleChangePage extends StatefulWidget {
-  final ApplicationData pageData;
+  final ApplicationData applicationData;
   final logger = Settings().logger;
-  RoleChangePageState lastState;
 
-  RoleChangePage(this.pageData, {Key key}) : super(key: key);
+  //RoleChangePageState lastState;
+
+  RoleChangePage(this.applicationData, {Key key}) : super(key: key);
 
   @override
   RoleChangePageState createState() {
-    final rc = lastState = RoleChangePageState(pageData);
+    final rc = RoleChangePageState(applicationData);
+    // lastState = rc;
     return rc;
   }
 }
 
 class RoleChangePageState extends State<RoleChangePage> {
-  final ApplicationData pageData;
+  final ApplicationData applicationData;
 
-  final GlobalKey<FormState> _formKey = GlobalKey<FormState>(debugLabel: 'role_change');
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'role_change');
 
   RoleController controller;
 
-  RoleChangePageState(this.pageData);
+  RoleChangePageState(this.applicationData);
 
   @override
   Widget build(BuildContext context) {
-    controller = controller ?? RoleController(_formKey, this);
+    controller = controller ??
+        RoleController(_formKey, this, 'change', context, applicationData);
     return Scaffold(
-        appBar: pageData.appBarBuilder('Rolle ändern'),
-        drawer: pageData.drawerBuilder(context),
+        appBar: applicationData.appBarBuilder('Rolle ändern'),
+        drawer: applicationData.drawerBuilder(context),
         body: EditForm.editForm(
           key: _formKey,
-          isCreateForm: false,
-          moduleController: controller,
-          configuration: pageData.configuration,
+          pageController: controller,
+          configuration: applicationData.configuration,
         ));
   }
 }
index 385777321b8baad4eff9d921ac150e7e35f8fab0..717cd166e0db57f37d20add099770744fa4a6a47 100644 (file)
@@ -1,29 +1,21 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_bones/flutter_bones.dart';
-import 'package:flutter_bones/src/widget/filter_fields.dart';
 
 import '../../model/standard/role_model.dart';
-import '../../widget/module_controller.dart';
+import '../../widget/page_controller_bones.dart';
 
-class RoleController extends ModuleController {
-  RoleController(GlobalKey<FormState> formKey, State<StatefulWidget> parent)
-      : super(formKey, parent, RoleModel(Settings().logger)) {
+class RoleController extends PageControllerBones {
+  /// Controller for a page named [pageName].
+  RoleController(GlobalKey<FormState> formKey, State<StatefulWidget> parent,
+      String pageName, BuildContext context, ApplicationData applicationData)
+      : super(
+          formKey,
+          parent,
+          RoleModel(Settings().logger),
+          pageName,
+          context,
+          applicationData,
+        ) {
     moduleModel.parse();
   }
-  FilterSet filterSet({@required String page}) {
-    final rc = FilterSet(globalKey, parent, moduleModel.logger);
-    moduleModel
-        .pageByName(page)
-        .fields
-        .values
-        .where((element) => element.filterType != null)
-        .forEach((element) {
-      rc.add(FilterItem(
-          label: element.label,
-          filterType: element.filterType,
-          toolTip: element.toolTip,
-          name: element.name));
-    });
-    return rc;
-  }
 }
index 40a4cf2c0a7d9983394dd9652e72724fe3d8afba..30174877d8a994fe5da988b6be84ff03322fc4a3 100644 (file)
@@ -7,37 +7,40 @@ import 'role_controller.dart';
 class RoleCreatePage extends StatefulWidget {
   final ApplicationData pageData;
   final logger = Settings().logger;
-  RoleCreatePageState lastState;
 
   RoleCreatePage(this.pageData, {Key key}) : super(key: key);
 
   @override
   RoleCreatePageState createState() {
-    final rc = lastState = RoleCreatePageState(pageData);
+    final rc = RoleCreatePageState(pageData);
     return rc;
   }
 }
 
 class RoleCreatePageState extends State<RoleCreatePage> {
-  final ApplicationData pageData;
+  final ApplicationData applicationData;
 
-  final GlobalKey<FormState> _formKey = GlobalKey<FormState>(debugLabel: 'role_create');
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'role_create');
 
   RoleController controller;
 
-  RoleCreatePageState(this.pageData);
+  RoleCreatePageState(this.applicationData);
 
   @override
   Widget build(BuildContext context) {
-    controller = controller ?? RoleController(_formKey, this);
+    if (controller == null) {
+      controller =
+          RoleController(_formKey, this, 'create', context, applicationData);
+      controller.initialize();
+    }
     return Scaffold(
-        appBar: pageData.appBarBuilder('Neue Rolle'),
-        drawer: pageData.drawerBuilder(context),
+        appBar: applicationData.appBarBuilder('Neue Rolle'),
+        drawer: applicationData.drawerBuilder(context),
         body: EditForm.editForm(
           key: _formKey,
-          isCreateForm: true,
-          moduleController: controller,
-          configuration: pageData.configuration,
+          pageController: controller,
+          configuration: applicationData.configuration,
         ));
   }
 }
index e51158ea9695fdd1668ed9bbfe1194e8ad4aa8eb..7398527e4a67bf37d64c97e43797118b6eee73ef 100644 (file)
@@ -1,10 +1,7 @@
 import 'package:flutter/material.dart';
 
-import '../../helper/settings.dart';
-import '../../model/model_types.dart';
-import '../../widget/filter_fields.dart';
+import '../../widget/filter_set.dart';
 import '../../widget/list_form.dart';
-import '../../widget/raised_button_bone.dart';
 import '../application_data.dart';
 import 'role_controller.dart';
 
@@ -23,27 +20,28 @@ class RoleListPage extends StatefulWidget {
 }
 
 class RoleListPageState extends State<RoleListPage> {
-  final ApplicationData pageData;
+  final ApplicationData applicationData;
 
   final GlobalKey<FormState> _formKey =
       GlobalKey<FormState>(debugLabel: 'role_list');
   Iterable<dynamic> rows;
   RoleController controller;
   FilterSet filters;
-  RoleListPageState(this.pageData);
+
+  RoleListPageState(this.applicationData);
 
   @override
   Widget build(BuildContext context) {
-    final logger = Settings().logger;
-    controller ??= RoleController(_formKey, this);
+    controller ??=
+        RoleController(_formKey, this, 'list', context, applicationData);
     filters ??= controller.filterSet(page: 'list');
     getRows();
     return Scaffold(
-      appBar: pageData.appBarBuilder('Rollen'),
-      drawer: pageData.drawerBuilder(context),
+      appBar: applicationData.appBarBuilder('Rollen'),
+      drawer: applicationData.drawerBuilder(context),
       body: ListForm.listForm(
         key: _formKey,
-        configuration: pageData.configuration,
+        configuration: applicationData.configuration,
         titles: ListForm.stringsToTitles(';Id;Name;Priorität'),
         columnNames: 'role_id role_name role_priority'.split(' '),
         rows: rows ?? [],
@@ -51,18 +49,20 @@ class RoleListPageState extends State<RoleListPage> {
         buttons: <Widget>[
           ButtonBar(alignment: MainAxisAlignment.center, children: [
             RaisedButton(
-                child: Text('Suchen'),
-                onPressed: () {
-                  rows = null;
-                  if (_formKey.currentState.validate()) {
-                    _formKey.currentState.save();
-                    getRows();
-                  }
-                },
+              child: Text('Suchen'),
+              onPressed: () {
+                rows = null;
+                if (_formKey.currentState.validate()) {
+                  _formKey.currentState.save();
+                  getRows();
+                }
+              },
             ),
             RaisedButton(
               child: Text('Neue Rolle'),
-              onPressed: () {},
+              onPressed: () {
+                Navigator.pushNamed(context, '/role/create');
+              },
             ),
           ]),
         ],
@@ -72,18 +72,17 @@ class RoleListPageState extends State<RoleListPage> {
   }
 
   void getRows() {
-    final persistence = pageData.persistence;
-    var rc;
+    final persistence = applicationData.persistence;
     final namePattern = filters.valueOf('role_name') ?? '';
-    persistence.list(
-        module: 'role',
-        params: {':role_name': namePattern.replaceAll('*', '%') + '%'}).then((list) {
+    persistence.list(module: 'role', params: {
+      ':role_name': namePattern.replaceAll('*', '%') + '%'
+    }).then((list) {
       if (rows == null) {
         rows = list;
         setState(() {});
       }
     }, onError: (error) {
-      pageData.configuration.logger.error('cannot retrieve role list: $error');
+      applicationData.logger.error('cannot retrieve role list: $error');
     });
   }
 }
index a770ffa69eb2ab49f830ac31e0014e942b98c592..50f2cab67c807008b2a04ffdbf6622e4fd0cd7e7 100644 (file)
@@ -5,39 +5,42 @@ import '../../widget/edit_form.dart';
 import 'user_controller.dart';
 
 class UserChangePage extends StatefulWidget {
-  final ApplicationData pageData;
+  final ApplicationData applicationData;
   final logger = Settings().logger;
-  UserChangePageState lastState;
 
-  UserChangePage(this.pageData, {Key key}) : super(key: key);
+  //UserChangePageState lastState;
+
+  UserChangePage(this.applicationData, {Key key}) : super(key: key);
 
   @override
   UserChangePageState createState() {
-    final rc = lastState = UserChangePageState(pageData);
+    final rc = UserChangePageState(applicationData);
+    // lastState = rc;
     return rc;
   }
 }
 
 class UserChangePageState extends State<UserChangePage> {
-  final ApplicationData pageData;
+  final ApplicationData applicationData;
 
-  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'user_change');
 
   UserController controller;
 
-  UserChangePageState(this.pageData);
+  UserChangePageState(this.applicationData);
 
   @override
   Widget build(BuildContext context) {
-    controller = controller ?? UserController(_formKey, this);
+    controller = controller ??
+        UserController(_formKey, this, 'change', context, applicationData);
     return Scaffold(
-        appBar: pageData.appBarBuilder('Rolle ändern'),
-        drawer: pageData.drawerBuilder(context),
+        appBar: applicationData.appBarBuilder('Rolle ändern'),
+        drawer: applicationData.drawerBuilder(context),
         body: EditForm.editForm(
           key: _formKey,
-          isCreateForm: false,
-          moduleController: controller,
-          configuration: pageData.configuration,
+          pageController: controller,
+          configuration: applicationData.configuration,
         ));
   }
 }
index 1532e090186b2f517a847b309b5c94f0bea3b805..22db52412c5d8b15ee4005ca342c10b204b1bb88 100644 (file)
@@ -2,11 +2,20 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bones/flutter_bones.dart';
 
 import '../../model/standard/user_model.dart';
-import '../../widget/module_controller.dart';
+import '../../widget/page_controller_bones.dart';
 
-class UserController extends ModuleController {
-  UserController(GlobalKey<FormState> formKey, State<StatefulWidget> parent)
-      : super(formKey, parent, UserModel(Settings().logger)) {
+class UserController extends PageControllerBones {
+  /// Controller for a page named [pageName].
+  UserController(GlobalKey<FormState> formKey, State<StatefulWidget> parent,
+      String pageName, BuildContext context, ApplicationData applicationData)
+      : super(
+          formKey,
+          parent,
+          UserModel(Settings().logger),
+          pageName,
+          context,
+          applicationData,
+        ) {
     moduleModel.parse();
   }
 }
index fb9a20a3e856d9cfe93bfc57185912549c0c22b1..eada47565b53aa161bcfb3e614d04976620fc483 100644 (file)
@@ -7,37 +7,37 @@ import 'user_controller.dart';
 class UserCreatePage extends StatefulWidget {
   final ApplicationData pageData;
   final logger = Settings().logger;
-  UserCreatePageState lastState;
 
   UserCreatePage(this.pageData, {Key key}) : super(key: key);
 
   @override
   UserCreatePageState createState() {
-    final rc = lastState = UserCreatePageState(pageData);
+    final rc = UserCreatePageState(pageData);
     return rc;
   }
 }
 
 class UserCreatePageState extends State<UserCreatePage> {
-  final ApplicationData pageData;
+  final ApplicationData applicationData;
 
-  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'user_create');
 
   UserController controller;
 
-  UserCreatePageState(this.pageData);
+  UserCreatePageState(this.applicationData);
 
   @override
   Widget build(BuildContext context) {
-    controller = controller ?? UserController(_formKey, this);
+    controller = controller ??
+        UserController(_formKey, this, 'create', context, applicationData);
     return Scaffold(
-        appBar: pageData.appBarBuilder('Neue Rolle'),
-        drawer: pageData.drawerBuilder(context),
+        appBar: applicationData.appBarBuilder('Neue Rolle'),
+        drawer: applicationData.drawerBuilder(context),
         body: EditForm.editForm(
           key: _formKey,
-          isCreateForm: true,
-          moduleController: controller,
-          configuration: pageData.configuration,
+          pageController: controller,
+          configuration: applicationData.configuration,
         ));
   }
 }
index fbdcf7003aaf9a727c02393a89ebe0a5b04f142d..a2d803953eb58e6e598b994075699b572b9e3a73 100644 (file)
@@ -1,11 +1,9 @@
 import 'package:flutter/material.dart';
 
-import '../../helper/settings.dart';
-import '../../model/model_types.dart';
-import '../../widget/filter_fields.dart';
+import '../../widget/filter_set.dart';
 import '../../widget/list_form.dart';
-import '../../widget/raised_button_bone.dart';
 import '../application_data.dart';
+import 'user_controller.dart';
 
 class UserListPage extends StatefulWidget {
   final ApplicationData pageData;
@@ -22,82 +20,69 @@ class UserListPage extends StatefulWidget {
 }
 
 class UserListPageState extends State<UserListPage> {
-  UserListPageState(this.pageData);
-
-  final ApplicationData pageData;
-
-  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
-  static User currentUser = User();
-
-  List<Map<String, dynamic>> getRows(FilterSet filters) {
-    final rows = <Map<String, dynamic>>[
-      {
-        'user_id': '1',
-        'user_name': 'Administrator',
-        'user_priority': '10',
-      },
-      {
-        'user_id': '2',
-        'user_name': 'Verwalter',
-        'user_priority': '20',
-      },
-      {
-        'user_id': '3',
-        'user_name': 'Benutzer',
-        'user_priority': '30',
-      },
-      {
-        'user_id': '4',
-        'user_name': 'Gast',
-        'user_priority': '40',
-      },
-    ];
-    final rc = rows.where((row) => filters.isValid(row)).toList();
-    return rc;
-  }
+  final ApplicationData applicationData;
 
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'user_list');
+  Iterable<dynamic> rows;
+  UserController controller;
   FilterSet filters;
 
+  UserListPageState(this.applicationData);
+
   @override
   Widget build(BuildContext context) {
-    final logger = Settings().logger;
-    if (filters == null) {
-      filters = FilterSet.ready(
-          _formKey,
-          this,
-          [
-            FilterItem(
-                name: 'user_name',
-                filterType: FilterType.pattern,
-                label: 'Name'),
-          ],
-          logger);
-    }
+    controller ??=
+        UserController(_formKey, this, 'list', context, applicationData);
+    filters ??= controller.filterSet(page: 'list');
+    getRows();
     return Scaffold(
-      appBar: pageData.appBarBuilder('Rollen'),
-      drawer: pageData.drawerBuilder(context),
+      appBar: applicationData.appBarBuilder('Rollen'),
+      drawer: applicationData.drawerBuilder(context),
       body: ListForm.listForm(
         key: _formKey,
-        configuration: pageData.configuration,
+        configuration: applicationData.configuration,
         titles: ListForm.stringsToTitles(';Id;Name;Priorität'),
         columnNames: 'user_id user_name user_priority'.split(' '),
-        rows: getRows(filters),
+        rows: rows ?? [],
         showEditIcon: true,
         buttons: <Widget>[
-          RaisedButtonBone(
-            'search',
-            filters,
-            child: Text('Suchen'),
-          ),
+          ButtonBar(alignment: MainAxisAlignment.center, children: [
+            RaisedButton(
+              child: Text('Suchen'),
+              onPressed: () {
+                rows = null;
+                if (_formKey.currentState.validate()) {
+                  _formKey.currentState.save();
+                  getRows();
+                }
+              },
+            ),
+            RaisedButton(
+              child: Text('Neue Rolle'),
+              onPressed: () {
+                Navigator.pushNamed(context, '/user/create');
+              },
+            ),
+          ]),
         ],
         filters: filters,
       ),
     );
   }
-}
 
-class User {
-  int id;
-  String priority;
-  String name;
+  void getRows() {
+    final persistence = applicationData.persistence;
+    final namePattern = filters.valueOf('user_name') ?? '';
+    persistence.list(module: 'user', params: {
+      ':user_name': namePattern.replaceAll('*', '%') + '%'
+    }).then((list) {
+      if (rows == null) {
+        rows = list;
+        setState(() {});
+      }
+    }, onError: (error) {
+      applicationData.logger.error('cannot retrieve user list: $error');
+    });
+  }
 }
index 20276e9a3b3fbfe1fe0431baafa46d91dbeaea92..c54eac7f0ffe4bde4bdf3d56d3c67e9c67571423 100644 (file)
@@ -1,5 +1,5 @@
-import 'package:flutter/material.dart';
 import 'package:dart_bones/dart_bones.dart';
+import 'package:flutter/material.dart';
 import 'package:flutter_bones/flutter_bones.dart';
 
 class UserPage extends StatefulWidget {
@@ -20,7 +20,8 @@ class UserPageState extends State<UserPage> {
 
   final ApplicationData pageData;
 
-  final GlobalKey<FormState> _formKey = GlobalKey<FormState>(debugLabel: 'user_page');
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'user_page');
   static User currentUser = User();
 
   @override
index f548a49d0cf4eccd736cba2a1be1c43f837d8819..cd2839ef59af873cf24e0e67ee0bf9e0d6be282c 100644 (file)
@@ -29,10 +29,12 @@ abstract class Persistence {
 
   /// Returns a record with primary key [id] of the [module] with the
   /// SQL statement [sqlName].
-  Future<Map<String, dynamic>> record({@required String module, String sqlName, @required int id});
+  Future<Map<String, dynamic>> record(
+      {@required String module, String sqlName, @required int id});
 
   /// Updates a record of [module] with the [data] using the SQL statement [sqlName].
-  Future update({@required String module,
-    String sqlName,
-    @required Map<String, dynamic> data});
+  Future update(
+      {@required String module,
+      String sqlName,
+      @required Map<String, dynamic> data});
 }
index 676fd5260bfe5652743d13c94a062019e0bd30f3..d6da8904f08ca79737464959b89a9dad590a52ee 100644 (file)
@@ -1,4 +1,5 @@
 import 'dart:convert' as convert;
+
 import 'package:dart_bones/dart_bones.dart';
 import 'package:flutter_bones/src/persistence/persistence.dart';
 import 'package:http/http.dart' as http;
@@ -50,20 +51,22 @@ class RestPersistence extends Persistence {
     return _instance;
   }
 
-  RestPersistence.internal({this.application,
-    @required this.version,
-    @required this.host,
-    @required this.port,
-    this.scheme = 'http',
-    @required this.logger}) {
+  RestPersistence.internal(
+      {this.application,
+      @required this.version,
+      @required this.host,
+      @required this.port,
+      this.scheme = 'http',
+      @required this.logger}) {
     uriPrefix = '$scheme://$host:$port';
   }
 
   @override
-  Future customQuery({String module,
-    String sqlName,
-    String sqlType,
-    Map<String, dynamic> params}) async {
+  Future customQuery(
+      {String module,
+      String sqlName,
+      String sqlType,
+      Map<String, dynamic> params}) async {
     var rc;
     assert(['list', 'record', 'update'].contains(sqlType));
     final params2 = params == null ? '{}' : convert.jsonEncode(params);
@@ -98,13 +101,11 @@ class RestPersistence extends Persistence {
   }
 
   @override
-  Future<dynamic> list(
-      {String module, String sqlName, Map params}) async {
+  Future<dynamic> list({String module, String sqlName, Map params}) async {
     sqlName ??= 'list';
     Iterable<dynamic> rc;
     final body = params == null ? '{}' : convert.jsonEncode(params);
-    final answer = await runRequest(module, sqlName, 'list',
-        body: body);
+    final answer = await runRequest(module, sqlName, 'list', body: body);
     if (answer.isNotEmpty) {
       rc = convert.jsonDecode(answer);
     }
index c9a993377952a4da9a986f3389e4cec8ce276373..dd72b93a4937018324b67169f64b59012d53d504 100644 (file)
@@ -1,9 +1,11 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_bones/flutter_bones.dart';
 import 'package:flutter_bones/src/private/bsettings.dart';
+
 import '../page/configuration/configuration_list_page.dart';
 import '../page/role/role_list_page.dart';
 import '../page/user/user_list_page.dart';
+
 class MenuItem {
   final String title;
   final dynamic page;
index 15b337c0e92ed7ba0a7c9956e3323394e91f347f..aeb84fa15365cc115b7d6f5cad986613f6a6f218 100644 (file)
@@ -9,6 +9,7 @@ class BSettings {
   final ApplicationData pageData;
   Persistence persistence;
   static BSettings lastInstance;
+
   /// Returns the singleton of VSetting.
   factory BSettings() {
     final map = {
@@ -22,11 +23,13 @@ class BSettings {
         port: 58011,
         host: 'localhost',
         logger: logger);
-    final pageData = ApplicationData(BaseConfiguration(map, logger), BAppBar.builder,
-        BonesDrawer.builder, persistence);
-    final rc =
-        BSettings.internal(BaseConfiguration(map, logger), pageData, persistence, logger);
+    final pageData = ApplicationData(BaseConfiguration(map, logger),
+        BAppBar.builder, BonesDrawer.builder, persistence, logger);
+    final rc = BSettings.internal(
+        BaseConfiguration(map, logger), pageData, persistence, logger);
     return lastInstance = rc;
   }
-  BSettings.internal(this.configuration, this.pageData, this.persistence, this.logger);
+
+  BSettings.internal(
+      this.configuration, this.pageData, this.persistence, this.logger);
 }
diff --git a/lib/src/widget/callback_controller_bones.dart b/lib/src/widget/callback_controller_bones.dart
new file mode 100644 (file)
index 0000000..b32910a
--- /dev/null
@@ -0,0 +1,60 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+
+/// Interface for a callback controller for flutter_bones specific widgets.
+/// flutter_bones specific widgets: [CheckboxListTileBone],
+/// [DropDownButtonFormBone], [RaisedButtonBone], [TextFormFieldBone]
+abstract class CallbackControllerBones {
+  List<String> comboboxTexts(
+      String customString, CallbackControllerBones controller);
+
+  List<dynamic> comboboxValues(
+      String customString, CallbackControllerBones controller);
+
+  ApplicationData getApplicationData();
+
+  BuildContext getContext();
+
+  FieldModel getModel(String customString, CallbackControllerBones controller);
+
+  String getName();
+
+  ValueChanged<String> getOnChanged(
+      String customString, CallbackControllerBones controller);
+
+  ValueChanged<bool> getOnChangedCheckbox(
+      String customString, CallbackControllerBones controller);
+
+  ValueChanged<T> getOnChangedCombobox<T>(
+      String customString, CallbackControllerBones controller);
+
+  VoidCallback getOnEditingComplete(
+      String customString, CallbackControllerBones controller);
+
+  ValueChanged<String> getOnFieldSubmitted(
+      String customString, CallbackControllerBones controller);
+
+  ValueChanged<bool> getOnHighlightChanged(
+      String customString, CallbackControllerBones controller);
+
+  VoidCallback getOnLongPressed(
+      String customString, CallbackControllerBones controller);
+
+  VoidCallback getOnPressed(
+      String customString, CallbackControllerBones controller);
+
+  FormFieldSetter<String> getOnSaved(
+      String customString, CallbackControllerBones controller);
+
+  DropdownButtonBuilder getOnSelectedItemBuilder(
+      String customString, CallbackControllerBones controller);
+
+  GestureTapCallback getOnTap(
+      String customString, CallbackControllerBones controller);
+
+  FormFieldValidator getOnValidator(
+      String customString, CallbackControllerBones controller);
+
+  FormFieldValidator<String> getValidator(
+      String customString, CallbackControllerBones controller);
+}
diff --git a/lib/src/widget/checkbox_list_tile_bone.dart b/lib/src/widget/checkbox_list_tile_bone.dart
new file mode 100644 (file)
index 0000000..51c34a3
--- /dev/null
@@ -0,0 +1,50 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter_bones/src/widget/page_controller_bones.dart';
+
+/// Implements a [Checkbox] with "outsourced" callbacks:
+/// [customString] a string mostly used for a name needed in the [callbackController]
+/// [callbackController] handles the callback methods.
+class CheckboxListTileBone extends CheckboxListTile {
+  final String customString;
+  final PageControllerBones callbackController;
+
+  CheckboxListTileBone(this.customString, this.callbackController,
+      {key,
+      bool value,
+      MouseCursor mouseCursor,
+      Color activeColor,
+      Color checkColor,
+      Color tileColor,
+      Widget title,
+      Widget subtitle,
+      bool isThreeLine = false,
+      bool dense,
+      Widget secondary,
+      bool selected = false,
+      ListTileControlAffinity controlAffinity =
+          ListTileControlAffinity.platform,
+      bool autofocus = false,
+      EdgeInsetsGeometry contentPadding,
+      bool tristate = false,
+      ShapeBorder shape})
+      : super(
+            key: key,
+            value: value,
+            activeColor: activeColor,
+            onChanged: callbackController.getOnChangedCheckbox(
+                customString, callbackController),
+            checkColor: checkColor,
+            tileColor: tileColor,
+            title: title,
+            subtitle: subtitle,
+            isThreeLine: isThreeLine,
+            dense: dense,
+            secondary: secondary,
+            selected: selected,
+            controlAffinity: controlAffinity,
+            autofocus: autofocus,
+            contentPadding: contentPadding,
+            tristate: tristate,
+            shape: shape);
+}
index 3bbedf6e4b749cc500cd230b7a4a95c3bd67fcce..5164046a626d33503ee4a1e02fbc11852865b032 100644 (file)
@@ -1,11 +1,13 @@
 import 'package:flutter/material.dart';
 
+import 'callback_controller_bones.dart';
+
 /// Implements a [DropdownButtonFormField] with "outsourced" callbacks:
 /// [customString] a string mostly used for a name needed in the [customController]
 /// [callbackController] handles the callback methods.
 class DropdownButtonFormBone<T> extends DropdownButtonFormField<T> {
   final String customString;
-  final ComboboxCallbackController callbackController;
+  final CallbackControllerBones callbackController;
 
   DropdownButtonFormBone(
     this.customString,
@@ -33,25 +35,25 @@ class DropdownButtonFormBone<T> extends DropdownButtonFormField<T> {
     FormFieldValidator<T> validator,
     AutovalidateMode autovalidateMode,
   }) : super(
-          key: key,
-          items: items,
-          selectedItemBuilder: callbackController.getOnSelectedItemBuilder(
-              customString, callbackController),
-          value: value,
-          hint: hint,
-          disabledHint: disabledHint,
-          onChanged:
-              callbackController.getOnChanged(customString, callbackController),
-          onTap: callbackController.getOnTap(customString, callbackController),
-          elevation: elevation,
-          style: style,
-          icon: icon,
-          iconDisabledColor: iconDisabledColor,
-          iconEnabledColor: iconEnabledColor,
-          iconSize: iconSize,
-          isDense: isDense,
-          isExpanded: isExpanded,
-          itemHeight: itemHeight,
+    key: key,
+    items: items,
+    selectedItemBuilder: callbackController.getOnSelectedItemBuilder(
+        customString, callbackController),
+    value: value,
+    hint: hint,
+    disabledHint: disabledHint,
+    onChanged: callbackController.getOnChangedCombobox<T>(
+        customString, callbackController),
+    onTap: callbackController.getOnTap(customString, callbackController),
+    elevation: elevation,
+    style: style,
+    icon: icon,
+    iconDisabledColor: iconDisabledColor,
+    iconEnabledColor: iconEnabledColor,
+    iconSize: iconSize,
+    isDense: isDense,
+    isExpanded: isExpanded,
+    itemHeight: itemHeight,
           focusColor: focusColor,
           focusNode: focusNode,
           autofocus: autofocus,
@@ -60,27 +62,3 @@ class DropdownButtonFormBone<T> extends DropdownButtonFormField<T> {
           autovalidateMode: autovalidateMode,
         );
 }
-
-/// Interface for a [DropdownButtonFormBone] specific callback controller.
-abstract class ComboboxCallbackController<T> {
-  ValueChanged<T> getOnChanged(
-      String customString, ComboboxCallbackController controller);
-
-  FormFieldSetter<T> getOnSaved(
-      String customString, ComboboxCallbackController controller);
-
-  DropdownButtonBuilder getOnSelectedItemBuilder(
-      String customString, ComboboxCallbackController controller);
-
-  VoidCallback getOnTap(
-      String customString, ComboboxCallbackController controller);
-
-  FormFieldValidator<T> getOnValidator(
-      String customString, ComboboxCallbackController controller);
-
-  String getName();
-
-  List<String> texts();
-
-  List<dynamic> values();
-}
index d33ddbd2acecf2358ba38eb5b5019aa79ca0fff5..4a3f9b6311bf56fe12e74305715377ed4921dda6 100644 (file)
@@ -1,7 +1,7 @@
 import 'package:dart_bones/dart_bones.dart';
 import 'package:flutter/material.dart';
 
-import 'module_controller.dart';
+import 'page_controller_bones.dart';
 import 'raised_button_bone.dart';
 
 typedef Function OnEditTap(Map<String, dynamic> row, int index);
@@ -10,19 +10,16 @@ typedef Function OnEditTap(Map<String, dynamic> row, int index);
 class EditForm {
   /// Returns a widget with a form containing at least some input fields
   /// and a save/cancel button.
-  /// [titles] is used for the table header.
-  /// [columnNames] are the keys to display: @precondition: titles.length == columnNames.length
-  /// [rows] is a list of rows normally delivered from a database query:
-  /// each row is a map with (key, value) pairs.
-  /// If [showEditItems] is true the edit icon is shown in the first column.
+  /// a change page.
+  /// [page] is the name of the page in the model.
   static Form editForm({
     @required Key key,
-    @required bool isCreateForm,
-    @required ModuleController moduleController,
+    @required PageControllerBones pageController,
     @required BaseConfiguration configuration,
   }) {
     final padding =
         configuration.asFloat('form.card.padding', defaultValue: 16.0);
+    final widgets = pageController.getWidgets();
     return Form(
       key: key,
       child: Card(
@@ -31,7 +28,7 @@ class EditForm {
               padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
               child: ListView(
                 children: <Widget>[
-                  ...moduleController.getWidgets(isCreateForm),
+                  ...widgets,
                   SizedBox(
                       height: configuration.asFloat(
                           'form.gap.field_button.height',
@@ -40,12 +37,12 @@ class EditForm {
                     children: <Widget>[
                       FlatButton(
                         child: Text('Abbruch'),
-                        onPressed: moduleController.getOnPressed(
-                            'cancel', moduleController),
+                        onPressed: pageController.getOnPressed(
+                            'cancel', pageController),
                       ),
                       RaisedButtonBone(
                         'store',
-                        moduleController,
+                        pageController,
                         child: Text('Speichern'),
                       ),
                     ],
diff --git a/lib/src/widget/filter_fields.dart b/lib/src/widget/filter_fields.dart
deleted file mode 100644 (file)
index c9b1554..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-import 'package:dart_bones/dart_bones.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bones/flutter_bones.dart';
-import 'package:flutter_bones/src/widget/list_form.dart';
-
-import 'text_form_field_bone.dart';
-
-typedef FilterPredicate = bool Function(Map<String, dynamic> row);
-
-class FilterItem {
-  final String name;
-  final String label;
-  final FilterType filterType;
-  final String toolTip;
-  final FilterPredicate filterPredicate;
-  var value;
-
-  FilterItem({
-    this.label,
-    this.filterType,
-    this.toolTip,
-    this.name,
-    this.filterPredicate,
-  });
-
-  bool isValid(Map<String, dynamic> row) {
-    bool rc = true;
-    if (filterPredicate != null) {
-      rc = filterPredicate(row);
-    } else {
-      final current = row[name].toString();
-      final value2 = value ?? '';
-      switch (filterType) {
-        case FilterType.pattern:
-          rc = value2 == ''
-              ? true
-              : (value2.startsWith('*')
-                  ? current.contains(value2.replaceAll('*', ''))
-                  : current.startsWith(value2));
-          break;
-        case FilterType.dateFrom:
-          // TODO: Handle this case.
-          break;
-        case FilterType.dateTil:
-          // TODO: Handle this case.
-          break;
-        case FilterType.dateTimeFrom:
-          // TODO: Handle this case.
-          break;
-        case FilterType.dateTimeTil:
-          // TODO: Handle this case.
-          break;
-        default:
-          rc = true;
-          break;
-      }
-    }
-    return rc;
-  }
-}
-
-class FilterSet
-    implements
-        TextFormCallbackController,
-        ButtonCallbackController,
-        TableCallbackController {
-  var filters = <FilterItem>[];
-  final BaseLogger logger;
-  final GlobalKey globalKey;
-
-  State<StatefulWidget> parent;
-
-  FilterSet(this.globalKey, this.parent, this.logger);
-
-  FilterSet.ready(this.globalKey, this.parent, this.filters, this.logger);
-
-  void add(FilterItem item) => filters.add(item);
-
-  FilterItem byName(String name) =>
-      filters.firstWhere((element) => element.name == name, orElse: () => null);
-  @override
-  getOnChanged(String customString, TextFormCallbackController controller) {
-    return null;
-  }
-  @override
-  getOnEditingComplete(
-      String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnEditTap(String customString, TableCallbackController controller,
-      Map<String, dynamic> row) {
-    return null;
-  }
-
-  @override
-  getOnFieldSubmitted(
-      String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnHighlightChanged(
-      String customString, ButtonCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnLongPressed(String customString, ButtonCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnPressed(String customString, ButtonCallbackController controller) {
-    var rc;
-    if (customString == 'search') {
-      rc = () {
-        final key = globalKey as GlobalKey<FormState>;
-        if (key.currentState.validate()) {
-          key.currentState.save();
-          parent.setState(() => null);
-        }
-      };
-    }
-    return rc;
-  }
-
-  @override
-  getOnSaved(String customString, TextFormCallbackController controller) {
-    return (input) {
-      byName(customString).value = input;
-    };
-  }
-
-  @override
-  getOnTap(String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getValidator(String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  /// Returns a list of widgets for the filter fields.
-  List<Widget> getWidgets() {
-    final rc = filters.map((filter) {
-      Widget rc = TextFormFieldBone(
-        filter.name,
-        this,
-        decoration: InputDecoration(labelText: filter.label),
-      );
-      if (filter.toolTip != null) {
-        rc = Tooltip(message: filter.toolTip, child: rc);
-      }
-      return rc;
-    }).toList();
-    return rc;
-  }
-
-  /// Tests whether the [row] belongs to the result.
-  bool isValid(Map<String, dynamic> row) {
-    var rc = true;
-    for (var filter in filters) {
-      if (!filter.isValid(row)) {
-        rc = false;
-        break;
-      }
-    }
-    return rc;
-  }
-
-  dynamic valueOf(String name) => byName(name).value;
-}
diff --git a/lib/src/widget/filter_set.dart b/lib/src/widget/filter_set.dart
new file mode 100644 (file)
index 0000000..34a70f9
--- /dev/null
@@ -0,0 +1,108 @@
+import 'package:dart_bones/dart_bones.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_bones/src/widget/page_controller_bones.dart';
+
+import 'text_form_field_bone.dart';
+
+typedef FilterPredicate = bool Function(Map<String, dynamic> row);
+
+class FilterItem {
+  final String name;
+  final String label;
+  final FilterType filterType;
+  final String toolTip;
+  final FilterPredicate filterPredicate;
+  var value;
+
+  FilterItem({
+    this.label,
+    this.filterType,
+    this.toolTip,
+    this.name,
+    this.filterPredicate,
+  });
+
+  bool isValid(Map<String, dynamic> row) {
+    bool rc = true;
+    if (filterPredicate != null) {
+      rc = filterPredicate(row);
+    } else {
+      final current = row[name].toString();
+      final value2 = value ?? '';
+      switch (filterType) {
+        case FilterType.pattern:
+          rc = value2 == ''
+              ? true
+              : (value2.startsWith('*')
+                  ? current.contains(value2.replaceAll('*', ''))
+                  : current.startsWith(value2));
+          break;
+        case FilterType.dateFrom:
+          // TODO: Handle this case.
+          break;
+        case FilterType.dateTil:
+          // TODO: Handle this case.
+          break;
+        case FilterType.dateTimeFrom:
+          // TODO: Handle this case.
+          break;
+        case FilterType.dateTimeTil:
+          // TODO: Handle this case.
+          break;
+        default:
+          rc = true;
+          break;
+      }
+    }
+    return rc;
+  }
+}
+
+class FilterSet {
+  var filters = <FilterItem>[];
+  final BaseLogger logger;
+  final GlobalKey globalKey;
+  final PageControllerBones pageController;
+  State<StatefulWidget> parent;
+
+  FilterSet(this.globalKey, this.parent, this.pageController, this.logger);
+
+  FilterSet.ready(this.globalKey, this.parent, this.filters,
+      this.pageController, this.logger);
+
+  void add(FilterItem item) => filters.add(item);
+
+  FilterItem byName(String name) =>
+      filters.firstWhere((element) => element.name == name, orElse: () => null);
+
+  /// Returns a list of widgets for the filter fields.
+  List<Widget> getWidgets() {
+    final rc = filters.map((filter) {
+      Widget rc = TextFormFieldBone(
+        filter.name,
+        pageController,
+        decoration: InputDecoration(labelText: filter.label),
+      );
+      if (filter.toolTip != null) {
+        rc = Tooltip(message: filter.toolTip, child: rc);
+      }
+      return rc;
+    }).toList();
+    return rc;
+  }
+
+  /// Tests whether the [row] belongs to the result.
+  bool isValid(Map<String, dynamic> row) {
+    var rc = true;
+    for (var filter in filters) {
+      if (!filter.isValid(row)) {
+        rc = false;
+        break;
+      }
+    }
+    return rc;
+  }
+
+  dynamic valueOf(String name) => byName(name).value;
+}
index 2154deace83826e6922b538c535836493fb67770..99988891ddfac602856ab3c0f6aa22c15f9a082e 100644 (file)
@@ -1,7 +1,8 @@
 import 'package:dart_bones/dart_bones.dart';
 import 'package:flutter/material.dart';
-import 'filter_fields.dart';
+
 import '../helper/string_helper.dart';
+import 'filter_set.dart';
 
 typedef Function OnEditTap(Map<String, dynamic> row, int index);
 
@@ -77,18 +78,19 @@ class ListForm {
   /// [rows] is a list of rows normally delivered from a database query:
   /// each row is a map with (key, value) pairs.
   /// If [showEditItems] is true the edit icon is shown in the first column.
-  static Form listForm({@required Key key,
-    @required FilterSet filters,
-    @required List<Widget> buttons,
-    @required List<Widget> titles,
-    @required List<String> columnNames,
-    @required Iterable<dynamic> rows,
-    bool showEditIcon = false,
-    TableCallbackController tableCallbackController,
-    @required BaseConfiguration configuration,
-    String customString}) {
+  static Form listForm(
+      {@required Key key,
+      @required FilterSet filters,
+      @required List<Widget> buttons,
+      @required List<Widget> titles,
+      @required List<String> columnNames,
+      @required Iterable<dynamic> rows,
+      bool showEditIcon = false,
+      TableCallbackController tableCallbackController,
+      @required BaseConfiguration configuration,
+      String customString}) {
     final padding =
-    configuration.asFloat('form.card.padding', defaultValue: 16.0);
+        configuration.asFloat('form.card.padding', defaultValue: 16.0);
     return Form(
         key: key,
         child: Card(
diff --git a/lib/src/widget/module_controller.dart b/lib/src/widget/module_controller.dart
deleted file mode 100644 (file)
index b27c639..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_bones/flutter_bones.dart';
-import 'package:flutter_bones/src/widget/widget_list.dart';
-
-import '../model/model_types.dart';
-import '../model/module_model.dart';
-import '../model/table_model.dart';
-import 'raised_button_bone.dart';
-import 'text_form_field_bone.dart';
-import 'widget_helper.dart';
-
-// This interface allows the generic handling of the edit form by a model driven module.
-class ModuleController
-    implements TextFormCallbackController, ButtonCallbackController {
-  final ModuleModel moduleModel;
-  String primaryColumn;
-  WidgetList createWidgets;
-  WidgetList changeWidgets;
-  final modelsMap = <String, ModelBase>{};
-  final dataTypes = <String, DataType>{};
-  final Map values = <String, dynamic>{};
-  final GlobalKey<FormState> globalKey;
-
-  State parent;
-
-  ModuleController(this.globalKey, this.parent, this.moduleModel) {
-    createWidgets = WidgetList(
-        '${moduleModel.fullName()}.createWidgets', moduleModel.logger);
-    changeWidgets = WidgetList(
-        '${moduleModel.fullName()}.changeWidgets', moduleModel.logger);
-  }
-
-  ModuleModel getModuleModel() => moduleModel;
-
-  @override
-  getOnChanged(String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnEditingComplete(
-      String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnFieldSubmitted(
-      String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnHighlightChanged(
-      String customString, ButtonCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnLongPressed(String customString, ButtonCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnPressed(String customString, ButtonCallbackController controller) {
-    var rc;
-    if (customString == 'store') {
-      rc = () {
-        if (globalKey.currentState.validate()) {
-          globalKey.currentState.save();
-          moduleModel.logger.log('missing storage in onPressed');
-        }
-      };
-    }
-    return rc;
-  }
-
-  @override
-  getOnSaved(String customString, TextFormCallbackController controller) {
-    final rc = (input) {
-      values[customString] =
-          StringHelper.fromString(input, dataTypes[customString]);
-    };
-    return rc;
-  }
-
-  @override
-  getOnTap(String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getValidator(String customString, TextFormCallbackController controller) {
-    return null;
-  }
-
-  /// Returns the widgets with at least the input fields of the form
-  ///
-  List<Widget> getWidgets(bool isCreateForm) {
-    List<Widget> rc;
-    if (isCreateForm) {
-      if (createWidgets == null || createWidgets.widgets.length == 0) {
-        modelToWidgets(moduleModel.mainTable(), this, createWidgets);
-      }
-      rc = createWidgets.widgets;
-    } else {
-      if (changeWidgets.widgets.length == 0) {
-        modelToWidgets(moduleModel.mainTable(), this, changeWidgets);
-      }
-      rc = changeWidgets.widgets;
-    }
-    return rc;
-  }
-
-  /// Reads the [tableModel] and creates the [widgetList] with all relevant
-  /// input fields.
-  void modelToWidgets(TableModel tableModel, ModuleController controller,
-      WidgetList widgetList) {
-    for (var column in tableModel.columns) {
-      if (!column.hasOption('hidden')) {
-        if (column.hasOption('primary')) {
-          primaryColumn = column.name;
-        }
-        Widget widget;
-        modelsMap[column.name] = column;
-        dataTypes[column.name] = column.dataType;
-        switch (column.dataType) {
-          case DataType.bool:
-            widget = null;
-            break;
-          default:
-            widget = WidgetHelper.toolTip(
-                TextFormFieldBone(
-                  column.name,
-                  controller,
-                  decoration: InputDecoration(labelText: column.label),
-                ),
-                column.toolTip);
-            break;
-        }
-        if (widget != null) {
-          widgetList.addWidget(column.name, widget);
-        }
-      }
-    }
-  }
-}
diff --git a/lib/src/widget/page_controller_bones.dart b/lib/src/widget/page_controller_bones.dart
new file mode 100644 (file)
index 0000000..8087368
--- /dev/null
@@ -0,0 +1,210 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_bones/src/widget/widget_list.dart';
+
+import '../model/module_model.dart';
+import '../page/application_data.dart';
+import 'callback_controller_bones.dart';
+import 'filter_set.dart';
+import 'view.dart';
+
+// This interface allows the generic handling of the edit form by a model driven module.
+class PageControllerBones implements CallbackControllerBones {
+  final ModuleModel moduleModel;
+  String primaryColumn;
+  WidgetList widgetList;
+
+  //WidgetList changeWidgets;
+  final GlobalKey<FormState> globalKey;
+  final String pageName;
+  PageModel page;
+  final ApplicationData applicationData;
+  State parent;
+  final BuildContext context;
+
+  PageControllerBones(this.globalKey, this.parent, this.moduleModel,
+      this.pageName, this.context, this.applicationData) {}
+
+  /// Initializes the controller when the instance is complete constructed,
+  /// e.g. ModuleModel.parse() is called.
+  /// This method must be called early after the constructor.
+  void initialize() {
+    widgetList =
+        WidgetList('${moduleModel.fullName()}.widgets', moduleModel.logger);
+    page = moduleModel.pageByName(pageName);
+    page.fields.forEach((model) {
+      widgetList.addWidget(
+          model.name, View(moduleModel.logger).modelToWidget(model, this));
+    });
+  }
+
+  @override
+  List<String> comboboxTexts(
+      String customString, CallbackControllerBones controller) {
+    return [];
+  }
+
+  @override
+  List comboboxValues(String customString, CallbackControllerBones controller) {
+    return [];
+  }
+
+  FilterSet filterSet({@required String page}) {
+    final rc = FilterSet(globalKey, parent, this, moduleModel.logger);
+    moduleModel
+        .pageByName(page)
+        .fields
+        .where((element) => element.filterType != null)
+        .forEach((element) {
+      rc.add(FilterItem(
+          label: element.label,
+          filterType: element.filterType,
+          toolTip: element.toolTip,
+          name: element.name));
+    });
+    return rc;
+  }
+
+  @override
+  BuildContext getContext() {
+    return context;
+  }
+
+  @override
+  FieldModel getModel(String customString, CallbackControllerBones controller) {
+    final rc = moduleModel.pageByName(pageName)?.getField(customString);
+    return rc;
+  }
+
+  ModuleModel getModuleModel() => moduleModel;
+
+  @override
+  String getName() {
+    return null;
+  }
+
+  @override
+  getOnChanged(String customString, CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  getOnChangedCheckbox(
+      String customString, CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  getOnChangedCombobox<T>(
+      String customString, CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  getOnEditingComplete(
+      String customString, CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  getOnFieldSubmitted(String customString, CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  getOnHighlightChanged(String customString,
+      CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  getOnLongPressed(String customString, CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  getOnPressed(String customString, CallbackControllerBones controller) {
+    var rc;
+    if (customString == 'store') {
+      rc = () {
+        if (globalKey.currentState.validate()) {
+          globalKey.currentState.save();
+          final params = widgetList.buildSqlParams(this);
+          PageModelType pageType =
+              moduleModel
+                  .pageByName(pageName)
+                  .pageModelType;
+          switch (pageType) {
+            case PageModelType.create:
+              applicationData.persistence
+                  .insert(module: moduleModel.name, data: params);
+              break;
+            case PageModelType.change:
+              applicationData.persistence
+                  .update(module: moduleModel.name, data: params);
+              break;
+            default:
+              moduleModel.logger
+                  .error('unexpected pageType $pageType for $customString');
+              break;
+          }
+        }
+      };
+    } else if (customString == 'cancel') {
+      rc = () => Navigator.pop(controller.getContext());
+    }
+    return rc;
+  }
+
+  @override
+  getOnSaved(String customString, CallbackControllerBones controller) {
+    final rc = (input) {
+      final page = moduleModel.pageByName(pageName);
+      FieldModel model = page.getField(customString);
+      model.value = StringHelper.fromString(input, model.dataType);
+    };
+    return rc;
+  }
+
+  @override
+  getOnSelectedItemBuilder(String customString,
+      CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  getOnTap(String customString, CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  getOnValidator(String customString, CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  TextEditingController getTextEditingController(String customString,
+      CallbackControllerBones controller) {
+    return null;
+  }
+
+  @override
+  getValidator(String customString, CallbackControllerBones controller) {
+    return null;
+  }
+
+  /// Returns the widgets with at least the input fields of the form defined
+  /// in the page named [pageName].
+  ///
+  List<Widget> getWidgets() {
+    List<Widget> rc;
+    final page = moduleModel.pageByName(pageName);
+    rc = View(moduleModel.logger).modelsToWidgets(page.fields, this);
+    return rc ?? [];
+  }
+
+  @override
+  ApplicationData getApplicationData() {
+    return applicationData;
+  }
+}
index 64bd8e8ecf7b7cd5dc7a11e9a43e673a781d9284..6875e3c895784707a4f960f1c3496428e2595fdc 100644 (file)
@@ -1,23 +1,13 @@
 import 'package:flutter/material.dart';
 
-/// Interface for a button specific callback controller.
-abstract class ButtonCallbackController {
-  ValueChanged<bool> getOnHighlightChanged(
-      String customString, ButtonCallbackController controller);
-
-  VoidCallback getOnLongPressed(
-      String customString, ButtonCallbackController controller);
-
-  VoidCallback getOnPressed(
-      String customString, ButtonCallbackController controller);
-}
+import 'callback_controller_bones.dart';
 
 /// Implements a raised button with two additional properties:
 /// [customString] a string often used for a name needed in a callback method
 /// [customObject] an object known by the controller, often used in callback methods like onPressed
 class RaisedButtonBone extends RaisedButton {
   final String customString;
-  final ButtonCallbackController callbackController;
+  final CallbackControllerBones callbackController;
 
   RaisedButtonBone(this.customString, this.callbackController,
       {ButtonTextTheme textTheme,
index 09b228994c77235fccdf3162d70a5b303b95ec49..8322fbe7357af64dbe543881e6e002100612ffb5 100644 (file)
@@ -1,6 +1,8 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 
+import 'callback_controller_bones.dart';
+
 /// Interface for a [TextFormField] specific callback controller.
 abstract class TextFormCallbackController {
   ValueChanged<String> getOnChanged(
@@ -23,11 +25,11 @@ abstract class TextFormCallbackController {
 }
 
 /// Implements a [TextFormField] with "outsourced" callbacks:
-/// [customString] a string mostly used for a name needed in the [customController]
+/// [customString] a string mostly used for a name needed in the [callbackController].
 /// [callbackController] handles the callback methods.
 class TextFormFieldBone extends TextFormField {
   final String customString;
-  final TextFormCallbackController callbackController;
+  final CallbackControllerBones callbackController;
 
   TextFormFieldBone(this.customString,
       this.callbackController, {
@@ -47,16 +49,16 @@ class TextFormFieldBone extends TextFormField {
         bool autofocus = false,
         bool readOnly = false,
         ToolbarOptions toolbarOptions,
-    bool showCursor,
-    String obscuringCharacter = '•',
-    bool obscureText = false,
-    bool autocorrect = true,
-    SmartDashesType smartDashesType,
-    SmartQuotesType smartQuotesType,
-    bool enableSuggestions = true,
-    bool maxLengthEnforced = true,
-    int maxLines = 1,
-    int minLines,
+        bool showCursor,
+        String obscuringCharacter = '•',
+        bool obscureText = false,
+        bool autocorrect = true,
+        SmartDashesType smartDashesType,
+        SmartQuotesType smartQuotesType,
+        bool enableSuggestions = true,
+        bool maxLengthEnforced = true,
+        int maxLines = 1,
+        int minLines,
     bool expands = false,
     int maxLength,
     List<TextInputFormatter> inputFormatters,
index 24c2b2a1422a6edf275a726a33b91217900cdf6d..71f7d40dcc7fd0ea0736a229c407ca367d05a873 100644 (file)
@@ -1,13 +1,14 @@
 import 'package:dart_bones/dart_bones.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter_bones/src/controller/button_controller.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_bones/src/widget/callback_controller_bones.dart';
+import 'package:flutter_bones/src/widget/checkbox_list_tile_bone.dart';
 
-import '../controller/combobox_controller.dart';
 import '../helper/settings.dart';
+import '../model/db_reference_model.dart';
 import '../model/empty_line_model.dart';
 import '../model/field_model.dart';
 import '../model/section_model.dart';
-import '../model/text_field_model.dart';
 import '../model/text_model.dart';
 import '../model/widget_model.dart';
 import 'dropdown_button_form_bone.dart';
@@ -32,46 +33,63 @@ class View {
   View.internal(this.logger);
 
   /// Creates a button from the [controller].
-  Widget button(ButtonController controller) {
+  Widget button(ButtonModel model, CallbackControllerBones controller) {
     final rc = RaisedButtonBone(
-      controller.getName(),
+      model.name,
       controller,
-      child: Text(controller.model.text),
+      child: Text(model.text),
     );
     return rc;
   }
 
   /// Creates a list of buttons from a list of [controllers].
-  List<Widget> buttonList(List<ButtonController> controllers) {
+  List<Widget> buttonList(
+      List<ButtonModel> buttonModels, CallbackControllerBones controller) {
     final rc = <Widget>[];
-    for (var item in controllers) {
-      rc.add(button(item));
+    for (var model in buttonModels) {
+      rc.add(button(model, controller));
     }
     return rc;
   }
 
   /// Creates a combobox via the [controller].
-  Widget combobox<T>(ComboboxController controller, onTap) {
-    final texts = controller.texts();
-    final values = controller.values() ?? texts;
+  Widget combobox<T>(FieldModel model, CallbackControllerBones controller) {
+    final texts = controller.comboboxTexts(model.name, controller);
+    final values = controller.comboboxValues(model.name, controller) ?? texts;
     final items = <DropdownMenuItem<T>>[];
     for (var ix = 0; ix < texts.length; ix++) {
       items.add(DropdownMenuItem(
-          onTap: onTap, value: values[ix], child: Text(texts[ix])));
+          onTap: controller.getOnTap(model.name, controller),
+          value: values[ix],
+          child: Text(texts[ix])));
     }
-    final rc =
-        DropdownButtonFormBone(controller.getName(), controller, items: items);
+    final rc = DropdownButtonFormBone(model.name, controller, items: items);
     return rc;
   }
 
   /// Creates a checkbox from the [model].
-  Widget checkbox(WidgetModel model) {
-    var rc;
+  Widget checkbox(FieldModel model, CallbackControllerBones controller) {
+    final tristate = model.hasOption('undef');
+    final rc = toolTip(
+        CheckboxListTileBone(model.name, controller,
+            value: tristate ? model.value : model.value ?? false,
+            tristate: tristate,
+            title: Text(model.label),
+            selected: model.value ?? false),
+        model);
     return rc;
   }
 
-  Widget dbReference(WidgetModel child) {
+  Widget dbReference(
+      DbReferenceModel model, CallbackControllerBones controller) {
     var rc;
+    if (model.dataType == DataType.bool) {
+      rc = checkbox(model, controller);
+    } else if (model.hasOption('combobox')) {
+      rc = combobox(model, controller);
+    } else {
+      rc = textField(model, controller);
+    }
     return rc;
   }
 
@@ -95,13 +113,13 @@ class View {
 
   /// Returns a form with the properties given by the [model]
   /// [formKey] identifies the form. Used for form validation and saving.
-  Form simpleForm({SectionModel model, Key formKey}) {
+  Form simpleForm(
+      {SectionModel model, CallbackControllerBones controller, Key formKey}) {
     assert(formKey != null);
     final padding =
-        widgetConfiguration.asFloat('form.card.padding', defaultValue: 16.0);
-    final children = widgetsOfSection(model.children);
-    final controllers = model.buttons.map((button) => ButtonController(button));
-    final buttons = buttonList(controllers);
+    widgetConfiguration.asFloat('form.card.padding', defaultValue: 16.0);
+    final children = modelsToWidgets(model.children, controller);
+    final buttons = buttonList(model.buttons, controller);
     final rc = Form(
         key: formKey,
         child: Card(
@@ -127,12 +145,12 @@ class View {
   }
 
   /// Creates a form text field from the [model].
-  Widget textField(TextFieldModel model) {
+  Widget textField(FieldModel model, CallbackControllerBones controller) {
     var rc = toolTip(
-        TextFormField(
+        TextFormFieldBone(
+          model.name, controller,
           //validator: model.validator,
           decoration: InputDecoration(labelText: model.label),
-          onSaved: (input) => model.value(input),
         ),
         model);
     return rc;
@@ -151,44 +169,57 @@ class View {
     return rc;
   }
 
-  List<Widget> widgetsOfSection(List<WidgetModel> children) {
+  /// Converts a list of [models] into a list of [Widget]s.
+  List<Widget> modelsToWidgets(List<WidgetModel> models,
+      CallbackControllerBones controller) {
     final rc = <Widget>[];
-    for (var child in children) {
-      switch (child.widgetModelType) {
-        case WidgetModelType.textField:
-          rc.add(textField(child));
-          break;
-        case WidgetModelType.button:
-          rc.add(button(ButtonController(child)));
-          break;
-        case WidgetModelType.emptyLine:
-          rc.add(emptyLine(child));
-          break;
-        case WidgetModelType.text:
-          rc.add(text(child));
-          break;
-        case WidgetModelType.checkbox:
-          rc.add(checkbox(child));
-          break;
-        case WidgetModelType.combobox:
-          rc.add(text(child));
-          break;
-        case WidgetModelType.image:
-          rc.add(image(child));
-          break;
-        case WidgetModelType.section:
-          rc.add(section(child));
-          break;
-        case WidgetModelType.dbReference:
-          rc.add(dbReference(child));
-          break;
-        case WidgetModelType.table:
-        case WidgetModelType.column:
-        case WidgetModelType.allDbFields:
-          logger.error('not allowed in section: ${child.fullName()}');
-          break;
+    for (var model in models) {
+      final widget = modelToWidget(model, controller);
+      if (widget != null) {
+        rc.add(widget);
       }
     }
     return rc;
   }
+
+  /// Converts a [model] into a [Widget].
+  Widget modelToWidget(WidgetModel model, CallbackControllerBones controller) {
+    Widget rc;
+    switch (model.widgetModelType) {
+      case WidgetModelType.textField:
+        rc = textField(model, controller);
+        break;
+      case WidgetModelType.button:
+        rc = button(model, controller);
+        break;
+      case WidgetModelType.emptyLine:
+        rc = emptyLine(model);
+        break;
+      case WidgetModelType.text:
+        rc = text(model);
+        break;
+      case WidgetModelType.checkbox:
+        rc = checkbox(model, controller);
+        break;
+      case WidgetModelType.combobox:
+        rc = text(model);
+        break;
+      case WidgetModelType.image:
+        rc = image(model);
+        break;
+      case WidgetModelType.section:
+        rc = section(model);
+        break;
+      case WidgetModelType.dbReference:
+        rc = dbReference(model, controller);
+        break;
+      case WidgetModelType.allDbFields:
+        break;
+      case WidgetModelType.table:
+      case WidgetModelType.column:
+        logger.error('not allowed in section: ${model.fullName()}');
+        break;
+    }
+    return rc;
+  }
 }
index 9ba2514d734b28c2e6fb33b62b290ee09bb78dc2..1957edda162a62e7895a54096b2a7b5f548f3b2e 100644 (file)
@@ -1,5 +1,12 @@
 import 'package:dart_bones/dart_bones.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+
+import '../helper/string_helper.dart';
+import '../model/model_types.dart';
+import 'dropdown_button_form_bone.dart';
+import 'page_controller_bones.dart';
+import 'text_form_field_bone.dart';
 
 /// Manages a list of named widgets.
 class WidgetList {
@@ -10,6 +17,9 @@ class WidgetList {
 
   WidgetList(this.name, this.logger);
 
+  /// Tests whether the widget list is empty.
+  bool get isEmpty => widgets.isEmpty;
+
   /// Adds a [widget] to the [widgets] behind the position of [predecessor].
   /// If [predecessor] is null the widget will be the first in [widgets]
   void addSuccessorOf(String predecessor, String name, Widget widget) {
@@ -37,6 +47,38 @@ class WidgetList {
     }
   }
 
+  /// Builds the SQL parameters of all members of the [widgets].
+  Map<String, String> buildSqlParams(PageControllerBones controller) {
+    final rc = <String, String>{};
+    final page = controller.moduleModel.pageByName(controller.pageName);
+    final isCreatePage = page.pageModelType == PageModelType.create;
+    widgets.forEach((element) {
+      dynamic value;
+      DataType dataType;
+      String name;
+      if (element is TextFormFieldBone) {
+        name = element.customString;
+        final model = page.getField(name, required: true);
+        value = model?.value;
+        dataType = model?.dataType;
+      } else if (element is DropdownButtonFormBone) {
+        name = element.customString;
+        final model = page.getField(name, required: true);
+        value = model?.value;
+        dataType = model?.dataType;
+      }
+      if (name != null && dataType != null) {
+        rc[':$name'] = StringHelper.asDatabaseString(value, dataType);
+      }
+    });
+    final name = controller.moduleModel.mainTable().name +
+        (isCreatePage ? '_createdby' : '_changedby');
+    if (!rc.containsKey(name)) {
+      rc[':$name'] = controller.getApplicationData().currentUser;
+    }
+    return rc;
+  }
+
   /// Returns the widget given by [name] or null if not found.
   Widget byName(String name) {
     final rc = widgetMap.containsKey(name) ? widgetMap[name] : null;
@@ -45,4 +87,10 @@ class WidgetList {
     }
     return rc;
   }
+
+  /// Remove all widgets from the list.
+  void clear() {
+    widgets.clear();
+    widgetMap.clear();
+  }
 }
index dda85c856ab6e2004cab12bb04263c9e7acf8013..efd22531f8f22e704cb1e4cd03addfeb092e2785 100644 (file)
@@ -14,7 +14,7 @@ void main() {
   setUpAll(() => BaseSettings.setLocaleByNames(language: 'en'));
   group('Settings', () {
     test('basic', () {
-      BaseSettings.setLocale(Locale('de', 'DE'));
+      BaseSettings.setLocale(SettingLocale(languageCode: 'de', countryCode: 'DE'));
       expect(BaseSettings.language, 'de');
       expect(BaseSettings.translate('abc', map), equals('abc'));
       expect(BaseSettings.translate('help', map), equals('Hilfe'));
index 0985f7162c68d0b9447b863c6ec9b67c3bea1723..e20269fba1635dbb9b88175ef0ce5e0d108589d3 100644 (file)
@@ -91,6 +91,19 @@ void main() {
               sortable: false, withSeconds: false),
           equals('4.3.2020 09:33'));
     });
+    test('asDatabaseString', () {
+      expect(StringHelper.asDatabaseString('abc', DataType.string), equals('abc'));
+      expect(StringHelper.asDatabaseString(345, DataType.int), equals('345'));
+      expect(StringHelper.asDatabaseString(34.5, DataType.float), equals('34.5'));
+      expect(StringHelper.asDatabaseString(true, DataType.bool), equals('T'));
+      expect(StringHelper.asDatabaseString(false, DataType.bool), equals('F'));
+      expect(StringHelper.asDatabaseString(null, DataType.bool), equals('NULL'));
+      expect(StringHelper.asDatabaseString(null, DataType.currency), equals('NULL'));
+      expect(StringHelper.asDatabaseString(DateTime(2020, 3, 4, 9, 33, 44), DataType.dateTime),
+          equals('2020-03-04 09:33:44'));
+      expect(StringHelper.asDatabaseString(DateTime(2020, 3, 4, 9, 33, 44), DataType.date),
+          equals('2020-03-04'));
+    });
   });
   group("diverse", () {
     test('splitArgs', () {
index 76ad5e60bbc2ab4d752db66b1b341a391edbbcbc..ce5d8026ec922ac9464b4f9fb8b9921bfa31fa4f 100644 (file)
@@ -25,17 +25,17 @@ void main() {
     column user_id: DataType.int "Id" options: primary notnull unique
     column user_name: DataType.string "User" options: unique
     column user_role: DataType.reference "Role" options: 
-    column user_createdat: DataType.dateTime "Erzeugt" options: hidden null doStore
-    column user_createdby: DataType.string "Erzeugt von" options: hidden doStore
-    column user_changedat: DataType.dateTime "Geändert" options: hidden null doStore
-    column user_changedby: DataType.string "Geändert von" options: hidden doStore
+    column user_createdat: DataType.dateTime "Erzeugt" options: hidden null
+    column user_createdby: DataType.string "Erzeugt von" options: hidden
+    column user_changedat: DataType.dateTime "Geändert" options: hidden null
+    column user_changedby: DataType.string "Geändert von" options: hidden
 == table role: options: 
     column role_id: DataType.int "Id" options: primary notnull unique
     column role_name: DataType.string "Role" options: unique
-    column role_createdat: DataType.dateTime "Erzeugt" options: hidden null doStore
-    column role_createdby: DataType.string "Erzeugt von" options: hidden doStore
-    column role_changedat: DataType.dateTime "Geändert" options: hidden null doStore
-    column role_changedby: DataType.string "Geändert von" options: hidden doStore
+    column role_createdat: DataType.dateTime "Erzeugt" options: hidden null
+    column role_createdby: DataType.string "Erzeugt von" options: hidden
+    column role_changedat: DataType.dateTime "Geändert" options: hidden null
+    column role_changedby: DataType.string "Geändert von" options: hidden
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
     textField user: options: required unique
index a9bc06792051b2ea30a01691be5b57f5ab87c49f..0301cd1744bd900f39b3684064f30b668a47701b 100644 (file)
@@ -18,11 +18,23 @@ void main() {
       expect(module.fullName(), equals('demo1'));
       final dump = module.dump(StringBuffer()).toString();
       expect(dump, '''= module demo1: options: 
+== table user: options: 
+    column user_id: DataType.int "Id" options: primary notnull unique
+    column user_name: DataType.string "User" options: unique notnull
+    column user_role: DataType.reference "Role" options: 
+    column user_createdat: DataType.dateTime "Erzeugt" options: hidden null
+    column user_createdby: DataType.string "Erzeugt von" options: hidden
+    column user_changedat: DataType.dateTime "Geändert" options: hidden null
+    column user_changedby: DataType.string "Geändert von" options: hidden
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
     textField user: options: required unique
     button buttonStore: text: options: null 
     ] # create.simpleForm1
+== page change: PageModelType.change options: 
+    = section simpleForm1: SectionModelType.simpleForm options:  [
+    allDbFields 12 options: 
+    ] # change.simpleForm1
 ''');
       final userField = page.getField('user');
       expect(userField, isNotNull);
@@ -136,11 +148,23 @@ void main() {
       expect(checkbox.dataType, DataType.bool);
       final dump = module.dump(StringBuffer()).toString();
       expect(dump, equals('''= module demo1: options: 
+== table user: options: 
+    column user_id: DataType.int "Id" options: primary notnull unique
+    column user_name: DataType.string "User" options: unique notnull
+    column user_role: DataType.reference "Role" options: 
+    column user_createdat: DataType.dateTime "Erzeugt" options: hidden null
+    column user_createdby: DataType.string "Erzeugt von" options: hidden
+    column user_changedat: DataType.dateTime "Geändert" options: hidden null
+    column user_changedby: DataType.string "Geändert von" options: hidden
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
     checkbox hidden: text: options: required
     button buttonStore: text: options: null 
     ] # create.simpleForm1
+== page change: PageModelType.change options: 
+    = section simpleForm1: SectionModelType.simpleForm options:  [
+    allDbFields 102 options: 
+    ] # change.simpleForm1
 '''));
     });
   });
@@ -167,14 +191,42 @@ void main() {
       expect(combobox.dataType, DataType.int);
       final dump = module.dump(StringBuffer()).toString();
       expect(dump, equals('''= module demo1: options: 
+== table user: options: 
+    column user_id: DataType.int "Id" options: primary notnull unique
+    column user_name: DataType.string "User" options: unique notnull
+    column user_role: DataType.reference "Role" options: 
+    column user_createdat: DataType.dateTime "Erzeugt" options: hidden null
+    column user_createdby: DataType.string "Erzeugt von" options: hidden
+    column user_changedat: DataType.dateTime "Geändert" options: hidden null
+    column user_changedby: DataType.string "Geändert von" options: hidden
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
     combobox class: texts: bad OK good options: undef
     button buttonStore: text: options: null 
     ] # create.simpleForm1
+== page change: PageModelType.change options: 
+    = section simpleForm1: SectionModelType.simpleForm options:  [
+    allDbFields 117 options: 
+    ] # change.simpleForm1
 '''));
     });
   });
+  group('allDbFields', (){
+    test('allDbFields', (){
+      logger.clear();
+      final map = cloneOfMap(userModel);
+      var module = Demo1(map, logger);
+      module.parse();
+      var errors = logger.errors;
+      expect(errors.length, equals(0));
+      final page = module.pageByName('change');
+      expect(page, isNotNull);
+      expect(page.fields.length, equals(3));
+      expect(page.hasField('user_id'), isTrue);
+      expect(page.hasField('user_name'), isTrue);
+      expect(page.hasField('user_role'), isTrue);
+    });
+  });
   group('Non field widgets', () {
     test('basic', () {
       logger.clear();
@@ -211,6 +263,33 @@ void main() {
 
 final userModel = <String, dynamic>{
   'module': 'demo1',
+  'tables': [
+    {
+      'table': 'user',
+      'columns': [
+        {
+          'column': 'user_id',
+          'dataType': 'int',
+          'label': 'Id',
+          'options': 'primary',
+        },
+        {
+          'column': 'user_name',
+          'dataType': 'string',
+          'label': 'User',
+          'size': 64,
+          'options': 'unique;notnull',
+        },
+        {
+          'column': 'user_role',
+          'dataType': 'reference',
+          'label': 'Role',
+          'foreignKey': 'role.role_id',
+          'widgetType': 'combobox',
+        },
+      ]
+    },
+  ],
   'pages': [
     {
       'page': 'create',
@@ -231,9 +310,23 @@ final userModel = <String, dynamic>{
               'label': 'Save',
             },
           ]
-        }
+        },
       ]
     },
+    {
+      'page': 'change',
+      'pageType': 'change',
+      'sections': [
+        {
+          'sectionType': 'simpleForm',
+          'children': [
+            {
+              'widgetType': 'allDbFields',
+            },
+          ],
+        },
+      ],
+    },
   ],
 };
 
index 27dd8b197d8a7f36d47d15d98202c0f1ccc612cc..e0cd2159805b0b7dd2a85793b7ca0692e23fc785 100644 (file)
@@ -20,17 +20,17 @@ void main() {
     column role_name: DataType.string "Rolle" options: unique notnull
     column role_priority: DataType.int "Priorität" options: 
     column role_active: DataType.bool "Aktiv" options: 
-    column role_createdat: DataType.dateTime "Erzeugt" options: hidden null doStore
-    column role_createdby: DataType.string "Erzeugt von" options: hidden doStore
-    column role_changedat: DataType.dateTime "Geändert" options: hidden null doStore
-    column role_changedby: DataType.string "Geändert von" options: hidden doStore
+    column role_createdat: DataType.dateTime "Erzeugt" options: hidden null
+    column role_createdby: DataType.string "Erzeugt von" options: hidden
+    column role_changedat: DataType.dateTime "Geändert" options: hidden null
+    column role_changedby: DataType.string "Geändert von" options: hidden
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
-    allDbFields 10 text: options: 
+    allDbFields 10 options: 
     ] # create.simpleForm1
 == page change: PageModelType.change options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
-    allDbFields 12 text: options: 
+    allDbFields 13 options: 
     ] # change.simpleForm1
 == page list: PageModelType.list options: 
     = section filterPanel1: SectionModelType.filterPanel options:  [
@@ -55,17 +55,17 @@ void main() {
     column user_email: DataType.string "EMail" options: unique
     column user_password: DataType.string "User" options: password
     column user_role: DataType.reference "Role" options: 
-    column user_createdat: DataType.dateTime "Erzeugt" options: hidden null doStore
-    column user_createdby: DataType.string "Erzeugt von" options: hidden doStore
-    column user_changedat: DataType.dateTime "Geändert" options: hidden null doStore
-    column user_changedby: DataType.string "Geändert von" options: hidden doStore
+    column user_createdat: DataType.dateTime "Erzeugt" options: hidden null
+    column user_createdby: DataType.string "Erzeugt von" options: hidden
+    column user_changedat: DataType.dateTime "Geändert" options: hidden null
+    column user_changedby: DataType.string "Geändert von" options: hidden
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
-    allDbFields 26 text: options: 
+    allDbFields 31 options: 
     ] # create.simpleForm1
 == page change: PageModelType.change options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
-    allDbFields 28 text: options: 
+    allDbFields 34 options: 
     ] # change.simpleForm1
 == page list: PageModelType.list options: 
     = section filterPanel1: SectionModelType.filterPanel options:  [
@@ -92,13 +92,13 @@ void main() {
     column configuration_type: DataType.string "Datentyp" options: 
     column configuration_value: DataType.string "Wert" options: 
     column configuration_description: DataType.string "Beschreibung" options: 
-    column configuration_createdat: DataType.dateTime "Erzeugt" options: hidden null doStore
-    column configuration_createdby: DataType.string "Erzeugt von" options: hidden doStore
-    column configuration_changedat: DataType.dateTime "Geändert" options: hidden null doStore
-    column configuration_changedby: DataType.string "Geändert von" options: hidden doStore
+    column configuration_createdat: DataType.dateTime "Erzeugt" options: hidden null
+    column configuration_createdby: DataType.string "Erzeugt von" options: hidden
+    column configuration_changedat: DataType.dateTime "Geändert" options: hidden null
+    column configuration_changedby: DataType.string "Geändert von" options: hidden
 == page create: PageModelType.create options: 
     = section simpleForm1: SectionModelType.simpleForm options:  [
-    allDbFields '''));
+    allDbFields'''));
       /*
       expect(dump.contains('''text: options:
     ] # create.simpleForm1
index 9b3c4ecdf548f989bc2d339e9dbf073d7618404d..2e35b4bbbe1c83853320ae83286ff12a103267fb 100644 (file)
@@ -26,16 +26,16 @@ void main() async {
   group('queries', () {
     test('list', () async {
       final rest = RestPersistence();
-      final list = await rest.list(
-          module: 'role', sqlName: 'list', params: {':role_name': 'A%'});
+      final list = await rest
+          .list(module: 'role', sqlName: 'list', params: {':role_name': 'A%'});
       expect(list, isNotNull);
       expect(list.length, equals(1));
       expect(list[0].containsKey('role_id'), isTrue);
     });
     test('list-all', () async {
       final rest = RestPersistence();
-      final list = await rest.list(
-          module: 'role', sqlName: 'list', params: {':role_name': '%'});
+      final list = await rest
+          .list(module: 'role', sqlName: 'list', params: {':role_name': '%'});
       expect(list, isNotNull);
       expect(list.length, greaterThanOrEqualTo(4));
       expect(list[0].containsKey('role_id'), isTrue);
@@ -66,8 +66,7 @@ void main() async {
         ':role_createdby': 'joe'
       });
       expect(id is int, isTrue);
-      final answer2 =
-          await rest.update(module: 'role', sqlName: 'update', data: {
+      await rest.update(module: 'role', sqlName: 'update', data: {
         ':role_id': id,
         ':role_name': 'dummy2',
         ':role_priority': 112,
index 5219b264b8431a9b4226f22ab62070f0b8292129..ed13de4416bc2f9336ddbf11e36dd3a7d3327230 100644 (file)
@@ -4,13 +4,11 @@
 // utility that Flutter provides. For example, you can send tap and scroll
 // gestures. You can also use WidgetTester to find child widgets in the widget
 // tree, read text, and verify that the values of widget properties are correct.
-
 import 'package:dart_bones/dart_bones.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/src/foundation/diagnostics.dart';
 import 'package:flutter/src/widgets/framework.dart' as x;
 import 'package:flutter_bones/flutter_bones.dart';
-import 'package:flutter_bones/src/private/bsettings.dart';
 import 'package:flutter_bones/src/widget/widget_helper.dart';
 import 'package:test/test.dart';
 
@@ -28,6 +26,7 @@ void main() {
       expect(toolTip.child, equals(widget));
     });
   });
+  /*
   group('ModuleController', () {
     test('basic', () {
       ApplicationData pageData = ApplicationData(
@@ -44,6 +43,7 @@ void main() {
       expect(widgets.length, equals(3));
     });
   });
+  */
 }
 
 class MyContext extends BuildContext {