คอลเลกชันของไลบรารี Flutter และ Dart ที่ให้บริการโซลูชันสำหรับ UI ที่ขับเคลื่อนด้วยเซิร์ฟเวอร์ในแอปพลิเคชัน Flutter ของคุณ
| บรรจุุภัณฑ์ | ผับ |
|---|---|
| Expression_language | |
| dynamic_forms | |
| dynamic_forms_generator | |
| flutter_dynamic_forms | |
| flutter_dynamic_forms_components |
แนวคิดที่อยู่เบื้องหลังโครงการนี้คือการสามารถกำหนดส่วนประกอบของคุณผ่าน XML หรือ JSON บนเซิร์ฟเวอร์และบริโภคในไคลเอนต์ Flutter โดยไม่ต้องใช้แอปใหม่ จุดสนใจหลักคือความสามารถในการกำหนดส่วนประกอบที่กำหนดเองและความสัมพันธ์ที่ซับซ้อนระหว่างคุณสมบัติของพวกเขา ตัวอย่างเช่นคุณสามารถกำหนดกฎการตรวจสอบความถูกต้องที่กำหนดเองสลับการมองเห็นตามเงื่อนไข ฯลฯ สิ่งนี้ทำให้มีประโยชน์อย่างยิ่งเมื่อทำงานกับรูปแบบการรวบรวมอินพุตของผู้ใช้บางส่วน แต่สามารถใช้เพื่อแสดงทรีวิดเจ็ต Flutter ใด ๆ
ดูตัวอย่างโครงการที่มีการสาธิตการทำงาน
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 ชั้นเรียนเหล่านั้นจะดูแลการแยกวิเคราะห์แบบฟอร์มของคุณ พวกเขายังมี form getter ซึ่งเป็นตัวแทนวัตถุของแบบฟอร์ม XML/JSON ของคุณใน DART พวกเขายังสามารถดำเนินการที่มีประโยชน์ในแบบฟอร์มเช่นการจัดการสถานะของแบบฟอร์มเมื่อมีบางสิ่งเกิดขึ้นใน 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 จะมีอยู่ในทรี subtree วิดเจ็ตของคุณโดยการโทรหา 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
วิดเจ็ตนี้ยังใช้รายการเรนเดอร์ที่ควบคุมวิธีการแปลแต่ละรุ่นไปยังวิดเจ็ต Flutter ในตัวอย่างด้านบนเราใช้ชุดเรนเดอร์ที่กำหนดไว้ล่วงหน้า คำว่าปฏิกิริยาหมายความว่าแต่ละองค์ประกอบจะรับฟังการเปลี่ยนแปลงในคุณสมบัติโมเดลฟอร์มและจะอัปเดตตัวเอง
พารามิเตอร์ตัวเลือกสุดท้ายคือ dispatcher ช่วยให้คุณจัดการกับเหตุการณ์ที่ได้จาก FormElementEvent ที่ผลิตในชั้นเรียนการแสดงผลของคุณ เมื่อไม่มีการจัดเตรียมพารามิเตอร์ dispatcher เฉพาะเหตุการณ์ของประเภท 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 :
คลาสนี้ควบคุมว่าส่วนประกอบจะถูก deserialized เป็นคลาสโมเดลที่สอดคล้องกันอย่างไร มันใช้งานได้ทั้ง 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 และ setters รอบแผนที่นี้เพื่อให้คุณสามารถเข้าถึงและตั้งค่าคุณสมบัติได้อย่างง่ายดาย
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 ();
}
} คลาสนี้ใช้แบบจำลองและส่งคืนวิดเจ็ต Flutter นอกจากนี้คุณยังสามารถสมัครรับการเปลี่ยนแปลงในคุณสมบัติเพื่อให้วิดเจ็ตของคุณได้รับการอัปเดตอย่างเหมาะสมเมื่อมีบางสิ่งเกิดขึ้นกับโมเดล เพื่อจุดประสงค์นี้ให้ใช้สตรีมที่กำหนดไว้ในแต่ละคุณสมบัติหรือใช้คุณสมบัติของคอมโพเนนต์คุณสมบัติ propertyChanged ซึ่งส่งคืนสตรีมและปล่อยค่าเมื่อใดก็ตามที่มีการเปลี่ยนแปลงคุณสมบัติใด ๆ ในการกำหนด UI ใหม่ของส่วนประกอบเริ่มต้นภายใน flutter_dynamic_forms_components เพียงแค่กำหนด renderers ของคุณสำหรับรุ่นที่มีอยู่ คุณสามารถมีผู้แสดงผลหลายตัวและแสดง 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 อย่างง่าย