플러터 응용 프로그램에서 서버 구동 UI를위한 솔루션을 제공하는 플러터 및 다트 라이브러리 모음.
| 패키지 | 선술집 |
|---|---|
| expression_language | |
| dynamic_forms | |
| dynamic_forms_generator | |
| flutter_dynamic_forms | |
| flutter_dynamic_forms_components |
이 프로젝트의 배후에있는 아이디어는 서버에서 XML 또는 JSON을 통해 구성 요소를 정의하고 앱을 재배치하지 않고 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 사용할 수 있습니다. 그 수업은 당신의 양식을 구문 분석 할 것입니다. 또한 다트에서 XML/JSON 양식의 객체 표현 인 getter form 있습니다. 또한 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 패키지를 사용하므로 FormProvider.of<YourFormProvider>(context) 호출하여 FormManager 위젯 하위 트리에서 사용할 수 있습니다.
화면에서 양식을 렌더링하려면 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 생성자로 전달할 수 있습니다.
이 위젯은 또한 각 모델이 플러터 위젯으로 변환되는 방식을 제어하는 렌더러 목록을 가져옵니다. 위의 예에서는 사전 정의 된 렌더러 세트를 사용합니다. 반응성이라는 단어는 각 구성 요소가 양식 모델 속성의 변경 사항을 듣고 스스로 업데이트한다는 것을 의미합니다.
마지막 선택적 매개 변수는 dispatcher 입니다. 렌더 클래스에서 생성 된 FormElementEvent 에서 파생 된 이벤트를 처리 할 수 있습니다. dispatcher 매개 변수가 제공되지 않으면 ChangeValueEvent 유형의 이벤트 만 처리되고 FormManager 인스턴스로 직접 위임되어 속성 값이 변경됩니다. 사용자 정의 이벤트를 보내야하는 경우 (버튼 클릭과 같은) 자신의 dispatcher 핸들러를 사용하지만 항상 폼 관리자가 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 패키지에는 간단한 양식 응용 프로그램과 관련된 일련의 기본 구성 요소 만 포함되어 있습니다. 이전 앱의 일부 요구 사항으로 인해 모든 구성 요소 이름 지정이 플러터 위젯에 직접 해당하는 것은 아닙니다. 나는 그것이 미래에 변할 것이라고 생각합니다. 또한 구성 요소를 디자인 할 때는 항상 Label 과 같은 저수준 구성 요소 또는 UserProfile 과 같은 고급 구성 요소 중에서 선택할 수 있습니다. 이 복잡한 고급 구성 요소의 경우 클라이언트 응용 프로그램이 최종 위젯의 모양을 완전히 제어 할 수 있도록합니다. 이러한 이유로 각 애플리케이션이 각 속성을 완전히 제어 할 수 있도록 사용자 정의 구성 요소 세트를 작성하는 것이 좋습니다.
사용자 정의 구성 요소를 구현하려면 Parser , Model 및 Renderer 의 3 가지 클래스를 제공해야합니다. 위의 코드에서 볼 수 있듯이 양식을 작성할 때는 구문 분석기와 모델을 등록해야합니다. 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 and 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 ();
}
} 이 클래스는 단순히 모델을 가져 와서 플러터 위젯을 반환합니다. 모델에서 어떤 일이 발생하면 위젯이 올바르게 업데이트되도록 속성의 변경 사항을 구독 할 수도 있습니다. 이 목적을 위해 각 속성에 정의 된 스트림을 사용하거나 부동산이 변경 될 때마다 스트림을 반환하고 값을 방출하는 구성 요소 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 구문을 사용하여 구성 요소와 해당 속성을 정의 할 수있는 생성기 패키지도 있습니다.