From: Hamatoma Date: Thu, 21 Oct 2021 20:27:30 +0000 (+0200) Subject: Refactoring: fetching backend data via FutureBuilder X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=b8a56a0b6946b2d48d92b800d13e13f7fb879d8d;p=exhibition.git Refactoring: fetching backend data via FutureBuilder * GlobalData: ** bookkeeping of the callstack of navigation: new NavigatorStack * AttendedPage: ** it is possible to call setState() in each page in the navigation call stack --- diff --git a/lib/common/random_data.dart b/lib/common/random_data.dart index e07a564..ea628d6 100644 --- a/lib/common/random_data.dart +++ b/lib/common/random_data.dart @@ -94,7 +94,7 @@ class RandomData { /// [gerund]: true: a ver is used in the gerund form: this is better to /// to read and remember. static String constructWord(List wordParts, - {bool normalName = true, gerund: true}) { + {bool normalName = true, gerund = true}) { String rc = ''; for (var item in wordParts) { switch (item.wordType) { diff --git a/lib/meta/modules.dart b/lib/meta/modules.dart index 484f179..10f555e 100644 --- a/lib/meta/modules.dart +++ b/lib/meta/modules.dart @@ -4,6 +4,7 @@ import 'benchmarks_meta.dart'; import 'roles_meta.dart'; import 'structures_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) { @@ -26,8 +27,9 @@ ModuleMetaData? moduleByName(String name) { } return rc; } + /// Returns the module names as string list. -List moduleNames(){ +List moduleNames() { return [ 'Benchmarks', 'Roles', diff --git a/lib/page/benchmarks/benchmark_data.dart b/lib/page/benchmarks/benchmark_data.dart index 929cce3..aacaf19 100644 --- a/lib/page/benchmarks/benchmark_data.dart +++ b/lib/page/benchmarks/benchmark_data.dart @@ -2,7 +2,8 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../persistence/data_record.dart'; -class BenchmarkData extends DataRecord{ + +class BenchmarkData extends DataRecord { int? id; String? lastName; String? firstName; @@ -15,78 +16,117 @@ class BenchmarkData extends DataRecord{ String? createdBy; DateTime? changed; String? changedBy; - BenchmarkData({ - this.id, this.lastName, this.firstName, this.email, this.birthday, this.active, this.weight, this.income, this.created, this.createdBy, this.changed, this.changedBy}); + BenchmarkData( + {this.id, + this.lastName, + this.firstName, + this.email, + this.birthday, + this.active, + this.weight, + this.income, + this.created, + this.createdBy, + this.changed, + this.changedBy}); BenchmarkData.createFromMap(DataMap map) { fromMap(map); } @override void fromMap(DataMap map) { - id = map.containsKey('benchmark_id') ? fromString(map['benchmark_id'], dataType: DataType.reference) : null; - lastName = map.containsKey('benchmark_lastname') ? fromString(map['benchmark_lastname'], dataType: DataType.string) : null; - firstName = map.containsKey('benchmark_firstname') ? fromString(map['benchmark_firstname'], dataType: DataType.string) : null; - email = map.containsKey('benchmark_email') ? fromString(map['benchmark_email'], dataType: DataType.string) : null; - birthday = map.containsKey('benchmark_birthday') ? fromString(map['benchmark_birthday'], dataType: DataType.date) : null; - active = map.containsKey('benchmark_active') ? fromString(map['benchmark_active'], dataType: DataType.bool) : null; - weight = map.containsKey('benchmark_weight') ? fromString(map['benchmark_weight'], dataType: DataType.float) : null; - income = map.containsKey('benchmark_income') ? fromString(map['benchmark_income'], dataType: DataType.currency) : null; - created = map.containsKey('benchmark_created') ? fromString(map['benchmark_created'], dataType: DataType.datetime) : null; - createdBy = map.containsKey('benchmark_createdby') ? fromString(map['benchmark_createdby'], dataType: DataType.string) : null; - changed = map.containsKey('benchmark_changed') ? fromString(map['benchmark_changed'], dataType: DataType.datetime) : null; - changedBy = map.containsKey('benchmark_changedby') ? fromString(map['benchmark_changedby'], dataType: DataType.string) : null; + id = map.containsKey('benchmark_id') + ? fromString(map['benchmark_id'], dataType: DataType.reference) + : null; + lastName = map.containsKey('benchmark_lastname') + ? fromString(map['benchmark_lastname'], dataType: DataType.string) + : null; + firstName = map.containsKey('benchmark_firstname') + ? fromString(map['benchmark_firstname'], dataType: DataType.string) + : null; + email = map.containsKey('benchmark_email') + ? fromString(map['benchmark_email'], dataType: DataType.string) + : null; + birthday = map.containsKey('benchmark_birthday') + ? fromString(map['benchmark_birthday'], dataType: DataType.date) + : null; + active = map.containsKey('benchmark_active') + ? fromString(map['benchmark_active'], dataType: DataType.bool) + : null; + weight = map.containsKey('benchmark_weight') + ? fromString(map['benchmark_weight'], dataType: DataType.float) + : null; + income = map.containsKey('benchmark_income') + ? fromString(map['benchmark_income'], dataType: DataType.currency) + : null; + created = map.containsKey('benchmark_created') + ? fromString(map['benchmark_created'], dataType: DataType.datetime) + : null; + createdBy = map.containsKey('benchmark_createdby') + ? fromString(map['benchmark_createdby'], dataType: DataType.string) + : null; + changed = map.containsKey('benchmark_changed') + ? fromString(map['benchmark_changed'], dataType: DataType.datetime) + : null; + changedBy = map.containsKey('benchmark_changedby') + ? fromString(map['benchmark_changedby'], dataType: DataType.string) + : null; } + @override int keyOf() { return id ?? 0; } + @override - String nameOfKey(){ + String nameOfKey() { return 'benchmark_id'; } + static DataType? dataTypeOf(String name) { DataType? rc; - switch(name){ - case 'id': - rc = DataType.reference; - break; - case 'lastName': - rc = DataType.string; - break; - case 'firstName': - rc = DataType.string; - break; - case 'email': - rc = DataType.string; - break; - case 'birthday': - rc = DataType.date; - break; - case 'active': - rc = DataType.bool; - break; - case 'weight': - rc = DataType.float; - break; - case 'income': - rc = DataType.currency; - 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 'lastName': + rc = DataType.string; + break; + case 'firstName': + rc = DataType.string; + break; + case 'email': + rc = DataType.string; + break; + case 'birthday': + rc = DataType.date; + break; + case 'active': + rc = DataType.bool; + break; + case 'weight': + rc = DataType.float; + break; + case 'income': + rc = DataType.currency; + 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(); diff --git a/lib/page/benchmarks/create_benchmark_custom.dart b/lib/page/benchmarks/create_benchmark_custom.dart index 62d07c8..8aa8fab 100644 --- a/lib/page/benchmarks/create_benchmark_custom.dart +++ b/lib/page/benchmarks/create_benchmark_custom.dart @@ -15,7 +15,8 @@ import 'create_benchmark_page.dart'; final i18n = I18N(); -class CreateBenchmarkCustom extends State with MessageLine { +class CreateBenchmarkCustom extends State + with MessageLine { final globalData = GlobalData(); AttendedPage? attendedPage; final _fieldData = _FieldData(); @@ -30,6 +31,14 @@ class CreateBenchmarkCustom extends State with MessageLine CreateBenchmarkCustom(); @override Widget build(BuildContext context) { + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('New Benchmark')), + drawer: globalData.drawerBuilder(context), + body: SafeArea(child: buildFrame())); + return rc; + } + + Widget buildFrame() { final padding = GlobalThemeData.padding; lastNameController.text = _fieldData.lastName; firstNameController.text = _fieldData.firstName; @@ -40,35 +49,32 @@ class CreateBenchmarkCustom extends State with MessageLine final formItems = [ FormItem( TextFormField( - controller: lastNameController, - decoration: InputDecoration(labelText: i18n.tr('Last Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.lastName = value ?? '' - ), + controller: lastNameController, + decoration: InputDecoration(labelText: i18n.tr('Last Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.lastName = value ?? ''), weight: 6), FormItem( TextFormField( - controller: firstNameController, - decoration: InputDecoration(labelText: i18n.tr('First Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.firstName = value ?? '' - ), + controller: firstNameController, + decoration: InputDecoration(labelText: i18n.tr('First Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.firstName = value ?? ''), weight: 6), FormItem( TextFormField( - controller: emailController, - decoration: InputDecoration(labelText: i18n.tr('EMail')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.email = value ?? '' - ), + controller: emailController, + decoration: InputDecoration(labelText: i18n.tr('EMail')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.email = value ?? ''), weight: 6), FormItem( TextFormField( - controller: birthdayController, - decoration: InputDecoration(labelText: i18n.tr('Birthday')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.birthday = jsonToObject(value ?? '', dataType: DataType.date) - ), + controller: birthdayController, + decoration: InputDecoration(labelText: i18n.tr('Birthday')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.birthday = + jsonToObject(value ?? '', dataType: DataType.date)), weight: 6), FormItem( Row(children: [ @@ -86,22 +92,21 @@ class CreateBenchmarkCustom extends State with MessageLine weight: 6), FormItem( TextFormField( - controller: weightController, - decoration: InputDecoration(labelText: i18n.tr('Weight')), - onSaved: (value) => _fieldData.weight = jsonToObject(value ?? '', dataType: DataType.float) - ), + controller: weightController, + decoration: InputDecoration(labelText: i18n.tr('Weight')), + onSaved: (value) => _fieldData.weight = + jsonToObject(value ?? '', dataType: DataType.float)), weight: 6), FormItem( TextFormField( - controller: incomeController, - decoration: InputDecoration(labelText: i18n.tr('Income')), - onSaved: (value) => _fieldData.income = jsonToObject(value ?? '', dataType: DataType.currency) - ), + controller: incomeController, + decoration: InputDecoration(labelText: i18n.tr('Income')), + onSaved: (value) => _fieldData.income = + jsonToObject(value ?? '', dataType: DataType.currency)), weight: 6), FormItem( ElevatedButton( - onPressed: () => verifyAndStore(), - child: Text(i18n.tr('Save'))), + onPressed: () => verifyAndStore(), child: Text(i18n.tr('Save'))), weight: 8, gapAbove: 2 * padding), FormItem( @@ -114,26 +119,35 @@ class CreateBenchmarkCustom extends State with MessageLine ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('New Benchmark')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + lastNameController.dispose(); + firstNameController.dispose(); + emailController.dispose(); + birthdayController.dispose(); + weightController.dispose(); + incomeController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -167,23 +181,9 @@ class CreateBenchmarkCustom extends State with MessageLine store(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - lastNameController.dispose(); - firstNameController.dispose(); - emailController.dispose(); - birthdayController.dispose(); - weightController.dispose(); - incomeController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String lastName = ''; String firstName = ''; String email = ''; @@ -192,7 +192,6 @@ class _FieldData { double weight = 0; double income = 0; - void toMap(Map map) { map[':lastName'] = lastName; map[':firstName'] = firstName; diff --git a/lib/page/benchmarks/create_benchmark_page.dart b/lib/page/benchmarks/create_benchmark_page.dart index 4fbfd28..f69e701 100644 --- a/lib/page/benchmarks/create_benchmark_page.dart +++ b/lib/page/benchmarks/create_benchmark_page.dart @@ -18,14 +18,15 @@ class CreateBenchmarkPage extends StatefulWidget { GlobalData(), BenchmarkMeta.instance.pageByName('create')!, BenchmarkMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _CreateBenchmarkPageState extends CreateBenchmarkCustom { - _CreateBenchmarkPageState(): super(); + _CreateBenchmarkPageState() : super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -33,4 +34,13 @@ class _CreateBenchmarkPageState extends CreateBenchmarkCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/benchmarks/delete_benchmark_custom.dart b/lib/page/benchmarks/delete_benchmark_custom.dart index 8a8c767..3503150 100644 --- a/lib/page/benchmarks/delete_benchmark_custom.dart +++ b/lib/page/benchmarks/delete_benchmark_custom.dart @@ -6,6 +6,7 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../base/i18n.dart'; import '../../base/validators.dart'; +import '../../persistence/persistence.dart'; import '../../services/global_widget.dart'; import '../../setting/global_data.dart'; import '../../widget/attended_page.dart'; @@ -15,9 +16,11 @@ import 'delete_benchmark_page.dart'; final i18n = I18N(); -class DeleteBenchmarkCustom extends State with MessageLine { +class DeleteBenchmarkCustom extends State + with MessageLine { final int primaryKey; final globalData = GlobalData(); + late Future _futureDbData; AttendedPage? attendedPage; final _fieldData = _FieldData(); final GlobalKey _formKey = @@ -31,21 +34,40 @@ class DeleteBenchmarkCustom extends State with MessageLine DeleteBenchmarkCustom(this.primaryKey); @override Widget build(BuildContext context) { - final padding = GlobalThemeData.padding; - if (!_fieldData.isFromBackend) { - attendedPage?.loadRecord( - name: 'record', - reload: () => setState(() => 1), - onDone: (record) { - _fieldData.fromMap(record); - _fieldData.isFromBackend = true; + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('Delete Benchmark')), + drawer: globalData.drawerBuilder(context), + body: SafeArea( + child: FutureBuilder( + future: _futureDbData, + builder: (context, snapshot) { + Widget rc; + if (snapshot.connectionState != ConnectionState.done) { + rc = const CircularProgressIndicator(); + } else { + if (snapshot.hasData) { + final dbData = snapshot.data!; + final record = dbData.singleRecord; + if (record == null) { + rc = + Text('backend problem: ${dbData.message ?? ''}'); + } + _fieldData.fromMap(record as JsonMap); + rc = buildFrame(); + } else if (snapshot.hasError) { + rc = Text('Backend problem: ${snapshot.error}'); + } else { + rc = const CircularProgressIndicator(); + } + } + return rc; }, - parameters: { - 'module': 'Benchmarks', - 'sql': 'byId', - ':id': primaryKey - }); - } + ))); + return rc; + } + + Widget buildFrame() { + final padding = GlobalThemeData.padding; lastNameController.text = _fieldData.lastName; firstNameController.text = _fieldData.firstName; emailController.text = _fieldData.email; @@ -55,35 +77,32 @@ class DeleteBenchmarkCustom extends State with MessageLine final formItems = [ FormItem( TextFormField( - controller: lastNameController, - decoration: InputDecoration(labelText: i18n.tr('Last Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.lastName = value ?? '' - ), + controller: lastNameController, + decoration: InputDecoration(labelText: i18n.tr('Last Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.lastName = value ?? ''), weight: 6), FormItem( TextFormField( - controller: firstNameController, - decoration: InputDecoration(labelText: i18n.tr('First Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.firstName = value ?? '' - ), + controller: firstNameController, + decoration: InputDecoration(labelText: i18n.tr('First Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.firstName = value ?? ''), weight: 6), FormItem( TextFormField( - controller: emailController, - decoration: InputDecoration(labelText: i18n.tr('EMail')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.email = value ?? '' - ), + controller: emailController, + decoration: InputDecoration(labelText: i18n.tr('EMail')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.email = value ?? ''), weight: 6), FormItem( TextFormField( - controller: birthdayController, - decoration: InputDecoration(labelText: i18n.tr('Birthday')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.birthday = jsonToObject(value ?? '', dataType: DataType.date) - ), + controller: birthdayController, + decoration: InputDecoration(labelText: i18n.tr('Birthday')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.birthday = + jsonToObject(value ?? '', dataType: DataType.date)), weight: 6), FormItem( Row(children: [ @@ -101,17 +120,17 @@ class DeleteBenchmarkCustom extends State with MessageLine weight: 6), FormItem( TextFormField( - controller: weightController, - decoration: InputDecoration(labelText: i18n.tr('Weight')), - onSaved: (value) => _fieldData.weight = jsonToObject(value ?? '', dataType: DataType.float) - ), + controller: weightController, + decoration: InputDecoration(labelText: i18n.tr('Weight')), + onSaved: (value) => _fieldData.weight = + jsonToObject(value ?? '', dataType: DataType.float)), weight: 6), FormItem( TextFormField( - controller: incomeController, - decoration: InputDecoration(labelText: i18n.tr('Income')), - onSaved: (value) => _fieldData.income = jsonToObject(value ?? '', dataType: DataType.currency) - ), + controller: incomeController, + decoration: InputDecoration(labelText: i18n.tr('Income')), + onSaved: (value) => _fieldData.income = + jsonToObject(value ?? '', dataType: DataType.currency)), weight: 6), FormItem( ElevatedButton( @@ -129,26 +148,43 @@ class DeleteBenchmarkCustom extends State with MessageLine ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Delete Benchmark')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _futureDbData = globalData.restPersistence!.query( + what: 'query', + data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey}); + } + + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + lastNameController.dispose(); + firstNameController.dispose(); + emailController.dispose(); + birthdayController.dispose(); + weightController.dispose(); + incomeController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -173,23 +209,9 @@ class DeleteBenchmarkCustom extends State with MessageLine delete(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - lastNameController.dispose(); - firstNameController.dispose(); - emailController.dispose(); - birthdayController.dispose(); - weightController.dispose(); - incomeController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String lastName = ''; String firstName = ''; String email = ''; diff --git a/lib/page/benchmarks/delete_benchmark_page.dart b/lib/page/benchmarks/delete_benchmark_page.dart index f0fc100..4a4bff4 100644 --- a/lib/page/benchmarks/delete_benchmark_page.dart +++ b/lib/page/benchmarks/delete_benchmark_page.dart @@ -19,14 +19,15 @@ class DeleteBenchmarkPage extends StatefulWidget { GlobalData(), BenchmarkMeta.instance.pageByName('delete')!, BenchmarkMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _DeleteBenchmarkPageState extends DeleteBenchmarkCustom { - _DeleteBenchmarkPageState(int primaryKey): super(primaryKey); + _DeleteBenchmarkPageState(int primaryKey) : super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -34,4 +35,13 @@ class _DeleteBenchmarkPageState extends DeleteBenchmarkCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/benchmarks/edit_benchmark_custom.dart b/lib/page/benchmarks/edit_benchmark_custom.dart index c933ecf..78664ae 100644 --- a/lib/page/benchmarks/edit_benchmark_custom.dart +++ b/lib/page/benchmarks/edit_benchmark_custom.dart @@ -6,6 +6,7 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../base/i18n.dart'; import '../../base/validators.dart'; +import '../../persistence/persistence.dart'; import '../../services/global_widget.dart'; import '../../setting/global_data.dart'; import '../../widget/attended_page.dart'; @@ -18,6 +19,7 @@ final i18n = I18N(); class EditBenchmarkCustom extends State with MessageLine { final int primaryKey; final globalData = GlobalData(); + late Future _futureDbData; AttendedPage? attendedPage; final _fieldData = _FieldData(); final GlobalKey _formKey = @@ -31,21 +33,40 @@ class EditBenchmarkCustom extends State with MessageLine { EditBenchmarkCustom(this.primaryKey); @override Widget build(BuildContext context) { - final padding = GlobalThemeData.padding; - if (!_fieldData.isFromBackend) { - attendedPage?.loadRecord( - name: 'record', - reload: () => setState(() => 1), - onDone: (record) { - _fieldData.fromMap(record); - _fieldData.isFromBackend = true; + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('Change Benchmark')), + drawer: globalData.drawerBuilder(context), + body: SafeArea( + child: FutureBuilder( + future: _futureDbData, + builder: (context, snapshot) { + Widget rc; + if (snapshot.connectionState != ConnectionState.done) { + rc = const CircularProgressIndicator(); + } else { + if (snapshot.hasData) { + final dbData = snapshot.data!; + final record = dbData.singleRecord; + if (record == null) { + rc = + Text('backend problem: ${dbData.message ?? ''}'); + } + _fieldData.fromMap(record as JsonMap); + rc = buildFrame(); + } else if (snapshot.hasError) { + rc = Text('Backend problem: ${snapshot.error}'); + } else { + rc = const CircularProgressIndicator(); + } + } + return rc; }, - parameters: { - 'module': 'Benchmarks', - 'sql': 'byId', - ':id': primaryKey - }); - } + ))); + return rc; + } + + Widget buildFrame() { + final padding = GlobalThemeData.padding; lastNameController.text = _fieldData.lastName; firstNameController.text = _fieldData.firstName; emailController.text = _fieldData.email; @@ -55,35 +76,32 @@ class EditBenchmarkCustom extends State with MessageLine { final formItems = [ FormItem( TextFormField( - controller: lastNameController, - decoration: InputDecoration(labelText: i18n.tr('Last Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.lastName = value ?? '' - ), + controller: lastNameController, + decoration: InputDecoration(labelText: i18n.tr('Last Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.lastName = value ?? ''), weight: 6), FormItem( TextFormField( - controller: firstNameController, - decoration: InputDecoration(labelText: i18n.tr('First Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.firstName = value ?? '' - ), + controller: firstNameController, + decoration: InputDecoration(labelText: i18n.tr('First Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.firstName = value ?? ''), weight: 6), FormItem( TextFormField( - controller: emailController, - decoration: InputDecoration(labelText: i18n.tr('EMail')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.email = value ?? '' - ), + controller: emailController, + decoration: InputDecoration(labelText: i18n.tr('EMail')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.email = value ?? ''), weight: 6), FormItem( TextFormField( - controller: birthdayController, - decoration: InputDecoration(labelText: i18n.tr('Birthday')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.birthday = jsonToObject(value ?? '', dataType: DataType.date) - ), + controller: birthdayController, + decoration: InputDecoration(labelText: i18n.tr('Birthday')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.birthday = + jsonToObject(value ?? '', dataType: DataType.date)), weight: 6), FormItem( Row(children: [ @@ -101,22 +119,21 @@ class EditBenchmarkCustom extends State with MessageLine { weight: 6), FormItem( TextFormField( - controller: weightController, - decoration: InputDecoration(labelText: i18n.tr('Weight')), - onSaved: (value) => _fieldData.weight = jsonToObject(value ?? '', dataType: DataType.float) - ), + controller: weightController, + decoration: InputDecoration(labelText: i18n.tr('Weight')), + onSaved: (value) => _fieldData.weight = + jsonToObject(value ?? '', dataType: DataType.float)), weight: 6), FormItem( TextFormField( - controller: incomeController, - decoration: InputDecoration(labelText: i18n.tr('Income')), - onSaved: (value) => _fieldData.income = jsonToObject(value ?? '', dataType: DataType.currency) - ), + controller: incomeController, + decoration: InputDecoration(labelText: i18n.tr('Income')), + onSaved: (value) => _fieldData.income = + jsonToObject(value ?? '', dataType: DataType.currency)), weight: 6), FormItem( ElevatedButton( - onPressed: () => verifyAndStore(), - child: Text(i18n.tr('Save'))), + onPressed: () => verifyAndStore(), child: Text(i18n.tr('Save'))), weight: 8, gapAbove: 2 * padding), FormItem( @@ -129,26 +146,43 @@ class EditBenchmarkCustom extends State with MessageLine { ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Change Benchmark')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _futureDbData = globalData.restPersistence!.query( + what: 'query', + data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey}); + } + + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + lastNameController.dispose(); + firstNameController.dispose(); + emailController.dispose(); + birthdayController.dispose(); + weightController.dispose(); + incomeController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -164,7 +198,6 @@ class EditBenchmarkCustom extends State with MessageLine { globalData.restPersistence! .store(what: 'store', map: parameters) .then((answer) { - _fieldData.isFromBackend = false; attendedPage!.pageStates.dbDataState.clear(); setState(() => 1); }); @@ -176,23 +209,9 @@ class EditBenchmarkCustom extends State with MessageLine { store(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - lastNameController.dispose(); - firstNameController.dispose(); - emailController.dispose(); - birthdayController.dispose(); - weightController.dispose(); - incomeController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String lastName = ''; String firstName = ''; String email = ''; diff --git a/lib/page/benchmarks/edit_benchmark_page.dart b/lib/page/benchmarks/edit_benchmark_page.dart index 9fca871..5b0aeae 100644 --- a/lib/page/benchmarks/edit_benchmark_page.dart +++ b/lib/page/benchmarks/edit_benchmark_page.dart @@ -19,14 +19,15 @@ class EditBenchmarkPage extends StatefulWidget { GlobalData(), BenchmarkMeta.instance.pageByName('edit')!, BenchmarkMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _EditBenchmarkPageState extends EditBenchmarkCustom { - _EditBenchmarkPageState(int primaryKey): super(primaryKey); + _EditBenchmarkPageState(int primaryKey) : super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -34,4 +35,13 @@ class _EditBenchmarkPageState extends EditBenchmarkCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/benchmarks/list_benchmark_custom.dart b/lib/page/benchmarks/list_benchmark_custom.dart index 5a54c4b..0707e76 100644 --- a/lib/page/benchmarks/list_benchmark_custom.dart +++ b/lib/page/benchmarks/list_benchmark_custom.dart @@ -47,8 +47,10 @@ class ListBenchmarkCustom extends State { rc = const CircularProgressIndicator(); } else { if (snapshot.hasData) { - final rows = attendedPage!.getRows(dbData: snapshot.data!, - columnList: 'benchmark_id;benchmark_lastname;benchmark_firstname;benchmark_birthday;benchmark_active;benchmark_income', + final rows = attendedPage!.getRows( + dbData: snapshot.data!, + columnList: + 'benchmark_id;benchmark_lastname;benchmark_firstname;benchmark_birthday;benchmark_active;benchmark_income', onDone: () => setState(() => 1), routeEdit: '/Benchmarks/edit', context: context); @@ -66,15 +68,14 @@ class ListBenchmarkCustom extends State { return rc; } - Widget buildFrame({required JsonList rows}){ + Widget buildFrame({required JsonList rows}) { final padding = GlobalThemeData.padding; final formItems = [ FormItem( TextFormField( - controller: textController, - decoration: InputDecoration(labelText: i18n.tr('Text')), - onSaved: (value) => _fieldData.text = value ?? '' - ), + controller: textController, + decoration: InputDecoration(labelText: i18n.tr('Text')), + onSaved: (value) => _fieldData.text = value ?? ''), weight: 6), FormItem( ElevatedButton( @@ -157,7 +158,7 @@ class ListBenchmarkCustom extends State { rows: rows as List, ); Widget? tabBar = - attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset)); + attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset)); final frameWidget = ListView(children: [ form, if (tabBar != null) tabBar, @@ -186,7 +187,7 @@ class ListBenchmarkCustom extends State { .then((message) => setState(() => resultController.text = message)); } -@override + @override void dispose() { helperDummyUsage(DataType.string); globalWidgetDummyUsage(); diff --git a/lib/page/benchmarks/list_benchmark_page.dart b/lib/page/benchmarks/list_benchmark_page.dart index 7ab8115..bad0013 100644 --- a/lib/page/benchmarks/list_benchmark_page.dart +++ b/lib/page/benchmarks/list_benchmark_page.dart @@ -18,14 +18,15 @@ class ListBenchmarkPage extends StatefulWidget { GlobalData(), BenchmarkMeta.instance.pageByName('list')!, BenchmarkMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _ListBenchmarkPageState extends ListBenchmarkCustom { - _ListBenchmarkPageState(): super(); + _ListBenchmarkPageState() : super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -33,4 +34,13 @@ class _ListBenchmarkPageState extends ListBenchmarkCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/roles/create_role_custom.dart b/lib/page/roles/create_role_custom.dart index 87c2b0e..a55c6a8 100644 --- a/lib/page/roles/create_role_custom.dart +++ b/lib/page/roles/create_role_custom.dart @@ -25,21 +25,27 @@ class CreateRoleCustom extends State with MessageLine { CreateRoleCustom(); @override Widget build(BuildContext context) { + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('New Role')), + drawer: globalData.drawerBuilder(context), + body: SafeArea(child: buildFrame())); + return rc; + } + + Widget buildFrame() { final padding = GlobalThemeData.padding; nameController.text = _fieldData.name; final formItems = [ FormItem( TextFormField( - controller: nameController, - decoration: InputDecoration(labelText: i18n.tr('Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.name = value ?? '' - ), + controller: nameController, + decoration: InputDecoration(labelText: i18n.tr('Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.name = value ?? ''), weight: 6), FormItem( ElevatedButton( - onPressed: () => verifyAndStore(), - child: Text(i18n.tr('Save'))), + onPressed: () => verifyAndStore(), child: Text(i18n.tr('Save'))), weight: 8, gapAbove: 2 * padding), FormItem( @@ -52,26 +58,30 @@ class CreateRoleCustom extends State with MessageLine { ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('New Role')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + nameController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -105,21 +115,11 @@ class CreateRoleCustom extends State with MessageLine { store(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - nameController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String name = ''; - void toMap(Map map) { map[':name'] = name; map[':createdBy'] = GlobalData.loginUserName; diff --git a/lib/page/roles/create_role_page.dart b/lib/page/roles/create_role_page.dart index 927f061..6df1eae 100644 --- a/lib/page/roles/create_role_page.dart +++ b/lib/page/roles/create_role_page.dart @@ -18,14 +18,15 @@ class CreateRolePage extends StatefulWidget { GlobalData(), RoleMeta.instance.pageByName('create')!, RoleMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _CreateRolePageState extends CreateRoleCustom { - _CreateRolePageState(): super(); + _CreateRolePageState() : super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -33,4 +34,13 @@ class _CreateRolePageState extends CreateRoleCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/roles/edit_role_custom.dart b/lib/page/roles/edit_role_custom.dart index 4108b6a..9008ce0 100644 --- a/lib/page/roles/edit_role_custom.dart +++ b/lib/page/roles/edit_role_custom.dart @@ -6,6 +6,7 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../base/i18n.dart'; import '../../base/validators.dart'; +import '../../persistence/persistence.dart'; import '../../services/global_widget.dart'; import '../../setting/global_data.dart'; import '../../widget/attended_page.dart'; @@ -18,6 +19,7 @@ final i18n = I18N(); class EditRoleCustom extends State with MessageLine { final int primaryKey; final globalData = GlobalData(); + late Future _futureDbData; AttendedPage? attendedPage; final _fieldData = _FieldData(); final GlobalKey _formKey = @@ -26,35 +28,52 @@ class EditRoleCustom extends State with MessageLine { EditRoleCustom(this.primaryKey); @override Widget build(BuildContext context) { - final padding = GlobalThemeData.padding; - if (!_fieldData.isFromBackend) { - attendedPage?.loadRecord( - name: 'record', - reload: () => setState(() => 1), - onDone: (record) { - _fieldData.fromMap(record); - _fieldData.isFromBackend = true; + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('Change Role')), + drawer: globalData.drawerBuilder(context), + body: SafeArea( + child: FutureBuilder( + future: _futureDbData, + builder: (context, snapshot) { + Widget rc; + if (snapshot.connectionState != ConnectionState.done) { + rc = const CircularProgressIndicator(); + } else { + if (snapshot.hasData) { + final dbData = snapshot.data!; + final record = dbData.singleRecord; + if (record == null) { + rc = + Text('backend problem: ${dbData.message ?? ''}'); + } + _fieldData.fromMap(record as JsonMap); + rc = buildFrame(); + } else if (snapshot.hasError) { + rc = Text('Backend problem: ${snapshot.error}'); + } else { + rc = const CircularProgressIndicator(); + } + } + return rc; }, - parameters: { - 'module': 'Roles', - 'sql': 'byId', - ':id': primaryKey - }); - } + ))); + return rc; + } + + Widget buildFrame() { + final padding = GlobalThemeData.padding; nameController.text = _fieldData.name; final formItems = [ FormItem( TextFormField( - controller: nameController, - decoration: InputDecoration(labelText: i18n.tr('Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.name = value ?? '' - ), + controller: nameController, + decoration: InputDecoration(labelText: i18n.tr('Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.name = value ?? ''), weight: 6), FormItem( ElevatedButton( - onPressed: () => verifyAndStore(), - child: Text(i18n.tr('Save'))), + onPressed: () => verifyAndStore(), child: Text(i18n.tr('Save'))), weight: 8, gapAbove: 2 * padding), FormItem( @@ -67,26 +86,38 @@ class EditRoleCustom extends State with MessageLine { ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Change Role')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _futureDbData = globalData.restPersistence!.query( + what: 'query', + data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey}); + } + + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + nameController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -102,7 +133,6 @@ class EditRoleCustom extends State with MessageLine { globalData.restPersistence! .store(what: 'store', map: parameters) .then((answer) { - _fieldData.isFromBackend = false; attendedPage!.pageStates.dbDataState.clear(); setState(() => 1); }); @@ -114,18 +144,9 @@ class EditRoleCustom extends State with MessageLine { store(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - nameController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String name = ''; void fromMap(Map map) { diff --git a/lib/page/roles/edit_role_page.dart b/lib/page/roles/edit_role_page.dart index b96e274..2c88a6f 100644 --- a/lib/page/roles/edit_role_page.dart +++ b/lib/page/roles/edit_role_page.dart @@ -19,14 +19,15 @@ class EditRolePage extends StatefulWidget { GlobalData(), RoleMeta.instance.pageByName('edit')!, RoleMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _EditRolePageState extends EditRoleCustom { - _EditRolePageState(int primaryKey): super(primaryKey); + _EditRolePageState(int primaryKey) : super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -34,4 +35,13 @@ class _EditRolePageState extends EditRoleCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/roles/list_role_custom.dart b/lib/page/roles/list_role_custom.dart index 69af556..c179cdf 100644 --- a/lib/page/roles/list_role_custom.dart +++ b/lib/page/roles/list_role_custom.dart @@ -43,7 +43,8 @@ class ListRoleCustom extends State { rc = const CircularProgressIndicator(); } else { if (snapshot.hasData) { - final rows = attendedPage!.getRows(dbData: snapshot.data!, + final rows = attendedPage!.getRows( + dbData: snapshot.data!, columnList: 'role_id;role_name', onDone: () => setState(() => 1), routeEdit: '/Roles/edit', @@ -62,15 +63,14 @@ class ListRoleCustom extends State { return rc; } - Widget buildFrame({required JsonList rows}){ + Widget buildFrame({required JsonList rows}) { final padding = GlobalThemeData.padding; final formItems = [ FormItem( TextFormField( - controller: textController, - decoration: InputDecoration(labelText: i18n.tr('Text')), - onSaved: (value) => _fieldData.text = value ?? '' - ), + controller: textController, + decoration: InputDecoration(labelText: i18n.tr('Text')), + onSaved: (value) => _fieldData.text = value ?? ''), weight: 6), FormItem( ElevatedButton( @@ -84,7 +84,7 @@ class ListRoleCustom extends State { color: GlobalThemeData.formBackgroundColor, elevation: GlobalThemeData.formElevation, margin: - EdgeInsets.symmetric(vertical: padding, horizontal: padding), + EdgeInsets.symmetric(vertical: padding, horizontal: padding), child: Padding( padding: EdgeInsets.symmetric( vertical: padding, horizontal: padding), @@ -100,7 +100,7 @@ class ListRoleCustom extends State { rows: rows as List, ); Widget? tabBar = - attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset)); + attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset)); final frameWidget = ListView(children: [ form, if (tabBar != null) tabBar, diff --git a/lib/page/roles/list_role_page.dart b/lib/page/roles/list_role_page.dart index 9a0b0a6..c4b1172 100644 --- a/lib/page/roles/list_role_page.dart +++ b/lib/page/roles/list_role_page.dart @@ -18,14 +18,15 @@ class ListRolePage extends StatefulWidget { GlobalData(), RoleMeta.instance.pageByName('list')!, RoleMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _ListRolePageState extends ListRoleCustom { - _ListRolePageState(): super(); + _ListRolePageState() : super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -33,4 +34,13 @@ class _ListRolePageState extends ListRoleCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/roles/role_data.dart b/lib/page/roles/role_data.dart index 74cd990..7b019ac 100644 --- a/lib/page/roles/role_data.dart +++ b/lib/page/roles/role_data.dart @@ -2,61 +2,83 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../persistence/data_record.dart'; -class RoleData extends DataRecord{ + +class RoleData extends DataRecord { 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( + {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') ? fromString(map['role_id'], dataType: DataType.reference) : null; - name = map.containsKey('role_name') ? fromString(map['role_name'], dataType: DataType.string) : null; - created = map.containsKey('role_created') ? fromString(map['role_created'], dataType: DataType.datetime) : null; - createdBy = map.containsKey('role_createdby') ? fromString(map['role_createdby'], dataType: DataType.string) : null; - changed = map.containsKey('role_changed') ? fromString(map['role_changed'], dataType: DataType.datetime) : null; - changedBy = map.containsKey('role_changedby') ? fromString(map['role_changedby'], dataType: DataType.string) : null; + id = map.containsKey('role_id') + ? fromString(map['role_id'], dataType: DataType.reference) + : null; + name = map.containsKey('role_name') + ? fromString(map['role_name'], dataType: DataType.string) + : null; + created = map.containsKey('role_created') + ? fromString(map['role_created'], dataType: DataType.datetime) + : null; + createdBy = map.containsKey('role_createdby') + ? fromString(map['role_createdby'], dataType: DataType.string) + : null; + changed = map.containsKey('role_changed') + ? fromString(map['role_changed'], dataType: DataType.datetime) + : null; + changedBy = map.containsKey('role_changedby') + ? fromString(map['role_changedby'], dataType: DataType.string) + : null; } + @override int keyOf() { return id ?? 0; } + @override - String nameOfKey(){ + 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; + 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(); diff --git a/lib/page/structures/create_structure_custom.dart b/lib/page/structures/create_structure_custom.dart index f05c1ee..d0f90df 100644 --- a/lib/page/structures/create_structure_custom.dart +++ b/lib/page/structures/create_structure_custom.dart @@ -15,7 +15,8 @@ import 'create_structure_page.dart'; final i18n = I18N(); -class CreateStructureCustom extends State with MessageLine { +class CreateStructureCustom extends State + with MessageLine { final globalData = GlobalData(); AttendedPage? attendedPage; final _fieldData = _FieldData(); @@ -28,6 +29,14 @@ class CreateStructureCustom extends State with MessageLine CreateStructureCustom(); @override Widget build(BuildContext context) { + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('New Structure')), + drawer: globalData.drawerBuilder(context), + body: SafeArea(child: buildFrame())); + return rc; + } + + Widget buildFrame() { final padding = GlobalThemeData.padding; scopeController.text = _fieldData.scope; nameController.text = _fieldData.name; @@ -36,39 +45,35 @@ class CreateStructureCustom extends State with MessageLine final formItems = [ FormItem( TextFormField( - controller: scopeController, - decoration: InputDecoration(labelText: i18n.tr('Scope')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.scope = value ?? '' - ), + controller: scopeController, + decoration: InputDecoration(labelText: i18n.tr('Scope')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.scope = value ?? ''), weight: 6), FormItem( TextFormField( - controller: nameController, - decoration: InputDecoration(labelText: i18n.tr('Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.name = value ?? '' - ), + controller: nameController, + decoration: InputDecoration(labelText: i18n.tr('Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.name = value ?? ''), weight: 6), FormItem( TextFormField( - controller: valueController, - decoration: InputDecoration(labelText: i18n.tr('Value')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.value = value ?? '' - ), + controller: valueController, + decoration: InputDecoration(labelText: i18n.tr('Value')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.value = value ?? ''), weight: 6), FormItem( TextFormField( - controller: positionController, - decoration: InputDecoration(labelText: i18n.tr('Position')), - onSaved: (value) => _fieldData.position = jsonToObject(value ?? '', dataType: DataType.int) - ), + controller: positionController, + decoration: InputDecoration(labelText: i18n.tr('Position')), + onSaved: (value) => _fieldData.position = + jsonToObject(value ?? '', dataType: DataType.int)), weight: 6), FormItem( ElevatedButton( - onPressed: () => verifyAndStore(), - child: Text(i18n.tr('Save'))), + onPressed: () => verifyAndStore(), child: Text(i18n.tr('Save'))), weight: 8, gapAbove: 2 * padding), FormItem( @@ -81,26 +86,33 @@ class CreateStructureCustom extends State with MessageLine ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('New Structure')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + scopeController.dispose(); + nameController.dispose(); + valueController.dispose(); + positionController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -134,27 +146,14 @@ class CreateStructureCustom extends State with MessageLine store(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - scopeController.dispose(); - nameController.dispose(); - valueController.dispose(); - positionController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String scope = ''; String name = ''; String value = ''; int position = 0; - void toMap(Map map) { map[':scope'] = scope; map[':name'] = name; diff --git a/lib/page/structures/create_structure_page.dart b/lib/page/structures/create_structure_page.dart index 9be367a..0773962 100644 --- a/lib/page/structures/create_structure_page.dart +++ b/lib/page/structures/create_structure_page.dart @@ -18,14 +18,15 @@ class CreateStructurePage extends StatefulWidget { GlobalData(), StructureMeta.instance.pageByName('create')!, StructureMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _CreateStructurePageState extends CreateStructureCustom { - _CreateStructurePageState(): super(); + _CreateStructurePageState() : super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -33,4 +34,13 @@ class _CreateStructurePageState extends CreateStructureCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/structures/delete_structure_custom.dart b/lib/page/structures/delete_structure_custom.dart index baad392..272fbb9 100644 --- a/lib/page/structures/delete_structure_custom.dart +++ b/lib/page/structures/delete_structure_custom.dart @@ -6,6 +6,7 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../base/i18n.dart'; import '../../base/validators.dart'; +import '../../persistence/persistence.dart'; import '../../services/global_widget.dart'; import '../../setting/global_data.dart'; import '../../widget/attended_page.dart'; @@ -15,9 +16,11 @@ import 'delete_structure_page.dart'; final i18n = I18N(); -class DeleteStructureCustom extends State with MessageLine { +class DeleteStructureCustom extends State + with MessageLine { final int primaryKey; final globalData = GlobalData(); + late Future _futureDbData; AttendedPage? attendedPage; final _fieldData = _FieldData(); final GlobalKey _formKey = @@ -29,21 +32,40 @@ class DeleteStructureCustom extends State with MessageLine DeleteStructureCustom(this.primaryKey); @override Widget build(BuildContext context) { - final padding = GlobalThemeData.padding; - if (!_fieldData.isFromBackend) { - attendedPage?.loadRecord( - name: 'record', - reload: () => setState(() => 1), - onDone: (record) { - _fieldData.fromMap(record); - _fieldData.isFromBackend = true; + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('Delete Structure')), + drawer: globalData.drawerBuilder(context), + body: SafeArea( + child: FutureBuilder( + future: _futureDbData, + builder: (context, snapshot) { + Widget rc; + if (snapshot.connectionState != ConnectionState.done) { + rc = const CircularProgressIndicator(); + } else { + if (snapshot.hasData) { + final dbData = snapshot.data!; + final record = dbData.singleRecord; + if (record == null) { + rc = + Text('backend problem: ${dbData.message ?? ''}'); + } + _fieldData.fromMap(record as JsonMap); + rc = buildFrame(); + } else if (snapshot.hasError) { + rc = Text('Backend problem: ${snapshot.error}'); + } else { + rc = const CircularProgressIndicator(); + } + } + return rc; }, - parameters: { - 'module': 'Structures', - 'sql': 'byId', - ':id': primaryKey - }); - } + ))); + return rc; + } + + Widget buildFrame() { + final padding = GlobalThemeData.padding; scopeController.text = _fieldData.scope; nameController.text = _fieldData.name; valueController.text = _fieldData.value; @@ -51,34 +73,31 @@ class DeleteStructureCustom extends State with MessageLine final formItems = [ FormItem( TextFormField( - controller: scopeController, - decoration: InputDecoration(labelText: i18n.tr('Scope')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.scope = value ?? '' - ), + controller: scopeController, + decoration: InputDecoration(labelText: i18n.tr('Scope')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.scope = value ?? ''), weight: 6), FormItem( TextFormField( - controller: nameController, - decoration: InputDecoration(labelText: i18n.tr('Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.name = value ?? '' - ), + controller: nameController, + decoration: InputDecoration(labelText: i18n.tr('Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.name = value ?? ''), weight: 6), FormItem( TextFormField( - controller: valueController, - decoration: InputDecoration(labelText: i18n.tr('Value')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.value = value ?? '' - ), + controller: valueController, + decoration: InputDecoration(labelText: i18n.tr('Value')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.value = value ?? ''), weight: 6), FormItem( TextFormField( - controller: positionController, - decoration: InputDecoration(labelText: i18n.tr('Position')), - onSaved: (value) => _fieldData.position = jsonToObject(value ?? '', dataType: DataType.int) - ), + controller: positionController, + decoration: InputDecoration(labelText: i18n.tr('Position')), + onSaved: (value) => _fieldData.position = + jsonToObject(value ?? '', dataType: DataType.int)), weight: 6), FormItem( ElevatedButton( @@ -96,26 +115,41 @@ class DeleteStructureCustom extends State with MessageLine ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Delete Structure')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _futureDbData = globalData.restPersistence!.query( + what: 'query', + data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey}); + } + + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + scopeController.dispose(); + nameController.dispose(); + valueController.dispose(); + positionController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -140,21 +174,9 @@ class DeleteStructureCustom extends State with MessageLine delete(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - scopeController.dispose(); - nameController.dispose(); - valueController.dispose(); - positionController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String scope = ''; String name = ''; String value = ''; diff --git a/lib/page/structures/delete_structure_page.dart b/lib/page/structures/delete_structure_page.dart index 1d89c7a..ed32671 100644 --- a/lib/page/structures/delete_structure_page.dart +++ b/lib/page/structures/delete_structure_page.dart @@ -19,14 +19,15 @@ class DeleteStructurePage extends StatefulWidget { GlobalData(), StructureMeta.instance.pageByName('delete')!, StructureMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _DeleteStructurePageState extends DeleteStructureCustom { - _DeleteStructurePageState(int primaryKey): super(primaryKey); + _DeleteStructurePageState(int primaryKey) : super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -34,4 +35,13 @@ class _DeleteStructurePageState extends DeleteStructureCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/structures/edit_structure_custom.dart b/lib/page/structures/edit_structure_custom.dart index 11bd3d0..bc38798 100644 --- a/lib/page/structures/edit_structure_custom.dart +++ b/lib/page/structures/edit_structure_custom.dart @@ -6,6 +6,7 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../base/i18n.dart'; import '../../base/validators.dart'; +import '../../persistence/persistence.dart'; import '../../services/global_widget.dart'; import '../../setting/global_data.dart'; import '../../widget/attended_page.dart'; @@ -18,6 +19,7 @@ final i18n = I18N(); class EditStructureCustom extends State with MessageLine { final int primaryKey; final globalData = GlobalData(); + late Future _futureDbData; AttendedPage? attendedPage; final _fieldData = _FieldData(); final GlobalKey _formKey = @@ -29,21 +31,40 @@ class EditStructureCustom extends State with MessageLine { EditStructureCustom(this.primaryKey); @override Widget build(BuildContext context) { - final padding = GlobalThemeData.padding; - if (!_fieldData.isFromBackend) { - attendedPage?.loadRecord( - name: 'record', - reload: () => setState(() => 1), - onDone: (record) { - _fieldData.fromMap(record); - _fieldData.isFromBackend = true; + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('Change Structure')), + drawer: globalData.drawerBuilder(context), + body: SafeArea( + child: FutureBuilder( + future: _futureDbData, + builder: (context, snapshot) { + Widget rc; + if (snapshot.connectionState != ConnectionState.done) { + rc = const CircularProgressIndicator(); + } else { + if (snapshot.hasData) { + final dbData = snapshot.data!; + final record = dbData.singleRecord; + if (record == null) { + rc = + Text('backend problem: ${dbData.message ?? ''}'); + } + _fieldData.fromMap(record as JsonMap); + rc = buildFrame(); + } else if (snapshot.hasError) { + rc = Text('Backend problem: ${snapshot.error}'); + } else { + rc = const CircularProgressIndicator(); + } + } + return rc; }, - parameters: { - 'module': 'Structures', - 'sql': 'byId', - ':id': primaryKey - }); - } + ))); + return rc; + } + + Widget buildFrame() { + final padding = GlobalThemeData.padding; scopeController.text = _fieldData.scope; nameController.text = _fieldData.name; valueController.text = _fieldData.value; @@ -51,39 +72,35 @@ class EditStructureCustom extends State with MessageLine { final formItems = [ FormItem( TextFormField( - controller: scopeController, - decoration: InputDecoration(labelText: i18n.tr('Scope')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.scope = value ?? '' - ), + controller: scopeController, + decoration: InputDecoration(labelText: i18n.tr('Scope')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.scope = value ?? ''), weight: 6), FormItem( TextFormField( - controller: nameController, - decoration: InputDecoration(labelText: i18n.tr('Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.name = value ?? '' - ), + controller: nameController, + decoration: InputDecoration(labelText: i18n.tr('Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.name = value ?? ''), weight: 6), FormItem( TextFormField( - controller: valueController, - decoration: InputDecoration(labelText: i18n.tr('Value')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.value = value ?? '' - ), + controller: valueController, + decoration: InputDecoration(labelText: i18n.tr('Value')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.value = value ?? ''), weight: 6), FormItem( TextFormField( - controller: positionController, - decoration: InputDecoration(labelText: i18n.tr('Position')), - onSaved: (value) => _fieldData.position = jsonToObject(value ?? '', dataType: DataType.int) - ), + controller: positionController, + decoration: InputDecoration(labelText: i18n.tr('Position')), + onSaved: (value) => _fieldData.position = + jsonToObject(value ?? '', dataType: DataType.int)), weight: 6), FormItem( ElevatedButton( - onPressed: () => verifyAndStore(), - child: Text(i18n.tr('Save'))), + onPressed: () => verifyAndStore(), child: Text(i18n.tr('Save'))), weight: 8, gapAbove: 2 * padding), FormItem( @@ -96,26 +113,41 @@ class EditStructureCustom extends State with MessageLine { ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Change Structure')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _futureDbData = globalData.restPersistence!.query( + what: 'query', + data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey}); + } + + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + scopeController.dispose(); + nameController.dispose(); + valueController.dispose(); + positionController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -131,7 +163,6 @@ class EditStructureCustom extends State with MessageLine { globalData.restPersistence! .store(what: 'store', map: parameters) .then((answer) { - _fieldData.isFromBackend = false; attendedPage!.pageStates.dbDataState.clear(); setState(() => 1); }); @@ -143,21 +174,9 @@ class EditStructureCustom extends State with MessageLine { store(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - scopeController.dispose(); - nameController.dispose(); - valueController.dispose(); - positionController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String scope = ''; String name = ''; String value = ''; diff --git a/lib/page/structures/edit_structure_page.dart b/lib/page/structures/edit_structure_page.dart index 516fdff..df271e3 100644 --- a/lib/page/structures/edit_structure_page.dart +++ b/lib/page/structures/edit_structure_page.dart @@ -19,14 +19,15 @@ class EditStructurePage extends StatefulWidget { GlobalData(), StructureMeta.instance.pageByName('edit')!, StructureMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _EditStructurePageState extends EditStructureCustom { - _EditStructurePageState(int primaryKey): super(primaryKey); + _EditStructurePageState(int primaryKey) : super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -34,4 +35,13 @@ class _EditStructurePageState extends EditStructureCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/structures/list_structure_custom.dart b/lib/page/structures/list_structure_custom.dart index 1380aad..7f09d04 100644 --- a/lib/page/structures/list_structure_custom.dart +++ b/lib/page/structures/list_structure_custom.dart @@ -43,8 +43,10 @@ class ListStructureCustom extends State { rc = const CircularProgressIndicator(); } else { if (snapshot.hasData) { - final rows = attendedPage!.getRows(dbData: snapshot.data!, - columnList: 'structure_id;structure_scope;structure_name;structure_value;structure_position', + final rows = attendedPage!.getRows( + dbData: snapshot.data!, + columnList: + 'structure_id;structure_scope;structure_name;structure_value;structure_position', onDone: () => setState(() => 1), routeEdit: '/Structures/edit', context: context); @@ -62,15 +64,14 @@ class ListStructureCustom extends State { return rc; } - Widget buildFrame({required JsonList rows}){ + Widget buildFrame({required JsonList rows}) { final padding = GlobalThemeData.padding; final formItems = [ FormItem( TextFormField( - controller: textController, - decoration: InputDecoration(labelText: i18n.tr('Text')), - onSaved: (value) => _fieldData.text = value ?? '' - ), + controller: textController, + decoration: InputDecoration(labelText: i18n.tr('Text')), + onSaved: (value) => _fieldData.text = value ?? ''), weight: 6), FormItem( ElevatedButton( @@ -84,7 +85,7 @@ class ListStructureCustom extends State { color: GlobalThemeData.formBackgroundColor, elevation: GlobalThemeData.formElevation, margin: - EdgeInsets.symmetric(vertical: padding, horizontal: padding), + EdgeInsets.symmetric(vertical: padding, horizontal: padding), child: Padding( padding: EdgeInsets.symmetric( vertical: padding, horizontal: padding), @@ -100,7 +101,7 @@ class ListStructureCustom extends State { rows: rows as List, ); Widget? tabBar = - attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset)); + attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset)); final frameWidget = ListView(children: [ form, if (tabBar != null) tabBar, diff --git a/lib/page/structures/list_structure_page.dart b/lib/page/structures/list_structure_page.dart index ad80fff..efeb5d7 100644 --- a/lib/page/structures/list_structure_page.dart +++ b/lib/page/structures/list_structure_page.dart @@ -18,14 +18,15 @@ class ListStructurePage extends StatefulWidget { GlobalData(), StructureMeta.instance.pageByName('list')!, StructureMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _ListStructurePageState extends ListStructureCustom { - _ListStructurePageState(): super(); + _ListStructurePageState() : super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -33,4 +34,13 @@ class _ListStructurePageState extends ListStructureCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/structures/structure_data.dart b/lib/page/structures/structure_data.dart index 85285ec..b09010a 100644 --- a/lib/page/structures/structure_data.dart +++ b/lib/page/structures/structure_data.dart @@ -2,7 +2,8 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../persistence/data_record.dart'; -class StructureData extends DataRecord{ + +class StructureData extends DataRecord { int? id; String? scope; String? name; @@ -12,66 +13,96 @@ class StructureData extends DataRecord{ String? createdBy; DateTime? changed; String? changedBy; - StructureData({ - this.id, this.scope, this.name, this.value, this.position, this.created, this.createdBy, this.changed, this.changedBy}); + StructureData( + {this.id, + this.scope, + this.name, + this.value, + this.position, + this.created, + this.createdBy, + this.changed, + this.changedBy}); StructureData.createFromMap(DataMap map) { fromMap(map); } @override void fromMap(DataMap map) { - id = map.containsKey('structure_id') ? fromString(map['structure_id'], dataType: DataType.reference) : null; - scope = map.containsKey('structure_scope') ? fromString(map['structure_scope'], dataType: DataType.string) : null; - name = map.containsKey('structure_name') ? fromString(map['structure_name'], dataType: DataType.string) : null; - value = map.containsKey('structure_value') ? fromString(map['structure_value'], dataType: DataType.string) : null; - position = map.containsKey('structure_position') ? fromString(map['structure_position'], dataType: DataType.int) : null; - created = map.containsKey('structure_created') ? fromString(map['structure_created'], dataType: DataType.datetime) : null; - createdBy = map.containsKey('structure_createdby') ? fromString(map['structure_createdby'], dataType: DataType.string) : null; - changed = map.containsKey('structure_changed') ? fromString(map['structure_changed'], dataType: DataType.datetime) : null; - changedBy = map.containsKey('structure_changedby') ? fromString(map['structure_changedby'], dataType: DataType.string) : null; + id = map.containsKey('structure_id') + ? fromString(map['structure_id'], dataType: DataType.reference) + : null; + scope = map.containsKey('structure_scope') + ? fromString(map['structure_scope'], dataType: DataType.string) + : null; + name = map.containsKey('structure_name') + ? fromString(map['structure_name'], dataType: DataType.string) + : null; + value = map.containsKey('structure_value') + ? fromString(map['structure_value'], dataType: DataType.string) + : null; + position = map.containsKey('structure_position') + ? fromString(map['structure_position'], dataType: DataType.int) + : null; + created = map.containsKey('structure_created') + ? fromString(map['structure_created'], dataType: DataType.datetime) + : null; + createdBy = map.containsKey('structure_createdby') + ? fromString(map['structure_createdby'], dataType: DataType.string) + : null; + changed = map.containsKey('structure_changed') + ? fromString(map['structure_changed'], dataType: DataType.datetime) + : null; + changedBy = map.containsKey('structure_changedby') + ? fromString(map['structure_changedby'], dataType: DataType.string) + : null; } + @override int keyOf() { return id ?? 0; } + @override - String nameOfKey(){ + String nameOfKey() { return 'structure_id'; } + static DataType? dataTypeOf(String name) { DataType? rc; - switch(name){ - case 'id': - rc = DataType.reference; - break; - case 'scope': - rc = DataType.string; - break; - case 'name': - rc = DataType.string; - break; - case 'value': - rc = DataType.string; - break; - case 'position': - rc = DataType.int; - 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 'scope': + rc = DataType.string; + break; + case 'name': + rc = DataType.string; + break; + case 'value': + rc = DataType.string; + break; + case 'position': + rc = DataType.int; + 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(); diff --git a/lib/page/users/create_user_custom.dart b/lib/page/users/create_user_custom.dart index d01ac15..33570bf 100644 --- a/lib/page/users/create_user_custom.dart +++ b/lib/page/users/create_user_custom.dart @@ -27,6 +27,14 @@ class CreateUserCustom extends State with MessageLine { CreateUserCustom(); @override Widget build(BuildContext context) { + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('New User')), + drawer: globalData.drawerBuilder(context), + body: SafeArea(child: buildFrame())); + return rc; + } + + Widget buildFrame() { final padding = GlobalThemeData.padding; comboRolesFromBackend( attendedPage: attendedPage!, onDone: () => setState(() => 1)); @@ -38,31 +46,30 @@ class CreateUserCustom extends State with MessageLine { final formItems = [ FormItem( TextFormField( - controller: nameController, - decoration: InputDecoration(labelText: i18n.tr('Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.name = value ?? '' - ), + controller: nameController, + decoration: InputDecoration(labelText: i18n.tr('Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.name = value ?? ''), weight: 6), FormItem( TextFormField( - controller: displayNameController, - decoration: InputDecoration(labelText: i18n.tr('Display name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.displayName = value ?? '' - ), + controller: displayNameController, + decoration: InputDecoration(labelText: i18n.tr('Display name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.displayName = value ?? ''), weight: 6), FormItem( TextFormField( - controller: emailController, - decoration: InputDecoration(labelText: i18n.tr('EMail')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.email = value ?? '' - ), + controller: emailController, + decoration: InputDecoration(labelText: i18n.tr('EMail')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.email = value ?? ''), weight: 6), FormItem( DropdownButtonFormField( - value: _fieldData.role, + value: itemsRoles == null || itemsRoles.length == 1 + ? 0 + : _fieldData.role, items: itemsRoles, isExpanded: true, decoration: InputDecoration(labelText: i18n.tr('Role')), @@ -71,8 +78,7 @@ class CreateUserCustom extends State with MessageLine { weight: 6), FormItem( ElevatedButton( - onPressed: () => verifyAndStore(), - child: Text(i18n.tr('Save'))), + onPressed: () => verifyAndStore(), child: Text(i18n.tr('Save'))), weight: 8, gapAbove: 2 * padding), FormItem( @@ -85,26 +91,32 @@ class CreateUserCustom extends State with MessageLine { ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('New User')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + nameController.dispose(); + displayNameController.dispose(); + emailController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -138,26 +150,14 @@ class CreateUserCustom extends State with MessageLine { store(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - nameController.dispose(); - displayNameController.dispose(); - emailController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String name = ''; String displayName = ''; String email = ''; int role = 0; - void toMap(Map map) { map[':name'] = name; map[':displayName'] = displayName; diff --git a/lib/page/users/create_user_page.dart b/lib/page/users/create_user_page.dart index 5fed6f1..3048fa1 100644 --- a/lib/page/users/create_user_page.dart +++ b/lib/page/users/create_user_page.dart @@ -18,14 +18,15 @@ class CreateUserPage extends StatefulWidget { GlobalData(), UserMeta.instance.pageByName('create')!, UserMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _CreateUserPageState extends CreateUserCustom { - _CreateUserPageState(): super(); + _CreateUserPageState() : super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -33,4 +34,13 @@ class _CreateUserPageState extends CreateUserCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/users/delete_user_custom.dart b/lib/page/users/delete_user_custom.dart index a1df6ea..1380cc4 100644 --- a/lib/page/users/delete_user_custom.dart +++ b/lib/page/users/delete_user_custom.dart @@ -6,6 +6,7 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../base/i18n.dart'; import '../../base/validators.dart'; +import '../../persistence/persistence.dart'; import '../../services/global_widget.dart'; import '../../setting/global_data.dart'; import '../../widget/attended_page.dart'; @@ -18,6 +19,7 @@ final i18n = I18N(); class DeleteUserCustom extends State with MessageLine { final int primaryKey; final globalData = GlobalData(); + late Future _futureDbData; AttendedPage? attendedPage; final _fieldData = _FieldData(); final GlobalKey _formKey = @@ -28,56 +30,74 @@ class DeleteUserCustom extends State with MessageLine { DeleteUserCustom(this.primaryKey); @override Widget build(BuildContext context) { + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('Delete User')), + drawer: globalData.drawerBuilder(context), + body: SafeArea( + child: FutureBuilder( + future: _futureDbData, + builder: (context, snapshot) { + Widget rc; + if (snapshot.connectionState != ConnectionState.done) { + rc = const CircularProgressIndicator(); + } else { + if (snapshot.hasData) { + final dbData = snapshot.data!; + final record = dbData.singleRecord; + if (record == null) { + rc = + Text('backend problem: ${dbData.message ?? ''}'); + } + _fieldData.fromMap(record as JsonMap); + rc = buildFrame(); + } else if (snapshot.hasError) { + rc = Text('Backend problem: ${snapshot.error}'); + } else { + rc = const CircularProgressIndicator(); + } + } + return rc; + }, + ))); + return rc; + } + + Widget buildFrame() { final padding = GlobalThemeData.padding; comboRolesFromBackend( attendedPage: attendedPage!, onDone: () => setState(() => 1)); final itemsRoles = comboRoles( i18n.trDyn(GlobalTranslations.comboboxSelect), attendedPage!); - if (!_fieldData.isFromBackend) { - attendedPage?.loadRecord( - name: 'record', - reload: () => setState(() => 1), - onDone: (record) { - _fieldData.fromMap(record); - _fieldData.isFromBackend = true; - }, - parameters: { - 'module': 'Users', - 'sql': 'byId', - ':id': primaryKey - }); - } nameController.text = _fieldData.name; displayNameController.text = _fieldData.displayName; emailController.text = _fieldData.email; final formItems = [ FormItem( TextFormField( - controller: nameController, - decoration: InputDecoration(labelText: i18n.tr('Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.name = value ?? '' - ), + controller: nameController, + decoration: InputDecoration(labelText: i18n.tr('Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.name = value ?? ''), weight: 6), FormItem( TextFormField( - controller: displayNameController, - decoration: InputDecoration(labelText: i18n.tr('Display name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.displayName = value ?? '' - ), + controller: displayNameController, + decoration: InputDecoration(labelText: i18n.tr('Display name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.displayName = value ?? ''), weight: 6), FormItem( TextFormField( - controller: emailController, - decoration: InputDecoration(labelText: i18n.tr('EMail')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.email = value ?? '' - ), + controller: emailController, + decoration: InputDecoration(labelText: i18n.tr('EMail')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.email = value ?? ''), weight: 6), FormItem( DropdownButtonFormField( - value: _fieldData.role, + value: itemsRoles == null || itemsRoles.length == 1 + ? 0 + : _fieldData.role, items: itemsRoles, isExpanded: true, decoration: InputDecoration(labelText: i18n.tr('Role')), @@ -100,26 +120,40 @@ class DeleteUserCustom extends State with MessageLine { ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Delete User')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _futureDbData = globalData.restPersistence!.query( + what: 'query', + data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey}); + } + + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + nameController.dispose(); + displayNameController.dispose(); + emailController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -144,20 +178,9 @@ class DeleteUserCustom extends State with MessageLine { delete(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - nameController.dispose(); - displayNameController.dispose(); - emailController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String name = ''; String displayName = ''; String email = ''; diff --git a/lib/page/users/delete_user_page.dart b/lib/page/users/delete_user_page.dart index 111ddc4..98871a5 100644 --- a/lib/page/users/delete_user_page.dart +++ b/lib/page/users/delete_user_page.dart @@ -19,14 +19,15 @@ class DeleteUserPage extends StatefulWidget { GlobalData(), UserMeta.instance.pageByName('delete')!, UserMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _DeleteUserPageState extends DeleteUserCustom { - _DeleteUserPageState(int primaryKey): super(primaryKey); + _DeleteUserPageState(int primaryKey) : super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -34,4 +35,13 @@ class _DeleteUserPageState extends DeleteUserCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/users/edit_user_custom.dart b/lib/page/users/edit_user_custom.dart index 374bc2a..311e124 100644 --- a/lib/page/users/edit_user_custom.dart +++ b/lib/page/users/edit_user_custom.dart @@ -6,6 +6,7 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../base/i18n.dart'; import '../../base/validators.dart'; +import '../../persistence/persistence.dart'; import '../../services/global_widget.dart'; import '../../setting/global_data.dart'; import '../../widget/attended_page.dart'; @@ -18,6 +19,7 @@ final i18n = I18N(); class EditUserCustom extends State with MessageLine { final int primaryKey; final globalData = GlobalData(); + late Future _futureDbData; AttendedPage? attendedPage; final _fieldData = _FieldData(); final GlobalKey _formKey = @@ -28,56 +30,74 @@ class EditUserCustom extends State with MessageLine { EditUserCustom(this.primaryKey); @override Widget build(BuildContext context) { + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('Change User')), + drawer: globalData.drawerBuilder(context), + body: SafeArea( + child: FutureBuilder( + future: _futureDbData, + builder: (context, snapshot) { + Widget rc; + if (snapshot.connectionState != ConnectionState.done) { + rc = const CircularProgressIndicator(); + } else { + if (snapshot.hasData) { + final dbData = snapshot.data!; + final record = dbData.singleRecord; + if (record == null) { + rc = + Text('backend problem: ${dbData.message ?? ''}'); + } + _fieldData.fromMap(record as JsonMap); + rc = buildFrame(); + } else if (snapshot.hasError) { + rc = Text('Backend problem: ${snapshot.error}'); + } else { + rc = const CircularProgressIndicator(); + } + } + return rc; + }, + ))); + return rc; + } + + Widget buildFrame() { final padding = GlobalThemeData.padding; comboRolesFromBackend( attendedPage: attendedPage!, onDone: () => setState(() => 1)); final itemsRoles = comboRoles( i18n.trDyn(GlobalTranslations.comboboxSelect), attendedPage!); - if (!_fieldData.isFromBackend) { - attendedPage?.loadRecord( - name: 'record', - reload: () => setState(() => 1), - onDone: (record) { - _fieldData.fromMap(record); - _fieldData.isFromBackend = true; - }, - parameters: { - 'module': 'Users', - 'sql': 'byId', - ':id': primaryKey - }); - } nameController.text = _fieldData.name; displayNameController.text = _fieldData.displayName; emailController.text = _fieldData.email; final formItems = [ FormItem( TextFormField( - controller: nameController, - decoration: InputDecoration(labelText: i18n.tr('Name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.name = value ?? '' - ), + controller: nameController, + decoration: InputDecoration(labelText: i18n.tr('Name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.name = value ?? ''), weight: 6), FormItem( TextFormField( - controller: displayNameController, - decoration: InputDecoration(labelText: i18n.tr('Display name')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.displayName = value ?? '' - ), + controller: displayNameController, + decoration: InputDecoration(labelText: i18n.tr('Display name')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.displayName = value ?? ''), weight: 6), FormItem( TextFormField( - controller: emailController, - decoration: InputDecoration(labelText: i18n.tr('EMail')), - validator: (input) => notEmpty(input), - onSaved: (value) => _fieldData.email = value ?? '' - ), + controller: emailController, + decoration: InputDecoration(labelText: i18n.tr('EMail')), + validator: (input) => notEmpty(input), + onSaved: (value) => _fieldData.email = value ?? ''), weight: 6), FormItem( DropdownButtonFormField( - value: _fieldData.role, + value: itemsRoles == null || itemsRoles.length == 1 + ? 0 + : _fieldData.role, items: itemsRoles, isExpanded: true, decoration: InputDecoration(labelText: i18n.tr('Role')), @@ -86,8 +106,7 @@ class EditUserCustom extends State with MessageLine { weight: 6), FormItem( ElevatedButton( - onPressed: () => verifyAndStore(), - child: Text(i18n.tr('Save'))), + onPressed: () => verifyAndStore(), child: Text(i18n.tr('Save'))), weight: 8, gapAbove: 2 * padding), FormItem( @@ -100,26 +119,40 @@ class EditUserCustom extends State with MessageLine { ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('Change User')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _futureDbData = globalData.restPersistence!.query( + what: 'query', + data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey}); + } + + @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); + nameController.dispose(); + displayNameController.dispose(); + emailController.dispose(); + super.dispose(); + } + @override void initState() { super.initState(); @@ -135,7 +168,6 @@ class EditUserCustom extends State with MessageLine { globalData.restPersistence! .store(what: 'store', map: parameters) .then((answer) { - _fieldData.isFromBackend = false; attendedPage!.pageStates.dbDataState.clear(); setState(() => 1); }); @@ -147,20 +179,9 @@ class EditUserCustom extends State with MessageLine { store(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); - nameController.dispose(); - displayNameController.dispose(); - emailController.dispose(); - super.dispose(); - } } class _FieldData { - bool isFromBackend = false; String name = ''; String displayName = ''; String email = ''; diff --git a/lib/page/users/edit_user_page.dart b/lib/page/users/edit_user_page.dart index 080f1d7..230fe4a 100644 --- a/lib/page/users/edit_user_page.dart +++ b/lib/page/users/edit_user_page.dart @@ -19,14 +19,15 @@ class EditUserPage extends StatefulWidget { GlobalData(), UserMeta.instance.pageByName('edit')!, UserMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _EditUserPageState extends EditUserCustom { - _EditUserPageState(int primaryKey): super(primaryKey); + _EditUserPageState(int primaryKey) : super(primaryKey); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -34,4 +35,13 @@ class _EditUserPageState extends EditUserCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/users/list_user_custom.dart b/lib/page/users/list_user_custom.dart index dd50e61..c23310e 100644 --- a/lib/page/users/list_user_custom.dart +++ b/lib/page/users/list_user_custom.dart @@ -43,8 +43,10 @@ class ListUserCustom extends State { rc = const CircularProgressIndicator(); } else { if (snapshot.hasData) { - final rows = attendedPage!.getRows(dbData: snapshot.data!, - columnList: 'user_id;user_name;user_displayname;user_email;role', + final rows = attendedPage!.getRows( + dbData: snapshot.data!, + columnList: + 'user_id;user_name;user_displayname;user_email;role', onDone: () => setState(() => 1), routeEdit: '/Users/edit', context: context); @@ -62,7 +64,7 @@ class ListUserCustom extends State { return rc; } - Widget buildFrame({required JsonList rows}){ + Widget buildFrame({required JsonList rows}) { final padding = GlobalThemeData.padding; comboRolesFromBackend( attendedPage: attendedPage!, onDone: () => setState(() => 1)); @@ -71,14 +73,15 @@ class ListUserCustom extends State { final formItems = [ FormItem( TextFormField( - controller: textController, - decoration: InputDecoration(labelText: i18n.tr('Text')), - onSaved: (value) => _fieldData.text = value ?? '' - ), + controller: textController, + decoration: InputDecoration(labelText: i18n.tr('Text')), + onSaved: (value) => _fieldData.text = value ?? ''), weight: 6), FormItem( DropdownButtonFormField( - value: _fieldData.role, + value: itemsRoles == null || itemsRoles.length == 1 + ? 0 + : _fieldData.role, items: itemsRoles, isExpanded: true, decoration: InputDecoration(labelText: i18n.tr('Role')), @@ -97,7 +100,7 @@ class ListUserCustom extends State { color: GlobalThemeData.formBackgroundColor, elevation: GlobalThemeData.formElevation, margin: - EdgeInsets.symmetric(vertical: padding, horizontal: padding), + EdgeInsets.symmetric(vertical: padding, horizontal: padding), child: Padding( padding: EdgeInsets.symmetric( vertical: padding, horizontal: padding), @@ -125,7 +128,7 @@ class ListUserCustom extends State { rows: rows as List, ); Widget? tabBar = - attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset)); + attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset)); final frameWidget = ListView(children: [ form, if (tabBar != null) tabBar, diff --git a/lib/page/users/list_user_page.dart b/lib/page/users/list_user_page.dart index d36e2bf..67ac443 100644 --- a/lib/page/users/list_user_page.dart +++ b/lib/page/users/list_user_page.dart @@ -18,14 +18,15 @@ class ListUserPage extends StatefulWidget { GlobalData(), UserMeta.instance.pageByName('list')!, UserMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } } class _ListUserPageState extends ListUserCustom { - _ListUserPageState(): super(); + _ListUserPageState() : super(); @override void didChangeDependencies() { final size = MediaQuery.of(context).size; @@ -33,4 +34,13 @@ class _ListUserPageState extends ListUserCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload) { + if (afterReload == null) { + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } diff --git a/lib/page/users/user_data.dart b/lib/page/users/user_data.dart index d27593e..427c74d 100644 --- a/lib/page/users/user_data.dart +++ b/lib/page/users/user_data.dart @@ -2,7 +2,8 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../persistence/data_record.dart'; -class UserData extends DataRecord{ + +class UserData extends DataRecord { int? id; String? name; String? displayName; @@ -12,66 +13,96 @@ class UserData extends DataRecord{ 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( + {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') ? fromString(map['user_id'], dataType: DataType.reference) : null; - name = map.containsKey('user_name') ? fromString(map['user_name'], dataType: DataType.string) : null; - displayName = map.containsKey('user_displayname') ? fromString(map['user_displayname'], dataType: DataType.string) : null; - email = map.containsKey('user_email') ? fromString(map['user_email'], dataType: DataType.string) : null; - role = map.containsKey('user_role') ? fromString(map['user_role'], dataType: DataType.reference) : null; - created = map.containsKey('user_created') ? fromString(map['user_created'], dataType: DataType.datetime) : null; - createdBy = map.containsKey('user_createdby') ? fromString(map['user_createdby'], dataType: DataType.string) : null; - changed = map.containsKey('user_changed') ? fromString(map['user_changed'], dataType: DataType.datetime) : null; - changedBy = map.containsKey('user_changedby') ? fromString(map['user_changedby'], dataType: DataType.string) : null; + id = map.containsKey('user_id') + ? fromString(map['user_id'], dataType: DataType.reference) + : null; + name = map.containsKey('user_name') + ? fromString(map['user_name'], dataType: DataType.string) + : null; + displayName = map.containsKey('user_displayname') + ? fromString(map['user_displayname'], dataType: DataType.string) + : null; + email = map.containsKey('user_email') + ? fromString(map['user_email'], dataType: DataType.string) + : null; + role = map.containsKey('user_role') + ? fromString(map['user_role'], dataType: DataType.reference) + : null; + created = map.containsKey('user_created') + ? fromString(map['user_created'], dataType: DataType.datetime) + : null; + createdBy = map.containsKey('user_createdby') + ? fromString(map['user_createdby'], dataType: DataType.string) + : null; + changed = map.containsKey('user_changed') + ? fromString(map['user_changed'], dataType: DataType.datetime) + : null; + changedBy = map.containsKey('user_changedby') + ? fromString(map['user_changedby'], dataType: DataType.string) + : null; } + @override int keyOf() { return id ?? 0; } + @override - String nameOfKey(){ + 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(); diff --git a/lib/persistence/rest_persistence.dart b/lib/persistence/rest_persistence.dart index a3a1872..438cdfe 100644 --- a/lib/persistence/rest_persistence.dart +++ b/lib/persistence/rest_persistence.dart @@ -119,7 +119,7 @@ class RestPersistence extends Persistence { '${answer.substring(0, min(answer.length, 8))}'); } } else { - rc = DbData.message(answer.substring(0, max(answer.length, 120))); + rc = DbData.message(answer.substring(0, min(answer.length, 120))); } if (logger.logLevel >= LEVEL_FINE) { logger.log('answer: ${limitString(answer, sqlTraceLimit)}'); diff --git a/lib/setting/global_data.dart b/lib/setting/global_data.dart index bab2129..fce281d 100644 --- a/lib/setting/global_data.dart +++ b/lib/setting/global_data.dart @@ -1,14 +1,15 @@ import 'dart:io'; + import 'package:dart_bones/dart_bones.dart'; import 'package:flutter/material.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; -import '../persistence/rest_persistence.dart'; -import '../widget/attended_page.dart'; import '../base/application_name.dart'; import '../base/defines.dart'; import '../page/page_controller_exhibition.dart'; +import '../persistence/rest_persistence.dart'; +import '../widget/attended_page.dart'; typedef AppBarBuilder = Function(String); @@ -30,35 +31,6 @@ abstract class FooterInterface { Widget widget(PageControllerExhibition controller); } -class HomeDirectories { - Directory configurations = Directory.current; - Directory localData = Directory.current; - HomeDirectories.dummy(); - HomeDirectories(String applicationName) { - if (Platform.isLinux) { - configurations = Directory('/etc/$applicationName'); - localData = Directory(path.join('/home', Platform.environment['LOGNAME'], - '.local/share/$applicationName')); - } else if (Platform.isAndroid) { - getExternalStorageDirectory().then((dir) => localData = dir!); - } else { - throw FormatException('HomeDirectory.getHomePath(): unknown platform'); - } - } -} - -class GlobalTranslations { - static final comboboxItemAll = i18n.tr(''); - static final comboboxSelect = i18n.tr(''); -} - -class GlobalThemeData { - static final formBackgroundColor = Color.fromARGB(0xee, 0xee, 0xee, 0xee); - static final formElevation = 10.0; - static final formTextBackgroundColor = Colors.white; - static final padding = 16.0; -} - /// Storage for global resources. This is a singleton. class GlobalData { static const version = '2021.08.24.00'; @@ -66,18 +38,20 @@ class GlobalData { static String baseDirectory = ''; static var serverEnvironment = ServerEnvironment.productive; static final String applicationName = theApplicationName; + static String loginUserName = 'guest'; + static int loginUserId = 0; + static int loginUserRole = 90; final BaseLogger logger; final AppBarBuilder appBarBuilder; final DrawerBuilder drawerBuilder; final FooterBuilder footerBuilder; final BaseConfiguration configuration; final RestPersistence? restPersistence; - static String loginUserName = 'guest'; - static int loginUserId = 0; - static int loginUserRole = 90; HomeDirectories homeDirectories = HomeDirectories.dummy(); + final navigatorStack = NavigatorStack(); factory GlobalData() => _instance ?? GlobalData(); + GlobalData.dummy() : this.internal(BaseConfiguration({}, globalLogger), (input) => '', (input) => '', DummyFooter.builder, null, globalLogger); @@ -94,6 +68,7 @@ class GlobalData { this.logger) { logger.log('Start'); _instance = this; + navigatorStack.globalData = this; } Future initializeAsync() async { // Do customize! @@ -101,7 +76,155 @@ class GlobalData { } /// Switches the page given by a [route]. - void navigate(BuildContext context, String route) { - Navigator.pushNamed(context, route); + /// + /// [context] ist the build context of the current page. + /// + /// If [popIfPossible] is true and the calling page has this route + /// the [Navigator.pop()] is called. Otherwise [Navigator.pushNamed()] + /// is called. The Difference: pop() restores the previous state (in + /// filters...). + void navigate(BuildContext context, String route, + {bool popIfPossible = true}) { + navigatorStack.goto(context, route, popIfPossible: popIfPossible); } } + +class GlobalThemeData { + static final formBackgroundColor = Color.fromARGB(0xee, 0xee, 0xee, 0xee); + static final formElevation = 10.0; + static final formTextBackgroundColor = Colors.white; + static final padding = 16.0; +} + +class GlobalTranslations { + static final comboboxItemAll = i18n.tr(''); + static final comboboxSelect = i18n.tr(''); +} + +class HomeDirectories { + Directory configurations = Directory.current; + Directory localData = Directory.current; + HomeDirectories(String applicationName) { + if (Platform.isLinux) { + configurations = Directory('/etc/$applicationName'); + localData = Directory(path.join('/home', Platform.environment['LOGNAME'], + '.local/share/$applicationName')); + } else if (Platform.isAndroid) { + getExternalStorageDirectory().then((dir) => localData = dir!); + } else { + throw FormatException('HomeDirectory.getHomePath(): unknown platform'); + } + } + HomeDirectories.dummy(); +} + +/// Manages the call stack of the navigator. +/// +/// This allows to access the calling page from the current page, +/// e.g. to inform about changes. +class NavigatorStack { + GlobalData? globalData; + final stack = []; + + /// Switches the page given by a [route]. + /// + /// [context] ist the build context of the current page. + /// + /// If [popIfPossible] is true and the calling page has this route + /// the [Navigator.pop()] is called. Otherwise [Navigator.pushNamed()] + /// is called. The Difference: pop() restores the previous state (in + /// filters...). + /// + /// If [registerWithParent] is true the parent calls register + void goto(BuildContext context, String route, + {bool popIfPossible = true, bool registerWithParent = false}) { + final ix = route.indexOf(';'); + if (ix > 0) { + route = route.substring(0, ix); + } + if (popIfPossible && + stack.length >= 2 && + stack[stack.length - 2].route == route) { + pop(context); + } else { + /// Note: constructor of AttendedPage calls supplyTopOfStack() + /// to set NavigatorStackEntry.attendedPage. + stack.add(NavigatorStackEntry( + route: route, registerWithParent: registerWithParent)); + Navigator.pushNamed(context, route); + } + } + + /// Informs the parent that the current page has been changed. + /// + /// [route]: identifies the calling page. If null the previous stack + /// entry of the top of stack is used. Otherwise the latest entry + /// with that route. + void informAboutChanges(String? route) { + AttendedPage? parent; + if (route == null) { + if (stack.length >= 2) { + parent = stack[stack.length - 2].attendedPage; + } else { + for (var ix = stack.length - 1; ix >= 0; ix--) { + if (stack[ix].route == route) { + parent = stack[ix].attendedPage; + } + } + } + if (parent == null) { + globalData!.logger + .error('informAboutChanges(): parent ${route!} not found'); + } else { + parent.noticeChange(); + } + } + } + + /// Removes the top of stack and return to the calling page. + /// + /// [context]: the context of the calling page. + void pop(BuildContext context) { + if (stack.isEmpty) { + globalData!.logger.error('NavigatorStack.pop(): empty stack'); + } else { + stack.removeLast(); + Navigator.pop(context); + } + } + + /// Adds the missing attended page in the top of stack. + /// + /// This method must be called if the attended page has been + /// constructed. + /// + /// [route] identifies the page. + /// + /// [attendedPage] will be stored in the top of stage if missing that. + void supplyTopOfStack(String route, AttendedPage attendedPage) { + NavigatorStackEntry? last; + if (stack.isNotEmpty && (last = stack.last).route == route) { + last.attendedPage = attendedPage; + if (last.registerWithParent && stack.length >= 2) { + stack[stack.length - 2] + .attendedPage + ?.addListener(() => attendedPage.reload()); + } + } else { + globalData!.logger.error( + 'supplyTopOfStack(): route $route not found. TOS: ' + + (last == null ? '' : last.route)); + } + } +} + +/// Data class to store a navigation info of a page. +class NavigatorStackEntry { + final String route; + final bool registerWithParent; + AttendedPage? attendedPage; + NavigatorStackEntry( + {required this.route, + required this.registerWithParent, + this.attendedPage}); +} diff --git a/lib/widget/attended_page.dart b/lib/widget/attended_page.dart index 08b577f..46f42f3 100644 --- a/lib/widget/attended_page.dart +++ b/lib/widget/attended_page.dart @@ -17,15 +17,33 @@ final i18n = I18N(); /// 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 { +/// +/// The [ChangeNotifier] base class allows to inform the calling +/// page. Example: the list page calls the edit page of one item. +/// If the edit page stores changed attributes the calling list page +/// must be informed and it rebuilds the list view. +/// The [GlobalData.informCaller()] method can do that. +class AttendedPage extends ChangeNotifier { + String route = ''; final ModuleMetaData moduleMetaData; final PageMetaData pageMetaData; final GlobalData globalData; final StatefulWidget statefulWidget; final State state; final PageStates pageStates; - AttendedPage(this.statefulWidget, this.state, this.globalData, - this.pageMetaData, this.moduleMetaData, this.pageStates); + final void Function(Function? afterReload)? reloadFunction; + AttendedPage( + this.statefulWidget, + this.state, + this.globalData, + this.pageMetaData, + this.moduleMetaData, + this.pageStates, + this.reloadFunction, + {String? route}) { + this.route = route ?? '/${moduleMetaData.moduleName}/${pageMetaData.name}'; + globalData.navigatorStack.supplyTopOfStack(this.route, this); + } /// Returns the list of attended widgets of this page. List attendedWidgets() { @@ -54,6 +72,14 @@ class AttendedPage { return rc; } + /// Calls [setState()] of the current page. + /// + /// If [afterReload] is not null this function is used as parameter of + /// setState(). + void reload({Function? afterReload}) { + reloadFunction!(afterReload); + } + /// Creates a chip bar: a row of ChoiceChip instances (special buttons). /// /// That chips allow to change the page of the db result. @@ -169,11 +195,11 @@ class AttendedPage { List getRows( {required DbData dbData, required String columnList, - required Function() onDone, + required Function() onDone, //@ToDo: remove required String routeEdit, required BuildContext context}) { List? rc; - if (dbData != null && dbData.recordList != null) { + if (dbData.recordList != null) { rc = []; dbData.recordList!.forEach((record) { final cells = []; @@ -197,6 +223,14 @@ class AttendedPage { return rc ?? []; } + void noticeChange() { + notifyListeners(); + } + + void informAboutChanges({String? route}) { + globalData.navigatorStack.informAboutChanges(route); + } + /// Loads a record from the backend. /// /// [name]: a unique name over all backend data requests. diff --git a/metatool/bin/page_generator.dart b/metatool/bin/page_generator.dart index cdb17da..aa41bba 100644 --- a/metatool/bin/page_generator.dart +++ b/metatool/bin/page_generator.dart @@ -11,6 +11,43 @@ import 'sql_generator.dart'; /// Handles the page generation. Is a base class of [Generator]. class PageGenerator extends SqlGenerator { + static final templateBodyLoadData = '''SafeArea( + child: FutureBuilder( + future: _futureDbData, + builder: (context, snapshot) { + Widget rc; + if (snapshot.connectionState != ConnectionState.done) { + rc = const CircularProgressIndicator(); + } else { + if (snapshot.hasData) { + final dbData = snapshot.data!; + final record = dbData.singleRecord; + if (record == null){ + rc = Text('backend problem: \${dbData.message ?? ''}'); + } + _fieldData.fromMap(record as JsonMap); + rc = buildFrame(); + } else if (snapshot.hasError) { + rc = Text('Backend problem: \${snapshot.error}'); + } else { + rc = const CircularProgressIndicator(); + } + } + return rc; + }, + )));'''; + static final templateBodySimple = '''SafeArea( + child: buildFrame()));'''; + static final templateDidChangeDependencies = ''' + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _futureDbData = globalData.restPersistence!.query( + what: 'query', + data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey}); + }'''; + static final templatePage = ''' // DO NOT CHANGE. This file is created by the meta_tool! import 'package:flutter/material.dart'; @@ -32,7 +69,8 @@ DEF_PRIMARY final PageStates pageStates = PageStates(); GlobalData(), UserMeta.instance.pageByName('edit')!, UserMeta.instance, - pageStates); + pageStates, + (afterReload) => rc.reload(afterReload)); pageStates.attendedPage = rc.attendedPage; return rc; } @@ -47,6 +85,15 @@ class _EditUserPageState extends EditUserCustom { attendedPage!.pageStates.screenHeight = size.height; super.didChangeDependencies(); } + + /// Renders the widget tree again. + void reload(Function? afterReload){ + if (afterReload == null){ + setState(() => 1); + } else { + setState(() => afterReload()); + } + } } '''; @@ -124,7 +171,8 @@ class ListUserCustom extends State { #INIT_COMBOS final formItems = [ #FORM_ITEMS FormItem( ElevatedButton( - onPressed: () => search(), child: Text(i18n.tr('Search'))), + onPressed: () => search(), + child: Text(i18n.tr('Search'))), weight: 12, gapAbove: padding), ]; @@ -204,7 +252,7 @@ import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../base/i18n.dart'; import '../../base/validators.dart'; -import '../../services/global_widget.dart'; +#IMP_PERSISTENCEimport '../../services/global_widget.dart'; import '../../setting/global_data.dart'; import '../../widget/attended_page.dart'; import '../../widget/message_line.dart'; @@ -215,15 +263,23 @@ final i18n = I18N(); class EditUserCustom extends State with MessageLine { #DEF_PRIMARY final globalData = GlobalData(); - AttendedPage? attendedPage; +#DEF_FUTURE AttendedPage? attendedPage; final _fieldData = _FieldData(); final GlobalKey _formKey = GlobalKey(debugLabel: 'EditUser'); #DEF_CONTROLLER EditUserCustom(#THIS_PRIMARY); @override Widget build(BuildContext context) { + final rc = Scaffold( + appBar: globalData.appBarBuilder(i18n.tr('#PAGE_LABEL')), + drawer: globalData.drawerBuilder(context), + body: #BODY_BUILD + return rc; + } + + Widget buildFrame() { final padding = GlobalThemeData.padding; -#INIT_COMBO#LOAD_RECORD#ASSIGN_CONTROLLER final formItems = [ +#INIT_COMBO#ASSIGN_CONTROLLER final formItems = [ #FORM_ITEMS FormItem( ElevatedButton( onPressed: () => #ACTION2(), @@ -240,26 +296,29 @@ class EditUserCustom extends State with MessageLine { ), weight: 4) ]; - final rc = Scaffold( - appBar: globalData.appBarBuilder(i18n.tr('#PAGE_LABEL')), - drawer: globalData.drawerBuilder(context), - body: SafeArea( - child: Form( - key: _formKey, - child: Card( - margin: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric( - vertical: padding, horizontal: padding), - child: WidgetForm.flexibleGrid( - formItems, - screenWidth: attendedPage!.pageStates.screenWidth, - padding: padding, - )))))); + final rc = Form( + key: _formKey, + child: Card( + margin: + EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: padding, horizontal: padding), + child: WidgetForm.flexibleGrid( + formItems, + screenWidth: attendedPage!.pageStates.screenWidth, + padding: padding, + )))); return rc; } +#DIDCHANGE @override + void dispose() { + helperDummyUsage(DataType.int); + globalWidgetDummyUsage(); +#DISP_CONTROLLER super.dispose(); + } + @override void initState() { super.initState(); @@ -281,17 +340,9 @@ class EditUserCustom extends State with MessageLine { #ACTION1(); } } - - @override - void dispose() { - helperDummyUsage(DataType.int); - globalWidgetDummyUsage(); -#DISP_CONTROLLER super.dispose(); - } } class _FieldData { - bool isFromBackend = false; #DEF_FIELD #FROM_MAP#TO_MAP} '''; @@ -310,7 +361,6 @@ class _FieldData { '''; static final templateStorageDoneEdit = ''' - _fieldData.isFromBackend = false; attendedPage!.pageStates.dbDataState.clear(); setState(() => 1); '''; @@ -541,10 +591,10 @@ StatefulWidget? customPageByRoute(String route) { } var validator = ''; if (validators.length == 1) { - validator = ' validator: (input) => ${validators[0]},\n'; + validator = ' validator: (input) => ${validators[0]},\n'; } else if (validators.length > 1) { validator = - ' validator: (input) => validateMultiple(input, [\n'; + ' validator: (input) => validateMultiple(input, [\n'; for (var item in validators) { validator += ' (input) => $item(input),\n'; } @@ -556,11 +606,11 @@ StatefulWidget? customPageByRoute(String route) { ? "jsonToObject(value ?? '', dataType: DataType.date)" : "jsonToObject(value ?? '', dataType: ${field.dataType})"); rc = '''^TextFormField( -#CONTR^ decoration: InputDecoration(labelText: i18n.tr('${field.label}')), -#VALIDATOR^ onSaved: (value) => _fieldData.$name = $value +#CONTR^ decoration: InputDecoration(labelText: i18n.tr('${field.label}')), +#VALIDATOR^ onSaved: (value) => _fieldData.$name = $value ^)''' .replaceFirst('#CONTR', - withController ? '^ controller: ${name}Controller,\n' : '') + withController ? '^ controller: ${name}Controller,\n' : '') .replaceFirst('#VALIDATOR', validator) .replaceAll('^', indent); break; @@ -572,7 +622,9 @@ StatefulWidget? customPageByRoute(String route) { name2 = field.name; } rc = '''^DropdownButtonFormField( -^ value: _fieldData.$name, +^ value: items$name2 == null || items$name2.length == 1 +^ ? 0 +^ : _fieldData.$name, ^ items: items$name2, ^ isExpanded: true, ^ decoration: InputDecoration(labelText: i18n.tr('${field.label}')), @@ -652,29 +704,6 @@ StatefulWidget? customPageByRoute(String route) { return buffer.toString(); } - /// Creates the part for loading the field data from the backend for [page]. - String buildLoadRecord(PageMetaData page) { - String? rc; - if (page.pageType == PageType.edit || page.pageType == PageType.delete) { - rc = ''' if (!_fieldData.isFromBackend) { - attendedPage?.loadRecord( - name: 'record', - reload: () => setState(() => 1), - onDone: (record) { - _fieldData.fromMap(record); - _fieldData.isFromBackend = true; - }, - parameters: { - 'module': '${page.module.moduleName}', - 'sql': 'byId', - ':id': primaryKey - }); - } -'''; - } - return rc ?? ''; - } - /// Creates the parameter definition of the [page]. /// /// The parameters will be used for storing the record at the backend. @@ -809,6 +838,10 @@ StatefulWidget? customPageByRoute(String route) { var sqlType = ''; var setPrimary = ''; var callToMap = ''; + var body = templateBodyLoadData; + var didChangeDependencies = templateDidChangeDependencies; + var definitionFuture = ' late Future _futureDbData;'; + var importPersistence = "import '../../persistence/persistence.dart';\n"; page.pageType == PageType.edit ? 'update' : (page.pageType == PageType.delete ? 'delete' : 'insert'); @@ -819,6 +852,10 @@ StatefulWidget? customPageByRoute(String route) { sqlType = 'insert'; storageDone = templateStorageDoneCreate.replaceFirst( '#MODULE', page.module.moduleName); + body = templateBodySimple; + didChangeDependencies = ''; + definitionFuture = ''; + importPersistence = ''; break; case PageType.edit: callToMap = ' _fieldData.toMap(parameters);\n'; @@ -843,11 +880,12 @@ StatefulWidget? customPageByRoute(String route) { break; } var rc = replaceVariables(templateRecordCustom, page) + .replaceFirst('#DIDCHANGE', didChangeDependencies) .replaceFirst('#DEF_CONTROLLER', buildDefinitionControllers(page)) .replaceFirst('#INIT_COMBO', buildInitializeComboBoxes(page)) .replaceFirst('#FORM_ITEMS', buildFormItems(page, withController: true)) - .replaceFirst('#LOAD_RECORD', buildLoadRecord(page)) .replaceFirst('#PAGE_LABEL', page.label) + .replaceFirst('#BODY_BUILD', body) .replaceFirst('#SQL_TYPE', sqlType) .replaceFirst('#SET_PRIMARY', setPrimary) .replaceFirst('#CALL_TO_MAP', callToMap) @@ -863,7 +901,9 @@ StatefulWidget? customPageByRoute(String route) { '#DEF_PRIMARY', hasPrimary ? ' final int primaryKey;\n' : '') .replaceFirst('#THIS_PRIMARY', hasPrimary ? 'this.primaryKey' : '') .replaceFirst('#TO_MAP', buildToMap(page)) - .replaceFirst('#FROM_MAP', buildFromMap(page)); + .replaceFirst('#FROM_MAP', buildFromMap(page)) + .replaceFirst('#DEF_FUTURE', definitionFuture) + .replaceFirst('#IMP_PERSISTENCE', importPersistence); return rc; } diff --git a/pubspec.yaml b/pubspec.yaml index 37e93f6..99dcd4f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,20 +29,21 @@ environment: dependencies: flutter: sdk: flutter +# provider: ^6.0.1 path: ^1.8.0 yaml: ^3.1.0 dart_bones: ^1.2.2 - url_launcher: ^6.0.9 - flutter_markdown: ^0.6.1 - flutter_bloc: ^7.1.0 - equatable: ^2.0.3 - permission_handler: ^8.1.6 - path_provider: ^2.0.1 + url_launcher: ^6.0.12 + flutter_markdown: ^0.6.8 +# flutter_bloc: ^7.1.0 +# equatable: ^2.0.3 + permission_handler: ^8.2.5 + path_provider: ^2.0.5 synchronized: ^3.0.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 + # cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: @@ -53,7 +54,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^1.0.0 + flutter_lints: ^1.0.4 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec