]> gitweb.hamatoma.de Git - exhibition.git/commitdiff
Refactoring: fetching backend data via FutureBuilder
authorHamatoma <author.hamatoma.de>
Thu, 21 Oct 2021 20:27:30 +0000 (22:27 +0200)
committerHamatoma <author.hamatoma.de>
Thu, 21 Oct 2021 20:27:30 +0000 (22:27 +0200)
* GlobalData:
** bookkeeping of the callstack of navigation: new NavigatorStack

* AttendedPage:
** it is possible to call setState() in each page in the navigation call stack

41 files changed:
lib/common/random_data.dart
lib/meta/modules.dart
lib/page/benchmarks/benchmark_data.dart
lib/page/benchmarks/create_benchmark_custom.dart
lib/page/benchmarks/create_benchmark_page.dart
lib/page/benchmarks/delete_benchmark_custom.dart
lib/page/benchmarks/delete_benchmark_page.dart
lib/page/benchmarks/edit_benchmark_custom.dart
lib/page/benchmarks/edit_benchmark_page.dart
lib/page/benchmarks/list_benchmark_custom.dart
lib/page/benchmarks/list_benchmark_page.dart
lib/page/roles/create_role_custom.dart
lib/page/roles/create_role_page.dart
lib/page/roles/edit_role_custom.dart
lib/page/roles/edit_role_page.dart
lib/page/roles/list_role_custom.dart
lib/page/roles/list_role_page.dart
lib/page/roles/role_data.dart
lib/page/structures/create_structure_custom.dart
lib/page/structures/create_structure_page.dart
lib/page/structures/delete_structure_custom.dart
lib/page/structures/delete_structure_page.dart
lib/page/structures/edit_structure_custom.dart
lib/page/structures/edit_structure_page.dart
lib/page/structures/list_structure_custom.dart
lib/page/structures/list_structure_page.dart
lib/page/structures/structure_data.dart
lib/page/users/create_user_custom.dart
lib/page/users/create_user_page.dart
lib/page/users/delete_user_custom.dart
lib/page/users/delete_user_page.dart
lib/page/users/edit_user_custom.dart
lib/page/users/edit_user_page.dart
lib/page/users/list_user_custom.dart
lib/page/users/list_user_page.dart
lib/page/users/user_data.dart
lib/persistence/rest_persistence.dart
lib/setting/global_data.dart
lib/widget/attended_page.dart
metatool/bin/page_generator.dart
pubspec.yaml

