扑来和飞镖库的集合为您的Flutter应用程序中的服务器驱动的UI提供了解决方案。
| 包裹 | 酒吧 |
|---|---|
| expression_language | |
| Dynamic_forms | |
| Dynamic_forms_generator | |
| flutter_dynamic_forms | |
| flutter_dynamic_forms_components |
该项目背后的想法是能够通过XML或服务器上的JSON定义您的组件,并在扑朔迷离的客户端中消耗它,而无需重新启动应用程序。主要重点是定义自定义组件和其属性之间的复杂关系的能力。例如,您可以定义自定义验证规则,基于条件切换可见性等。这使得在收集某些用户输入的表单上工作时特别有用,但可用于显示任何扑朔迷离的小部件树。
另请参阅包含工作演示的示例项目。
import 'package:flutter/material.dart' ;
import 'package:dynamic_forms/dynamic_forms.dart' ;
import 'package:flutter_dynamic_forms/flutter_dynamic_forms.dart' ;
import 'package:flutter_dynamic_forms_components/flutter_dynamic_forms_components.dart' ;
class SimpleForm extends StatelessWidget {
final String xmlString;
const SimpleForm ({ Key key, this .xmlString}) : super (key : key);
@override
Widget build ( BuildContext context) {
return Scaffold (
body : SingleChildScrollView (
child : ParsedFormProvider (
create : (_) => XmlFormManager (), // Or use JsonFormManager() to parse JSON data
content : xmlString,
parsers : getDefaultParserList (), // Optionally add your custom parsers
child : FormRenderer < XmlFormManager >( // Use matching FormManager type registered above
renderers : getReactiveRenderers (), // Optionally add your custom renderers
),
),
),
);
}
}<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< form id = " form1 " >
< textField
id = " firstName "
label = " Enter your first name " >
</ textField >
< textField
id = " lastName "
label = " Enter your last name " >
< textField .validations>
< requiredValidation
message = " Last name is required " />
</ textField .validations>
</ textField >
< label
id = " fullNameLabel " >
< label .value>
< expression >
<![CDATA[
@firstName + (length(@firstName) > 0 && length(@lastName) > 0 ? " " : "") + @lastName
]]>
</ expression >
</ label .value>
</ label >
< label >
< label .value>
< expression >
<![CDATA[
"Welcome " + @fullNameLabel + "!"
]]>
</ expression >
</ label .value>
< label .isVisible>
< expression >
<![CDATA[
!@hideWelcomeCheckBox && length(@fullNameLabel) > 0
]]>
</ expression >
</ label .isVisible>
</ label >
< checkBox
id = " hideWelcomeCheckBox "
value = " false "
label = " Hide welcome message " />
</ form >如果您希望JSON描述您的表格,请检查此JSON示例。

