Kumpulan perpustakaan flutter dan panah yang menyediakan solusi untuk UI yang digerakkan server di aplikasi Flutter Anda.
| Kemasan | Pub |
|---|---|
| Expression_lyuage | |
| Dynamic_Forms | |
| dynamic_forms_generator | |
| flutter_dynamic_forms | |
| flutter_dynamic_forms_components |
Gagasan di balik proyek ini adalah untuk dapat mendefinisikan komponen Anda melalui XML atau JSON di server dan mengkonsumsinya di klien Flutter tanpa menggunakan kembali aplikasi. Fokus utama adalah pada kemampuan untuk mendefinisikan komponen khusus dan hubungan yang kompleks antara sifat -sifatnya. Misalnya, Anda dapat mendefinisikan aturan validasi khusus, sakelar visibilitas berdasarkan suatu kondisi, dll. Ini membuatnya sangat berguna ketika bekerja dengan formulir mengumpulkan beberapa input pengguna tetapi dapat digunakan untuk menampilkan pohon widget flutter.
Juga lihat contoh proyek yang berisi demo kerja.
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 >Jika Anda lebih suka JSON untuk menjelaskan formulir Anda, silakan periksa contoh JSON ini.

Tambahkan dependensi berikut ke file pubspec.yaml Anda:
flutter_dynamic_forms : <latest version>
flutter_dynamic_forms_components : <latest version> Perpustakaan flutter_dynamic_forms_components berisi set komponen yang telah ditentukan seperti Label , CheckBox , RadioButtonGroup dll. Untuk membuat aplikasi Anda berfungsi dengan komponen yang Anda butuhkan untuk melakukan langkah -langkah berikut:
Pertama, Anda perlu memutuskan apakah Anda ingin mendapatkan data formulir dari XML atau JSON. Anda dapat menggunakan JsonFormManager atau XmlFormManager . Kelas -kelas itu akan mengurus mem -parsing formulir Anda. Mereka juga memiliki form pengambil yang merupakan representasi objek dari formulir XML/JSON Anda di DART. Mereka juga dapat melakukan beberapa operasi yang berguna pada formulir, seperti memanipulasi keadaan formulir ketika sesuatu terjadi di UI, memvalidasi formulir, atau mengumpulkan semua data dari formulir sehingga dapat dikirim kembali ke server. Jika Anda perlu menulis logika khusus ke dalam formManager, Anda dapat dengan mudah memperpanjangnya:
class CustomFormManager extends JsonFormManager {
Future < void > sendDataToServer () async {
var properties = getFormProperties ();
// send properties to the server
}
} Cara termudah untuk menginisialisasi FormManager Anda adalah melalui widget ParsedFormProvider . Ini akan mengambil konten XML/JSON Anda, daftar parser dan itu akan membuat instance FormManager dan juga menangani mem -parsing data Anda. ParsedFormProvider menggunakan paket Provider di bawah kap, sehingga FormManager akan tersedia di subtree widget Anda dengan memanggil FormProvider.of<YourFormProvider>(context) .
Untuk membuat formulir Anda di layar, Anda dapat menggunakan Widget 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
) Anda dapat memberikan FormManager Anda sebagai tipe: FormRenderer<XmlFormManager>(...) dan itu akan secara otomatis diselesaikan dari FormProvider yang ditentukan sebelumnya atau Anda dapat meneruskan instance FormManager tertentu ke dalam konstruktor FormRenderer .
Widget ini juga mengambil daftar renderer yang mengontrol bagaimana masing -masing model akan diterjemahkan ke widget Flutter. Dalam contoh di atas, kami menggunakan satu set penyaji yang telah ditentukan. Kata reaktif berarti bahwa setiap komponen akan mendengarkan perubahan dalam properti model formulir dan akan memperbarui dirinya sendiri.
Parameter opsional terakhir adalah dispatcher . Ini memungkinkan Anda untuk menangani acara yang berasal dari FormElementEvent yang diproduksi di kelas render Anda. Ketika parameter dispatcher tidak disediakan, hanya peristiwa tipe ChangeValueEvent yang diproses dan didelegasikan langsung ke instance FormManager yang menyebabkan perubahan nilai properti. Gunakan penangan dispatcher Anda sendiri jika Anda perlu mengirim acara khusus (seperti klik tombol), tetapi Anda harus selalu membiarkan Form Manager menangani 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
}Gagasan di balik proses mengirim data kembali ke server adalah bahwa kita tidak boleh mengirim kembali seluruh formulir tetapi hanya nilai yang diubah oleh pengguna.
Untuk mengumpulkan data, hubungi:
List < FormPropertyValue > data = formManager. getFormData ()Ini berisi daftar semua properti yang ditandai sebagai dapat berubah dalam definisi parser komponen. Dalam komponen default, itu adalah properti yang diharapkan akan diubah oleh pengguna. Setiap item berisi ID dari elemen sumber, nama properti, dan nilai properti. Untuk mengirimkan formulir yang biasanya Anda ingin membuat serial daftar ini dan mengirimkannya kembali ke server Anda.
Paket flutter_dynamic_forms_components hanya berisi satu set komponen dasar yang terkait dengan aplikasi formulir sederhana. Karena beberapa persyaratan pada aplikasi lama saya, tidak semua komponen penamaan secara langsung sesuai dengan widget flutter. Saya pikir itu mungkin berubah di masa depan. Juga saat merancang komponen, Anda selalu dapat memilih antara komponen tingkat rendah seperti Label atau komponen tingkat tinggi seperti UserProfile . Dalam hal komponen tingkat tinggi yang kompleks ini, Anda membiarkan aplikasi klien sepenuhnya mengontrol tampilan widget akhir. Untuk alasan itu, saya akan merekomendasikan untuk setiap aplikasi untuk menulis set komponen khusus untuk memiliki kontrol penuh atas setiap properti.
Untuk mengimplementasikan komponen khusus, Anda perlu menyediakan 3 kelas: Parser , Model , dan Renderer . Parser dan model kemudian perlu terdaftar saat Anda membangun formulir seperti yang Anda lihat dalam kode di atas. Mari kita tunjukkan pada contoh CheckBox :
Kelas ini mengontrol bagaimana komponen akan deserialized menjadi kelas model yang sesuai. Ini bekerja pada XML dan JSON. Parameter ParserNode berisi kumpulan metode yang memungkinkan Anda menguraikan nilai dari node XML/JSON saat ini. Gunakan parameter ElementParserFunction parser dari metode parse untuk mem -parsing node anak -anak secara rekursif.
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 ,
);
}
} Model adalah definisi komponen utama tanpa ketergantungan flutter. Komponen dapat memperluas komponen lain yang mewarisi semua properti. Ini juga dapat berisi komponen sebagai anak -anaknya. Setiap properti dapat berisi nilai atau ekspresi sederhana yang dievaluasi dengan nilai. Untuk dapat mencakup kedua kasus tersebut, semua properti harus didefinisikan menggunakan sintaks Property<T> . Properti disimpan dalam satu peta yang disebut properties sehingga Anda dapat dengan mudah melintasi seluruh pohon komponen. Merupakan ide yang bagus untuk membuat getter dan setter di sekitar peta ini sehingga Anda dapat dengan mudah mengakses dan mengatur nilai properti.
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 ();
}
} Kelas ini hanya mengambil model dan mengembalikan widget flutter. Anda juga dapat berlangganan perubahan properti sehingga widget Anda akan diperbarui dengan benar ketika sesuatu terjadi pada model. Untuk tujuan ini, gunakan aliran yang ditentukan pada setiap properti atau gunakan propertyChanged yang mengembalikan aliran dan memancarkan nilai setiap kali properti berubah. Untuk mendefinisikan ulang UI dari komponen default di dalam flutter_dynamic_forms_components cukup menentukan renderer Anda untuk model yang ada. Anda bahkan dapat memiliki banyak renderer dan menampilkan UI yang berbeda di layar yang berbeda. Gunakan parameter FormElementRendererFunction renderer dari metode render untuk secara rekursif membuat anak -anak.
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);
},
),
)
],
),
);
}
} Ada banyak boilerplate saat menerapkan kelas Parser dan Model . Karena sebagian besar aplikasi mungkin perlu membuat banyak komponen khusus, ada juga paket generator yang memungkinkan Anda mendefinisikan komponen dan propertinya menggunakan sintaks YAML sederhana.