index e07a564ec312592b5b66fcc2dc5a283e22bf064c..ea628d66f23637c6d2f4a8a8fb57838cb9e29a14 100644 (file)
@@ -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<WordPart> wordParts,
-      {bool normalName = true, gerund: true}) {
+      {bool normalName = true, gerund = true}) {
     String rc = '';
     for (var item in wordParts) {
       switch (item.wordType) {
index 484f1799eb656320084f09a29a5b6d3ac48ce7b8..10f555eb1da275d0f6de2b9bd1b7a8386cf69e84 100644 (file)
@@ -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<String> moduleNames(){
+List<String> moduleNames() {
   return [
     'Benchmarks',
     'Roles',
index 929cce3f82233d45a0ff07d01895469016f2d2c9..aacaf1966463e07155464c1331dbb890230aa045 100644 (file)
@@ -2,7 +2,8 @@
 import '../../base/defines.dart';
 import '../../base/helper.dart';
 import '../../persistence/data_record.dart';
-class BenchmarkData extends DataRecord<int>{
+
+class BenchmarkData extends DataRecord<int> {
   int? id;
   String? lastName;
   String? firstName;
@@ -15,78 +16,117 @@ class BenchmarkData extends DataRecord<int>{
   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();
index 62d07c87a10f63d1a97f7867dd3466fb539d5746..8aa8fab53ec3009630ff7e65153eef69b7edd189 100644 (file)
@@ -15,7 +15,8 @@ import 'create_benchmark_page.dart';
 
 final i18n = I18N();
 
-class CreateBenchmarkCustom extends State<CreateBenchmarkPage> with MessageLine {
+class CreateBenchmarkCustom extends State<CreateBenchmarkPage>
+    with MessageLine {
   final globalData = GlobalData();
   AttendedPage? attendedPage;
   final _fieldData = _FieldData();
@@ -30,6 +31,14 @@ class CreateBenchmarkCustom extends State<CreateBenchmarkPage> 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<CreateBenchmarkPage> with MessageLine
     final formItems = <FormItem>[
       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<CreateBenchmarkPage> 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<CreateBenchmarkPage> 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<CreateBenchmarkPage> 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<String, dynamic> map) {
     map[':lastName'] = lastName;
     map[':firstName'] = firstName;
index 4fbfd288281aa237785bc3eb8dd839a3cd686c61..f69e701395fe931a7ea1a3177d8436dbfa5af042 100644 (file)
@@ -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());
+    }
+  }
 }
index 8a8c7677687d0c3c1c6911e776a5caf3aa9f12a5..35031504c5f19367bbf0f7c0103224b6e7cbf49a 100644 (file)
@@ -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<DeleteBenchmarkPage> with MessageLine {
+class DeleteBenchmarkCustom extends State<DeleteBenchmarkPage>
+    with MessageLine {
   final int primaryKey;
   final globalData = GlobalData();
+  late Future<DbData> _futureDbData;
   AttendedPage? attendedPage;
   final _fieldData = _FieldData();
   final GlobalKey<FormState> _formKey =
@@ -31,21 +34,40 @@ class DeleteBenchmarkCustom extends State<DeleteBenchmarkPage> 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<DbData>(
+          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 ?? '<unknown>'}');
+                }
+                _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<DeleteBenchmarkPage> with MessageLine
     final formItems = <FormItem>[
       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<DeleteBenchmarkPage> 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<DeleteBenchmarkPage> 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<DeleteBenchmarkPage> 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 = '';
index f0fc1002005a4e7c0eccf039122ba896a5e57669..4a4bff4dfd4412d4b21e12daae702123efbfdde3 100644 (file)
@@ -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());
+    }
+  }
 }
index c933ecfdc304a8e766060cb186df72ebc2f94665..78664aee277432712e7a7ff9d30988b2629fbb93 100644 (file)
@@ -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<EditBenchmarkPage> with MessageLine {
   final int primaryKey;
   final globalData = GlobalData();
+  late Future<DbData> _futureDbData;
   AttendedPage? attendedPage;
   final _fieldData = _FieldData();
   final GlobalKey<FormState> _formKey =
@@ -31,21 +33,40 @@ class EditBenchmarkCustom extends State<EditBenchmarkPage> 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<DbData>(
+          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 ?? '<unknown>'}');
+                }
+                _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<EditBenchmarkPage> with MessageLine {
     final formItems = <FormItem>[
       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<EditBenchmarkPage> 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<EditBenchmarkPage> 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<EditBenchmarkPage> 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<EditBenchmarkPage> 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 = '';
index 9fca8712730bdd6bf7407d687bd41e730a37b94e..5b0aeae45b96196fe3475f07b6c44e7938f2409d 100644 (file)
@@ -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());
+    }
+  }
 }
index 5a54c4b6a27e49582e0ae3b458eed970ca3e02db..0707e76ae8df3253afc67f3dce8e656ec7eac98f 100644 (file)
@@ -47,8 +47,10 @@ class ListBenchmarkCustom extends State<ListBenchmarkPage> {
             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<ListBenchmarkPage> {
     return rc;
   }
 
-  Widget buildFrame({required JsonList rows}){
+  Widget buildFrame({required JsonList rows}) {
     final padding = GlobalThemeData.padding;
     final formItems = <FormItem>[
       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<ListBenchmarkPage> {
       rows: rows as List<DataRow>,
     );
     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<ListBenchmarkPage> {
         .then((message) => setState(() => resultController.text = message));
   }
 
-@override
+  @override
   void dispose() {
     helperDummyUsage(DataType.string);
     globalWidgetDummyUsage();
index 7ab8115dee7e5f65be8a057be3929434a54e16d2..bad001348e1729299b52189ba2d744d6eed050b5 100644 (file)
@@ -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());
+    }
+  }
 }
index 87c2b0ec19e3f7f4954a00b10f0043b25803ca71..a55c6a8cbdab59374d8581a05e4cef4d3508047c 100644 (file)
@@ -25,21 +25,27 @@ class CreateRoleCustom extends State<CreateRolePage> 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>[
       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<CreateRolePage> 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<CreateRolePage> with MessageLine {
       store();
     }
   }
-
-  @override
-  void dispose() {
-    helperDummyUsage(DataType.int);
-    globalWidgetDummyUsage();
-    nameController.dispose();
-    super.dispose();
-  }
 }
 
 class _FieldData {
-  bool isFromBackend = false;
   String name = '';
 
-
   void toMap(Map<String, dynamic> map) {
     map[':name'] = name;
     map[':createdBy'] = GlobalData.loginUserName;
index 927f06196ee2c30004c7f774a330566538ebddd9..6df1eae3ca692a005d1e6c20bb3ddc147c79617e 100644 (file)
@@ -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());
+    }
+  }
 }
index 4108b6a2ada46bf07d0ac28ae04c4906cc09ad52..9008ce040a0f2d687da86910c9ab0dd7f9c9fa2a 100644 (file)
@@ -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<EditRolePage> with MessageLine {
   final int primaryKey;
   final globalData = GlobalData();
+  late Future<DbData> _futureDbData;
   AttendedPage? attendedPage;
   final _fieldData = _FieldData();
   final GlobalKey<FormState> _formKey =
@@ -26,35 +28,52 @@ class EditRoleCustom extends State<EditRolePage> 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<DbData>(
+          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 ?? '<unknown>'}');
+                }
+                _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>[
       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<EditRolePage> 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<EditRolePage> 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<EditRolePage> with MessageLine {
       store();
     }
   }
-
-  @override
-  void dispose() {
-    helperDummyUsage(DataType.int);
-    globalWidgetDummyUsage();
-    nameController.dispose();
-    super.dispose();
-  }
 }
 
 class _FieldData {
-  bool isFromBackend = false;
   String name = '';
 
   void fromMap(Map<String, dynamic> map) {
index b96e2749efb2713949c020cbdfcb26e2e82f287b..2c88a6fcfb3df23fa123813c81866788eb854c38 100644 (file)
@@ -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());
+    }
+  }
 }
