撲來和飛鏢庫的集合為您的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 Changed返回流並在任何屬性更改時發出值的流屬性。要重新定義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語法來定義組件及其屬性。