From 5ec60c61428f52d3d5bb63f02384fc74e7d6121d Mon Sep 17 00:00:00 2001 From: Hamatoma Date: Wed, 21 Oct 2020 14:05:31 +0200 Subject: [PATCH] daily work: role.create works --- CreateModule | 21 ++ lib/app.dart | 10 +- ...ontroller.dart => base_controller.dart.01} | 0 ...troller.dart => button_controller.dart.01} | 9 +- ...oller.dart => combobox_controller.dart.01} | 0 ...ller.dart => text_form_controller.dart.01} | 0 lib/src/helper/settings.dart | 1 + lib/src/helper/string_helper.dart | 23 ++ lib/src/model/all_db_fields_model.dart | 21 +- lib/src/model/combobox_model.dart | 2 +- lib/src/model/db_reference_model.dart | 27 ++- lib/src/model/field_model.dart | 21 ++ lib/src/model/module_model.dart | 25 ++- lib/src/model/page_model.dart | 27 ++- lib/src/model/section_model.dart | 7 +- lib/src/model/standard/role_model.dart | 4 +- lib/src/page/application_data.dart | 10 +- .../configuration_change_page.dart | 30 +-- .../configuration_controller.dart | 21 +- .../configuration_create_page.dart | 23 +- .../configuration_list_page.dart | 118 +++++----- lib/src/page/role/role_change_page.dart | 29 +-- lib/src/page/role/role_controller.dart | 34 ++- lib/src/page/role/role_create_page.dart | 25 ++- lib/src/page/role/role_list_page.dart | 51 +++-- lib/src/page/user/user_change_page.dart | 29 +-- lib/src/page/user/user_controller.dart | 17 +- lib/src/page/user/user_create_page.dart | 22 +- lib/src/page/user/user_list_page.dart | 113 ++++------ lib/src/page/user_page.dart | 5 +- lib/src/persistence/persistence.dart | 10 +- lib/src/persistence/rest_persistence.dart | 29 +-- lib/src/private/bdrawer.dart | 2 + lib/src/private/bsettings.dart | 13 +- lib/src/widget/callback_controller_bones.dart | 60 +++++ lib/src/widget/checkbox_list_tile_bone.dart | 50 +++++ lib/src/widget/dropdown_button_form_bone.dart | 66 ++---- lib/src/widget/edit_form.dart | 21 +- .../{filter_fields.dart => filter_set.dart} | 81 +------ lib/src/widget/list_form.dart | 26 ++- lib/src/widget/module_controller.dart | 146 ------------ lib/src/widget/page_controller_bones.dart | 210 ++++++++++++++++++ lib/src/widget/raised_button_bone.dart | 14 +- lib/src/widget/text_form_field_bone.dart | 26 ++- lib/src/widget/view.dart | 153 ++++++++----- lib/src/widget/widget_list.dart | 48 ++++ test/helpers/settings_test.dart | 2 +- test/helpers/string_helper_test.dart | 13 ++ test/model/db_model_test.dart | 16 +- test/model/model_test.dart | 95 +++++++- test/model/standard_test.dart | 34 +-- test/rest_persistence_test.dart | 11 +- test/widget/widget_test.dart | 4 +- 53 files changed, 1120 insertions(+), 735 deletions(-) create mode 100755 CreateModule rename lib/src/controller/{base_controller.dart => base_controller.dart.01} (100%) rename lib/src/controller/{button_controller.dart => button_controller.dart.01} (83%) rename lib/src/controller/{combobox_controller.dart => combobox_controller.dart.01} (100%) rename lib/src/controller/{text_form_controller.dart => text_form_controller.dart.01} (100%) create mode 100644 lib/src/widget/callback_controller_bones.dart create mode 100644 lib/src/widget/checkbox_list_tile_bone.dart rename lib/src/widget/{filter_fields.dart => filter_set.dart} (57%) delete mode 100644 lib/src/widget/module_controller.dart create mode 100644 lib/src/widget/page_controller_bones.dart diff --git a/CreateModule b/CreateModule new file mode 100755 index 0000000..baf291f --- /dev/null +++ b/CreateModule @@ -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 diff --git a/lib/app.dart b/lib/app.dart index 51fc23d..6c5f4a3 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -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 { primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), - initialRoute: '/role/list', + initialRoute: '/role/create', onGenerateRoute: _getRoute, ); } @@ -58,7 +52,7 @@ Route _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.01 similarity index 100% rename from lib/src/controller/base_controller.dart rename to lib/src/controller/base_controller.dart.01 diff --git a/lib/src/controller/button_controller.dart b/lib/src/controller/button_controller.dart.01 similarity index 83% rename from lib/src/controller/button_controller.dart rename to lib/src/controller/button_controller.dart.01 index 7d5347c..e608ab7 100644 --- a/lib/src/controller/button_controller.dart +++ b/lib/src/controller/button_controller.dart.01 @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:flutter_bones/flutter_bones.dart'; import 'package:flutter_bones/src/controller/base_controller.dart'; @@ -20,8 +21,7 @@ class ButtonController extends BaseController } @override - getOnHighlightChanged( - String customString, ButtonCallbackController controller) { + getOnHighlightChanged(String customString, ButtonCallbackController controller) { return null; } @@ -45,4 +45,9 @@ class ButtonController extends BaseController } return rc; } + + @override + BuildContext getContext() { + return null; + } } diff --git a/lib/src/controller/combobox_controller.dart b/lib/src/controller/combobox_controller.dart.01 similarity index 100% rename from lib/src/controller/combobox_controller.dart rename to lib/src/controller/combobox_controller.dart.01 diff --git a/lib/src/controller/text_form_controller.dart b/lib/src/controller/text_form_controller.dart.01 similarity index 100% rename from lib/src/controller/text_form_controller.dart rename to lib/src/controller/text_form_controller.dart.01 diff --git a/lib/src/helper/settings.dart b/lib/src/helper/settings.dart index 7e814f6..dfe23ef 100644 --- a/lib/src/helper/settings.dart +++ b/lib/src/helper/settings.dart @@ -10,6 +10,7 @@ class SettingLocale { String countryCode; SettingLocale({this.languageCode, this.countryCode}); } + class BaseSettings { static var locale = SettingLocale(countryCode: 'US', languageCode: 'en'); diff --git a/lib/src/helper/string_helper.dart b/lib/src/helper/string_helper.dart index a2cca4e..298f70c 100644 --- a/lib/src/helper/string_helper.dart +++ b/lib/src/helper/string_helper.dart @@ -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; + } } diff --git a/lib/src/model/all_db_fields_model.dart b/lib/src/model/all_db_fields_model.dart index 72128b3..361395d 100644 --- a/lib/src/model/all_db_fields_model.dart +++ b/lib/src/model/all_db_fields_model.dart @@ -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); + } + }); + } } diff --git a/lib/src/model/combobox_model.dart b/lib/src/model/combobox_model.dart index 159bd3c..5a3dc47 100644 --- a/lib/src/model/combobox_model.dart +++ b/lib/src/model/combobox_model.dart @@ -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); diff --git a/lib/src/model/db_reference_model.dart b/lib/src/model/db_reference_model.dart index 83f8081..f93faba 100644 --- a/lib/src/model/db_reference_model.dart +++ b/lib/src/model/db_reference_model.dart @@ -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 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) { diff --git a/lib/src/model/field_model.dart b/lib/src/model/field_model.dart index a833b02..d388646 100644 --- a/lib/src/model/field_model.dart +++ b/lib/src/model/field_model.dart @@ -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 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. diff --git a/lib/src/model/module_model.dart b/lib/src/model/module_model.dart index e93e6e5..13acc33 100644 --- a/lib/src/model/module_model.dart +++ b/lib/src/model/module_model.dart @@ -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; } diff --git a/lib/src/model/page_model.dart b/lib/src/model/page_model.dart index cc1fc91..ccd7475 100644 --- a/lib/src/model/page_model.dart +++ b/lib/src/model/page_model.dart @@ -18,7 +18,7 @@ class PageModel extends ModelBase { final List sections = []; PageModelType pageModelType; List options; - final fields = {}; + final fields = []; final buttons = {}; final widgets = []; @@ -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; } diff --git a/lib/src/model/section_model.dart b/lib/src/model/section_model.dart index 05f49f6..0e06a88 100644 --- a/lib/src/model/section_model.dart +++ b/lib/src/model/section_model.dart @@ -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: diff --git a/lib/src/model/standard/role_model.dart b/lib/src/model/standard/role_model.dart index 1d3e283..32af803 100644 --- a/lib/src/model/standard/role_model.dart +++ b/lib/src/model/standard/role_model.dart @@ -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*'" } ] } diff --git a/lib/src/page/application_data.dart b/lib/src/page/application_data.dart index 3cc7b5c..99b1dc1 100644 --- a/lib/src/page/application_data.dart +++ b/lib/src/page/application_data.dart @@ -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; + } } diff --git a/lib/src/page/configuration/configuration_change_page.dart b/lib/src/page/configuration/configuration_change_page.dart index eff1594..55c0e60 100644 --- a/lib/src/page/configuration/configuration_change_page.dart +++ b/lib/src/page/configuration/configuration_change_page.dart @@ -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 { - final ApplicationData pageData; + final ApplicationData applicationData; - final GlobalKey _formKey = GlobalKey(debugLabel: 'configuration'); + final GlobalKey _formKey = + GlobalKey(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, )); } } diff --git a/lib/src/page/configuration/configuration_controller.dart b/lib/src/page/configuration/configuration_controller.dart index 2645369..9049089 100644 --- a/lib/src/page/configuration/configuration_controller.dart +++ b/lib/src/page/configuration/configuration_controller.dart @@ -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 formKey, State parent) - : super(formKey, parent, ConfigurationModel(Settings().logger)) { +class ConfigurationController extends PageControllerBones { + /// Controller for a page named [pageName]. + ConfigurationController( + GlobalKey formKey, + State parent, + String pageName, + BuildContext context, + ApplicationData applicationData) + : super( + formKey, + parent, + ConfigurationModel(Settings().logger), + pageName, + context, + applicationData, + ) { moduleModel.parse(); } } diff --git a/lib/src/page/configuration/configuration_create_page.dart b/lib/src/page/configuration/configuration_create_page.dart index a776116..6e6b0d3 100644 --- a/lib/src/page/configuration/configuration_create_page.dart +++ b/lib/src/page/configuration/configuration_create_page.dart @@ -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 { - final ApplicationData pageData; + final ApplicationData applicationData; - final GlobalKey _formKey = GlobalKey(); + final GlobalKey _formKey = + GlobalKey(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, )); } } diff --git a/lib/src/page/configuration/configuration_list_page.dart b/lib/src/page/configuration/configuration_list_page.dart index b9b0332..4cae1cb 100644 --- a/lib/src/page/configuration/configuration_list_page.dart +++ b/lib/src/page/configuration/configuration_list_page.dart @@ -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 { - ConfigurationListPageState(this.pageData); - - final ApplicationData pageData; - - final GlobalKey _formKey = GlobalKey(); - static Configuration currentConfiguration = Configuration(); - - Iterable getRows(FilterSet filters) { - final rows = >[ - { - '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 _formKey = + GlobalKey(debugLabel: 'configuration_list'); + Iterable 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: [ - 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'); + }); + } } diff --git a/lib/src/page/role/role_change_page.dart b/lib/src/page/role/role_change_page.dart index c0e2e2b..e1c5633 100644 --- a/lib/src/page/role/role_change_page.dart +++ b/lib/src/page/role/role_change_page.dart @@ -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 { - final ApplicationData pageData; + final ApplicationData applicationData; - final GlobalKey _formKey = GlobalKey(debugLabel: 'role_change'); + final GlobalKey _formKey = + GlobalKey(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, )); } } diff --git a/lib/src/page/role/role_controller.dart b/lib/src/page/role/role_controller.dart index 3857773..717cd16 100644 --- a/lib/src/page/role/role_controller.dart +++ b/lib/src/page/role/role_controller.dart @@ -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 formKey, State parent) - : super(formKey, parent, RoleModel(Settings().logger)) { +class RoleController extends PageControllerBones { + /// Controller for a page named [pageName]. + RoleController(GlobalKey formKey, State 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; - } } diff --git a/lib/src/page/role/role_create_page.dart b/lib/src/page/role/role_create_page.dart index 40a4cf2..3017487 100644 --- a/lib/src/page/role/role_create_page.dart +++ b/lib/src/page/role/role_create_page.dart @@ -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 { - final ApplicationData pageData; + final ApplicationData applicationData; - final GlobalKey _formKey = GlobalKey(debugLabel: 'role_create'); + final GlobalKey _formKey = + GlobalKey(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, )); } } diff --git a/lib/src/page/role/role_list_page.dart b/lib/src/page/role/role_list_page.dart index e51158e..7398527 100644 --- a/lib/src/page/role/role_list_page.dart +++ b/lib/src/page/role/role_list_page.dart @@ -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 { - final ApplicationData pageData; + final ApplicationData applicationData; final GlobalKey _formKey = GlobalKey(debugLabel: 'role_list'); Iterable 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 { buttons: [ 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 { } 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'); }); } } diff --git a/lib/src/page/user/user_change_page.dart b/lib/src/page/user/user_change_page.dart index a770ffa..50f2cab 100644 --- a/lib/src/page/user/user_change_page.dart +++ b/lib/src/page/user/user_change_page.dart @@ -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 { - final ApplicationData pageData; + final ApplicationData applicationData; - final GlobalKey _formKey = GlobalKey(); + final GlobalKey _formKey = + GlobalKey(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, )); } } diff --git a/lib/src/page/user/user_controller.dart b/lib/src/page/user/user_controller.dart index 1532e09..22db524 100644 --- a/lib/src/page/user/user_controller.dart +++ b/lib/src/page/user/user_controller.dart @@ -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 formKey, State parent) - : super(formKey, parent, UserModel(Settings().logger)) { +class UserController extends PageControllerBones { + /// Controller for a page named [pageName]. + UserController(GlobalKey formKey, State parent, + String pageName, BuildContext context, ApplicationData applicationData) + : super( + formKey, + parent, + UserModel(Settings().logger), + pageName, + context, + applicationData, + ) { moduleModel.parse(); } } diff --git a/lib/src/page/user/user_create_page.dart b/lib/src/page/user/user_create_page.dart index fb9a20a..eada475 100644 --- a/lib/src/page/user/user_create_page.dart +++ b/lib/src/page/user/user_create_page.dart @@ -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 { - final ApplicationData pageData; + final ApplicationData applicationData; - final GlobalKey _formKey = GlobalKey(); + final GlobalKey _formKey = + GlobalKey(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, )); } } diff --git a/lib/src/page/user/user_list_page.dart b/lib/src/page/user/user_list_page.dart index fbdcf70..a2d8039 100644 --- a/lib/src/page/user/user_list_page.dart +++ b/lib/src/page/user/user_list_page.dart @@ -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 { - UserListPageState(this.pageData); - - final ApplicationData pageData; - - final GlobalKey _formKey = GlobalKey(); - static User currentUser = User(); - - List> getRows(FilterSet filters) { - final rows = >[ - { - '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 _formKey = + GlobalKey(debugLabel: 'user_list'); + Iterable 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: [ - 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'); + }); + } } diff --git a/lib/src/page/user_page.dart b/lib/src/page/user_page.dart index 20276e9..c54eac7 100644 --- a/lib/src/page/user_page.dart +++ b/lib/src/page/user_page.dart @@ -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 { final ApplicationData pageData; - final GlobalKey _formKey = GlobalKey(debugLabel: 'user_page'); + final GlobalKey _formKey = + GlobalKey(debugLabel: 'user_page'); static User currentUser = User(); @override diff --git a/lib/src/persistence/persistence.dart b/lib/src/persistence/persistence.dart index f548a49..cd2839e 100644 --- a/lib/src/persistence/persistence.dart +++ b/lib/src/persistence/persistence.dart @@ -29,10 +29,12 @@ abstract class Persistence { /// Returns a record with primary key [id] of the [module] with the /// SQL statement [sqlName]. - Future> record({@required String module, String sqlName, @required int id}); + Future> 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 data}); + Future update( + {@required String module, + String sqlName, + @required Map data}); } diff --git a/lib/src/persistence/rest_persistence.dart b/lib/src/persistence/rest_persistence.dart index 676fd52..d6da890 100644 --- a/lib/src/persistence/rest_persistence.dart +++ b/lib/src/persistence/rest_persistence.dart @@ -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 params}) async { + Future customQuery( + {String module, + String sqlName, + String sqlType, + Map 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 list( - {String module, String sqlName, Map params}) async { + Future list({String module, String sqlName, Map params}) async { sqlName ??= 'list'; Iterable 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); } diff --git a/lib/src/private/bdrawer.dart b/lib/src/private/bdrawer.dart index c9a9933..dd72b93 100644 --- a/lib/src/private/bdrawer.dart +++ b/lib/src/private/bdrawer.dart @@ -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; diff --git a/lib/src/private/bsettings.dart b/lib/src/private/bsettings.dart index 15b337c..aeb84fa 100644 --- a/lib/src/private/bsettings.dart +++ b/lib/src/private/bsettings.dart @@ -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 index 0000000..b32910a --- /dev/null +++ b/lib/src/widget/callback_controller_bones.dart @@ -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 comboboxTexts( + String customString, CallbackControllerBones controller); + + List comboboxValues( + String customString, CallbackControllerBones controller); + + ApplicationData getApplicationData(); + + BuildContext getContext(); + + FieldModel getModel(String customString, CallbackControllerBones controller); + + String getName(); + + ValueChanged getOnChanged( + String customString, CallbackControllerBones controller); + + ValueChanged getOnChangedCheckbox( + String customString, CallbackControllerBones controller); + + ValueChanged getOnChangedCombobox( + String customString, CallbackControllerBones controller); + + VoidCallback getOnEditingComplete( + String customString, CallbackControllerBones controller); + + ValueChanged getOnFieldSubmitted( + String customString, CallbackControllerBones controller); + + ValueChanged getOnHighlightChanged( + String customString, CallbackControllerBones controller); + + VoidCallback getOnLongPressed( + String customString, CallbackControllerBones controller); + + VoidCallback getOnPressed( + String customString, CallbackControllerBones controller); + + FormFieldSetter getOnSaved( + String customString, CallbackControllerBones controller); + + DropdownButtonBuilder getOnSelectedItemBuilder( + String customString, CallbackControllerBones controller); + + GestureTapCallback getOnTap( + String customString, CallbackControllerBones controller); + + FormFieldValidator getOnValidator( + String customString, CallbackControllerBones controller); + + FormFieldValidator 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 index 0000000..51c34a3 --- /dev/null +++ b/lib/src/widget/checkbox_list_tile_bone.dart @@ -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); +} diff --git a/lib/src/widget/dropdown_button_form_bone.dart b/lib/src/widget/dropdown_button_form_bone.dart index 3bbedf6..5164046 100644 --- a/lib/src/widget/dropdown_button_form_bone.dart +++ b/lib/src/widget/dropdown_button_form_bone.dart @@ -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 extends DropdownButtonFormField { final String customString; - final ComboboxCallbackController callbackController; + final CallbackControllerBones callbackController; DropdownButtonFormBone( this.customString, @@ -33,25 +35,25 @@ class DropdownButtonFormBone extends DropdownButtonFormField { FormFieldValidator 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( + 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 extends DropdownButtonFormField { autovalidateMode: autovalidateMode, ); } - -/// Interface for a [DropdownButtonFormBone] specific callback controller. -abstract class ComboboxCallbackController { - ValueChanged getOnChanged( - String customString, ComboboxCallbackController controller); - - FormFieldSetter getOnSaved( - String customString, ComboboxCallbackController controller); - - DropdownButtonBuilder getOnSelectedItemBuilder( - String customString, ComboboxCallbackController controller); - - VoidCallback getOnTap( - String customString, ComboboxCallbackController controller); - - FormFieldValidator getOnValidator( - String customString, ComboboxCallbackController controller); - - String getName(); - - List texts(); - - List values(); -} diff --git a/lib/src/widget/edit_form.dart b/lib/src/widget/edit_form.dart index d33ddbd..4a3f9b6 100644 --- a/lib/src/widget/edit_form.dart +++ b/lib/src/widget/edit_form.dart @@ -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 row, int index); @@ -10,19 +10,16 @@ typedef Function OnEditTap(Map 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: [ - ...moduleController.getWidgets(isCreateForm), + ...widgets, SizedBox( height: configuration.asFloat( 'form.gap.field_button.height', @@ -40,12 +37,12 @@ class EditForm { children: [ 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_set.dart similarity index 57% rename from lib/src/widget/filter_fields.dart rename to lib/src/widget/filter_set.dart index c9b1554..34a70f9 100644 --- a/lib/src/widget/filter_fields.dart +++ b/lib/src/widget/filter_set.dart @@ -1,7 +1,7 @@ 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 'package:flutter_bones/src/widget/page_controller_bones.dart'; import 'text_form_field_bone.dart'; @@ -59,96 +59,29 @@ class FilterItem { } } -class FilterSet - implements - TextFormCallbackController, - ButtonCallbackController, - TableCallbackController { +class FilterSet { var filters = []; final BaseLogger logger; final GlobalKey globalKey; - + final PageControllerBones pageController; State parent; - FilterSet(this.globalKey, this.parent, this.logger); + FilterSet(this.globalKey, this.parent, this.pageController, this.logger); - FilterSet.ready(this.globalKey, this.parent, this.filters, 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); - @override - getOnChanged(String customString, TextFormCallbackController controller) { - return null; - } - @override - getOnEditingComplete( - String customString, TextFormCallbackController controller) { - return null; - } - - @override - getOnEditTap(String customString, TableCallbackController controller, - Map 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; - 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 getWidgets() { final rc = filters.map((filter) { Widget rc = TextFormFieldBone( filter.name, - this, + pageController, decoration: InputDecoration(labelText: filter.label), ); if (filter.toolTip != null) { diff --git a/lib/src/widget/list_form.dart b/lib/src/widget/list_form.dart index 2154dea..9998889 100644 --- a/lib/src/widget/list_form.dart +++ b/lib/src/widget/list_form.dart @@ -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 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 buttons, - @required List titles, - @required List columnNames, - @required Iterable rows, - bool showEditIcon = false, - TableCallbackController tableCallbackController, - @required BaseConfiguration configuration, - String customString}) { + static Form listForm( + {@required Key key, + @required FilterSet filters, + @required List buttons, + @required List titles, + @required List columnNames, + @required Iterable 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 index b27c639..0000000 --- a/lib/src/widget/module_controller.dart +++ /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 = {}; - final dataTypes = {}; - final Map values = {}; - final GlobalKey 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 getWidgets(bool isCreateForm) { - List 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 index 0000000..8087368 --- /dev/null +++ b/lib/src/widget/page_controller_bones.dart @@ -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 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 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( + 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 getWidgets() { + List rc; + final page = moduleModel.pageByName(pageName); + rc = View(moduleModel.logger).modelsToWidgets(page.fields, this); + return rc ?? []; + } + + @override + ApplicationData getApplicationData() { + return applicationData; + } +} diff --git a/lib/src/widget/raised_button_bone.dart b/lib/src/widget/raised_button_bone.dart index 64bd8e8..6875e3c 100644 --- a/lib/src/widget/raised_button_bone.dart +++ b/lib/src/widget/raised_button_bone.dart @@ -1,23 +1,13 @@ import 'package:flutter/material.dart'; -/// Interface for a button specific callback controller. -abstract class ButtonCallbackController { - ValueChanged 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, diff --git a/lib/src/widget/text_form_field_bone.dart b/lib/src/widget/text_form_field_bone.dart index 09b2289..8322fbe 100644 --- a/lib/src/widget/text_form_field_bone.dart +++ b/lib/src/widget/text_form_field_bone.dart @@ -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 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 inputFormatters, diff --git a/lib/src/widget/view.dart b/lib/src/widget/view.dart index 24c2b2a..71f7d40 100644 --- a/lib/src/widget/view.dart +++ b/lib/src/widget/view.dart @@ -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 buttonList(List controllers) { + List buttonList( + List buttonModels, CallbackControllerBones controller) { final rc = []; - 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(ComboboxController controller, onTap) { - final texts = controller.texts(); - final values = controller.values() ?? texts; + Widget combobox(FieldModel model, CallbackControllerBones controller) { + final texts = controller.comboboxTexts(model.name, controller); + final values = controller.comboboxValues(model.name, controller) ?? texts; final items = >[]; 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 widgetsOfSection(List children) { + /// Converts a list of [models] into a list of [Widget]s. + List modelsToWidgets(List models, + CallbackControllerBones controller) { final rc = []; - 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; + } } diff --git a/lib/src/widget/widget_list.dart b/lib/src/widget/widget_list.dart index 9ba2514..1957edd 100644 --- a/lib/src/widget/widget_list.dart +++ b/lib/src/widget/widget_list.dart @@ -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 buildSqlParams(PageControllerBones controller) { + final rc = {}; + 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(); + } } diff --git a/test/helpers/settings_test.dart b/test/helpers/settings_test.dart index dda85c8..efd2253 100644 --- a/test/helpers/settings_test.dart +++ b/test/helpers/settings_test.dart @@ -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')); diff --git a/test/helpers/string_helper_test.dart b/test/helpers/string_helper_test.dart index 0985f71..e20269f 100644 --- a/test/helpers/string_helper_test.dart +++ b/test/helpers/string_helper_test.dart @@ -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', () { diff --git a/test/model/db_model_test.dart b/test/model/db_model_test.dart index 76ad5e6..ce5d802 100644 --- a/test/model/db_model_test.dart +++ b/test/model/db_model_test.dart @@ -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 diff --git a/test/model/model_test.dart b/test/model/model_test.dart index a9bc067..0301cd1 100644 --- a/test/model/model_test.dart +++ b/test/model/model_test.dart @@ -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 = { '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 = { 'label': 'Save', }, ] - } + }, ] }, + { + 'page': 'change', + 'pageType': 'change', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'widgetType': 'allDbFields', + }, + ], + }, + ], + }, ], }; diff --git a/test/model/standard_test.dart b/test/model/standard_test.dart index 27dd8b1..e0cd215 100644 --- a/test/model/standard_test.dart +++ b/test/model/standard_test.dart @@ -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 diff --git a/test/rest_persistence_test.dart b/test/rest_persistence_test.dart index 9b3c4ec..2e35b4b 100644 --- a/test/rest_persistence_test.dart +++ b/test/rest_persistence_test.dart @@ -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, diff --git a/test/widget/widget_test.dart b/test/widget/widget_test.dart index 5219b26..ed13de4 100644 --- a/test/widget/widget_test.dart +++ b/test/widget/widget_test.dart @@ -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 { -- 2.39.5