index 69af556923cba672e57fcc402ed108662307cb95..c179cdfb41905896be192d4e3f2cb606455819ee 100644 (file)
@@ -43,7 +43,8 @@ class ListRoleCustom extends State<ListRolePage> {
             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<ListRolePage> {
     return rc;
   }
 
-  Widget buildFrame({required JsonList rows}){
+  Widget buildFrame({required JsonList rows}) {
     final padding = GlobalThemeData.padding;
     final formItems = <FormItem>[
       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<ListRolePage> {
             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<ListRolePage> {
       rows: rows as List<DataRow>,
     );
     Widget? tabBar =
-    attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset));
+        attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset));
     final frameWidget = ListView(children: [
       form,
       if (tabBar != null) tabBar,
index 9a0b0a6be7d5a855a7418cd57086dfafeabe3928..c4b1172f655c38ef69ed9de9d8c0ec6ae51b83ca 100644 (file)
@@ -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());
+    }
+  }
 }
index 74cd990221e674676d25a22ea1f9b71a7176ed77..7b019ac2a91f45014e85d58768020138f8a121a4 100644 (file)
@@ -2,61 +2,83 @@
 import '../../base/defines.dart';
 import '../../base/helper.dart';
 import '../../persistence/data_record.dart';
-class RoleData extends DataRecord<int>{
+
+class RoleData extends DataRecord<int> {
   int? id;
   String? name;
   DateTime? created;
   String? createdBy;
   DateTime? changed;
   String? changedBy;
-  RoleData({
- this.id, this.name, this.created, this.createdBy, this.changed, this.changedBy});
+  RoleData(
+      {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();
index f05c1ee24a7f6a8b760123a5248761e1e8655c80..d0f90dfafd97d5fd8318916a44bb48dabdfe4f1e 100644 (file)
@@ -15,7 +15,8 @@ import 'create_structure_page.dart';
 
 final i18n = I18N();
 
-class CreateStructureCustom extends State<CreateStructurePage> with MessageLine {
+class CreateStructureCustom extends State<CreateStructurePage>
+    with MessageLine {
   final globalData = GlobalData();
   AttendedPage? attendedPage;
   final _fieldData = _FieldData();
@@ -28,6 +29,14 @@ class CreateStructureCustom extends State<CreateStructurePage> 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<CreateStructurePage> with MessageLine
     final formItems = <FormItem>[
       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<CreateStructurePage> 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<CreateStructurePage> 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<String, dynamic> map) {
     map[':scope'] = scope;
     map[':name'] = name;
index 9be367aee76937ed8e2077d9ae6ef516bd274e3f..0773962bd78b64903ba7209738d4729c7ba4b0cb 100644 (file)
@@ -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());
+    }
+  }
 }
index baad392504e32b9409bc94a48eadc5fbf46b8583..272fbb9f3b23893cd59d6f5d622a7cdb319a6354 100644 (file)
@@ -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<DeleteStructurePage> with MessageLine {
+class DeleteStructureCustom extends State<DeleteStructurePage>
+    with MessageLine {
   final int primaryKey;
   final globalData = GlobalData();
+  late Future<DbData> _futureDbData;
   AttendedPage? attendedPage;
   final _fieldData = _FieldData();
   final GlobalKey<FormState> _formKey =
@@ -29,21 +32,40 @@ class DeleteStructureCustom extends State<DeleteStructurePage> 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<DbData>(
+          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 ?? '<unknown>'}');
+                }
+                _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<DeleteStructurePage> with MessageLine
     final formItems = <FormItem>[
       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<DeleteStructurePage> 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<DeleteStructurePage> 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 = '';
index 1d89c7abfbbfd9de83e4496fcbe3bc7bc4d9b366..ed326715b5fb0588039f24deadf3184736a67fe2 100644 (file)
@@ -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());
+    }
+  }
 }
index 11bd3d0bf91f96411a62553096634f7b840a79a9..bc3879888c2b658bd91d625d871610cf44d61e3a 100644 (file)
@@ -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<EditStructurePage> with MessageLine {
   final int primaryKey;
   final globalData = GlobalData();
+  late Future<DbData> _futureDbData;
   AttendedPage? attendedPage;
   final _fieldData = _FieldData();
   final GlobalKey<FormState> _formKey =
@@ -29,21 +31,40 @@ class EditStructureCustom extends State<EditStructurePage> 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<DbData>(
+          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 ?? '<unknown>'}');
+                }
+                _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<EditStructurePage> with MessageLine {
     final formItems = <FormItem>[
       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<EditStructurePage> 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<EditStructurePage> 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<EditStructurePage> 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 = '';
index 516fdff680d177e919e9e7d0f19d1f92a5159ceb..df271e30058102561059b2dd26885178495932d5 100644 (file)
@@ -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());
+    }
+  }
 }