将以下依赖项添加到您的pubspec.yaml文件:
flutter_dynamic_forms : <latest version>
flutter_dynamic_forms_components : <latest version>flutter_dynamic_forms_components库包含一组预定义的组件,例如Label , CheckBox , RadioButtonGroup等。为了使您的应用程序与这些组件一起使用,您需要执行以下步骤:
首先,您需要确定是否要从XML或JSON获取表单数据。您可以使用JsonFormManager或XmlFormManager 。这些课程将负责解析您的表格。它们还具有getter form ,这是DART中XML/JSON表格的对象表示。他们还可以在表单上执行一些有用的操作,例如在UI中发生某些事物,验证表单或从表单中收集所有数据时操纵表单状态,以便将其发送回服务器。如果您需要将自定义逻辑写入FormManager,则可以轻松地将其扩展:
class CustomFormManager extends JsonFormManager {
Future < void > sendDataToServer () async {
var properties = getFormProperties ();
// send properties to the server
}
}初始化FormManager最简单方法是通过ParsedFormProvider小部件。它将获取您的XML/JSON内容,解析器列表,并将创建FormManager实例,并照顾您的数据。 ParsedFormProvider正在使用引擎盖下的Provider软件包,因此FormManager将通过调用FormProvider.of<YourFormProvider>(context) 。
要在屏幕上呈现您的表格,您可以使用FormRenderer窗口小部件:
FormRenderer < XmlFormManager >(
renderers : getReactiveRenderers (),
formManager : myFormManagerInstance, // this is optional, can be resolved from the FormProvider
dispatcher : _onFormElementEvent, // optional, when omitted, it will delegate all change events to from manager
)您可以将您的FormManager作为一种类型提供: FormRenderer<XmlFormManager>(...) ,它将自动从先前定义的FormProvider解析,或者您可以将特定的FormManager实例传递到FormRenderer constructor中。
该小部件还列出了一个渲染器列表,这些渲染器控制如何将每个模型转换为颤音小部件。在上面的示例中,我们使用一组预定义的渲染器。反应性一词意味着每个组件将聆听表单模型属性中的更改,并会自行更新。
最后一个可选参数是dispatcher 。它允许您处理从渲染类中产生的FormElementEvent衍生的事件。当未提供dispatcher程序参数时,仅处理类型the ChangeValueEvent的事件并将直接委派给formmanager实例,从而导致属性值的更改。如果您需要发送自定义事件(例如单击按钮),请使用自己的dispatcher处理程序,但是您应始终让Form Manager处理ChangeValueEvent :
void _onFormElementEvent ( FormElementEvent event) {
if (event is ChangeValueEvent ) {
_formManager. changeValue (
value : event.value,
elementId : event.elementId,
propertyName : event.propertyName,
ignoreLastChange : event.ignoreLastChange);
}
// process your own events
}将数据发送回服务器的过程背后的想法是,我们不应该将整个表单发送回整个表单,而只能由用户更改。
收集数据只需调用:
List < FormPropertyValue > data = formManager. getFormData ()它包含所有属性的列表,这些属性在组件解析器定义中被标记为可变的列表。在默认的组件中,这些是用户预期更改的属性。每个项目都包含源元素,属性名称和属性值的ID。要提交通常需要序列化此列表并将其发送回服务器的表格。
flutter_dynamic_forms_components软件包仅包含一组与简单表单应用程序相关的基本组件。由于对我旧应用的某些要求,并非所有命名的组件都直接对应于Flutter小部件。我认为这可能在将来发生变化。同样,在设计组件时,您始终可以在Label或高级组件(例如UserProfile等低级组件之间进行选择。在此复杂的高级组件的情况下,您让客户端应用程序完全控制最终小部件的外观。由于这些原因,我建议每个应用程序编写其自定义组件集以完全控制每个属性。
要实现自定义组件,您需要提供3个类: Parser , Model和Renderer 。当您在上面的代码中看到的构建表单时,需要注册解析器和模型。让我们在CheckBox示例中显示:
此类控制该组件如何被序列化为相应的模型类。它在XML和JSON上都起作用。 ParserNode参数包含一系列方法,可让您从当前XML/JSON节点中解析值。使用解析方法的ElementParserFunction parser参数递归解析儿童节点。
import 'package:dynamic_forms/dynamic_forms.dart' ;
import 'check_box.dart' ;
class CheckBoxParser extends FormElementParser < CheckBox > {
@override
String get name => 'checkBox' ;
@override
FormElement getInstance () => CheckBox ();
@override
void fillProperties (
CheckBox checkBox,
ParserNode parserNode,
Element ? parent,
ElementParserFunction parser,
) {
super . fillProperties (checkBox, parserNode, parent, parser);
checkBox
..labelProperty = parserNode. getStringProperty ( 'label' )
..valueProperty = parserNode. getBoolProperty (
'value' ,
isImmutable : false ,
);
}
}模型是没有任何颤音依赖性的主要组件定义。组件可以扩展其他组件继承所有属性。它也可以包含成分作为孩子。每个属性都可以包含对值评估的简单值或表达式。为了能够涵盖这两种情况,必须使用Property<T>语法来定义所有属性。属性存储在一个名为properties的单个地图中,因此您可以轻松地穿越整个组件树。最好在此地图上创建Getters和Setter,以便您可以轻松访问和设置属性值。
import 'package:dynamic_forms/dynamic_forms.dart' ;
class CheckBox extends FormElement {
static const String labelPropertyName = 'label' ;
static const String valuePropertyName = 'value' ;
Property < String > get labelProperty => properties[labelPropertyName];
set labelProperty ( Property < String > value) =>
registerProperty (labelPropertyName, value);
String get label =>
labelProperty.value;
Stream < String > get labelChanged => labelProperty.valueChanged;
Property < bool > get valueProperty => properties[valuePropertyName];
set valueProperty ( Property < bool > value) =>
registerProperty (valuePropertyName, value);
bool get value =>
valueProperty.value;
Stream < bool > get valueChanged => valueProperty.valueChanged;
@override
FormElement getInstance () {
return CheckBox ();
}
}该类仅采用该模型并返回一个颤动的小部件。您还可以订阅属性的更改,因此在模型上发生某些事情时,将正确更新小部件。为此,请使用每个属性上定义的流或使用Component Property propertyChanged返回流并在任何属性更改时发出值的流属性。要重新定义flutter_dynamic_forms_components中默认组件的UI,只需为现有模型定义渲染器即可。您甚至可以拥有多个渲染器,并在不同的屏幕上显示不同的UI。使用渲染方法的FormElementRendererFunction renderer参数递归渲染儿童。
import 'package:flutter/material.dart' ;
import 'package:flutter_dynamic_forms/flutter_dynamic_forms.dart' ;
import 'check_box.dart' ;
class CheckBoxRenderer extends FormElementRenderer < CheckBox > {
@override
Widget render (
CheckBox element,
BuildContext context,
FormElementEventDispatcherFunction dispatcher,
FormElementRendererFunction renderer) {
return Padding (
padding : const EdgeInsets . all ( 8.0 ),
child : Row (
children : < Widget > [
StreamBuilder < bool >(
initialData : element.value,
stream : element.valueChanged,
builder : (context, snapshot) {
return Checkbox (
onChanged : (value) => dispatcher (
ChangeValueEvent (
value : value,
elementId : element.id,
),
),
value : snapshot.data,
);
},
),
Padding (
padding : EdgeInsets . only (left : 8 ),
child : StreamBuilder < String >(
initialData : element.label,
stream : element.labelChanged,
builder : (context, snapshot) {
return Text (snapshot.data);
},
),
)
],
),
);
}
}实现Parser和Model类时,有很多样板。由于大多数应用程序可能需要创建大量自定义组件,因此还有一个发电机软件包,可以使用简单的YAML语法来定义组件及其属性。