颤振/飞镖模式,用于显示带有表单字段的对话框/屏幕



在我的颤振应用程序中,我显示了一个对话框,其中包含由表单的 JSON 表示控制的一系列字段。 控制表单小部件的函数位于对话框实现之外(因此可以在对话框以外的小部件中重用(。

受 jsonToForm 模块 (https://github.com/VictorRancesCode/json_to_form( 的启发,我的代码如下:

List<Widget> jsonToForm() {
List<Widget> widgetList = new List<Widget>();
for (var count = 0; count < formItems.length; count++) {
FormItem field = FormItem.fromJson( formItems[count] );
switch( field.type ) {
case FieldType.input:
case FieldType.password:
case FieldType.email:
case FieldType.numeric:
case FieldType.textarea: 
widgetList.add( _buildLabel(field.title) );
widgetList.add( _buildTextField(field) );
break;
case FieldType.radiobutton: 
widgetList.add( _buildLabel(field.title) );
for (var i = 0; i < field.fields.length; i++) {
widgetList.add( _buildRadioField( field, field.fields[i] ));
}
break;
case FieldType.color:
widgetList.add( _buildColorPicker(field) );
break;
case FieldType.toggle:
widgetList.add( _buildSwitch(field) );
break;
case FieldType.checkbox:
widgetList.add( _buildLabel(field.title) );
for (var i = 0; i < field.fields.length; i++) {
widgetList.add( _buildCheckbox(field, field.fields[i] ) );
}
break;
}
}
return widgetList;
}

当表单值更改时,它会调用_handleChanged,然后通过事件回调将表单字段列表传递给父级。

void _handleChanged( FormItem field, { FieldItem item } ) {
var fieldIndex = formItems.indexWhere( (i) => i['name'] == field.name );
if( fieldIndex != -1 ) {
// If specified, update the subitem
if( item != null ) {
var itemIndex = field.fields.indexWhere( (i) => i.title == item.title );
field.fields.replaceRange(itemIndex, itemIndex+1, [item]);
}
// Now update the state
this.setState(() {
formItems.replaceRange(fieldIndex, fieldIndex+1, [ field.toJson() ]);
});
// Notify parents
widget.onChanged(formItems);
}
}

这种方法的问题(特别是对于文本字段(,没有onComplete事件,该事件仅在输入所有文本后触发。 onChanged 以及 TextEditorController 方法在键入每个字符时触发。 但是,我不想将对话框按钮放在 jsonToForm 例程中,因为这样它就不再可以在其他(非对话框(屏幕中重用。

我也不喜欢执行 onChanged 回调的表单返回,直到只有一些字段被更新时返回所有字段。 通过在每个更改事件上发出信号,它最终也会在每个键入的字符上重新构建(我也想避免这种情况(。

理想的情况是,仅在所有字段的所有编辑都完成时才使用 jsonToForm 执行回调,但没有按钮,jsonToForm 模块中没有任何内容可以发出"我已完成"的信号。

var response;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Settings"),
actions: <Widget>[
DialogButton(
child: new Text( 'Save', style: TextStyle(color: Colors.white,fontWeight: FontWeight.w600, fontSize: 16)),
color: Colors.blue,
width: 110,
onPressed: () {
var data = List<Property>();
if( response != null ) {
response.forEach((item) => data.add( Property( name: item['name'], value: item['value'], 
type: AppUtils.enumFromString( PropertyType.values, item['type'], PropertyType.string ) )));
}
var lib = this.widget.item.libraryItem;
var logger = AppConfig.newLogger(lib.id, lib.valueType, properties: data);
Navigator.pop(context, logger);                   
})
],
),
body: SingleChildScrollView(
child: Container(
child:  Column(children: <Widget>[
JsonForm(
form: json.encode(this.widget.item.formItems),
onChanged: (dynamic response) {
this.setState(() => this.response = response);
},
),
]),
),
),
);
}

我是否坚持将脚手架/小部件构建放在 jsonToForm 中,然后为屏幕、子小部件等复制此小部件,或者是否有更优雅的解决方案将表单与容器分开?

似乎我已经发现了问题的实际问题所在。

我建模代码后的 jsonToForm 库的问题将表单的创建放在 Build 函数而不是 InitState 函数中。 这会导致表单在每次更改时重新构建(非常糟糕(,随后如果您将 TextController 添加到 TextInputField,它会变得疯狂。

任何使用 jsonToForm 的人都需要更新代码。

我也不喜欢它创建一个小部件数组而不是使用未来的构建器方法。 ....改天我会杀了那条龙。