--- /dev/null
+#! /bin/bash
+cd lib
+dart format --fix .
+cd ..
+
if (className[ix] == upperCase[ix] && ix > 0) {
rc += '_';
}
- rc = className[ix];
+ rc += className[ix];
}
return rc.toLowerCase();
}
class Generator {
BaseLogger logger = globalLogger;
Generator(this.logger);
+
/// Appends a [string] at the end of a [buffer].
/// If [buffer] is null a new instance is created.
/// If the last line in [buffer] has a length greater than [maxLength]
/// Returns a DDL statement creating the database table of the [module].
String createDbTable(ModuleMetaData module) {
final tableName = module.tableName;
- final list = module.list;
+ final list = module.propertyList;
final buffer = StringBuffer();
buffer.write('-- DO NOT CHANGE. This file is created by the meta_tool\n');
buffer.write('CREATE TABLE $tableName (\n');
/// Returns a Dart class definition of the [module].
String createModuleData(ModuleMetaData module) {
final buffer = StringBuffer();
- buffer.write('// DO NOT CHANGE. This file is created by the meta_tool\n');
- buffer.write("import '../../base/defines.dart';\n");
- buffer.write("import '../../base/helper.dart';\n");
- buffer.write('class ${module.moduleNameSingular}Data{\n');
- for (var item in module.list) {
- buffer.write(' ' + module.dartType(item.dataType) + '? ${item.name};\n');
+ PropertyMetaData? primary = module.primaryOf();
+ buffer.writeln('// DO NOT CHANGE. This file is created by the meta_tool');
+ buffer.writeln("import '../../base/defines.dart';");
+ buffer.writeln("import '../../base/helper.dart';");
+ buffer.writeln("import '../../persistence/data_record.dart';");
+ final type = 'int';
+ buffer.writeln(
+ 'class ${module.moduleNameSingular}Data extends DataRecord<$type>{');
+ for (var item in module.propertyList) {
+ buffer.writeln(' ' + module.dartType(item.dataType) + '? ${item.name};');
}
- buffer.write(' ${module.moduleNameSingular}Data({');
+ buffer.writeln(' ${module.moduleNameSingular}Data({');
String separator = '';
- for (var item in module.list) {
+ for (var item in module.propertyList) {
buffer.write('$separator this.${item.name}');
separator = ',';
}
- buffer.write('});\n');
- buffer
- .write(' ${module.moduleNameSingular}Data.fromYaml(YamlMap data) {\n');
- for (var item in module.list) {
+ buffer.writeln('});');
+ buffer.writeln(' ${module.moduleNameSingular}Data.createFromMap(DataMap map) {');
+ buffer.writeln(' fromMap(map);');
+ buffer.writeln(' }');
+ buffer.writeln(' @override');
+ buffer.writeln(' void fromMap(DataMap map) {');
+ for (var item in module.propertyList) {
final name = item.columnName;
final type = item.dataType.toString();
//id = data.containsKey('id') ? int.tryParse(data['id']) : null;
- buffer.write(
- " ${item.name} = data.containsKey('$name') ? valueOf($type, data['$name']) : null;\n");
+ buffer.writeln(
+ " ${item.name} = map.containsKey('$name') ? valueOf($type, "
+ "map['$name']) : null;");
}
- buffer.write(' }\n');
- buffer.write(' static DataType? dataTypeOf(String name) {\n');
- buffer.write(' DataType? rc;\n');
- buffer.write(' switch(name){\n');
- for (var item in module.list) {
- buffer.write(" case '${item.name}':\n");
- buffer.write(' rc = ${item.dataType};\n');
- buffer.write(' break;\n');
+ buffer.writeln(' }');
+ final name = primary == null ? 'id' : primary.name;
+ final name2 = primary == null ? 'id' : primary.columnName;
+ buffer.write(''' @override
+ int keyOf() {
+ return $name ?? 0;
+ }
+ @override
+ String nameOfKey(){
+ return '$name2';
+ }
+''');
+ buffer.writeln(' static DataType? dataTypeOf(String name) {');
+ buffer.writeln(' DataType? rc;');
+ buffer.writeln(' switch(name){');
+ for (var item in module.propertyList) {
+ buffer.writeln(" case '${item.name}':");
+ buffer.writeln(' rc = ${item.dataType};');
+ buffer.writeln(' break;');
}
buffer.write(''' default:
break;
return rc;
}
''');
- buffer.write('}\n');
-
+ buffer.write(''' @override
+ DataMap toMap({DataMap? map, bool clear = true}) {
+ map ??= DataMap();
+ if (clear) {
+ map.clear();
+ }
+''');
+ for (var item in module.propertyList) {
+ final name1 = item.columnName;
+ final name2 = item.name;
+ buffer.writeln(" map['$name1'] = $name2;");
+ }
+ buffer.write(''' return map;
+ }
+}
+''');
return buffer.toString();
}
String createModules() {
final buffer = StringBuffer();
buffer.write('// DO NOT CHANGE. This file is created by the meta_tool\n');
- buffer.write("import 'dart:io';\n");
buffer.write("import 'module_meta_data.dart';\n");
final modules = <String>[];
final files = <String>[];
String createSqlStatements(ModuleMetaData module) {
final moduleName = module.moduleName;
final tableName = module.tableName;
- final list = module.list;
+ final list = module.propertyList;
final buffer = StringBuffer();
buffer.write('---\n');
buffer.write('# DO NOT CHANGE. This file is created by the meta_tool\n');
void out(String line) {
logger.log(line);
}
+
/// Generates all modules defined in the meta data.
void updateModules(Generator generator) {
writeFile('lib/meta/modules.dart', createModules());
}
}
}
+
+ /// Generates the modules.dart.
+ void updateModulesList(Generator generator) {
+ writeFile('lib/meta/modules.dart', createModules());
+ }
+
/// Generates the Sql statement file for each module defined in the meta data.
void updateSql(Generator generator) {
final modules = moduleNames();
if (module == null) {
logger.error('+++ unknown module: $name');
} else {
+ logger.log('current directory: ${Directory.current.path}');
String filename = 'rest_server/data/sql/${name.toLowerCase()}.sql.yaml';
writeFile(filename, generator.createSqlStatements(module));
}
}
}
+ /// Writes a given [contents] into a file named [filename];
void writeFile(String filename, String contents) {
logger.log('creating $filename ...');
final file = File(filename);
+ final parent = Directory(dirname(filename));
+ if (!parent.existsSync()) {
+ parent.createSync(recursive: true);
+ }
file.writeAsStringSync(contents);
}
}
case 'all-modules':
generator.out(moduleNames().join('\n'));
break;
- case 'print-modules':
+ case 'print-modules-list':
generator.out(generator.createModules());
break;
case 'print-table':
case 'update-modules':
generator.updateModules(generator);
break;
+ case 'update-modules-list':
+ generator.updateModulesList(generator);
+ break;
case 'update-sql':
generator.updateSql(generator);
break;
MODE:
all-modules
Prints the module names.
- print-modules
+ print-modules-list
Prints the file lib/meta/modules.dart.
+ That works without of meta-data: It can be called before
+ creating a the meta_tool.
print-table MODULE
Prints the SQL table definition of MODULE.
print-data MODULE
Creates the data storage class of MODULE
print-sql MODULE
Creates the SQL statements (for the rest_server) of MODULE
+ update-modules-list
+ Generates the modules.dart file in the correct directory.
+ That works without of meta-data: It can be called before
+ creating a the meta_tool.
update-modules
Generates all module files depending on the meta data.
update-sql
enum ServerEnvironment { productive, development }
-typedef YamlMap = Map<String, dynamic>;
-typedef YamlList = List<YamlMap>;
+typedef JsonMap = Map<String, dynamic>;
+typedef JsonList = List<JsonMap>;
///JsonMap or JsonList
-typedef YamlData = Object;
+typedef JsonData = Object;
enum EventSource { undef, localData, remoteData }
enum EventCardinality { undef, record, list }
import 'package:dart_bones/dart_bones.dart';
+
typedef MapModule = Map<String, String>;
typedef MapPlural = Map<String, PluralInfo>;
+
class I18N {
static I18N? instance;
final BaseLogger logger;
final regExpTranslation = RegExp(r'^msgstr "(.+?)"$');
final regExpPlural = RegExp(r'^msg_plural "(.+?)"$');
final regExpList = RegExp(r'^msgstr\[\d+\] "(.+?)"$');
- I18nIo.internal(this.dataDirectory, BaseLogger logger) : super.internal(logger);
+ I18nIo.internal(this.dataDirectory, BaseLogger logger)
+ : super.internal(logger);
/// Puts a [key] with its [translation] into the [mapModule].
/// Returns the [module] entry of [mapModule].
if (lastEntry.list == null) {
lastEntry.list = <String>[];
int count = lastEntry.list!.length;
- if (count != int.parse(match!.group(1)!)){
+ if (count != int.parse(match!.group(1)!)) {
logger.error('wrong index in $line. Expected: $count');
}
lastEntry.list!.add(fromConstant(match.group(2)!));
-import 'package:dart_bones/dart_bones.dart';
-import 'package:exhibition/page/start_page.dart';
+import 'package:exhibition/page/page_collection.dart';
import 'package:flutter/material.dart';
-import 'page/users/users_list_page.dart';
+import 'package:dart_bones/dart_bones.dart';
+
+import 'page/start_page.dart';
import 'page/info_page.dart';
import 'page/log_page.dart';
-
import 'setting/global_data.dart';
+Route<dynamic>? _getRoute(RouteSettings settings) {
+ MaterialPageRoute? route;
+ StatefulWidget? page;
+ switch (settings.name) {
+ case '/start':
+ page = StartPage(globalLogger);
+ break;
+ case '/info':
+ page = InfoPage(GlobalData());
+ break;
+ case '/log':
+ page = LogPage(GlobalData());
+ break;
+ default:
+ page = PageCollection().newPageByRoute(settings.name ?? '');
+ break;
+ }
+ if (page != null) {
+ route = MaterialPageRoute<void>(
+ settings: settings,
+ builder: (BuildContext context) => page!,
+ fullscreenDialog: false,
+ );
+ }
+ return route;
+}
+
class ExhibitionApp extends StatefulWidget {
static BaseLogger logger = globalLogger;
);
}
}
-
-Route<dynamic>? _getRoute(RouteSettings settings) {
- MaterialPageRoute? route;
- StatefulWidget? page;
- switch (settings.name) {
- case '/users/list':
- page = UsersListPage(GlobalData());
- break;
- case '/start':
- page = StartPage(globalLogger);
- break;
- case '/info':
- page = InfoPage(GlobalData());
- break;
- case '/log':
- page = LogPage(GlobalData());
- break;
- }
- if (page != null) {
- route = MaterialPageRoute<void>(
- settings: settings,
- builder: (BuildContext context) => page!,
- fullscreenDialog: false,
- );
- }
- return route;
-}
+import 'package:flutter/material.dart';
+
import '../base/defines.dart';
+typedef MenuItemBuilder = List<DropdownMenuItem<T>> Function<T>(
+ PropertyMetaData propertyMetaData);
+
/// Describes a button of a page.
class ButtonMetaData extends WidgetMetaData {
ButtonMetaData(String name) : super(name, WidgetType.button) {
//@ToDo
}
}
+
/// Describes a field related to a database column.
class DbFieldMetaData extends WidgetMetaData {
PropertyMetaData? reference;
}
enum DisplayType {
- line,
- multiLine,
+ text,
combobox,
+ checkbox,
+ custom,
+}
+
+class DummyModule extends ModuleMetaData {
+ DummyModule() : super('', [], []);
}
/// Describes a field of the page.
DisplayType displayType;
final String options;
FieldMetaData(String name, this.options,
- {this.dataType = DataType.string, this.displayType = DisplayType.line})
+ {this.dataType = DataType.string, this.displayType = DisplayType.text})
: super(name, WidgetType.field);
}
];
/// The module name, e.g. users
- final String moduleName;
+ String moduleName = '';
/// The singular version of the module name, e.g. 'user'
- String moduleNameSingular;
+ String moduleNameSingular = '';
/// The related database table.
- String tableName;
- final bool shortModifiedLabel;
- List<PropertyMetaData> list;
+ String tableName = '';
+
+ /// If true the fields create, createdBy ... have the same ("short") column name
+ /// instead of prefix and name.
+ bool shortModifiedLabel = false;
+ List<PropertyMetaData> propertyList;
+ List<PageMetaData> pageList;
final Map<String, PropertyMetaData> properties = {};
- String columnPrefix;
- ModuleMetaData(this.moduleName, this.list,
- {this.tableName = '',
- this.moduleNameSingular = '',
- this.columnPrefix = '',
- this.shortModifiedLabel = false}) {
- tableName = tableName.isEmpty ? moduleName : tableName;
- moduleNameSingular = moduleNameSingular.isEmpty
- ? (moduleName.endsWith('s')
- ? moduleName.substring(0, moduleName.length - 1)
- : moduleName)
- : moduleNameSingular;
- columnPrefix = columnPrefix.isNotEmpty
+ String columnPrefix = '';
+ ModuleMetaData(this.moduleName, this.propertyList, this.pageList,
+ {String tableName = '',
+ String moduleNameSingular = '',
+ String columnPrefix = '',
+ bool shortModifiedLabel = false}) {
+ this.tableName = tableName.isEmpty ? moduleName : tableName;
+ this.shortModifiedLabel = shortModifiedLabel;
+ if (moduleNameSingular.isEmpty) {
+ if (!moduleName.endsWith('s')) {
+ moduleNameSingular = moduleName;
+ } else {
+ final length = moduleName.length - 1;
+ moduleNameSingular = moduleName.substring(0, length);
+ }
+ }
+ this.moduleNameSingular = moduleNameSingular;
+ this.columnPrefix = columnPrefix.isNotEmpty
? columnPrefix
- : moduleNameSingular.toLowerCase();
- for (var item in list) {
+ : this.moduleNameSingular.toLowerCase();
+
+ for (var item in propertyList) {
item.module = this;
properties[item.name] = item;
if (item.columnName.isEmpty) {
final prefix = shortModifiedLabel && metaColumns.contains(item.name)
? ''
- : columnPrefix + '_';
+ : (this.columnPrefix + '_');
item.columnName = prefix + item.name.toLowerCase();
}
}
+ for (var item in pageList) {
+ item.module = this;
+ }
}
/// Returns a given [dataType] as string.
return rc;
}
+ /// Returns the primary key of the relation.
+ PropertyMetaData? primaryOf() {
+ final rc = propertyList.firstWhere(
+ (element) => element.options.contains('primary'),
+ orElse: null);
+ return rc;
+ }
+
/// Returns the properties that are not in [metaColumns].
/// : if the name of the property is [included] the property is always part of
/// the result.
Iterable<PropertyMetaData> standardColumns([String included = '']) {
- final rc = list.where(
+ final rc = propertyList.where(
(item) => item.name == included || !metaColumns.contains(item.name));
return rc;
}
class PageMetaData {
final String name;
final PageType pageType;
+ ModuleMetaData module = DummyModule();
List<FieldMetaData>? fields;
PageMetaData(
this.name,
class PropertyMetaData {
/// Name of the property (Dart name).
final String name;
- ModuleMetaData module = ModuleMetaData('', []);
+ ModuleMetaData module = DummyModule();
final String label;
+ /// Relative width of the field in a 12 column form: 1 <= width <= 12
+ int weight = 6;
+
/// A colon delimited list of options, e.g. ':notnull:unique:'.
final String options;
-
- /// Empty or 'combobox' or 'checkbox'.
- final String widgetType;
+ final DisplayType displayType;
final DataType dataType;
+ MenuItemBuilder? menuItemBuilder;
/// The size if dataType is DataType.string.
final int size;
/// The foreign key if dataType is DataType.reference, e.g. 'users.user_id'
String? reference;
- PropertyMetaData(
- this.name, this.dataType, this.options, this.widgetType, this.label,
- {this.columnName = '', this.size = 0, this.reference});
+ PropertyMetaData(this.name, this.label, this.dataType, this.options,
+ {this.displayType = DisplayType.text,
+ this.columnName = '',
+ this.size = 0,
+ this.reference,
+ int weight = 6}) {
+ this.weight = weight > 12 ? 12 : weight;
+ }
+
+ /// Returns whether a given [option] is set.
+ ///
+ /// [option] is a word delimited by ':', e.g. ":notnull:"
+ ///
+ /// Returns true, if [option] is part of [options], false otherwise.
+ bool hasOption(String option) {
+ return options.contains(option);
+ }
}
/// Describes a widget used in the page.
// DO NOT CHANGE. This file is created by the meta_tool
import 'module_meta_data.dart';
+import 'roles_meta.dart';
import 'users_meta.dart';
+
/// Returns the meta data of the module given by [name].
/// Returns null if not found.
ModuleMetaData? moduleByName(String name) {
ModuleMetaData? rc;
switch (name) {
+ case 'Roles':
+ rc = RoleMeta();
+ break;
case 'Users':
rc = UserMeta();
break;
}
return rc;
}
+
/// Returns the module names as string list.
-List<String> moduleNames(){
+List<String> moduleNames() {
return [
+ 'Roles',
'Users',
];
}
-
import '../base/defines.dart';
-import 'module_meta_data.dart';
import '../base/i18n.dart';
+import 'module_meta_data.dart';
final i18n = I18N();
final M = i18n.module("Roles");
class RoleMeta extends ModuleMetaData {
static RoleMeta instance = RoleMeta.internal();
- RoleMeta.internal()
- : super(
- 'Roles',
- [
- PropertyMetaData('id', DataType.reference, ':primary:', 'combo',
- i18n.tr('Id')),
- PropertyMetaData(
- 'name', DataType.string, ':notnull:', '', i18n.tr('Name'),
- size: 64),
- PropertyMetaData('created', DataType.datetime, ':hidden:', '',
- i18n.tr('Created')),
- PropertyMetaData('createdBy', DataType.string, ':hidden:', '',
- i18n.tr('Created by'),
- size: 32),
- PropertyMetaData('changed', DataType.datetime, ':hidden:', '',
- i18n.tr('Changed')),
- PropertyMetaData('changedBy', DataType.string, ':hidden:', '',
- i18n.tr('Changed by'),
- size: 32),
- ],
- );
factory RoleMeta() {
return instance;
}
+ RoleMeta.internal()
+ : super('Roles', [
+ PropertyMetaData('id', i18n.tr('Id'), DataType.reference, ':primary:',
+ displayType: DisplayType.combobox),
+ PropertyMetaData(
+ 'name', i18n.tr('Name'), DataType.string, ':notnull:',
+ size: 64),
+ PropertyMetaData(
+ 'created', i18n.tr('Created'), DataType.datetime, ':hidden:'),
+ PropertyMetaData(
+ 'createdBy', i18n.tr('Created by'), DataType.string, ':hidden:',
+ size: 32),
+ PropertyMetaData(
+ 'changed', i18n.tr('Changed'), DataType.datetime, ':hidden:'),
+ PropertyMetaData(
+ 'changedBy', i18n.tr('Changed by'), DataType.string, ':hidden:',
+ size: 32),
+ ], [
+ PageMetaData('New Role', PageType.create, []),
+ PageMetaData('Change Role', PageType.edit, []),
+ PageMetaData('Roles Overview', PageType.list, []),
+ ]);
}
import '../base/defines.dart';
-import 'module_meta_data.dart';
import '../base/i18n.dart';
+import 'module_meta_data.dart';
+
final i18n = I18N();
final M = i18n.module("Users");
class UserMeta extends ModuleMetaData {
static UserMeta instance = UserMeta.internal();
- UserMeta.internal()
- : super(
- 'Users',
- [
- PropertyMetaData('id', DataType.reference, ':primary:', 'combo',
- i18n.tr('Id')),
- PropertyMetaData('name', DataType.string, ':notnull:', '',
- i18n.tr('Name'),
- size: 64),
- PropertyMetaData('displayName', DataType.string, ':unique:notnull:',
- '', i18n.tr('Display name', M),
- size: 32),
- PropertyMetaData('email', DataType.string, ':unique:notnull:', '',
- i18n.tr('EMail', M),
- size: 255),
- PropertyMetaData('role', DataType.reference, ':notnull:', '',
- i18n.tr('Role'), reference: 'roles.role_id'),
- PropertyMetaData('created', DataType.datetime, ':hidden:', '',
- i18n.tr('Created')),
- PropertyMetaData('createdBy', DataType.string, ':hidden:', '',
- i18n.tr('Created by'),
- size: 32),
- PropertyMetaData('changed', DataType.datetime, ':hidden:', '',
- i18n.tr('Changed')),
- PropertyMetaData('changedBy', DataType.string, ':hidden:', '',
- i18n.tr('Changed by'),
- size: 32),
- ],
- );
factory UserMeta() {
return instance;
}
+ UserMeta.internal()
+ : super('Users', [
+ PropertyMetaData('id', i18n.tr('Id'), DataType.reference, ':primary:',
+ displayType: DisplayType.combobox),
+ PropertyMetaData(
+ 'name', i18n.tr('Name'), DataType.string, ':notnull:',
+ size: 64),
+ PropertyMetaData('displayName', i18n.tr('Display name', M),
+ DataType.string, ':unique:notnull:',
+ size: 32),
+ PropertyMetaData(
+ 'email', i18n.tr('EMail', M), DataType.string, ':unique:notnull:',
+ size: 255),
+ PropertyMetaData(
+ 'role', i18n.tr('Role'), DataType.reference, ':notnull:',
+ displayType: DisplayType.combobox, reference: 'roles.role_id'),
+ PropertyMetaData(
+ 'created', i18n.tr('Created'), DataType.datetime, ':hidden:'),
+ PropertyMetaData(
+ 'createdBy', i18n.tr('Created by'), DataType.string, ':hidden:',
+ size: 32),
+ PropertyMetaData(
+ 'changed', i18n.tr('Changed'), DataType.datetime, ':hidden:'),
+ PropertyMetaData(
+ 'changedBy', i18n.tr('Changed by'), DataType.string, ':hidden:',
+ size: 32),
+ ], [
+ PageMetaData('New User', PageType.create, []),
+ PageMetaData('Change User', PageType.edit, []),
+ PageMetaData('Delete User', PageType.delete, []),
+ PageMetaData('Users Overview', PageType.list, []),
+ ]);
}
) async {
if (href!.startsWith('file:')) {
if (++clickCounter > 3) {
- Navigator.pushNamed(context, '/configuration');
+ // globalData.navigate(context, '/configurations/base');
}
} else {
await canLaunch(href)
--- /dev/null
+import 'package:flutter/material.dart';
+import '../setting/global_data.dart';
+
+import 'users/users_edit_page.dart';
+
+/// Manages all meta data driven pages of the program.
+class PageCollection {
+ static PageCollection? _instance;
+ PageCollection.internal();
+ factory PageCollection() {
+ _instance ??= PageCollection.internal();
+ return _instance!;
+ }
+ final map = <String, StatefulWidget>{};
+
+ /// Creates a page defined by a [route].
+ StatefulWidget? newPageByRoute(String route) {
+ StatefulWidget? rc;
+ final globalData = GlobalData();
+ switch (route) {
+ case '/users/edit':
+ rc = UsersEditPage(globalData);
+ break;
+ }
+ if (rc != null) {
+ map[route] = rc;
+ }
+ return rc;
+ }
+
+ /// Returns the last generated page of a given [route].
+ StatefulWidget? existingPageByRoute(String route) {
+ return map[route];
+ }
+}
--- /dev/null
+// DO NOT CHANGE. This file is created by the meta_tool
+import '../../base/defines.dart';
+import '../../base/helper.dart';
+import '../../persistence/data_record.dart';
+
+class RoleData extends DataRecord<int> {
+ int? id;
+ String? name;
+ DateTime? created;
+ String? createdBy;
+ DateTime? changed;
+ String? changedBy;
+ RoleData(
+ {this.id,
+ this.name,
+ this.created,
+ this.createdBy,
+ this.changed,
+ this.changedBy});
+ RoleData.createFromMap(DataMap map) {
+ fromMap(map);
+ }
+ @override
+ void fromMap(DataMap map) {
+ id = map.containsKey('role_id')
+ ? valueOf(DataType.reference, map['role_id'])
+ : null;
+ name = map.containsKey('role_name')
+ ? valueOf(DataType.string, map['role_name'])
+ : null;
+ created = map.containsKey('role_created')
+ ? valueOf(DataType.datetime, map['role_created'])
+ : null;
+ createdBy = map.containsKey('role_createdby')
+ ? valueOf(DataType.string, map['role_createdby'])
+ : null;
+ changed = map.containsKey('role_changed')
+ ? valueOf(DataType.datetime, map['role_changed'])
+ : null;
+ changedBy = map.containsKey('role_changedby')
+ ? valueOf(DataType.string, map['role_changedby'])
+ : null;
+ }
+
+ @override
+ int keyOf() {
+ return id ?? 0;
+ }
+
+ @override
+ String nameOfKey() {
+ return 'role_id';
+ }
+
+ static DataType? dataTypeOf(String name) {
+ DataType? rc;
+ switch (name) {
+ case 'id':
+ rc = DataType.reference;
+ break;
+ case 'name':
+ rc = DataType.string;
+ break;
+ case 'created':
+ rc = DataType.datetime;
+ break;
+ case 'createdBy':
+ rc = DataType.string;
+ break;
+ case 'changed':
+ rc = DataType.datetime;
+ break;
+ case 'changedBy':
+ rc = DataType.string;
+ break;
+ default:
+ break;
+ }
+ return rc;
+ }
+
+ @override
+ DataMap toMap({DataMap? map, bool clear = true}) {
+ map ??= DataMap();
+ if (clear) {
+ map.clear();
+ }
+ map['role_id'] = id;
+ map['role_name'] = name;
+ map['role_created'] = created;
+ map['role_createdby'] = createdBy;
+ map['role_changed'] = changed;
+ map['role_changedby'] = changedBy;
+ return map;
+ }
+}
import 'package:exhibition/setting/footer_exhibition.dart';
import 'package:flutter/material.dart';
+import '../setting/global_data.dart';
//import '../helper/exhibition_defines.dart';
import '../setting/installation.dart';
-import '../setting/global_data.dart';
class StartPage extends StatefulWidget {
final BaseLogger logger;
RestPersistence.fromConfig(configuration, logger),
logger);
globalData.initializeAsync().then((value) {
- Navigator.pushNamed(context, '/users/list');
+ globalData.navigate(context, '/users/edit');
});
}
});
});
- };
+ }
+ ;
});
});
}
// DO NOT CHANGE. This file is created by the meta_tool
import '../../base/defines.dart';
import '../../base/helper.dart';
-class UserData{
+import '../../persistence/data_record.dart';
+
+class UserData extends DataRecord<int> {
int? id;
String? name;
String? displayName;
String? createdBy;
DateTime? changed;
String? changedBy;
- UserData({ this.id, this.name, this.displayName, this.email, this.role, this.created, this.createdBy, this.changed, this.changedBy});
- UserData.fromYaml(YamlMap data) {
- id = data.containsKey('user_id') ? valueOf(DataType.reference, data['user_id']) : null;
- name = data.containsKey('user_name') ? valueOf(DataType.string, data['user_name']) : null;
- displayName = data.containsKey('user_displayname') ? valueOf(DataType.string, data['user_displayname']) : null;
- email = data.containsKey('user_email') ? valueOf(DataType.string, data['user_email']) : null;
- role = data.containsKey('user_role') ? valueOf(DataType.reference, data['user_role']) : null;
- created = data.containsKey('created') ? valueOf(DataType.datetime, data['created']) : null;
- createdBy = data.containsKey('createdby') ? valueOf(DataType.string, data['createdby']) : null;
- changed = data.containsKey('changed') ? valueOf(DataType.datetime, data['changed']) : null;
- changedBy = data.containsKey('changedby') ? valueOf(DataType.string, data['changedby']) : null;
+ UserData(
+ {this.id,
+ this.name,
+ this.displayName,
+ this.email,
+ this.role,
+ this.created,
+ this.createdBy,
+ this.changed,
+ this.changedBy});
+ UserData.createFromMap(DataMap map) {
+ fromMap(map);
+ }
+ @override
+ void fromMap(DataMap map) {
+ id = map.containsKey('user_id')
+ ? valueOf(DataType.reference, map['user_id'])
+ : null;
+ name = map.containsKey('user_name')
+ ? valueOf(DataType.string, map['user_name'])
+ : null;
+ displayName = map.containsKey('user_displayname')
+ ? valueOf(DataType.string, map['user_displayname'])
+ : null;
+ email = map.containsKey('user_email')
+ ? valueOf(DataType.string, map['user_email'])
+ : null;
+ role = map.containsKey('user_role')
+ ? valueOf(DataType.reference, map['user_role'])
+ : null;
+ created = map.containsKey('user_created')
+ ? valueOf(DataType.datetime, map['user_created'])
+ : null;
+ createdBy = map.containsKey('user_createdby')
+ ? valueOf(DataType.string, map['user_createdby'])
+ : null;
+ changed = map.containsKey('user_changed')
+ ? valueOf(DataType.datetime, map['user_changed'])
+ : null;
+ changedBy = map.containsKey('user_changedby')
+ ? valueOf(DataType.string, map['user_changedby'])
+ : null;
+ }
+
+ @override
+ int keyOf() {
+ return id ?? 0;
+ }
+
+ @override
+ String nameOfKey() {
+ return 'user_id';
}
+
static DataType? dataTypeOf(String name) {
DataType? rc;
- switch(name){
- case 'id':
- rc = DataType.reference;
- break;
- case 'name':
- rc = DataType.string;
- break;
- case 'displayName':
- rc = DataType.string;
- break;
- case 'email':
- rc = DataType.string;
- break;
- case 'role':
- rc = DataType.reference;
- break;
- case 'created':
- rc = DataType.datetime;
- break;
- case 'createdBy':
- rc = DataType.string;
- break;
- case 'changed':
- rc = DataType.datetime;
- break;
- case 'changedBy':
- rc = DataType.string;
- break;
- default:
- break;
+ switch (name) {
+ case 'id':
+ rc = DataType.reference;
+ break;
+ case 'name':
+ rc = DataType.string;
+ break;
+ case 'displayName':
+ rc = DataType.string;
+ break;
+ case 'email':
+ rc = DataType.string;
+ break;
+ case 'role':
+ rc = DataType.reference;
+ break;
+ case 'created':
+ rc = DataType.datetime;
+ break;
+ case 'createdBy':
+ rc = DataType.string;
+ break;
+ case 'changed':
+ rc = DataType.datetime;
+ break;
+ case 'changedBy':
+ rc = DataType.string;
+ break;
+ default:
+ break;
}
return rc;
}
-}
+ @override
+ DataMap toMap({DataMap? map, bool clear = true}) {
+ map ??= DataMap();
+ if (clear) {
+ map.clear();
+ }
+ map['user_id'] = id;
+ map['user_name'] = name;
+ map['user_displayname'] = displayName;
+ map['user_email'] = email;
+ map['user_role'] = role;
+ map['user_created'] = created;
+ map['user_createdby'] = createdBy;
+ map['user_changed'] = changed;
+ map['user_changedby'] = changedBy;
+ return map;
+ }
+}
+++ /dev/null
-import 'package:equatable/equatable.dart';
-import 'package:exhibition/setting/global_data.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-import '../../base/defines.dart';
-import '../../persistence/rest_persistence.dart';
-import 'user_data.dart';
-
-class RemoteListUserEvent extends UserEvent {
- final Map<String, dynamic> filters;
- RemoteListUserEvent(this.filters)
- : super(EventSource.remoteData, EventCardinality.list);
- @override
- List<Object> get props => [filters];
-}
-
-class UserBloc extends Bloc<UserEvent, UserState> {
- final YamlList? recordList;
-
- UserBloc({this.recordList}) : super(UserStateInitial());
-
- @override
- Stream<UserState> mapEventToState(
- UserEvent event,
- ) async* {
- if (event is RemoteListUserEvent) {
- final list = await GlobalData()
- .restPersistence
- ?.query(what: 'query', data: event.filters);
- yield UserListRemote(list ?? []);
- } else {
- yield UserStateInitial();
- }
- }
-}
-
-abstract class UserEvent extends Equatable {
- final EventSource eventSource;
- final EventCardinality eventCardinality;
- UserEvent(this.eventSource, this.eventCardinality);
-}
-
-class UserListRemote extends UserState {
- final YamlList records;
- UserListRemote(this.records);
- @override
- List<Object> get props => [records];
-}
-
-abstract class UserState extends Equatable {}
-
-class UserStateInitial extends UserState {
- @override
- List<Object> get props => [];
-}
--- /dev/null
+import 'package:flutter/material.dart';
+
+import '../../base/i18n.dart';
+import '../../meta/users_meta.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+
+final i18n = I18N();
+
+class UsersEditPage extends StatefulWidget {
+ final GlobalData globalData;
+ final PageStates pageStates = PageStates();
+ UsersEditPage(this.globalData) : super();
+ @override
+ _UsersEditPageState createState() {
+ final rc = _UsersEditPageState();
+ rc.attendedPage =
+ AttendedPage(this, rc, globalData, UserMeta.instance, pageStates);
+ pageStates.attendedPage = rc.attendedPage;
+ return rc;
+ }
+}
+
+class _UsersEditPageState extends State<UsersEditPage> {
+ AttendedPage? attendedPage;
+
+ final nameController = TextEditingController();
+ final globalData = GlobalData();
+ @override
+ Widget build(BuildContext context) {
+ final padding = 16.0;
+ final rc = Scaffold(
+ appBar: globalData.appBarBuilder(i18n.tr('Change User data')),
+ drawer: globalData.drawerBuilder(context),
+ body: SafeArea(
+ child: WidgetForm.flexibleGrid(attendedPage!.attendedWidgets(),
+ screenWidth: attendedPage!.pageStates.screenWidth,
+ padding: padding)));
+ return rc;
+ }
+
+ @override
+ void didChangeDependencies() {
+ final size = MediaQuery.of(context).size;
+ attendedPage!.pageStates.screenWidth = size.width;
+ attendedPage!.pageStates.screenHeight = size.height;
+ super.didChangeDependencies();
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ }
+}
+++ /dev/null
-import 'package:exhibition/setting/global_data.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import '../../base/defines.dart';
-import 'user_state.dart';
-import 'package:equatable/equatable.dart';
-import '../../base/i18n.dart';
-final i18n = I18N();
-class UsersListPage extends StatefulWidget {
- final GlobalData globalData;
- UsersListPage(this.globalData): super();
- @override
- _UsersListPageState createState() => _UsersListPageState();
-}
-
-class _UsersListPageState extends State<UsersListPage> {
- double? width;
-
- @override
- void initState() {
- //context.read<UserBloc>().add(UserEvent(EventSource.undef, EventCardinality.undef));
- super.initState();
- }
-
- @override
- void didChangeDependencies() {
- width = MediaQuery.of(context).size.width;
- super.didChangeDependencies();
- }
- final nameController = TextEditingController();
- @override
- Widget build(BuildContext context) {
- final padding = 16.0;
- return BlocBuilder<UserBloc, UserState>(
- builder: (context, UserState state) {
- return Column(
- children: [
- TextFormField(
- controller: nameController,
- readOnly: true,
- decoration: InputDecoration(labelText: i18n.tr('Name')),
- ),
- SizedBox(height: padding),
- Text('Liste'),
- Spacer(),
- //UserHistoryContainer(
- // calculations: state.history.reversed.toList())
- ],
- );
- },
- );
- }
-
-}
--- /dev/null
+typedef DataMap = Map<String, dynamic>;
+
+/// Implements a base class for entities handled in [Persistence].
+///
+/// Such an entity is a collection of "values" like a table in a relational
+/// database with its columns as "values".
+///
+/// T is the data type of the key "value", which is unique over all records.
+abstract class DataRecord<T> {
+ /// Takes the data from the [map] and put it in the instance of the
+ /// derived class.
+ void fromMap(DataMap map);
+
+ /// Returns the "key" of the record. This a unique value over all
+ /// existing records.
+ T keyOf();
+
+ /// Returns name of the "key" of the record, for example "id".
+ String nameOfKey();
+
+ /// Stores the "values" of the derived class as a Json like map.
+ ///
+ /// If [map] is not null this map is used for storage and that map is returned.
+ ///
+ /// If [clear] is true and [map] is not null [map] will be cleared before
+ /// populating that.
+ ///
+ /// Returns a map with the "values" of the derived class. If [map] is not
+ /// null [map] will be returned.
+ DataMap toMap({DataMap? map, bool clear = true});
+}
--- /dev/null
+import 'data_record.dart';
+
+typedef RecordFilter = bool Function<T>(DataRecord<T> element);
+typedef KeyFilter = bool Function<T>(T key);
+
+/// Implements a container of [DataRecord]s.
+///
+/// T is the data type of the primary key in [DataRecord].
+class DataTable<T> {
+ final Map<T, DataRecord<T>> records = {};
+ final keys = <List<T>>[];
+
+ /// Returns the record with the given [key] or null if not found.
+ DataRecord<T>? byKey(T key) {
+ final rc = records[key];
+ return rc;
+ }
+
+ /// Returns a sequence of [DataRecord]s.
+ ///
+ /// [recordFilter]: Must return true on a given record.
+ ///
+ /// [keyFilter]: Must return true on a given key.
+ ///
+ /// If both filters are not null the [keyFilter] is applied first.
+ ///
+ /// Returns the records matching the filters. If no filter is given all
+ /// records are returned.
+ Iterable<DataRecord<T>> recordsOf<T>(
+ {RecordFilter? recordFilter, KeyFilter? keyFilter}) {
+ final keys2 =
+ keyFilter == null ? keys : keys.where((element) => keyFilter(element));
+ final records2 = keys2.map((key) => records[key]);
+ final rc = recordFilter == null
+ ? records2
+ : records2.where((element) => recordFilter(element as DataRecord<T>));
+ return rc as Iterable<DataRecord<T>>;
+ }
+}
--- /dev/null
+import 'dart:convert' as convert;
+import 'dart:io';
+
+import 'package:dart_bones/dart_bones.dart';
+import 'package:path/path.dart' as path;
+
+import 'persistence.dart';
+import 'data_record.dart';
+
+/// Implements a persistence layer storing data in Json files.
+class FilePersistence extends Persistence {
+ final BaseLogger logger;
+ String dataDirectory;
+ //String configDirectory;
+
+ FilePersistence(
+ {required this.dataDirectory,
+ //required this.configDirectory,
+ required this.logger});
+
+ FilePersistence.fromConfig(BaseConfiguration configuration, BaseLogger logger,
+ {String section = 'persistence'})
+ : this(
+ dataDirectory:
+ configuration.asString('sampleDirectory', section: section) ??
+ '',
+ // configDirectory:
+ // configuration.asString('configDirectory', section: section) ??
+ // '',
+ logger: logger);
+
+ @override
+ Future<dynamic> query({required String what, DataMap? data}) async {
+ var rc;
+ final fn = path.join(dataDirectory, '$what.json');
+ final file = File(fn);
+ if (!await file.exists()) {
+ logger.error('missing $fn');
+ } else {
+ final content = await file.readAsString();
+ rc = convert.jsonDecode(content);
+ }
+
+ return rc;
+ }
+
+ @override
+ Future<String> store(
+ {required String what, DataMap? map, DataRecord? dataContainer}) async {
+ String rc = 'OK';
+ assert(map != null && dataContainer == null ||
+ map == null && dataContainer != null);
+ if (dataContainer != null) {
+ map = dataContainer.toMap();
+ }
+ final fn = path.join(dataDirectory, '$what.json');
+ final file = File(fn);
+ logger.log('Storing data in $what.json');
+ try {
+ final content = convert.jsonEncode(map);
+ await file.writeAsString(content);
+ } on FileSystemException catch (exc) {
+ logger.error(rc = 'cannot write into $fn: $exc');
+ }
+ return rc;
+ }
+}
+import 'data_record.dart';
+
abstract class Persistence {
/// Fetches data specified by [what] and some [data].
+ ///
/// Returns null or a record (as a Map instance)
/// or a list of records (as a List<Map> instance).
- Future<dynamic> query({required String what, Map<String, dynamic>? data});
+ Future<dynamic> query({required String what, DataMap? data});
- /// Executes a SQL statement with not or a very short result:
- /// insert: answers the primary key.
- /// update: returns the count of changed records.
- /// delete: ...
- /// Returns the answer. If a blank is part of the answer that is an error
- /// message.
+ /// Stores a map or a data container in the persistence layer.
+ ///
+ /// [what] specifies the kind of data. The backend server must "understand"
+ /// that value.
+ ///
+ /// [map] is a Json like map with the data to store.
+ ///
+ /// [dataContainer] is the container with the values to store.
+ ///
+ /// Exactly one of [map] must be null and the other must be not null.
+ ///
+ /// Returns the "answer" of the persistence layer. Convention: a single word
+ /// defines a state like "OK" and a blank inside defines an human readable
+ /// error message.
Future<String> store(
- {required String what, required Map<String, dynamic> data});
+ {required String what, DataMap? map, DataRecord? dataContainer});
}
import 'package:dart_bones/dart_bones.dart';
import 'package:http/http.dart' as http;
+import 'data_record.dart';
import 'persistence.dart';
import '../setting/error_handler_exhibition.dart';
netStatus = NetStatus.noConnection;
errorHandler?.criticalError(exc.toString(),
caller: 'RestPersistence.runRequest');
- } on Exception catch(exc){
+ } on Exception catch (exc) {
logger.error('$exc');
netStatus = NetStatus.noConnection;
}
@override
Future<String> store(
- {required String what, Map<String, dynamic>? data}) async {
+ {required String what, DataMap? map, DataRecord? dataContainer}) async {
var rc = 'OK';
- final data2 = data == null ? '{}' : convert.jsonEncode(data);
- final answer = await runRequest(what, body: data2, headers: jsonHeader);
+ assert(map != null && dataContainer == null ||
+ map == null && dataContainer != null);
+ if (dataContainer != null) {
+ map = dataContainer.toMap();
+ }
+ final data = convert.jsonEncode(map);
+ final answer = await runRequest(what, body: data, headers: jsonHeader);
if (answer.isNotEmpty) {
rc = answer;
}
String rc;
final params2 = data == null ? '{}' : convert.jsonEncode(data);
final answer = await runRequest(what, body: params2, headers: jsonHeader);
- if (answer.isNotEmpty && (answer.startsWith('{') || answer.startsWith('['))) {
+ if (answer.isNotEmpty &&
+ (answer.startsWith('{') || answer.startsWith('['))) {
rc = convert.jsonDecode(answer);
} else {
rc = answer;
return rc;
}
}
-enum NetStatus {
- undefined, noConnection, fail, ok
-}
\ No newline at end of file
+
+enum NetStatus { undefined, noConnection, fail, ok }
: super(title: Text(title), key: key);
static AppBarBuilder builder() =>
- (String title) => AppBarExhibition(title: title);
+ (String title) => AppBarExhibition(title: title);
}
.map((item) => ListTile(
title: Text(item.title),
onTap: () {
- // What happens after you tap the navigation item
Navigator.push(
context,
MaterialPageRoute(
import 'package:dart_bones/dart_bones.dart';
+import 'package:exhibition/page/page_collection.dart';
import 'package:exhibition/persistence/rest_persistence.dart';
+import 'package:exhibition/widget/attended_page.dart';
import 'package:flutter/material.dart';
import '../base/defines.dart';
final FooterBuilder footerBuilder;
final BaseConfiguration configuration;
final RestPersistence? restPersistence;
+ AttendedPage? currentPage;
factory GlobalData() => _instance ?? GlobalData();
GlobalData.dummy()
/// [configuration]: general settings.
/// [appBarBuilder]: a factory to create the Hamburger menu.
/// [footerBuilder]: a factory to create a footer area.
- GlobalData.internal(this.configuration, this.appBarBuilder,
- this.drawerBuilder, this.footerBuilder,
- this.restPersistence, this.logger) {
+ GlobalData.internal(
+ this.configuration,
+ this.appBarBuilder,
+ this.drawerBuilder,
+ this.footerBuilder,
+ this.restPersistence,
+ this.logger) {
logger.log('Start');
_instance = this;
}
- Future initializeAsync() async{
+ Future initializeAsync() async {
// Do customize!
return;
}
+
+ /// Switches the page given by a [route].
+ void navigate(BuildContext context, String route) {
+ final page = PageCollection().existingPageByRoute(route);
+ if (page != null) {
+ currentPage = page as AttendedPage;
+ }
+ Navigator.pushNamed(context, route);
+ }
}
--- /dev/null
+import 'package:exhibition/widget/attended_widget.dart';
+import 'package:flutter/material.dart';
+
+import '../../base/i18n.dart';
+import '../../setting/global_data.dart';
+import '../meta/module_meta_data.dart';
+
+final i18n = I18N();
+
+/// Links some instances belonging to a module page (given as
+/// member "statefulWidget").
+///
+/// Note: There may be several instances of AttendedPage belonging to one
+/// derived class of StatefulWidget (member "statefulWidget"). Do not store
+/// states directly in this class. Store it in [PageStates].
+class AttendedPage {
+ final ModuleMetaData moduleMetaData;
+ final GlobalData globalData;
+ final StatefulWidget statefulWidget;
+ final State<StatefulWidget> state;
+ final PageStates pageStates;
+ AttendedPage(this.statefulWidget, this.state, this.globalData,
+ this.moduleMetaData, this.pageStates);
+
+ /// Returns the list of attended widgets of this page.
+ List<AttendedWidget> attendedWidgets() {
+ final rc = <AttendedWidget>[];
+ for (var item in moduleMetaData.propertyList) {
+ if (!item.hasOption(':hidden:')) {
+ switch (item.displayType) {
+ case DisplayType.text:
+ rc.add(TextAttended(item, this));
+ break;
+ case DisplayType.combobox:
+ rc.add(ComboboxAttended(item, this));
+ break;
+ case DisplayType.checkbox:
+ throw FormatException(
+ 'AttendedPage.widgetOf(): not implemented: checkbox');
+ case DisplayType.custom:
+ throw FormatException(
+ 'AttendedPage.widgetOf(): not implemented: custom');
+ }
+ }
+ }
+ return rc;
+ }
+
+ /// Validates the text [value] of the [textAttended].
+ ///
+ /// Returns null on success or the error message on error.
+ String? validateText(String? value, TextAttended textAttended) {
+ PropertyMetaData property = textAttended.propertyMetaData;
+ String? rc;
+ if ((value == null || value.isEmpty) &&
+ (property.hasOption(':mandatory:') ||
+ property.hasOption(':notnull:'))) {
+ rc = i18n.tr('This field must not be empty.');
+ }
+ return rc;
+ }
+}
+
+enum DeliveryState { undef, waiting, delivered }
+
+class PageStates {
+ final DeliveryState deliveryState = DeliveryState.undef;
+ double screenWidth = 0;
+ double screenHeight = 0;
+ AttendedPage? attendedPage;
+}
--- /dev/null
+import 'package:flutter/material.dart';
+
+import '../meta/module_meta_data.dart';
+import '../widget/attended_page.dart';
+
+/// Manages a widget controlled by the meta data.
+abstract class AttendedWidget {
+ final AttendedPage attendedPage;
+ final PropertyMetaData propertyMetaData;
+
+ AttendedWidget(this.propertyMetaData, this.attendedPage);
+ Widget widgetOf();
+}
+
+class ComboboxAttended<T> extends FieldAttended {
+ T? value;
+ MenuItemBuilder menuItemBuilder =
+ <T>(PropertyMetaData _) => <DropdownMenuItem<T>>[];
+ // MenuItemBuilder menuItemBuilder =
+ // ((PropertyMetaData _) => <DropdownMenuItem<T>>[]) as MenuItemBuilder;
+ ComboboxAttended(PropertyMetaData propertyMetaData, AttendedPage attendedPage)
+ : super(propertyMetaData, attendedPage);
+ Widget widgetOf() {
+ final rc = DropdownButtonFormField<T>(
+ value: value,
+ items: menuItemBuilder(propertyMetaData),
+ isExpanded: true,
+ decoration: InputDecoration(labelText: propertyMetaData.label),
+ onChanged: (value) => this.value = value,
+ );
+ return rc;
+ }
+}
+
+abstract class FieldAttended extends AttendedWidget {
+ var readonly = false;
+ var enabled = true;
+ FieldAttended(PropertyMetaData propertyMetaData, AttendedPage attendedPage)
+ : super(propertyMetaData, attendedPage);
+}
+
+/// Implements a single or multiline line text field controlled by the
+/// meta data of a module.
+class TextAttended extends FieldAttended {
+ final controller = TextEditingController();
+ int minLines = 1;
+ int maxLines = 1;
+ String value = '';
+
+ TextAttended(PropertyMetaData propertyMetaData, AttendedPage attendedPage)
+ : super(propertyMetaData, attendedPage);
+
+ /// Returns a concrete widget representing the instance.
+ Widget widgetOf() {
+ final rc = TextFormField(
+ controller: controller,
+ minLines: minLines,
+ maxLines: maxLines,
+ onSaved: (value) => this.value = value ?? '',
+ validator: (value) => attendedPage.validateText(value, this),
+ decoration: InputDecoration(labelText: propertyMetaData.label),
+ );
+ return rc;
+ }
+}
--- /dev/null
+import 'package:exhibition/widget/attended_widget.dart';
+import 'package:flutter/material.dart';
+
+class WidgetForm {
+ /// Places the widgets in a flexible grid.
+ ///
+ /// Returns a column of rows.
+ ///
+ /// The place of the row is divided in 12 segments with the same width.
+ /// Each widget has a "weight" which means the count of segments to use.
+ ///
+ /// [widgets] is the list of widgets to position in the grid.
+ ///
+ /// [screenWidth] is the width of the output device in pixel.
+ ///
+ /// [minWidth] is the minimum width of the above defined segment.
+ /// If the [screenwidth] has not place for 6 segments all widgets are placed
+ /// in a single row.
+ static Widget flexibleGrid(List<AttendedWidget> widgets,
+ {required double screenWidth,
+ double minWidth = 800,
+ double padding = 16.0}) {
+ Widget rc;
+ if (minWidth > screenWidth) {
+ final children = widgets.map((element) => element.widgetOf()).toList();
+ rc = Column(children: children);
+ } else {
+ int position = 0;
+ final List<Widget> childrenColumn = [];
+ List<Widget> childrenRow = [];
+ for (var widget in widgets) {
+ final flex = widget.propertyMetaData.weight;
+ if (position + flex <= 12) {
+ var child2 = position == 0
+ ? widget.widgetOf()
+ : Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ SizedBox(width: padding),
+ Expanded(child: widget.widgetOf())
+ ],
+ );
+ childrenRow.add(Expanded(flex: flex, child: child2));
+ position += flex;
+ } else {
+ if (position < 12) {
+ childrenRow
+ .add(Expanded(flex: 12 - position, child: SizedBox(width: 1)));
+ }
+ position = 0;
+ childrenColumn.add(Row(children: childrenRow));
+ childrenRow = [];
+ final child2 = position == 0
+ ? widget.widgetOf()
+ : Row(
+ children: [
+ SizedBox(width: padding),
+ Expanded(child: widget.widgetOf())
+ ],
+ );
+ childrenRow.add(Expanded(flex: flex, child: child2));
+ position = flex;
+ }
+ }
+ if (position < 12) {
+ childrenRow
+ .add(Expanded(flex: 12 - position, child: SizedBox(width: 1)));
+ }
+ childrenColumn.add(Row(children: childrenRow));
+ rc = Column(children: childrenColumn);
+ }
+ return rc;
+ }
+}
# homepage: https://www.example.com
environment:
- sdk: '>=2.12.0 <3.0.0'
+ sdk: '>=2.14.0 <3.0.0'
dependencies:
http: ^0.13.0
':email': 'mozart@salzburg.au',
':createdby': 'unittest'
};
- var result = await client.store(what: 'store', data: parameters);
+ var result = await client.store(what: 'store', map: parameters);
expect(result, isNotNull);
final match = RegExp(r'^id:(\d+)$').firstMatch(result);
expect(match, isNotNull);
':email': 'bach@wien.at',
':changedby': 'unittest'
};
- var result = await client.store(what: 'store', data: parameters);
+ var result = await client.store(what: 'store', map: parameters);
expect(result, isNotNull);
final match = RegExp(r'^rows:(\d+)$').firstMatch(result);
expect(match, isNotNull);
'sql': 'delete',
':id': id.toString(),
};
- var result = await client.store(what: 'store', data: parameters);
+ var result = await client.store(what: 'store', map: parameters);
expect(result, isNotNull);
final match = RegExp(r'^rows:(\d+)$').firstMatch(result);
expect(match, isNotNull);
--- /dev/null
+---
+# DO NOT CHANGE. This file is created by the meta_tool
+# SQL statements of the module "Roles":
+
+module: Roles
+list:
+ type: list
+ parameters: []
+ sql: "select * from Roles;"
+byId:
+ type: record
+ parameters: [ "id" ]
+ sql: "select * from Roles where role_id=:id;"
+delete:
+ type: delete
+ parameters: [ "id" ]
+ sql: "delete * from Roles where role_id=:id;"
+update:
+ type: update
+ parameters: [":id",":name",":changedBy"]
+ sql: "UPDATE Roles SET
+ role_id=:id,role_name=:name,role_changedby=:changedBy,role_changed=NOW()
+ WHERE role_id=:id;"
+insert:
+ type: insert
+ parameters: [":id",":name",":createdBy"]
+ sql: "INSERT INTO Roles(role_id,role_name,role_createdby,role_created)
+ VALUES(:id,:name,:createdBy,NOW());"
# homepage: https://www.example.com
environment:
- sdk: '>=2.12.0 <3.0.0'
+ sdk: '>=2.14.0 <3.0.0'
dependencies:
http: ^0.13.0
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
-import 'package:exhibition/main.dart';
-
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
test -d data/sql/precedence || mkdir -p data/sql/precedence
cd ..
cp -av rest_server/tools/rest_server $BASE_DIR/$EXE
+ mkdir -p $BASE_DIR/data/sql
tools/yaml_merger rest_server/data/sql rest_server/data/sql/precedence $BASE_DIR/data/sql
cat <<EOS >$BASE_DIR/INSTALL.TXT
# ------------