import 'src/helper/settings.dart';
import 'src/page/login_page.dart';
-import 'src/page/role/role_change_page.dart';
import 'src/page/role/role_create_page.dart';
import 'src/page/role/role_list_page.dart';
import 'src/private/bsettings.dart';
this.column = column;
maxSize = column.size;
rows = column.rows;
+ if (column.hasOption('primary') && ! column.hasOption('readonly')){
+ options.add('readonly');
+ }
}
/// Dumps the internal structure into a [stringBuffer]
final fields = <FieldModel>[];
final buttons = <ButtonModel>[];
final widgets = <WidgetModel>[];
+ List<String> tableTitles;
+ List<String> tableColumns;
PageModel(this.module, this.map, BaseLogger logger) : super(logger);
final name = field.name;
if (hasField(name)) {
logger.error('field ${field.fullName()} already defined: ' +
- getField(name).fullName());
+ fieldByName(name).fullName());
} else {
fields.add(field);
}
String fullName() => '${module.name}.$name';
/// Returns a field by [name] or null on error.
- FieldModel getField(String name, {bool required = true}) {
+ FieldModel fieldByName(String name, {bool required = true}) {
final rc = fields.firstWhere((element) => element.name == name,
orElse: () => null);
if (required && rc == null) {
void parse() {
name = parseString('page', map, required: true);
checkSuperfluousAttributes(
- map, 'options page pageType sections'.split(' '));
+ map, 'options page pageType sections tableColumns tableTitles'.split(' '));
pageModelType = parseEnum<PageModelType>(
'pageType', map, PageModelType.values,
required: true);
SectionModel.parseSections(this, null, item, logger);
}
}
+ tableTitles = parseStringList('tableTitles', map);
+ tableColumns = parseString('tableColumns', map)?.split(' ');
+ if (pageModelType != PageModelType.list &&
+ (tableTitles != null || tableColumns != null)) {
+ logger.error(
+ 'tableTitles and tableColumns are only meaningful in list pages: ${fullName()}');
+ }
+ if (tableTitles != null &&
+ tableColumns != null &&
+ tableTitles.length != tableColumns.length) {
+ logger.error(
+ 'different sizes of tableTitles/tableColumns: ${tableTitles.length}/${tableColumns.length}');
+ }
+
if (!options.contains('noAutoButton') &&
buttonByName('search', required: false) == null) {
final section = null;
{
"page": "list",
"pageType": "list",
+ "tableColumns": "configuration_id configuration_scope configuration_property configuration_order configuration_value configuration_type",
+ "tableTitles": ";Id;Bereich;Eigenschaft;Reihe;Wert;Typ",
"sections": [
{
"sectionType": "filterPanel",
{
"page": "list",
"pageType": "list",
+ "tableColumns": "role_id role_name role_prio",
+ "tableTitles": ";Id;Rolle;Priorität",
"sections": [
{
"sectionType": "filterPanel",
{
"page": "list",
"pageType": "list",
+ "tableColumns": "user_id user_name user_displayname user_email user_role",
+ "tableTitles": ";Id;Name;Anzeigename;EMail;Rolle",
"sections": [
{
"sectionType": "filterPanel",
import 'package:dart_bones/dart_bones.dart';
-import 'package:meta/meta.dart';
import 'column_model.dart';
import 'model_base.dart';
final Map<String, dynamic> map;
String name;
List<String> options;
- @protected
final columns = <ColumnModel>[];
ColumnModel primary;
import 'package:dart_bones/dart_bones.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_bones/src/widget/callback_controller_bones.dart';
+import 'package:flutter_bones/src/widget/page_controller_bones.dart';
/// Data class for storing parameter to build a page.
class ApplicationData {
final Persistence persistence;
String currentUser;
int currentRoleId;
+ /// for unittests:
+ dynamic lastModuleState;
+ @protected
+ final callerStack = <PageControllerBones>[];
/// Constructor.
/// [configuration] is a map with the widget data (e.g. padding)
currentUser = 'Gast';
currentRoleId = 100;
}
+ /// Enforces a redraw of the caller of the current page.
+ /// [reason] specifies why the redraw is needed and [customString]
+ /// is an additional info for the reason.
+ void callerRedraw(RedrawReason reason, [String customString]){
+ if (callerStack.isNotEmpty && callerStack.last != null){
+ callerStack.last.redraw(reason, customString);
+ }
+ }
+ void pushCaller(PageControllerBones controller){
+ callerStack.add(controller);
+ }
+ void popCaller(){
+ if (callerStack.isNotEmpty){
+ callerStack.removeLast();
+ }
+ }
}
import 'package:flutter/material.dart';
import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_bones/src/widget/callback_controller_bones.dart';
import '../../widget/edit_form.dart';
import 'configuration_controller.dart';
class ConfigurationChangePage extends StatefulWidget {
final ApplicationData applicationData;
+ final Map initialRow;
final logger = Settings().logger;
-
+ final int primaryId;
//ConfigurationChangePageState lastState;
- ConfigurationChangePage(this.applicationData, {Key key}) : super(key: key);
+ ConfigurationChangePage(this.primaryId, this.applicationData, this.initialRow,
+ {Key key})
+ : super(key: key);
@override
ConfigurationChangePageState createState() {
- final rc = ConfigurationChangePageState(applicationData);
- // lastState = rc;
+ final rc = ConfigurationChangePageState(primaryId, applicationData, initialRow);
+
+ /// for unittests:
+ applicationData.lastModuleState = rc;
return rc;
}
}
class ConfigurationChangePageState extends State<ConfigurationChangePage> {
final ApplicationData applicationData;
-
+ final int primaryId;
+ final Map initialRow;
final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: 'configuration_change');
ConfigurationController controller;
- ConfigurationChangePageState(this.applicationData);
+ ConfigurationChangePageState(this.primaryId, this.applicationData, this.initialRow);
@override
Widget build(BuildContext context) {
- controller = controller ??
- ConfigurationController(
- _formKey, this, 'change', context, applicationData);
+ if (controller == null) {
+ controller = ConfigurationController(
+ _formKey, this, 'change', context, applicationData,
+ redrawCallback: (RedrawReason reason, String customString) =>
+ setState(() => null));
+ controller.initialize();
+ }
+ // controller.buildWidgetList() is called in editForm
return Scaffold(
appBar: applicationData.appBarBuilder('Rolle ändern'),
drawer: applicationData.drawerBuilder(context),
key: _formKey,
pageController: controller,
configuration: applicationData.configuration,
+ primaryId: primaryId,
+ initialRow: initialRow,
));
}
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
}
import 'package:flutter_bones/flutter_bones.dart';
import '../../model/standard/configuration_model.dart';
+import 'configuration_change_page.dart';
import '../../widget/page_controller_bones.dart';
class ConfigurationController extends PageControllerBones {
/// Controller for a page named [pageName].
- ConfigurationController(
- GlobalKey<FormState> formKey,
- State<StatefulWidget> parent,
- String pageName,
- BuildContext context,
- ApplicationData applicationData,
- {Function fetchRows})
- : super(
- formKey,
- parent,
- ConfigurationModel(Settings().logger),
- pageName,
- context,
- applicationData,
- ) {
+ ConfigurationController(GlobalKey<FormState> formKey, State<StatefulWidget> parent,
+ String pageName, BuildContext context, ApplicationData applicationData,
+ {Function redrawCallback})
+ : super(formKey, parent, ConfigurationModel(Settings().logger), pageName, context,
+ applicationData, redrawCallback) {
moduleModel.parse();
}
+ @override
+ void startChange(int id, Map row) {
+ applicationData.pushCaller(this);
+ Navigator.push(context,
+ MaterialPageRoute(builder: (context) => ConfigurationChangePage(id, applicationData, row)));
+ }
}
import 'configuration_controller.dart';
class ConfigurationCreatePage extends StatefulWidget {
- final ApplicationData pageData;
+ final ApplicationData applicationData;
final logger = Settings().logger;
- ConfigurationCreatePage(this.pageData, {Key key}) : super(key: key);
+ ConfigurationCreatePage(this.applicationData, {Key key}) : super(key: key);
@override
ConfigurationCreatePageState createState() {
- final rc = ConfigurationCreatePageState(pageData);
+ final rc = ConfigurationCreatePageState(applicationData);
+
+ /// for unittests:
+ applicationData.lastModuleState = rc;
return rc;
}
}
@override
Widget build(BuildContext context) {
- controller = controller ??
- ConfigurationController(
- _formKey, this, 'create', context, applicationData);
+ if (controller == null) {
+ controller =
+ ConfigurationController(_formKey, this, 'create', context, applicationData);
+ controller.initialize();
+ }
+ controller.buildWidgetList();
return Scaffold(
appBar: applicationData.appBarBuilder('Neue Rolle'),
drawer: applicationData.drawerBuilder(context),
import 'package:flutter/material.dart';
+import 'package:flutter_bones/src/widget/callback_controller_bones.dart';
import '../../widget/filter_set.dart';
import '../../widget/list_form.dart';
import 'configuration_controller.dart';
class ConfigurationListPage extends StatefulWidget {
- final ApplicationData pageData;
+ final ApplicationData applicationData;
- ConfigurationListPage(this.pageData, {Key key}) : super(key: key);
+ ConfigurationListPage(this.applicationData, {Key key}) : super(key: key);
@override
ConfigurationListPageState createState() {
// ConfigurationListPageState.setPageData(pageData);
- final rc = ConfigurationListPageState(pageData);
-
+ final rc = ConfigurationListPageState(applicationData);
+ /// for unittests:
+ applicationData.lastModuleState = rc;
return rc;
}
}
final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: 'configuration_list');
- Iterable<dynamic> rows;
+ Iterable<dynamic> rowsDeprecated;
ConfigurationController controller;
FilterSet filters;
@override
Widget build(BuildContext context) {
- controller ??= ConfigurationController(
- _formKey, this, 'list', context, applicationData);
+ if (controller == null) {
+ controller = ConfigurationController(
+ _formKey, this, 'list', context, applicationData,
+ redrawCallback: (RedrawReason reason, String customString) => setState(() => null));
+ controller.initialize();
+ controller.buildWidgetList();
+ }
filters ??= controller.filterSet(pageName: 'list');
- getRows();
+ controller.buildRows();
return Scaffold(
appBar: applicationData.appBarBuilder('Rollen'),
drawer: applicationData.drawerBuilder(context),
body: ListForm.listForm(
key: _formKey,
configuration: applicationData.configuration,
- titles: ListForm.stringsToTitles(';Id;Name;Priorität'),
- columnNames:
- 'configuration_id configuration_name configuration_priority'
- .split(' '),
- rows: rows ?? [],
+ titles: ListForm.stringsToTitles(controller.page.tableTitles),
+ columnNames: controller.page.tableColumns ?? [],
+ rows: controller.listRows ?? [],
showEditIcon: true,
+ pageController: controller,
buttons: <Widget>[
ButtonBar(alignment: MainAxisAlignment.center, children: [
- RaisedButton(
- child: Text('Suchen'),
- onPressed: () {
- rows = null;
- if (_formKey.currentState.validate()) {
- _formKey.currentState.save();
- getRows();
- }
- },
- ),
+ controller.searchButton(),
RaisedButton(
child: Text('Neue Rolle'),
onPressed: () {
Navigator.pushNamed(context, '/configuration/create');
+
+ /// Force the redraw on return:
+ controller.listRows = null;
},
),
]),
),
);
}
-
- void getRows() {
- final persistence = applicationData.persistence;
- final namePattern = filters.valueOf('configuration_name') ?? '';
- persistence.list(module: 'configuration', params: {
- ':configuration_name': namePattern.replaceAll('*', '%') + '%'
- }).then((list) {
- if (rows == null) {
- rows = list;
- setState(() {});
- }
- }, onError: (error) {
- applicationData.logger
- .error('cannot retrieve configuration list: $error');
- });
- }
}
import 'package:flutter/material.dart';
import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_bones/src/widget/callback_controller_bones.dart';
import '../../widget/edit_form.dart';
import 'role_controller.dart';
class RoleChangePage extends StatefulWidget {
final ApplicationData applicationData;
+ final Map initialRow;
final logger = Settings().logger;
final int primaryId;
//RoleChangePageState lastState;
- RoleChangePage(this.primaryId, this.applicationData, {Key key})
+ RoleChangePage(this.primaryId, this.applicationData, this.initialRow,
+ {Key key})
: super(key: key);
@override
RoleChangePageState createState() {
- final rc = RoleChangePageState(primaryId, applicationData);
- // lastState = rc;
+ final rc = RoleChangePageState(primaryId, applicationData, initialRow);
+
+ /// for unittests:
+ applicationData.lastModuleState = rc;
return rc;
}
}
class RoleChangePageState extends State<RoleChangePage> {
final ApplicationData applicationData;
final int primaryId;
+ final Map initialRow;
final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: 'role_change');
RoleController controller;
- RoleChangePageState(this.primaryId, this.applicationData);
+ RoleChangePageState(this.primaryId, this.applicationData, this.initialRow);
@override
Widget build(BuildContext context) {
if (controller == null) {
controller = RoleController(
- _formKey, this, 'list', context, applicationData,
- redrawCallback: () => setState(() => null));
+ _formKey, this, 'change', context, applicationData,
+ redrawCallback: (RedrawReason reason, String customString) =>
+ setState(() => null));
controller.initialize();
}
+ // controller.buildWidgetList() is called in editForm
return Scaffold(
appBar: applicationData.appBarBuilder('Rolle ändern'),
drawer: applicationData.drawerBuilder(context),
pageController: controller,
configuration: applicationData.configuration,
primaryId: primaryId,
+ initialRow: initialRow,
));
}
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
}
moduleModel.parse();
}
@override
- void startChange(int id) {
+ void startChange(int id, Map row) {
+ applicationData.pushCaller(this);
Navigator.push(context,
- MaterialPageRoute(builder: (context) => RoleChangePage(id, applicationData)));
+ MaterialPageRoute(builder: (context) => RoleChangePage(id, applicationData, row)));
}
}
import 'role_controller.dart';
class RoleCreatePage extends StatefulWidget {
- final ApplicationData pageData;
+ final ApplicationData applicationData;
final logger = Settings().logger;
- RoleCreatePage(this.pageData, {Key key}) : super(key: key);
+ RoleCreatePage(this.applicationData, {Key key}) : super(key: key);
@override
RoleCreatePageState createState() {
- final rc = RoleCreatePageState(pageData);
+ final rc = RoleCreatePageState(applicationData);
+
+ /// for unittests:
+ applicationData.lastModuleState = rc;
return rc;
}
}
RoleController(_formKey, this, 'create', context, applicationData);
controller.initialize();
}
+ controller.buildWidgetList();
return Scaffold(
appBar: applicationData.appBarBuilder('Neue Rolle'),
drawer: applicationData.drawerBuilder(context),
import 'package:flutter/material.dart';
+import 'package:flutter_bones/src/widget/callback_controller_bones.dart';
import '../../widget/filter_set.dart';
import '../../widget/list_form.dart';
import 'role_controller.dart';
class RoleListPage extends StatefulWidget {
- final ApplicationData pageData;
+ final ApplicationData applicationData;
- RoleListPage(this.pageData, {Key key}) : super(key: key);
+ RoleListPage(this.applicationData, {Key key}) : super(key: key);
@override
RoleListPageState createState() {
// RoleListPageState.setPageData(pageData);
- final rc = RoleListPageState(pageData);
+ final rc = RoleListPageState(applicationData);
+ /// for unittests:
+ applicationData.lastModuleState = rc;
return rc;
}
}
@override
Widget build(BuildContext context) {
if (controller == null) {
- controller = RoleController(
- _formKey, this, 'list', context, applicationData,
- redrawCallback: () => setState(() => null));
+ controller =
+ RoleController(_formKey, this, 'list', context, applicationData,
+ redrawCallback: (RedrawReason reason, String customString) {
+ if (reason == RedrawReason.fetchList) {
+ applicationData.persistence
+ .list(
+ module: controller.moduleModel.name,
+ params: controller.buildSqlParams())
+ .then((rows) {
+ setState(() => null);
+ });
+ }
+ });
controller.initialize();
+ controller.buildWidgetList();
}
filters ??= controller.filterSet(pageName: 'list');
controller.buildRows();
body: ListForm.listForm(
key: _formKey,
configuration: applicationData.configuration,
- titles: ListForm.stringsToTitles(';Id;Name;Priorität'),
- columnNames: 'role_id role_name role_priority'.split(' '),
+ titles: ListForm.stringsToTitles(controller.page.tableTitles),
+ columnNames: controller.page.tableColumns ?? [],
rows: controller.listRows ?? [],
showEditIcon: true,
pageController: controller,
import 'package:flutter/material.dart';
import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_bones/src/widget/callback_controller_bones.dart';
import '../../widget/edit_form.dart';
import 'user_controller.dart';
class UserChangePage extends StatefulWidget {
final ApplicationData applicationData;
+ final Map initialRow;
final logger = Settings().logger;
-
+ final int primaryId;
//UserChangePageState lastState;
- UserChangePage(this.applicationData, {Key key}) : super(key: key);
+ UserChangePage(this.primaryId, this.applicationData, this.initialRow,
+ {Key key})
+ : super(key: key);
@override
UserChangePageState createState() {
- final rc = UserChangePageState(applicationData);
- // lastState = rc;
+ final rc = UserChangePageState(primaryId, applicationData, initialRow);
+
+ /// for unittests:
+ applicationData.lastModuleState = rc;
return rc;
}
}
class UserChangePageState extends State<UserChangePage> {
final ApplicationData applicationData;
-
+ final int primaryId;
+ final Map initialRow;
final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: 'user_change');
UserController controller;
- UserChangePageState(this.applicationData);
+ UserChangePageState(this.primaryId, this.applicationData, this.initialRow);
@override
Widget build(BuildContext context) {
- controller = controller ??
- UserController(_formKey, this, 'change', context, applicationData);
+ if (controller == null) {
+ controller = UserController(
+ _formKey, this, 'change', context, applicationData,
+ redrawCallback: (RedrawReason reason, String customString) =>
+ setState(() => null));
+ controller.initialize();
+ }
+ // controller.buildWidgetList() is called in editForm
return Scaffold(
appBar: applicationData.appBarBuilder('Rolle ändern'),
drawer: applicationData.drawerBuilder(context),
key: _formKey,
pageController: controller,
configuration: applicationData.configuration,
+ primaryId: primaryId,
+ initialRow: initialRow,
));
}
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
}
import 'package:flutter_bones/flutter_bones.dart';
import '../../model/standard/user_model.dart';
+import 'user_change_page.dart';
import '../../widget/page_controller_bones.dart';
class UserController extends PageControllerBones {
/// Controller for a page named [pageName].
UserController(GlobalKey<FormState> formKey, State<StatefulWidget> parent,
- String pageName, BuildContext context, ApplicationData applicationData)
- : super(
- formKey,
- parent,
- UserModel(Settings().logger),
- pageName,
- context,
- applicationData,
- ) {
+ String pageName, BuildContext context, ApplicationData applicationData,
+ {Function redrawCallback})
+ : super(formKey, parent, UserModel(Settings().logger), pageName, context,
+ applicationData, redrawCallback) {
moduleModel.parse();
}
+ @override
+ void startChange(int id, Map row) {
+ applicationData.pushCaller(this);
+ Navigator.push(context,
+ MaterialPageRoute(builder: (context) => UserChangePage(id, applicationData, row)));
+ }
}
import 'user_controller.dart';
class UserCreatePage extends StatefulWidget {
- final ApplicationData pageData;
+ final ApplicationData applicationData;
final logger = Settings().logger;
- UserCreatePage(this.pageData, {Key key}) : super(key: key);
+ UserCreatePage(this.applicationData, {Key key}) : super(key: key);
@override
UserCreatePageState createState() {
- final rc = UserCreatePageState(pageData);
+ final rc = UserCreatePageState(applicationData);
+
+ /// for unittests:
+ applicationData.lastModuleState = rc;
return rc;
}
}
@override
Widget build(BuildContext context) {
- controller = controller ??
- UserController(_formKey, this, 'create', context, applicationData);
+ if (controller == null) {
+ controller =
+ UserController(_formKey, this, 'create', context, applicationData);
+ controller.initialize();
+ }
+ controller.buildWidgetList();
return Scaffold(
appBar: applicationData.appBarBuilder('Neue Rolle'),
drawer: applicationData.drawerBuilder(context),
import 'package:flutter/material.dart';
+import 'package:flutter_bones/src/widget/callback_controller_bones.dart';
import '../../widget/filter_set.dart';
import '../../widget/list_form.dart';
import 'user_controller.dart';
class UserListPage extends StatefulWidget {
- final ApplicationData pageData;
+ final ApplicationData applicationData;
- UserListPage(this.pageData, {Key key}) : super(key: key);
+ UserListPage(this.applicationData, {Key key}) : super(key: key);
@override
UserListPageState createState() {
// UserListPageState.setPageData(pageData);
- final rc = UserListPageState(pageData);
-
+ final rc = UserListPageState(applicationData);
+ /// for unittests:
+ applicationData.lastModuleState = rc;
return rc;
}
}
final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: 'user_list');
- Iterable<dynamic> rows;
+ Iterable<dynamic> rowsDeprecated;
UserController controller;
FilterSet filters;
@override
Widget build(BuildContext context) {
- controller ??=
- UserController(_formKey, this, 'list', context, applicationData);
+ if (controller == null) {
+ controller = UserController(
+ _formKey, this, 'list', context, applicationData,
+ redrawCallback: (RedrawReason reason, String customString) => setState(() => null));
+ controller.initialize();
+ controller.buildWidgetList();
+ }
filters ??= controller.filterSet(pageName: 'list');
- getRows();
+ controller.buildRows();
return Scaffold(
appBar: applicationData.appBarBuilder('Rollen'),
drawer: applicationData.drawerBuilder(context),
body: ListForm.listForm(
key: _formKey,
configuration: applicationData.configuration,
- titles: ListForm.stringsToTitles(';Id;Name;Priorität'),
- columnNames: 'user_id user_name user_priority'.split(' '),
- rows: rows ?? [],
+ titles: ListForm.stringsToTitles(controller.page.tableTitles),
+ columnNames: controller.page.tableColumns ?? [],
+ rows: controller.listRows ?? [],
showEditIcon: true,
+ pageController: controller,
buttons: <Widget>[
ButtonBar(alignment: MainAxisAlignment.center, children: [
- RaisedButton(
- child: Text('Suchen'),
- onPressed: () {
- rows = null;
- if (_formKey.currentState.validate()) {
- _formKey.currentState.save();
- getRows();
- }
- },
- ),
+ controller.searchButton(),
RaisedButton(
child: Text('Neue Rolle'),
onPressed: () {
Navigator.pushNamed(context, '/user/create');
+
+ /// Force the redraw on return:
+ controller.listRows = null;
},
),
]),
),
);
}
-
- void getRows() {
- final persistence = applicationData.persistence;
- final namePattern = filters.valueOf('user_name') ?? '';
- persistence.list(module: 'user', params: {
- ':user_name': namePattern.replaceAll('*', '%') + '%'
- }).then((list) {
- if (rows == null) {
- rows = list;
- setState(() {});
- }
- }, onError: (error) {
- applicationData.logger.error('cannot retrieve user list: $error');
- });
- }
}
),
),
),
- onTap: () => Navigator.push(context,
- MaterialPageRoute(builder: (context) => item.page())),
+ onTap: () {
+ BSettings().pageData.pushCaller(null);
+ Navigator.push(context,
+ MaterialPageRoute(builder: (context) => item.page()));
+ },
),
))
.toList(),
import 'package:flutter/material.dart';
import 'package:flutter_bones/flutter_bones.dart';
+typedef RedrawCallbackFunction = Function(
+ RedrawReason reason, String customString);
+
/// Interface for a callback controller for flutter_bones specific widgets.
/// flutter_bones specific widgets: [CheckboxListTileBone],
/// [DropDownButtonFormBone], [RaisedButtonBone], [TextFormFieldBone]
/// Prepares the rows shown in the list page
void buildRows();
- List<String> comboboxTexts(
- String customString, CallbackControllerBones controller);
+ /// Returns the texts of the [ComboboxModel] named [name].
+ List<String> comboboxTexts(String name);
- List<dynamic> comboboxValues(
- String customString, CallbackControllerBones controller);
+ /// Returns the values of the [ComboboxModel] named [name].
+ List<dynamic> comboboxValues(String name);
+ /// Frees all resources.
+ void dispose();
+
+ /// Returns the container for application specific information.
ApplicationData getApplicationData();
+ /// Returns the [BuildContext] instance of the page.
BuildContext getContext();
- FieldModel getModel(String customString, CallbackControllerBones controller);
-
- String getName();
-
+ /// Returns the [model] named [name].
+ FieldModel getModel(String name);
ValueChanged<String> getOnChanged(
String customString, CallbackControllerBones controller);
String customString, CallbackControllerBones controller, Map row);
/// Rebuilds the view of the page.
- void redraw();
+ /// [reason] and [customString] will be forwarded to the callback function.
+ void redraw(RedrawReason reason, String customString);
/// Returns a standard search button for the list page.
Widget searchButton();
/// Starts the change page to edit the record with primary key [id].
- void startChange(int id);
+ /// [row] contains the db record of [id].
+ void startChange(int id, Map row);
+
+ /// Returns the [TextEditingController] instance of a [TextFormField] assigned
+ /// to a model named [name].
+ TextEditingController textController(String name);
- /// Returns the field value of field named [fieldName] or null if not found.
+ /// Returns the field value of [FieldModel] named [fieldName] or null if not found.
dynamic valueOf(String fieldName);
}
+
+enum RedrawReason {
+ custom,
+ fetchList,
+ fetchRecord,
+}
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
-import 'page_controller_bones.dart';
class CheckboxFormField extends FormField<bool> {
CheckboxFormField({
@required PageControllerBones pageController,
@required BaseConfiguration configuration,
int primaryId,
+ Map initialRow,
}) {
final padding =
configuration.asFloat('form.card.padding', defaultValue: 16.0);
+ pageController.buildWidgetList(initialRow);
final widgets = pageController.getWidgets();
- if (primaryId != null){
- pageController.fetchData(primaryId);
- }
return Form(
key: key,
child: Card(
/// Format of [titles]: first character is the delimiter, e.g. ";Id;Name;"
/// This allows to use different delimiters when the title text
/// contains characters normally used as delimiters.
- static List<Widget> stringsToTitles(String titles) {
+ static List<Widget> stringsToTitles(List<String> titles) {
final rc = titles
- .substring(1)
- .split(titles[0])
.map((String title) =>
Text(title, style: TextStyle(fontWeight: FontWeight.bold)))
.toList();
TableCallbackController tableCallbackController,
String customString,
}) {
- assert(titles.length == columnNames.length);
+ if (titles.length != columnNames.length){
+ controller.moduleModel.logger.error('titles.length != columnNames.length: ${titles.length}/${columnNames.length}');
+ }
final titles2 = titles.map((item) => DataColumn(label: item)).toList();
if (showEditIcon) {
titles2.insert(0, DataColumn(label: SizedBox(width: 1)));
+import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bones/flutter_bones.dart';
import 'package:flutter_bones/src/widget/widget_list.dart';
final ModuleModel moduleModel;
String primaryColumn;
WidgetList widgetList;
- final Function redrawCallback;
- bool needsRedraw;
- //WidgetList changeWidgets;
+ final RedrawCallbackFunction redrawCallback;
final GlobalKey<FormState> globalKey;
final String pageName;
PageModel page;
State parent;
final BuildContext context;
Iterable listRows;
+ final textControllers = <String, TextEditingController>{};
PageControllerBones(this.globalKey, this.parent, this.moduleModel,
this.pageName, this.context, this.applicationData,
[this.redrawCallback]);
+
@override
void buildRows() {
- needsRedraw = true;
final persistence = applicationData.persistence;
final params = buildSqlParams() ?? {};
persistence.list(module: 'role', params: params).then((list) {
if (listRows == null) {
listRows = list;
- redraw();
+ redraw(RedrawReason.fetchList, null);
}
}, onError: (error) {
applicationData.logger.error('cannot retrieve role list: $error');
dynamic value = element.value;
DataType dataType = element.dataType;
String name = element.name;
- String operator;
if (element.filterType == FilterType.pattern) {
if (value == null || value.isEmpty) {
value = '%';
return rc;
}
+ /// Prepares the widgetList: builds the widgets of the page.
+ /// [initialRow] is null or a map with the field values, e.g. { 'role_name': 'admin' ...}
+ void buildWidgetList([Map initialRow]) {
+ widgetList.clear();
+ page.fields.forEach((model) {
+ final value = initialRow == null ? null : initialRow[model.name];
+ widgetList.addWidget(model.name,
+ View(moduleModel.logger).modelToWidget(model, this, value));
+ });
+ }
+
@override
- List<String> comboboxTexts(
- String customString, CallbackControllerBones controller) {
+ List<String> comboboxTexts(String name) {
return [];
}
@override
- List comboboxValues(String customString, CallbackControllerBones controller) {
+ List comboboxValues(String name) {
return [];
}
+ @override
+ void dispose() {
+ textControllers.values.forEach((controller) => controller.dispose());
+ textControllers.clear();
+ }
+
/// Gets the data from the database using the [primaryId].
fetchData(int primaryId) async {
applicationData.persistence
page.fields.forEach((model) {
model.value = row[model.name];
});
- needsRedraw = true;
- redraw();
+
+ /// navigate to the page "change":
+ startChange(primaryId, row);
});
}
}
@override
- FieldModel getModel(String customString, CallbackControllerBones controller) {
- final rc = moduleModel.pageByName(pageName)?.getField(customString);
+ FieldModel getModel(String name) {
+ final rc = moduleModel.pageByName(pageName)?.fieldByName(name);
return rc;
}
ModuleModel getModuleModel() => moduleModel;
- @override
- String getName() {
- return null;
- }
-
@override
getOnChanged(String customString, CallbackControllerBones controller) {
return null;
case PageModelType.create:
applicationData.persistence
.insert(module: moduleModel.name, data: params);
+ applicationData.popCaller();
Navigator.pop(controller.getContext());
break;
case PageModelType.change:
applicationData.persistence
- .update(module: moduleModel.name, data: params);
- Navigator.pop(controller.getContext());
+ .update(module: moduleModel.name, data: params)
+ .then((value) {
+ applicationData.callerRedraw(RedrawReason.fetchList);
+ applicationData.popCaller();
+ Navigator.pop(controller.getContext());
+ });
break;
default:
moduleModel.logger
}
};
} else if (customString == 'cancel') {
- rc = () => Navigator.pop(controller.getContext());
+ rc = () {
+ applicationData.popCaller();
+ Navigator.pop(controller.getContext());
+ };
}
return rc;
}
@override
getOnSaved(String customString, CallbackControllerBones controller) {
final rc = (input) {
- FieldModel model = page.getField(customString);
+ FieldModel model = page.fieldByName(customString);
model.value = StringHelper.fromString(input, model.dataType);
};
return rc;
/// e.g. ModuleModel.parse() is called.
/// This method must be called early after the constructor.
void initialize() {
- widgetList =
- WidgetList('${moduleModel.fullName()}.widgets', moduleModel.logger);
page = moduleModel.pageByName(pageName);
- page.fields.forEach((model) {
- widgetList.addWidget(
- model.name, View(moduleModel.logger).modelToWidget(model, this));
+ widgetList = WidgetList('${page.fullName()}.widgets', moduleModel.logger);
+ }
+
+ /// Copies the values of [initialRow] into the values of the fields.
+ void initializeFields(Map initialRow) {
+ widgetList.widgetMap.keys.forEach((name) {
+ if (initialRow.containsKey(name)) {
+ page.fieldByName(name)?.value = initialRow[name];
+ }
});
}
onEditTap(String customString, CallbackControllerBones controller, Map row) {
ColumnModel primary = moduleModel.mainTable().primary;
final id = row[primary.name];
- controller.startChange(id);
+ applicationData.persistence
+ .record(module: moduleModel.name, id: id)
+ .then((row) => startChange(id, row));
}
@override
- void redraw() {
+ void redraw(RedrawReason reason, [String customString]) {
if (redrawCallback == null) {
moduleModel.logger.error(
'not overridden: fetchListRows() in ${moduleModel.widgetName()}.$pageName');
} else {
- if (needsRedraw) {
- needsRedraw = false;
- redrawCallback();
- }
+ redrawCallback(reason, customString);
}
}
}
@override
- void startChange(int id) {
+ void startChange(int id, Map row) {
moduleModel.logger.error(
'missing override of startChange() in ${moduleModel.fullName()}');
}
+ /// Returns the [TextEditingController] of a [TextFormField] assigned to
+ /// a model named [name].
+ @override
+ TextEditingController textController(String name) {
+ if (!textControllers.containsKey(name)) {
+ textControllers[name] = TextEditingController();
+ }
+ return textControllers[name];
+ }
+
@override
dynamic valueOf(String fieldName) {
- final rc = page.getField(fieldName)?.value;
+ final rc = page.fieldByName(fieldName)?.value;
return rc;
}
}
return rc;
}
- /// Creates a checkbox from the [model].
- Widget checkbox(FieldModel model, CallbackControllerBones controller) {
+ /// Creates a checkbox from the [model] using the controller with the value [initialValue].
+ Widget checkbox(FieldModel model, CallbackControllerBones controller,
+ [bool initialValue]) {
final tristate = model.hasOption('undef');
final rc = toolTip(
CheckboxListTileBone(model.name, controller,
- value: tristate ? model.value : model.value ?? false,
+ value: tristate ? initialValue : initialValue ?? false,
tristate: tristate,
title: Text(model.label),
- selected: model.value ?? false),
+ selected: initialValue ?? false),
model);
return rc;
}
/// Creates a combobox via the [controller].
- Widget combobox<T>(FieldModel model, CallbackControllerBones controller) {
- final texts = controller.comboboxTexts(model.name, controller);
- final values = controller.comboboxValues(model.name, controller) ?? texts;
+ Widget combobox<T>(
+ FieldModel model, CallbackControllerBones controller, initialValue) {
+ final texts = controller.comboboxTexts(model.name);
+ final values = controller.comboboxValues(model.name) ?? texts;
final items = <DropdownMenuItem<T>>[];
for (var ix = 0; ix < texts.length; ix++) {
items.add(DropdownMenuItem(
value: values[ix],
child: Text(texts[ix])));
}
- final rc = DropdownButtonFormBone(model.name, controller, items: items);
+ final rc = DropdownButtonFormBone(model.name, controller,
+ items: items, value: initialValue);
return rc;
}
- Widget dbReference(
- DbReferenceModel model, CallbackControllerBones controller) {
+ Widget dbReference(DbReferenceModel model, CallbackControllerBones controller,
+ initialValue) {
var rc;
if (model.dataType == DataType.bool) {
- rc = checkbox(model, controller);
+ rc = checkbox(model, controller, initialValue);
} else if (model.hasOption('combobox')) {
- rc = combobox(model, controller);
+ rc = combobox(model, controller, initialValue);
} else {
- rc = textField(model, controller);
+ rc = textField(model, controller, initialValue);
}
return rc;
}
return rc;
}
- /// Converts a [model] into a [Widget].
- Widget modelToWidget(WidgetModel model, CallbackControllerBones controller) {
+ /// Converts a [model] into a [Widget] under control of [controller]
+ /// with the [initialValue].
+ Widget modelToWidget(WidgetModel model, CallbackControllerBones controller,
+ [dynamic initialValue]) {
Widget rc;
switch (model?.widgetModelType) {
case WidgetModelType.textField:
- rc = textField(model, controller);
+ rc = textField(model, controller, initialValue);
break;
case WidgetModelType.button:
rc = button(model, controller);
rc = text(model);
break;
case WidgetModelType.checkbox:
- rc = checkbox(model, controller);
+ rc = checkbox(model, controller, initialValue);
break;
case WidgetModelType.combobox:
rc = text(model);
rc = section(model);
break;
case WidgetModelType.dbReference:
- rc = dbReference(model, controller);
+ rc = dbReference(model, controller, initialValue);
break;
case WidgetModelType.allDbFields:
break;
}
/// Creates a form text field from the [model].
- Widget textField(FieldModel model, CallbackControllerBones controller) {
+ Widget textField(
+ FieldModel model, CallbackControllerBones controller, initialValue) {
+ final value =
+ initialValue == null ? null : StringHelper.asString(initialValue);
+ final textController = controller.textController(model.name);
+ textController.text = value;
var rc = toolTip(
TextFormFieldBone(
model.name, controller,
//validator: model.validator,
+ controller: textController,
+ readOnly: model.hasOption('readonly'),
decoration: InputDecoration(labelText: model.label),
),
model);
String name;
if (element is TextFormFieldBone) {
name = element.customString;
- final model = page.getField(name, required: true);
+ final model = page.fieldByName(name, required: true);
value = model?.value;
dataType = model?.dataType;
} else if (element is DropdownButtonFormBone) {
name = element.customString;
- final model = page.getField(name, required: true);
+ final model = page.fieldByName(name, required: true);
value = model?.value;
dataType = model?.dataType;
}
expect(userField, isNotNull);
expect(userField.widgetName(), 'user_id');
expect(userField.fullName(), equals('user.user_id'));
- final userRef = page.getField('user');
+ final userRef = page.fieldByName('user');
expect(userRef, isNotNull);
expect(userRef.fullName(), equals('create.user'));
expect(userRef.widgetName(), equals('user'));
allDbFields 12 options:
] # change.simpleForm1
''');
- final userField = page.getField('user');
+ final userField = page.fieldByName('user');
expect(userField, isNotNull);
expect(userField.widgetName(), 'user');
userField.value = 'Hi';
module.parse();
var errors = logger.errors;
expect(errors.length, equals(0));
- final checkbox = module.pageByName('create').getField('hidden');
+ final checkbox = module.pageByName('create').fieldByName('hidden');
expect(checkbox.hasOption('required'), isTrue);
checkbox.value = true;
expect(checkbox.value, isTrue);
module.parse();
var errors = logger.errors;
expect(errors.length, equals(0));
- final checkbox = module.pageByName('create').getField('hidden');
+ final checkbox = module.pageByName('create').fieldByName('hidden');
expect(checkbox, isNotNull);
expect(checkbox.dataType, DataType.bool);
final dump = module.dump(StringBuffer()).toString();
module.parse();
var errors = logger.errors;
expect(errors.length, equals(0));
- final combobox = module.pageByName('create').getField('class');
+ final combobox = module.pageByName('create').fieldByName('class');
expect(combobox, isNotNull);
expect(combobox.dataType, DataType.int);
final dump = module.dump(StringBuffer()).toString();
import 'package:flutter/src/widgets/framework.dart' as x;
import 'package:flutter_bones/flutter_bones.dart';
import 'package:flutter_bones/src/widget/widget_helper.dart';
+import 'package:flutter_bones/src/private/bsettings.dart';
import 'package:test/test.dart';
void main() {
expect(toolTip.child, equals(widget));
});
});
- /*
group('ModuleController', () {
test('basic', () {
- ApplicationData pageData = ApplicationData(
+ ApplicationData appData = ApplicationData(
BaseConfiguration({}, logger), (title) => null, (context) => null,
- BSettings.lastInstance.persistence);
- final role = RoleCreatePage(pageData);
- if (role.lastState == null) {
+ BSettings.lastInstance.persistence, logger);
+ final role = RoleCreatePage(appData);
+ if (appData.lastModuleState == null) {
role.createState();
}
expect(role, isNotNull);
BuildContext context = MyContext();
- role.lastState.build(context);
- final widgets = role.lastState.controller.getWidgets(true);
+ RoleCreatePageState lastInstance = appData.lastModuleState;
+ lastInstance.build(context);
+ final widgets = lastInstance.controller.getWidgets();
expect(widgets.length, equals(3));
});
});
- */
}
class MyContext extends BuildContext {