Коллекция библиотеки Flutter и DART, предоставляющая решение для пользовательского интерфейса, управляемого сервером, в вашем приложении 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. Они также могут выполнить некоторую полезную операцию в форме, например, манипулирование состоянием формы, когда что -то происходит в пользовательском интерфейсе, проверка формы или сбор всех данных из формы, чтобы его можно было отправить обратно на сервер. Если вам нужно написать пользовательскую логику в 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 .
Этот виджет также принимает список рендеристов, которые контролирует, как будет переведена каждая модель в виджет Flutter. В приведенном выше примере мы используем набор предопределенных рендереров. Слово реактивное означает, что каждый компонент будет прослушать изменения в свойстве модели формы и будет обновлять себя.
Последний необязательный параметр - 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 ()Он содержит список всех свойств, которые были помечены как изменчивые в определении компонента. В компонентах по умолчанию это свойства, которые, как ожидается, будут изменены пользователем. Каждый элемент содержит идентификатор исходного элемента, имя свойства и значение свойства. Чтобы отправить форму, вы обычно хотите сериализовать этот список и отправить его обратно на свой сервер.
Пакет flutter_dynamic_forms_components содержит только набор основных компонентов, связанных с простой приложением формы. Из -за некоторых требований в моем старом приложении не все, что именование компонентов напрямую соответствует виджетам Flutter. Я думаю, что это может измениться в будущем. Кроме того, при разработке компонентов вы всегда можете выбрать между компонентами низкого уровня, такими как Label или компонент высокого уровня, такие как UserProfile . В случае этого сложного компонента высокого уровня вы позволяете клиентскому приложению полностью контролировать внешний вид конечного виджета. По этим причинам я бы порекомендовал для каждого приложения написать свой пользовательский набор компонентов, чтобы иметь полный контроль над каждым свойством.
Чтобы реализовать пользовательский компонент, вам необходимо предоставить 3 класса: Parser , Model и Renderer . Парсеры и модели должны быть зарегистрированы, когда вы создаете форму, как вы можете видеть в коде выше. Давайте покажем это в примере CheckBox :
Этот класс контролирует, как компонент будет опустошен в соответствующий класс модели. Это работает как на XML, так и на JSON. Параметр ParserNode содержит набор методов, которые позволяют вам анализировать значения из текущего узла XML/JSON. Используйте параметр ElementParserFunction parser метода Parse, чтобы рекурсивно разрабатывать детские узлы.
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 ();
}
} Этот класс просто берет модель и возвращает виджет трепета. Вы также можете подписаться на изменения в свойствах, чтобы ваш виджет был должным образом обновлен, когда что -то происходит на модели. Для этой цели используйте поток, определенный на каждом свойстве, или используйте propertyChanged свойства компонента, которое возвращает поток и издает значение, когда какое -либо свойство меняется. Чтобы переопределить пользовательский интерфейс компонентов по умолчанию внутри flutter_dynamic_forms_components просто определите ваши рендереры для существующих моделей. Вы даже можете иметь несколько рендеристов и показать другой пользовательский интерфейс на другом экране. Используйте параметр 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.