index 1380aad64bcfae4e03ec2ba3a53388e7c59060d9..7f09d04bcdfe7d5710a3a823f224a3bf94fc365c 100644 (file)
@@ -43,8 +43,10 @@ class ListStructureCustom extends State<ListStructurePage> {
             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<ListStructurePage> {
     return rc;
   }
 
-  Widget buildFrame({required JsonList rows}){
+  Widget buildFrame({required JsonList rows}) {
     final padding = GlobalThemeData.padding;
     final formItems = <FormItem>[
       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<ListStructurePage> {
             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<ListStructurePage> {
       rows: rows as List<DataRow>,
     );
     Widget? tabBar =
-    attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset));
+        attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset));
     final frameWidget = ListView(children: [
       form,
       if (tabBar != null) tabBar,
index ad80fff8e3d8b4e924c0ff6343f3292eac1e4525..efeb5d71332449e3c4af6fb069881e1babd46f00 100644 (file)
@@ -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());
+    }
+  }
 }
index 85285ec0527a790f6eb5ccc245291c94f861f044..b09010a3f2f345e492af1876b00ee51fcbf1fe5b 100644 (file)
@@ -2,7 +2,8 @@
 import '../../base/defines.dart';
 import '../../base/helper.dart';
 import '../../persistence/data_record.dart';
