--- /dev/null
+drop table if exists role;
+create table role (
+ role_id int(10) notnull unique,
+ role_name varchar(32) unique notnull,
+ role_priority int(10),
+ role_active char(1),
+ role_createdat timestamp null,
+ role_createdby varchar(16),
+ role_changedat timestamp null,
+ role_changedby varchar(16),
+ primary role_id
+);
--- /dev/null
+drop table if exists user;
+create table user (
+ user_id int(10) notnull unique,
+ user_name varchar(64) unique notnull,
+ user_displayname varchar(32) unique,
+ user_email varchar(128) unique,
+ user_password varchar(128),
+ user_role int(10),
+ user_createdat timestamp null,
+ user_createdby varchar(16),
+ user_changedat timestamp null,
+ user_changedby varchar(16),
+ primary user_id
+);
--- /dev/null
+---
+# configuration of the bones backend for role:
+created: 2020.10.17 01:05:34
+author: flutter_bones.module_model.exportSqlBackend()
+version: 1.0.0
+modules:
+ - module: role
+ list:
+ - name: insert
+ type: insert
+ sql: "INSERT INTO role(role_name,role_priority,role_active,role_createdat,role_createdby,role_changedat,role_changedby)
+ VALUES(:role_name,:role_priority,:role_active,NOW(),:role_createdby,:role_changedat,:role_changedby);"
+ - name: update
+ type: update
+ sql: "UPDATE role SET
+ role_name=:role_name,role_priority=:role_priority,role_active=:role_active,role_createdat=:role_createdat,role_createdby=:role_createdby,role_changedat=NOW(),role_changedby=:role_changedby
+ WHERE role_id=:role_id";
+ - name: delete
+ type: delete
+ sql: "DELETE from role WHERE role_id=:role_id;"
+ - name: record
+ type: record
+ sql: "SELECT * from role WHERE role_id=:role_id;"
--- /dev/null
+---
+# configuration of the bones backend for user:
+created: 2020.10.17 01:05:34
+author: flutter_bones.module_model.exportSqlBackend()
+version: 1.0.0
+modules:
+ - module: user
+ list:
+ - name: insert
+ type: insert
+ sql: "INSERT INTO user(user_name,user_displayname,user_email,user_password,user_role,user_createdat,user_createdby,user_changedat,user_changedby)
+ VALUES(:user_name,:user_displayname,:user_email,:user_password,:user_role,NOW(),:user_createdby,:user_changedat,:user_changedby);"
+ - name: update
+ type: update
+ sql: "UPDATE user SET
+ user_name=:user_name,user_displayname=:user_displayname,user_email=:user_email,user_password=:user_password,user_role=:user_role,user_createdat=:user_createdat,user_createdby=:user_createdby,user_changedat=NOW(),user_changedby=:user_changedby
+ WHERE user_id=:user_id";
+ - name: delete
+ type: delete
+ sql: "DELETE from user WHERE user_id=:user_id;"
+ - name: record
+ type: record
+ sql: "SELECT * from user WHERE user_id=:user_id;"
export 'src/model/module_model.dart';
export 'src/model/page_model.dart';
export 'src/model/section_model.dart';
+export 'src/model/standard/configuration_model.dart';
export 'src/model/standard/role_model.dart';
export 'src/model/standard/user_model.dart';
export 'src/model/text_field_model.dart';
export 'src/page/page_data.dart';
export 'src/page/role/role_create_page.dart';
export 'src/page/user_page.dart';
+export 'src/persistence/persistence.dart';
+export 'src/persistence/rest_persistence.dart';
export 'src/widget/raised_button_bone.dart';
export 'src/widget/simple_form.dart';
export 'src/widget/text_form_field_bone.dart';
--- /dev/null
+import 'package:flutter_bones/flutter_bones.dart';
+
+/// Base class of all controller classes in this package.
+abstract class BaseController {
+ /// Returns the assigned model.
+ WidgetModel getModel();
+
+ /// Returns a unique name of the controller inside the module, mostly the
+ /// widget name of the assigned model.
+ String getName();
+}
--- /dev/null
+import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_bones/src/controller/base_controller.dart';
+
+import '../widget/raised_button_bone.dart';
+
+class ButtonController extends BaseController
+ implements ButtonCallbackController {
+ ButtonModel model;
+
+ ButtonController(this.model);
+
+ @override
+ WidgetModel getModel() {
+ return model;
+ }
+
+ @override
+ String getName() {
+ return model.widgetName();
+ }
+
+ @override
+ getOnHighlightChanged(
+ String customString, ButtonCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getOnLongPressed(String customString, ButtonCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getOnPressed(String customString, ButtonCallbackController controller) {
+ var rc;
+ switch (model.buttonModelType) {
+ case ButtonModelType.cancel:
+ break;
+ case ButtonModelType.custom:
+ break;
+ case ButtonModelType.search:
+ break;
+ case ButtonModelType.store:
+ break;
+ }
+ return rc;
+ }
+}
--- /dev/null
+import 'package:flutter_bones/flutter_bones.dart';
+
+import '../widget/dropdown_button_form_bone.dart';
+import 'base_controller.dart';
+
+class ComboboxController<T> extends BaseController
+ implements ComboboxCallbackController {
+ ComboboxModel model;
+
+ ComboboxController(this.model);
+
+ @override
+ WidgetModel getModel() {
+ return model;
+ }
+
+ @override
+ String getName() {
+ return model.widgetName();
+ }
+
+ @override
+ getOnChanged(String customString, ComboboxCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getOnSaved(String customString, ComboboxCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getOnSelectedItemBuilder(
+ String customString, ComboboxCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getOnTap(String customString, ComboboxCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getOnValidator(String customString, ComboboxCallbackController controller) {
+ return null;
+ }
+
+ @override
+ List<String> texts() {
+ return model.texts;
+ }
+
+ @override
+ List<dynamic> values() {
+ return model.values;
+ }
+}
--- /dev/null
+import 'package:flutter_bones/flutter_bones.dart';
+
+import '../widget/text_form_field_bone.dart';
+import 'base_controller.dart';
+
+class TextFormController extends BaseController
+ implements TextFormCallbackController {
+ ComboboxModel model;
+
+ TextFormController(this.model);
+
+ @override
+ WidgetModel getModel() {
+ return model;
+ }
+
+ @override
+ String getName() {
+ return model.widgetName();
+ }
+
+ @override
+ getOnChanged(String customString, TextFormCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getOnEditingComplete(
+ String customString, TextFormCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getOnFieldSubmitted(
+ String customString, TextFormCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getOnSaved(String customString, TextFormCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getOnTap(String customString, TextFormCallbackController controller) {
+ return null;
+ }
+
+ @override
+ getValidator(String customString, TextFormCallbackController controller) {
+ return null;
+ }
+}
import 'package:dart_bones/dart_bones.dart';
import 'package:flutter/material.dart';
+import 'package:intl/date_symbol_data_local.dart';
+import 'package:intl/intl.dart';
-class Settings {
+class BaseSettings {
static Locale locale = Locale('US', 'en');
- static final mapWidgetData = <String, dynamic>{
- 'form.card.padding': '16.0',
- 'form.gap.field_button.height': '16.0',
- };
- static Settings _instance;
+ static get language => locale.languageCode;
- final BaseConfiguration widgetConfiguration;
+ final BaseConfiguration configuration;
final BaseLogger logger;
- factory Settings({BaseLogger logger, BaseConfiguration widgetConfiguration}) {
- if (_instance == null) {
- _instance = Settings.internal(
- widgetConfiguration == null
- ? BaseConfiguration(mapWidgetData, logger)
- : widgetConfiguration,
- logger);
- }
- return _instance;
- }
-
- Settings.internal(this.widgetConfiguration, this.logger);
+ BaseSettings(this.configuration, this.logger);
/// Sets the locale code.
/// [locale] the info about localisation
/// Finding [locale] of an app: @see https://github.com/flutter/website/blob/master/examples/internationalization/minimal/lib/main.dart
- static setLocale(locale) => Settings.locale = locale;
+ static setLocale(locale) {
+ BaseSettings.locale = locale;
+ Intl.defaultLocale = "${locale.languageCode}_$locale.countryCode";
+ initializeDateFormatting();
+ }
- static setLocaleByNames({String country = 'US', String language = 'en'}) =>
- Settings.locale = Locale(country, language);
+ /// Sets the locales with [language] and [country].
+ static setLocaleByNames({String country, @required String language}) {
+ country ??= language;
+ country = country.toUpperCase();
+ country = country == 'EN' ? 'US' : country;
+ language = language.toLowerCase();
+ setLocale(Locale(language, country));
+ }
/// Translates a [text] with a given translation [map].
/// Structure of the [map]: { <English text> : { <language code> : translation } }
return rc;
}
}
+
+class Settings extends BaseSettings {
+ static final mapWidgetData = <String, dynamic>{
+ 'form.card.padding': '16.0',
+ 'form.gap.field_button.height': '16.0',
+ };
+ static Settings _instance;
+
+ factory Settings({BaseLogger logger, BaseConfiguration widgetConfiguration}) {
+ if (_instance == null) {
+ _instance = Settings.internal(
+ widgetConfiguration == null
+ ? BaseConfiguration(mapWidgetData, logger)
+ : widgetConfiguration,
+ logger);
+ }
+ return _instance;
+ }
+
+ Settings.internal(BaseConfiguration configuration, BaseLogger logger)
+ : super(configuration, logger);
+}
{'0': input});
String _vt(String key, [Map<String, String> placeholders]) =>
- Settings.translate(key, ValidatorTranslations.translations,
+ BaseSettings.translate(key, ValidatorTranslations.translations,
placeholders: placeholders);
@protected
typedef ButtonOnPressed = void Function(String name);
/// Describes a button widget.
-class ButtonModel extends WidgetModel implements ButtonCallbackController {
+class ButtonModel extends WidgetModel {
static final regExprOptions = RegExp(r'^(undef)$');
String text;
String name;
@override
String widgetName() => name;
-
- @override
- getOnHighlightChanged(
- String customString, ButtonCallbackController controller) {
- return null;
- }
-
- @override
- getOnLongPressed(String customString, ButtonCallbackController controller) {
- return null;
- }
-
- @override
- getOnPressed(String customString, ButtonCallbackController controller) {
- return onPressed;
- }
}
enum ButtonModelType {
super.parse();
checkSuperfluousAttributes(
map,
- 'name label dataType options texts toolTip widgetType values'
+ 'name label dataType filterType options texts toolTip widgetType values'
.split(' '));
texts = parseStringList('texts', map);
values = parseValueList('values', map, dataType);
/// Base class of widgets with user interaction like text field, combobox, checkbox.
abstract class FieldModel extends WidgetModel {
- static final regExprOptions =
- RegExp(r'^(undef|readonly|disabled|password|required)$');
String name;
String label;
String toolTip;
case FilterType.dateTimeTil:
buffer.write('${field.name}<=:${field.name}');
break;
+ case FilterType.equals:
+ buffer.write('${field.name} like :${field.name}');
+ break;
case FilterType.pattern:
buffer.write('${field.name} like :${field.name}');
break;
type = 'DOUBLE';
break;
case DataType.int:
- type = 'INT(10)';
+ type = column.hasOption('primary') ? 'INT(10) UNSIGNED' : 'INT(10)';
break;
case DataType.reference:
type = 'INT(10) UNSIGNED';
}
String options = '';
for (var option in column.options) {
- if ('notnull null unique'.contains(option)) {
- options += ' ' + option;
+ if (option == 'notnull') {
+ options += ' NOT NULL';
+ } else if ('null unique'.contains(option)) {
+ options += ' ' + option.toUpperCase();
}
}
+ if (column.hasOption('primary')) {
+ options += ' AUTO_INCREMENT';
+ }
buffer.write(' ${column.name} $type$options,\n');
}
buffer.write(' PRIMARY KEY(${table.name}_id)\n');
--- /dev/null
+import 'package:dart_bones/dart_bones.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+
+///
+class ConfigurationModel extends ModuleModel {
+ static final yamlMap = <String, dynamic>{
+ //
+ "module": "configuration",
+ "tables": [
+ {
+ // configuration_id | configuration_scope | configuration_property | configuration_order | configuration_type
+ // | configuration_value | configuration_description
+ 'table': 'configuration',
+ 'columns': [
+ {
+ 'column': 'configuration_id',
+ 'dataType': 'int',
+ 'label': 'Id',
+ 'options': 'primary',
+ },
+ {
+ 'column': 'configuration_scope',
+ 'dataType': 'string',
+ 'label': 'Bereich',
+ 'size': 64,
+ 'options': 'unique;notnull',
+ },
+ {
+ 'column': 'configuration_property',
+ 'dataType': 'string',
+ 'size': 64,
+ 'label': 'Eigenschaft',
+ },
+ {
+ 'column': 'configuration_order',
+ 'dataType': 'int',
+ 'label': 'Reihe',
+ },
+ {
+ 'column': 'configuration_type',
+ 'dataType': 'string',
+ 'size': 16,
+ 'label': 'Datentyp',
+ },
+ {
+ 'column': 'configuration_value',
+ 'dataType': 'string',
+ 'size': 255,
+ 'label': 'Wert',
+ },
+ {
+ 'column': 'configuration_description',
+ 'dataType': 'string',
+ 'size': 255,
+ 'label': 'Beschreibung',
+ },
+ ]
+ },
+ ],
+ 'pages': [
+ {
+ "page": "create",
+ "pageType": "create",
+ "sections": [
+ {
+ "sectionType": "simpleForm",
+ "children": [
+ {
+ "widgetType": "allDbFields",
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "page": "change",
+ "pageType": "change",
+ "sections": [
+ {
+ "sectionType": "simpleForm",
+ "children": [
+ {
+ "widgetType": "allDbFields",
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "page": "list",
+ "pageType": "list",
+ "sections": [
+ {
+ "sectionType": "filterPanel",
+ "children": [
+ {
+ "name": "configuration_name",
+ "widgetType": "textField",
+ "filterType": "pattern",
+ },
+ {
+ "name": "configuration_scope",
+ "widgetType": "combobox",
+ "filterType": "equals",
+ "options": 'undef',
+ },
+ ]
+ }
+ ]
+ },
+ ],
+ };
+
+ ConfigurationModel(BaseLogger logger) : super(yamlMap, logger);
+
+ /// Returns the name including the names of the parent
+ @override
+ String fullName() => name;
+
+ @override
+ String widgetName() => name;
+}
--- /dev/null
+import 'package:meta/meta.dart';
+
+abstract class Persistence {
+ /// Deletes a record with primary key [id] of the [module] with the
+ /// SQL statement [sqlName].
+ Future delete({@required String module, String sqlName, @required int id});
+
+ /// Inserts a record of the [module] with the SQL statement [sqlName]
+ /// and returns the primary key.
+ Future<int> insert(
+ {@required String module,
+ String sqlName,
+ @required Map<String, dynamic> data});
+
+ /// Returns all records of the [module] specified with [params] using
+ /// a SQL statement named [sqlName].
+ Future<List<Map<String, dynamic>>> list(
+ {@required String module, String sqlName, Map<String, dynamic> params});
+
+ /// Returns a record with primary key [id] of the [module] with the
+ /// SQL statement [sqlName].
+ Future<Map<String, dynamic>> record(
+ {@required String module, String sqlName, @required int id});
+
+ /// Updates a record of [module] with the [data] using the SQL statement [sqlName].
+ Future update(
+ {@required String module,
+ String sqlName,
+ @required Map<String, dynamic> data});
+}
--- /dev/null
+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;
+import 'package:meta/meta.dart';
+
+/// A persistence layer using a REST interface to store/fetch the data.
+class RestPersistence extends Persistence {
+ static RestPersistence _instance;
+ final String application;
+ final String version;
+ final BaseLogger logger;
+ final String host;
+
+ final String scheme;
+ int port;
+ int sessionTimeout;
+ String uriPrefix;
+
+ Map<String, String> headers;
+
+ factory RestPersistence(
+ {String application,
+ String version,
+ int port,
+ String host,
+ BaseLogger logger}) {
+ _instance ??= RestPersistence.internal(
+ application: application,
+ host: host,
+ port: port,
+ version: version,
+ logger: logger);
+ return _instance;
+ }
+
+ factory RestPersistence.fromConfig(
+ BaseConfiguration configuration, BaseLogger logger) {
+ final section = 'client';
+ _instance = RestPersistence.internal(
+ application: configuration.asString('application', section: section),
+ version: configuration.asString('version', section: section),
+ host: configuration.asString('host', section: section),
+ port: configuration.asInt('port', section: section),
+ scheme: configuration.asString('schema',
+ section: section, defaultValue: 'https'),
+ logger: logger);
+
+ return _instance;
+ }
+
+ 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 delete({String module, String sqlName, int id}) async {
+ sqlName ??= 'delete';
+ await runRequest(module, sqlName, 'delete', body: '{${module}_id:$id}');
+ }
+
+ @override
+ Future<int> insert(
+ {String module, String sqlName, Map<String, dynamic> data}) async {
+ sqlName ??= 'insert';
+ var rc = 0;
+ final data2 = data == null ? '{}' : convert.jsonEncode(data);
+ final answer = await runRequest(module, sqlName, 'insert', body: data2);
+ if (answer.isNotEmpty) {
+ final map = convert.jsonDecode(answer);
+ rc = map['id'];
+ }
+ return rc;
+ }
+
+ /// Handles a HTTP request with a single HTTP connection.
+ /// [module]: the module which implies the requested table.
+ /// [sqlName]: the name of the SQL statement.
+ /// [sqlType]: 'insert', 'record', 'update', 'list', 'delete'
+ /// [body]: null or the body of the POST request, e.g. '{ 'role_id'
+ /// [headers]: the optional HTTP headers.
+ /// [result]: the body of the response or ''
+ Future<String> runRequest(String module, String sqlName, String sqlType,
+ {String body, Map<String, String> headers}) async {
+ var rc = '';
+ final uri = '$uriPrefix/$application/$module/$sqlType/$sqlName/$version';
+ http.Response response;
+ logger.log('request: POST $module $sqlName', LEVEL_LOOP);
+ response = await http.post(uri, body: body, headers: headers);
+ logger.log('status: ${response.statusCode}', LEVEL_LOOP);
+ if (response.statusCode != 200) {
+ logger.error('$uri: status: ${response.statusCode}');
+ } else {
+ rc = response.body ?? '';
+ }
+ return rc;
+ }
+
+ @override
+ Future<List<Map<String, dynamic>>> list(
+ {String module, String sqlName, Map<String, dynamic> params}) async {
+ sqlName ??= 'list';
+ List<Map<String, dynamic>> rc;
+ final answer = await runRequest(module, sqlName, 'list',
+ body: params == null ? '{}' : convert.jsonEncode(params));
+ if (answer.isNotEmpty) {
+ rc = convert.jsonDecode(answer);
+ }
+ return rc;
+ }
+
+ @override
+ Future<Map<String, dynamic>> record(
+ {String module, String sqlName, int id}) async {
+ sqlName ??= 'record';
+ Map rc;
+ final answer =
+ await runRequest(module, sqlName, 'record', body: '{${module}_id:$id}');
+ if (answer.isNotEmpty) {
+ rc = convert.jsonDecode(answer);
+ }
+ return rc;
+ }
+
+ @override
+ Future update(
+ {String module, String sqlName, Map<String, dynamic> data}) async {
+ sqlName ??= 'update';
+ final data2 = data == null ? '{}' : convert.jsonEncode(data);
+ await runRequest(module, sqlName, 'record', body: data2);
+ }
+}
--- /dev/null
+import 'package:flutter/material.dart';
+
+/// Implements a [DropdownButtonFormField] with "outsourced" callbacks:
+/// [customString] a string mostly used for a name needed in the [customController]
+/// [callbackController] handles the callback methods.
+class DropdownButtonFormBone<T> extends DropdownButtonFormField<T> {
+ final String customString;
+ final ComboboxCallbackController callbackController;
+
+ DropdownButtonFormBone(
+ this.customString,
+ this.callbackController, {
+ Key key,
+ @required List<DropdownMenuItem<T>> items,
+ T value,
+ Widget hint,
+ Widget disabledHint,
+ int elevation: 8,
+ TextStyle style,
+ Widget icon,
+ Color iconDisabledColor,
+ Color iconEnabledColor,
+ double iconSize: 24.0,
+ bool isDense: true,
+ bool isExpanded: false,
+ double itemHeight,
+ Color focusColor,
+ FocusNode focusNode,
+ bool autofocus: false,
+ Color dropdownColor,
+ InputDecoration decoration,
+ FormFieldSetter<T> onSaved,
+ FormFieldValidator<T> validator,
+ AutovalidateMode autovalidateMode,
+ }) : super(
+ key: key,
+ items: items,
+ selectedItemBuilder: callbackController.getOnSelectedItemBuilder(
+ customString, callbackController),
+ value: value,
+ hint: hint,
+ disabledHint: disabledHint,
+ onChanged:
+ callbackController.getOnChanged(customString, callbackController),
+ onTap: callbackController.getOnTap(customString, callbackController),
+ elevation: elevation,
+ style: style,
+ icon: icon,
+ iconDisabledColor: iconDisabledColor,
+ iconEnabledColor: iconEnabledColor,
+ iconSize: iconSize,
+ isDense: isDense,
+ isExpanded: isExpanded,
+ itemHeight: itemHeight,
+ focusColor: focusColor,
+ focusNode: focusNode,
+ autofocus: autofocus,
+ dropdownColor: dropdownColor,
+ decoration: decoration,
+ autovalidateMode: autovalidateMode,
+ );
+}
+
+/// Interface for a [DropdownButtonFormBone] specific callback controller.
+abstract class ComboboxCallbackController<T> {
+ ValueChanged<T> getOnChanged(
+ String customString, ComboboxCallbackController controller);
+
+ FormFieldSetter<T> getOnSaved(
+ String customString, ComboboxCallbackController controller);
+
+ DropdownButtonBuilder getOnSelectedItemBuilder(
+ String customString, ComboboxCallbackController controller);
+
+ VoidCallback getOnTap(
+ String customString, ComboboxCallbackController controller);
+
+ FormFieldValidator<T> getOnValidator(
+ String customString, ComboboxCallbackController controller);
+
+ String getName();
+
+ List<String> texts();
+
+ List<dynamic> values();
+}
class Filters
implements
- TextCallbackController,
+ TextFormCallbackController,
ButtonCallbackController,
TableCallbackController {
var filters = <FilterItem>[];
filters.firstWhere((element) => element.name == name, orElse: () => null);
@override
- getOnChanged(String customString, TextCallbackController controller) {
+ getOnChanged(String customString, TextFormCallbackController controller) {
return null;
}
@override
- getOnEditingComplete(String customString, TextCallbackController controller) {
+ getOnEditingComplete(
+ String customString, TextFormCallbackController controller) {
return null;
}
}
@override
- getOnFieldSubmitted(String customString, TextCallbackController controller) {
+ getOnFieldSubmitted(String customString,
+ TextFormCallbackController controller) {
return null;
}
}
@override
- getOnSaved(String customString, TextCallbackController controller) {
+ getOnSaved(String customString, TextFormCallbackController controller) {
return (input) {
byName(customString).value = input;
};
}
@override
- getOnTap(String customString, TextCallbackController controller) {
+ getOnTap(String customString, TextFormCallbackController controller) {
return null;
}
@override
- getValidator(String customString, TextCallbackController controller) {
+ getValidator(String customString, TextFormCallbackController controller) {
return null;
}
// This interface allows the generic handling of the edit form by a model driven module.
class ModuleController
- implements TextCallbackController, ButtonCallbackController {
+ implements TextFormCallbackController, ButtonCallbackController {
final ModuleModel moduleModel;
String primaryColumn;
WidgetList createWidgets;
ModuleModel getModuleModel() => moduleModel;
@override
- getOnChanged(String customString, TextCallbackController controller) {
+ getOnChanged(String customString, TextFormCallbackController controller) {
return null;
}
@override
- getOnEditingComplete(String customString, TextCallbackController controller) {
+ getOnEditingComplete(
+ String customString, TextFormCallbackController controller) {
return null;
}
@override
- getOnFieldSubmitted(String customString, TextCallbackController controller) {
+ getOnFieldSubmitted(String customString,
+ TextFormCallbackController controller) {
return null;
}
}
@override
- getOnSaved(String customString, TextCallbackController controller) {
+ getOnSaved(String customString, TextFormCallbackController controller) {
final rc = (input) {
values[customString] =
StringHelper.fromString(input, dataTypes[customString]);
}
@override
- getOnTap(String customString, TextCallbackController controller) {
+ getOnTap(String customString, TextFormCallbackController controller) {
return null;
}
@override
- getValidator(String customString, TextCallbackController controller) {
+ getValidator(String customString, TextFormCallbackController controller) {
return null;
}
import 'package:flutter/services.dart';
/// Interface for a [TextFormField] specific callback controller.
-abstract class TextCallbackController {
+abstract class TextFormCallbackController {
ValueChanged<String> getOnChanged(
- String customString, TextCallbackController controller);
+ String customString, TextFormCallbackController controller);
VoidCallback getOnEditingComplete(
- String customString, TextCallbackController controller);
+ String customString, TextFormCallbackController controller);
ValueChanged<String> getOnFieldSubmitted(
- String customString, TextCallbackController controller);
+ String customString, TextFormCallbackController controller);
FormFieldSetter<String> getOnSaved(
- String customString, TextCallbackController controller);
+ String customString, TextFormCallbackController controller);
- GestureTapCallback getOnTap(
- String customString, TextCallbackController controller);
+ GestureTapCallback getOnTap(String customString,
+ TextFormCallbackController controller);
- FormFieldValidator<String> getValidator(
- String customString, TextCallbackController controller);
+ FormFieldValidator<String> getValidator(String customString,
+ TextFormCallbackController controller);
}
/// Implements a [TextFormField] with "outsourced" callbacks:
/// [callbackController] handles the callback methods.
class TextFormFieldBone extends TextFormField {
final String customString;
- final TextCallbackController callbackController;
+ final TextFormCallbackController callbackController;
- TextFormFieldBone(
- this.customString,
- this.callbackController, {
- Key key,
- TextEditingController controller,
- String initialValue,
- FocusNode focusNode,
- InputDecoration decoration = const InputDecoration(),
- TextInputType keyboardType,
+ TextFormFieldBone(this.customString,
+ this.callbackController, {
+ Key key,
+ TextEditingController controller,
+ String initialValue,
+ FocusNode focusNode,
+ InputDecoration decoration = const InputDecoration(),
+ TextInputType keyboardType,
TextCapitalization textCapitalization = TextCapitalization.none,
TextInputAction textInputAction,
TextStyle style,
import 'package:dart_bones/dart_bones.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_bones/src/controller/button_controller.dart';
+import '../controller/combobox_controller.dart';
import '../helper/settings.dart';
-import '../model/button_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';
import 'raised_button_bone.dart';
class View {
if (_instance == null) {
_instance = View.internal(logger);
_instance.settings = Settings(logger: logger);
- _instance.widgetConfiguration = _instance.settings.widgetConfiguration;
+ _instance.widgetConfiguration = _instance.settings.configuration;
}
return _instance;
}
View.internal(this.logger);
- /// Creates a button from the [model].
- Widget button(ButtonModel model) {
+ /// Creates a button from the [controller].
+ Widget button(ButtonController controller) {
final rc = RaisedButtonBone(
- model.widgetName(),
- model,
- child: Text(model.text),
+ controller.getName(),
+ controller,
+ child: Text(controller.model.text),
);
return rc;
}
- /// Creates a list of buttons from a list of [models].
- List<Widget> buttonList(List<WidgetModel> models) {
+ /// Creates a list of buttons from a list of [controllers].
+ List<Widget> buttonList(List<ButtonController> controllers) {
final rc = <Widget>[];
- for (var item in models) {
+ for (var item in controllers) {
rc.add(button(item));
}
return rc;
}
+ /// Creates a combobox via the [controller].
+ Widget combobox<T>(ComboboxController controller, onTap) {
+ final texts = controller.texts();
+ final values = controller.values() ?? texts;
+ final items = <DropdownMenuItem<T>>[];
+ for (var ix = 0; ix < texts.length; ix++) {
+ items.add(DropdownMenuItem(
+ onTap: onTap, value: values[ix], child: Text(texts[ix])));
+ }
+ final rc =
+ DropdownButtonFormBone(controller.getName(), controller, items: items);
+ return rc;
+ }
+
/// Creates a checkbox from the [model].
Widget checkbox(WidgetModel model) {
var rc;
Form simpleForm({SectionModel model, Key formKey}) {
assert(formKey != null);
final padding =
- widgetConfiguration.asFloat('form.card.padding', defaultValue: 16.0);
+ widgetConfiguration.asFloat('form.card.padding', defaultValue: 16.0);
final children = widgetsOfSection(model.children);
- final buttons = buttonList(model.buttons);
+ final controllers = model.buttons.map((button) => ButtonController(button));
+ final buttons = buttonList(controllers);
final rc = Form(
key: formKey,
child: Card(
rc.add(textField(child));
break;
case WidgetModelType.button:
- rc.add(button(child));
+ rc.add(button(ButtonController(child)));
break;
case WidgetModelType.emptyLine:
rc.add(emptyLine(child));
+import 'package:dart_bones/dart_bones.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bones/flutter_bones.dart';
+import 'package:intl/intl.dart';
import 'package:test/test.dart';
void main() {
+ Intl.defaultLocale = "de_DE";
+ final logger = MemoryLogger(LEVEL_FINE);
final map = {
'help': {'de': 'Hilfe'},
- 'wrong name %{0}': {'de': 'falscher name %{0}'}
+ 'wrong name %{0}': {'de': 'falscher Name %{0}'}
};
- setUpAll(() => Settings.setLocaleByNames(language: 'en'));
+ setUpAll(() => BaseSettings.setLocaleByNames(language: 'en'));
group('Settings', () {
test('basic', () {
- Settings.setLocale(Locale('de', 'de'));
- expect(Settings.translate('abc', map), equals('abc'));
- expect(Settings.translate('help', map), equals('help'));
+ BaseSettings.setLocale(Locale('de', 'DE'));
+ expect(BaseSettings.language, 'de');
+ expect(BaseSettings.translate('abc', map), equals('abc'));
+ expect(BaseSettings.translate('help', map), equals('Hilfe'));
expect(
- Settings.translate('wrong name %{0}', map,
+ BaseSettings.translate('wrong name %{0}', map,
placeholders: {'0': 'Joe'}),
- equals('wrong name Joe'));
+ equals('falscher Name Joe'));
+ expect(Settings(logger: logger), isNotNull);
});
test('translate-en', () {
- Settings.setLocaleByNames(language: 'en');
- expect(Settings.translate('abc', map), equals('abc'));
- expect(Settings.translate('help', map), equals('help'));
+ BaseSettings.setLocaleByNames(language: 'en');
+ expect(BaseSettings.language, 'en');
+ expect(BaseSettings.translate('abc', map), equals('abc'));
+ expect(BaseSettings.translate('help', map), equals('help'));
expect(
- Settings.translate('wrong name %{0}', map,
+ BaseSettings.translate('wrong name %{0}', map,
placeholders: {'0': 'Joe'}),
equals('wrong name Joe'));
});
test('translate-de', () {
- Settings.setLocaleByNames(language: 'de');
- expect(Settings.translate('abc', map), equals('abc'));
- expect(Settings.translate('help', map), equals('Hilfe'));
- expect(Settings.translate('wrong name %{0}', map, placeholders: {'0' : 'Joe'}), equals('falscher name Joe'));
+ BaseSettings.setLocaleByNames(language: 'de');
+ expect(BaseSettings.translate('abc', map), equals('abc'));
+ expect(BaseSettings.translate('help', map), equals('Hilfe'));
+ expect(
+ BaseSettings.translate('wrong name %{0}', map,
+ placeholders: {'0': 'Joe'}),
+ equals('falscher Name Joe'));
});
});
}
});
});
group('ModelBase', () {
- test('errors', () {
+ test('basic', () {
logger.clear();
final map = cloneOfMap(userModel);
final field = <String, dynamic>{
});
});
group('CheckboxModel', () {
- logger.clear();
+ test('basic', () {
+ logger.clear();
+ final map = cloneOfMap(userModel);
+ final field = <String, dynamic>{
+ 'widgetType': 'checkbox',
+ 'name': 'hidden',
+ 'label': 'Hidden',
+ 'options': 'required',
+ };
+ map['pages'][0]['sections'][0]['children'][0] = field;
+ var module = Demo1(map, logger);
+ module.parse();
+ var errors = logger.errors;
+ expect(errors.length, equals(0));
+ final checkbox = module.pageByName('create').getField('hidden');
+ expect(checkbox.hasOption('required'), isTrue);
+ checkbox.value = true;
+ expect(checkbox.value, isTrue);
+ checkbox.value = false;
+ expect(checkbox.value, isFalse);
+ checkbox.value = 'true';
+ expect(checkbox.value, isTrue);
+ });
test('errors', () {
+ logger.clear();
final map = cloneOfMap(userModel);
final field = <String, dynamic>{
'widgetType': 'checkbox',
var module = Demo1(map, logger);
module.parse();
var errors = logger.errors;
- expect(errors.length, equals(2));
+ expect(errors.length, equals(0));
final checkbox = module.pageByName('create').getField('hidden');
expect(checkbox, isNotNull);
expect(checkbox.dataType, DataType.bool);
var module = Demo1(map, logger);
module.parse();
var errors = logger.errors;
- expect(errors.length, equals(2));
+ expect(errors.length, equals(0));
final combobox = module.pageByName('create').getField('class');
expect(combobox, isNotNull);
expect(combobox.dataType, DataType.int);
});
});
group('Non field widgets', () {
- logger.clear();
test('basic', () {
+ logger.clear();
final map = cloneOfMap(userModel);
final list = [
{
var module = Demo1(map, logger);
module.parse();
var errors = logger.errors;
- expect(errors.length, equals(2));
+ expect(errors.length, equals(0));
final dump = module.dump(StringBuffer()).toString();
- expect(dump, equals('''= module demo1: options:
-== page create: PageModelType.create options:
- = section simpleForm1: SectionModelType.simpleForm options: [
- emptyLine: options:
- text 24 text: *Hi world*: options: rich
- ] # create.simpleForm1
-'''));
+ expect(dump.contains('text: *Hi world*: options: rich'), isTrue);
final page = module.pageByName('create');
final allWidgets = page.getWidgets(null);
expect(allWidgets.length, equals(2));
expect(nonFieldWidgets.length, equals(2));
});
});
- group('standard-models', () {
- test('user', () {
- final model = UserModel(logger);
- model.parse();
- });
- });
}
final userModel = <String, dynamic>{
--- /dev/null
+import 'package:dart_bones/dart_bones.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+import 'package:test/test.dart';
+
+void main() {
+ final logger = MemoryLogger(LEVEL_FINE);
+ group('module', () {
+ test('role', () {
+ logger.clear();
+ final module = RoleModel(logger);
+ module.parse();
+ expect(module.fullName(), equals('role'));
+ expect(module.widgetName(), equals('role'));
+ final errors = logger.errors;
+ expect(errors.length, equals(0));
+ final dump = module.dump(StringBuffer()).toString();
+ expect(dump, '''= module role: options:
+== table role: options:
+ column role_id: DataType.int "Id" options: primary notnull unique
+ 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
+== page create: PageModelType.create options:
+ = section simpleForm1: SectionModelType.simpleForm options: [
+ allDbFields 10 text: options:
+ ] # create.simpleForm1
+== page change: PageModelType.change options:
+ = section simpleForm1: SectionModelType.simpleForm options: [
+ allDbFields 12 text: options:
+ ] # change.simpleForm1
+== page list: PageModelType.list options:
+ = section filterPanel1: SectionModelType.filterPanel options: [
+ textField role_name: options:
+ ] # list.filterPanel1
+''');
+ });
+ test('user', () {
+ logger.clear();
+ final module = UserModel(logger);
+ module.parse();
+ expect(module.fullName(), equals('user'));
+ expect(module.widgetName(), equals('user'));
+ final errors = logger.errors;
+ expect(errors.length, equals(0));
+ final dump = module.dump(StringBuffer()).toString();
+ expect(dump, '''= module user: 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_displayname: DataType.string "Anzeigename" options: unique
+ 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
+== page create: PageModelType.create options:
+ = section simpleForm1: SectionModelType.simpleForm options: [
+ allDbFields 26 text: options:
+ ] # create.simpleForm1
+== page change: PageModelType.change options:
+ = section simpleForm1: SectionModelType.simpleForm options: [
+ allDbFields 28 text: options:
+ ] # change.simpleForm1
+== page list: PageModelType.list options:
+ = section filterPanel1: SectionModelType.filterPanel options: [
+ textField user_name: options:
+ combobox user_role: texts: options:
+ ] # list.filterPanel1
+''');
+ });
+ test('configuration', () {
+ logger.clear();
+ final module = ConfigurationModel(logger);
+ module.parse();
+ expect(module.fullName(), equals('configuration'));
+ expect(module.widgetName(), equals('configuration'));
+ final errors = logger.errors;
+ expect(errors.length, equals(0));
+ final dump = module.dump(StringBuffer()).toString();
+ expect(dump, startsWith('''= module configuration: options:
+== table configuration: options:
+ column configuration_id: DataType.int "Id" options: primary notnull unique
+ column configuration_scope: DataType.string "Bereich" options: unique notnull
+ column configuration_property: DataType.string "Eigenschaft" options:
+ column configuration_order: DataType.int "Reihe" options:
+ 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
+== page create: PageModelType.create options:
+ = section simpleForm1: SectionModelType.simpleForm options: [
+ allDbFields '''));
+ /*
+ expect(dump.contains('''text: options:
+ ] # create.simpleForm1
+== page change: PageModelType.change options:
+ = section simpleForm1: SectionModelType.simpleForm options: [
+ allDbFields '''), isTrue);
+ */
+ expect(dump,
+ contains('combobox configuration_scope: texts: options: undef'));
+ expect(dump, contains('textField configuration_name: options:'));
+ });
+ });
+}
--- /dev/null
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// 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_bones/flutter_bones.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() async {
+ final logger = MemoryLogger(LEVEL_FINE);
+ setUpAll(() {
+ final configuration = BaseConfiguration({
+ 'client': {
+ 'host': 'localhost',
+ 'port': 58011,
+ 'schema': 'http',
+ 'application': 'unittest',
+ 'version': '1.0.0',
+ }
+ }, logger);
+ RestPersistence.fromConfig(configuration, logger);
+ });
+ group('queries', () {
+ test('list', () async {
+ final rest = RestPersistence();
+ final list = await rest
+ .list(module: 'role', sqlName: 'list', params: {'role_name': 'A'});
+ expect(list.length, equals(1));
+ expect(list[0].containsKey('role_id'), isTrue);
+ });
+ test('record', () async {
+ final rest = RestPersistence();
+ final record = await rest.record(module: 'role', id: 2);
+ expect(record.length, greaterThan(5));
+ expect(record.containsKey('role_id'), isTrue);
+ });
+ });
+}
expect(errors.length, equals(0));
expect(content, equals('''DROP TABLE IF EXISTS role;
CREATE TABLE role (
- role_id INT(10) notnull unique,
- role_name VARCHAR(32) unique notnull,
+ role_id INT(10) UNSIGNED NOT NULL UNIQUE AUTO_INCREMENT,
+ role_name VARCHAR(32) UNIQUE NOT NULL,
role_priority INT(10),
role_active CHAR(1),
- role_createdat TIMESTAMP null,
+ role_createdat TIMESTAMP NULL,
role_createdby VARCHAR(16),
- role_changedat TIMESTAMP null,
+ role_changedat TIMESTAMP NULL,
role_changedby VARCHAR(16),
PRIMARY KEY(role_id)
);