--- /dev/null
+#! /bin/bash
+APP=meta_tool
+dart compile exe bin/$APP.dart -o tools/$APP
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
-include: package:flutter_lints/flutter.yaml
+#include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
for (var name in modules){
ModuleMetaData module = moduleByName(name);
String filename = classToFilename(module.moduleNameSingular) + '_data.dart';
- writeFile('lib/page/$filename', module.createModuleData());
+ final directory = name.toLowerCase();
+ writeFile('lib/page/$directory/$filename', module.createModuleData());
}
}
\ No newline at end of file
--- /dev/null
+typedef YamlMap = Map<String, dynamic>;
+typedef YamlList = List<YamlMap>;
+
+///JsonMap or JsonList
+typedef YamlData = Object;
+
+enum EventSource { undef, localData, remoteData }
+enum EventCardinality { undef, record, list }
+enum DataType {
+ bool,
+ currency,
+ date,
+ datetime,
+ float,
+ int,
+ nat,
+ reference,
+ string
+}
--- /dev/null
+import 'defines.dart';
+
+/// Returns the [dataType] specific value of [data].
+dynamic valueOf(DataType dataType, String data) {
+ dynamic rc;
+ switch (dataType) {
+ case DataType.bool:
+ rc = data == 'true';
+ break;
+ case DataType.currency:
+ case DataType.float:
+ rc = double.tryParse(data);
+ break;
+ case DataType.date:
+ case DataType.datetime:
+ rc = DateTime.parse(data);
+ break;
+ case DataType.int:
+ case DataType.nat:
+ case DataType.reference:
+ rc = int.tryParse(data);
+ break;
+ case DataType.string:
+ rc = data;
+ break;
+ }
+ return rc;
+}
import 'package:path/path.dart';
-enum DataType {
- bool,
- currency,
- date,
- datetime,
- float,
- int,
- nat,
- reference,
- string,
-}
+import '../base/defines.dart';
+
+class MetaException extends FormatException {}
-class MetaException extends FormatException{
-}
/// Stores the meta data of a module.
class ModuleMetaData {
/// The module name, e.g. users
/// The related database table.
String tableName;
+ final bool shortModifiedLabel;
List<PropertyMetaData> list;
final Map<String, PropertyMetaData> properties = {};
String columnPrefix;
ModuleMetaData(this.moduleName, this.list,
{this.tableName = '',
this.moduleNameSingular = '',
- this.columnPrefix = ''}) {
+ this.columnPrefix = '',
+ this.shortModifiedLabel = false}) {
tableName = tableName.isEmpty ? moduleName : tableName;
moduleNameSingular = moduleNameSingular.isEmpty
? (moduleName.endsWith('s')
? moduleName.substring(0, moduleName.length - 1)
: moduleName)
: moduleNameSingular;
- for (var item in list) {
- properties[item.name] = item;
- item.columnName ??= columnPrefix + '_' + item.name.toLowerCase();
- }
columnPrefix = columnPrefix.isNotEmpty
? columnPrefix
: moduleNameSingular.toLowerCase();
+ for (var item in list) {
+ item.module = this;
+ properties[item.name] = item;
+ if (item.columnName.isEmpty) {
+ final prefix = shortModifiedLabel &&
+ ['created', 'createdBy', 'changed', 'changedBy']
+ .contains(item.name)
+ ? ''
+ : columnPrefix + '_';
+ item.columnName = prefix + item.name.toLowerCase();
+ }
+ }
}
/// Returns a DDL statement creating the database table.
String createModuleData() {
final buffer = StringBuffer();
buffer.write('// DO NOT CHANGE. This file is created by the meta_tool\n');
- buffer.write('class $moduleNameSingular{\n');
+ buffer.write("import '../../base/defines.dart';\n");
+ buffer.write("import '../../base/helper.dart';\n");
+ buffer.write('class ${moduleNameSingular}Data{\n');
for (var item in list) {
buffer.write(' ' + dartType(item.dataType) + '? ${item.name};\n');
}
- buffer.write(' $moduleNameSingular({');
+ buffer.write(' ${moduleNameSingular}Data({');
String separator = '';
for (var item in list) {
buffer.write('$separator this.${item.name}');
separator = ',';
}
- buffer.write('});\n}\n');
+ buffer.write('});\n');
+ buffer.write(' ${moduleNameSingular}Data.fromYaml(YamlMap data) {\n');
+ for (var item in list) {
+ 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.write(' }\n');
+ buffer.write(' static DataType? dataTypeOf(String name) {\n');
+ buffer.write(' DataType? rc;\n');
+ buffer.write(' switch(name){\n');
+ for (var item in list) {
+ buffer.write(" case '${item.name}':\n");
+ buffer.write(' rc = ${item.dataType};\n');
+ buffer.write(' break;\n');
+ }
+ buffer.write(''' default:
+ break;
+ }
+ return rc;
+ }
+''');
+ buffer.write('}\n');
return buffer.toString();
}
class PropertyMetaData {
/// Name of the property (Dart name).
final String name;
+ ModuleMetaData module = ModuleMetaData('', []);
/// A colon delimited list of options, e.g. ':notnull:unique:'.
final String options;
/// The size if dataType is DataType.string.
final int size;
- String? columnName;
+ String columnName;
/// 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.columnName, this.size = 0, this.reference});
+ {this.columnName = '', this.size = 0, this.reference});
}
-String filenameToClass(String filename){
+String filenameToClass(String filename) {
final parts = basenameWithoutExtension(filename).split('_');
String rc = '';
for (var part in parts) {
}
return rc;
}
-String classToFilename(String className){
+
+String classToFilename(String className) {
String upperCase = className.toUpperCase();
String rc = '';
- for (var ix = 0; ix < className.length; ix++){
- if (className[ix] == upperCase[ix] && ix > 0){
+ for (var ix = 0; ix < className.length; ix++) {
+ if (className[ix] == upperCase[ix] && ix > 0) {
rc += '_';
}
rc = className[ix];
}
return rc.toLowerCase();
-}
\ No newline at end of file
+}
+import '../base/defines.dart';
import 'module_meta_data.dart';
-class UserMeta extends ModuleMetaData {
+class UserMeta extends ModuleMetaData {
static UserMeta instance = UserMeta.internal();
- UserMeta.internal() : super('Users',
- [
- PropertyMetaData('id', DataType.reference, ':primary:', 'combo'),
- PropertyMetaData('name', DataType.string, ':notnull:', '', size: 64),
- PropertyMetaData('displayName', DataType.string, ':unique:notnull:', '', size: 32),
- PropertyMetaData('email', DataType.string, ':unique:notnull:', '', size: 255),
- PropertyMetaData('role', DataType.reference, ':notnull:', ''),
- PropertyMetaData('created', DataType.datetime, '', ''),
- PropertyMetaData('createdBy', DataType.string, '', '', size: 32),
- PropertyMetaData('changed', DataType.datetime, '', ''),
- PropertyMetaData('changedBy', DataType.string, '', '', size: 32),
- ],
- tableName: 'loginusers',
- );
- factory UserMeta(){
+ UserMeta.internal()
+ : super(
+ 'Users',
+ [
+ PropertyMetaData('id', DataType.reference, ':primary:', 'combo'),
+ PropertyMetaData('name', DataType.string, ':notnull:', '',
+ size: 64),
+ PropertyMetaData(
+ 'displayName', DataType.string, ':unique:notnull:', '',
+ size: 32),
+ PropertyMetaData('email', DataType.string, ':unique:notnull:', '',
+ size: 255),
+ PropertyMetaData('role', DataType.reference, ':notnull:', ''),
+ PropertyMetaData('created', DataType.datetime, '', ''),
+ PropertyMetaData('createdBy', DataType.string, '', '', size: 32),
+ PropertyMetaData('changed', DataType.datetime, '', ''),
+ PropertyMetaData('changedBy', DataType.string, '', '', size: 32),
+ ],
+ tableName: 'loginusers',
+ shortModifiedLabel: true,
+ );
+ factory UserMeta() {
return instance;
}
-}
\ No newline at end of file
+}
+++ /dev/null
-class User {
- int? id;
- String? name;
- String? displayName;
- String? email;
- int? role;
- DateTime? created;
- String? createdBy;
- DateTime? changed;
- String? changedBy;
- User(
- {this.id,
- this.name,
- this.displayName,
- this.email,
- this.role,
- this.created,
- this.createdBy,
- this.changed,
- this.changedBy});
-}
--- /dev/null
+// DO NOT CHANGE. This file is created by the meta_tool
+import '../../base/defines.dart';
+import '../../base/helper.dart';
+class UserData{
+ int? id;
+ String? name;
+ String? displayName;
+ String? email;
+ int? role;
+ DateTime? created;
+ 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;
+ }
+ 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;
+ }
+ return rc;
+ }
+}
+
--- /dev/null
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import '../../base/defines.dart';
+import 'user_state.dart';
+import 'user_data.dart';
+import 'package:equatable/equatable.dart';
+
+class User extends StatefulWidget {
+ @override
+ _UserState createState() => _UserState();
+}
+
+class _UserState extends State<User> {
+ 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();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocBuilder<UserBloc, UserState>(
+ builder: (context, UserState state) {
+ return Column(
+ children: [
+ ResultDisplay(
+ text: _getDisplayText(state.calculationModel),
+ ),
+ Row(
+ children: [
+ _getButton(text: '7', onTap: () => numberPressed(7)),
+ _getButton(text: '8', onTap: () => numberPressed(8)),
+ _getButton(text: '9', onTap: () => numberPressed(9)),
+ _getButton(
+ text: 'x',
+ onTap: () => operatorPressed('*'),
+ backgroundColor: Color.fromRGBO(220, 220, 220, 1)),
+ ],
+ ),
+ Row(
+ children: [
+ _getButton(text: '4', onTap: () => numberPressed(4)),
+ _getButton(text: '5', onTap: () => numberPressed(5)),
+ _getButton(text: '6', onTap: () => numberPressed(6)),
+ _getButton(
+ text: '/',
+ onTap: () => operatorPressed('/'),
+ backgroundColor: Color.fromRGBO(220, 220, 220, 1)),
+ ],
+ ),
+ Row(
+ children: [
+ _getButton(text: '1', onTap: () => numberPressed(1)),
+ _getButton(text: '2', onTap: () => numberPressed(2)),
+ _getButton(text: '3', onTap: () => numberPressed(3)),
+ _getButton(
+ text: '+',
+ onTap: () => operatorPressed('+'),
+ backgroundColor: Color.fromRGBO(220, 220, 220, 1))
+ ],
+ ),
+ Row(
+ children: [
+ _getButton(
+ text: '=',
+ onTap: calculateResult,
+ backgroundColor: Colors.orange,
+ textColor: Colors.white),
+ _getButton(text: '0', onTap: () => numberPressed(0)),
+ _getButton(
+ text: 'C',
+ onTap: clear,
+ backgroundColor: Color.fromRGBO(220, 220, 220, 1)),
+ _getButton(
+ text: '-',
+ onTap: () => operatorPressed('-'),
+ backgroundColor: Color.fromRGBO(220, 220, 220, 1)),
+ ],
+ ),
+ Spacer(),
+ UserHistoryContainer(
+ calculations: state.history.reversed.toList())
+ ],
+ );
+ },
+ );
+ }
+
+ Widget _getButton(
+ {required String text,
+ required void Function() onTap,
+ Color backgroundColor = Colors.white,
+ Color textColor = Colors.black}) {
+ return CalculatorButton(
+ label: text,
+ onTap: onTap,
+ // @ToDo: width always != null?
+ size: width! / 4 - 12,
+ backgroundColor: backgroundColor,
+ labelColor: textColor,
+ );
+ }
+
+ numberPressed(int number) {
+ context.read<UserBloc>().add(NumberPressed(number: number));
+ }
+
+ operatorPressed(String operator) {
+ context.read<UserBloc>().add(OperatorPressed(operator: operator));
+ }
+
+ calculateResult() {
+ context.read<UserBloc>().add(CalculateResult());
+ }
+
+ clear() {
+ context.read<UserBloc>().add(ClearUser());
+ }
+
+ String _getDisplayText(UserModel model) {
+ if (model.result != null) {
+ return '${model.result}';
+ }
+
+ if (model.secondOperand != null) {
+ return '${model.firstOperand}${model.operator}${model.secondOperand}';
+ }
+
+ if (model.operator != null) {
+ return '${model.firstOperand}${model.operator}';
+ }
+
+ if (model.firstOperand != null) {
+ return '${model.firstOperand}';
+ }
+
+ return "${model.result ?? 0}";
+ }
+}
--- /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 => [];
+}
@override
Future<dynamic> query(
{required String what, Map<String, dynamic>? data}) async {
- var rc;
+ 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('['))) {
import 'package:dart_bones/dart_bones.dart';
+import 'package:exhibition/persistence/rest_persistence.dart';
import 'package:flutter/material.dart';
import '../page/page_controller_exhibition.dart';
final DrawerBuilder drawerBuilder;
final FooterBuilder footerBuilder;
final BaseConfiguration configuration;
+ final RestPersistence? restPersistence;
factory GlobalData() => _instance ?? GlobalData();
GlobalData.dummy()
: this.internal(BaseConfiguration({}, globalLogger), (input) => '',
- (input) => '', DummyFooter.builder, globalLogger);
+ (input) => '', DummyFooter.builder, null, globalLogger);
/// [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.logger) {
+ this.drawerBuilder, this.footerBuilder,
+ this.restPersistence, this.logger) {
logger.log('Start');
}
}
version: 1.0.0+1
environment:
- sdk: ">=2.12.0 <3.0.0"
+ sdk: ">=2.14.0-321.0.dev <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
dart_bones: "^1.1.1"
url_launcher: ^6.0.3
flutter_markdown: ^0.6.1
+ flutter_bloc: ^7.1.0
+ equatable: ^2.0.3
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
final configuration = Configuration.fromFile(config, logger);
final client = RestPersistence.fromConfig(configuration, logger);
group('query', () {
- Map result;
Map<String, String> data;
test('list', () async {
data = {'module': 'Persons', 'sql': 'list'};
import 'package:dart_bones/dart_bones.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as path;
-import 'package:rest_server/sql_storage.dart';
+import 'sql_storage.dart';
import 'package:mysql1/mysql1.dart';
const forbidden = 'forbidden';