-class StructureData extends DataRecord<int>{
+
+class StructureData extends DataRecord<int> {
   int? id;
   String? scope;
   String? name;
@@ -12,66 +13,96 @@ class StructureData extends DataRecord<int>{
   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();
index d01ac1525a6efb4808b83dc54fdcb2cd730ab617..33570bf09f5d692609e57f8c26f42f634472110e 100644 (file)
@@ -27,6 +27,14 @@ class CreateUserCustom extends State<CreateUserPage> 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<CreateUserPage> with MessageLine {
     final formItems = <FormItem>[
       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<int>(
-            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<CreateUserPage> 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<CreateUserPage> 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<CreateUserPage> 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<String, dynamic> map) {
     map[':name'] = name;
     map[':displayName'] = displayName;
index 5fed6f1b3039a93ec91bd6d820a3f4747dbe78b5..3048fa1feb9eeb424229f9908fd98087bc2761bf 100644 (file)
@@ -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());
+    }
+  }
 }
index a1df6ea79685151cc247d133fdca9be1a47c40f7..1380cc410213672226882ca67238bc47991fe769 100644 (file)
@@ -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<DeleteUserPage> with MessageLine {
   final int primaryKey;
   final globalData = GlobalData();
+  late Future<DbData> _futureDbData;
   AttendedPage? attendedPage;
   final _fieldData = _FieldData();
   final GlobalKey<FormState> _formKey =
@@ -28,56 +30,74 @@ class DeleteUserCustom extends State<DeleteUserPage> 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<DbData>(
+          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 ?? '<unknown>'}');
+                }
+                _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>[
       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<int>(
-            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<DeleteUserPage> 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<DeleteUserPage> 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 = '';
index 111ddc4dc9a0f07134592bde48706f536521bf09..98871a5a43bad45711c0dedd93207c75bf6d716f 100644 (file)
@@ -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());
+    }
+  }
 }
index 374bc2ad2def88a580c5379df73b0f0080381f98..311e124e338b82c7013188697f6894abbfe44ffc 100644 (file)
@@ -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<EditUserPage> with MessageLine {
   final int primaryKey;
   final globalData = GlobalData();
+  late Future<DbData> _futureDbData;
   AttendedPage? attendedPage;
   final _fieldData = _FieldData();
   final GlobalKey<FormState> _formKey =
@@ -28,56 +30,74 @@ class EditUserCustom extends State<EditUserPage> 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<DbData>(
+          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 ?? '<unknown>'}');
+                }
+                _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>[
       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<int>(
-            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<EditUserPage> 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<EditUserPage> 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<EditUserPage> 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<EditUserPage> 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 = '';
index 080f1d7900157f89fa102b57a003efcf54fae64f..230fe4a008068fa053a1183e5a8c86558adc12a7 100644 (file)
@@ -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());
+    }
+  }
 }
index dd50e6145b38d6d1e48bd77d12aeb4eae90f7fbf..c23310e610c2d122f496e0197d393a45193a31c3 100644 (file)
@@ -43,8 +43,10 @@ class ListUserCustom extends State<ListUserPage> {
             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<ListUserPage> {
     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<ListUserPage> {
     final formItems = <FormItem>[
       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<int>(
-            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<ListUserPage> {
             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<ListUserPage> {
       rows: rows as List<DataRow>,
     );
     Widget? tabBar =
-    attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset));
+        attendedPage!.buildChipBar(onTap: (offset) => setState(() => offset));
     final frameWidget = ListView(children: [
       form,
       if (tabBar != null) tabBar,
index d36e2bf41b3d0d06eeff9d340a58e3f14aa2f436..67ac443b4d43867f4fc636de7f72ba25bcfe10cd 100644 (file)
@@ -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());
+    }
+  }
 }
index d27593e5643dcb2caefe9083e133bf5b52c412c2..427c74dd6d64aa0a256204886143e310c2180530 100644 (file)
@@ -2,7 +2,8 @@
 import '../../base/defines.dart';
 import '../../base/helper.dart';
 import '../../persistence/data_record.dart';
-class UserData extends DataRecord<int>{
+
+class UserData extends DataRecord<int> {
   int? id;
   String? name;
   String? displayName;
@@ -12,66 +13,96 @@ class UserData extends DataRecord<int>{
   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();
index a3a18724fba0971ceae5dc1fccf2bf4eb4f87207..438cdfe47ff16c44c1a67a58e229bc7d81f7450b 100644 (file)
@@ -119,7 +119,7 @@ class RestPersistence extends Persistence {
             '${answer.substring(0, min<int>(answer.length, 8))}');
       }
     } else {
-      rc = DbData.message(answer.substring(0, max<int>(answer.length, 120)));
+      rc = DbData.message(answer.substring(0, min<int>(answer.length, 120)));
     }
     if (logger.logLevel >= LEVEL_FINE) {
       logger.log('answer: ${limitString(answer, sqlTraceLimit)}');
index bab2129249dfdfd200fd1b4d7ce3adc7b0172cf7..fce281d8cea82b893cf1c721f36bda52362979f8 100644 (file)
@@ -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('<All>');
-  static final comboboxSelect = i18n.tr('<Please select>');
-}
-
-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('<All>');
+  static final comboboxSelect = i18n.tr('<Please select>');
+}
+
+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 = <NavigatorStackEntry>[];
+
+  /// 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 ? '<none>' : 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});
+}
index 08b577fd713bae1f19c6b4717cb5e80ccebbd0a7..46f42f31c4d4986737ab15d7ea553f9904e57465 100644 (file)
@@ -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<StatefulWidget> 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<AttendedWidget> 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<DataRow> getRows(
       {required DbData dbData,
       required String columnList,
-      required Function() onDone,
+      required Function() onDone, //@ToDo: remove
       required String routeEdit,
       required BuildContext context}) {
     List<DataRow>? rc;
-    if (dbData != null && dbData.recordList != null) {
+    if (dbData.recordList != null) {
       rc = <DataRow>[];
       dbData.recordList!.forEach((record) {
         final cells = <DataCell>[];
@@ -197,6 +223,14 @@ class AttendedPage {
     return rc ?? <DataRow>[];
   }
 
+  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.
index cdb17daae0becbd640836619c385340be188e9a8..aa41bbabf0a25bbefa6c93b7428d2538aaffc341 100644 (file)
@@ -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<DbData>(
+          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 ?? '<unknown>'}');
+                }
+                _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<ListUserPage> {
 #INIT_COMBOS    final formItems = <FormItem>[
 #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<EditUserPage> with MessageLine {
 #DEF_PRIMARY  final globalData = GlobalData();
-  AttendedPage? attendedPage;
+#DEF_FUTURE  AttendedPage? attendedPage;
   final _fieldData = _FieldData();
   final GlobalKey<FormState> _formKey =
       GlobalKey<FormState>(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 = <FormItem>[
+#INIT_COMBO#ASSIGN_CONTROLLER    final formItems = <FormItem>[
 #FORM_ITEMS      FormItem(
           ElevatedButton(
               onPressed: () => #ACTION2(),
@@ -240,26 +296,29 @@ class EditUserCustom extends State<EditUserPage> 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<EditUserPage> 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<int>(
-^  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<DbData> _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;
   }
 
index 37e93f631d925319d787d80ef3450e8c0a8e8f34..99dcd4faf2a1a194f41d7951d9ff4d83b114ab81 100644 (file)
@@ -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