Formulaires
Introduction
Nylo Website v7 fournit un systeme de formulaires construit autour de NyFormWidget. Votre classe de formulaire etend NyFormWidget et est le widget -- aucun wrapper separe n'est necessaire. Les formulaires prennent en charge la validation integree, de nombreux types de champs, le style et la gestion des donnees.
import 'package:nylo_framework/nylo_framework.dart';
// 1. Define a form
class LoginForm extends NyFormWidget {
LoginForm({super.key, super.submitButton, super.onSubmit, super.onFailure});
@override
fields() => [
Field.email("Email", validator: FormValidator.email()),
Field.password("Password", validator: FormValidator.password()),
];
static NyFormActions get actions => const NyFormActions('LoginForm');
}
// 2. Display and submit it
LoginForm(
submitButton: Button.primary(text: "Login"),
onSubmit: (data) {
print(data); // {email: "...", password: "..."}
},
)
Creer un formulaire
Utilisez le Metro CLI pour creer un nouveau formulaire :
metro make:form LoginForm
Cela cree lib/app/forms/login_form.dart :
import 'package:nylo_framework/nylo_framework.dart';
class LoginForm extends NyFormWidget {
LoginForm({super.key, super.submitButton, super.onSubmit, super.onFailure});
@override
fields() => [
Field.email("Email", validator: FormValidator.email()),
Field.password("Password", validator: FormValidator.password()),
];
static NyFormActions get actions => const NyFormActions('LoginForm');
}
Les formulaires etendent NyFormWidget et surchargent la methode fields() pour definir les champs du formulaire. Chaque champ utilise un constructeur nomme comme Field.text(), Field.email() ou Field.password(). Le getter static NyFormActions get actions offre un moyen pratique d'interagir avec le formulaire depuis n'importe ou dans votre application.
Afficher un formulaire
Puisque votre classe de formulaire etend NyFormWidget, elle est le widget. Utilisez-le directement dans votre arbre de widgets :
@override
Widget view(BuildContext context) {
return Scaffold(
body: SafeArea(
child: LoginForm(
submitButton: Button.primary(text: "Submit"),
onSubmit: (data) {
print(data);
},
),
),
);
}
Soumettre un formulaire
Il existe trois facons de soumettre un formulaire :
Utiliser onSubmit et submitButton
Passez onSubmit et un submitButton lors de la construction du formulaire. Nylo Website fournit des boutons preconstruits qui fonctionnent comme boutons de soumission :
LoginForm(
submitButton: Button.primary(text: "Submit"),
onSubmit: (data) {
print(data); // {email: "...", password: "..."}
},
onFailure: (errors) {
print(errors.first.rule.getMessage());
},
)
Styles de boutons disponibles : Button.primary, Button.secondary, Button.outlined, Button.textOnly, Button.icon, Button.gradient, Button.rounded, Button.transparency.
Utiliser NyFormActions
Utilisez le getter actions pour soumettre depuis n'importe ou :
LoginForm.actions.submit(
onSuccess: (data) {
print(data);
},
onFailure: (errors) {
print(errors.first.rule.getMessage());
},
showToastError: true,
);
Utiliser la methode statique NyFormWidget.submit()
Soumettez un formulaire par son nom depuis n'importe ou :
NyFormWidget.submit("LoginForm",
onSuccess: (data) {
print(data);
},
onFailure: (errors) {
print(errors.first.rule.getMessage());
},
showToastError: true,
);
Lors de la soumission, le formulaire valide tous les champs. Si la validation reussit, onSuccess est appele avec un Map<String, dynamic> des donnees du formulaire (les cles sont des versions snake_case des noms de champs). Si la validation echoue, une erreur toast est affichee par defaut et onFailure est appele si fourni.
Types de champs
Nylo Website v7 fournit 22 types de champs via des constructeurs nommes sur la classe Field. Tous les constructeurs de champs partagent ces parametres communs :
| Parametre | Type | Defaut | Description |
|---|---|---|---|
key |
String |
Requis | L'identifiant du champ (positionnel) |
label |
String? |
null |
Libelle d'affichage personnalise (par defaut la cle en titre) |
value |
dynamic |
null |
Valeur initiale |
validator |
FormValidator? |
null |
Regles de validation |
autofocus |
bool |
false |
Focus automatique au chargement |
dummyData |
String? |
null |
Donnees de test/developpement |
header |
Widget? |
null |
Widget affiche au-dessus du champ |
footer |
Widget? |
null |
Widget affiche en dessous du champ |
titleStyle |
TextStyle? |
null |
Style de texte personnalise pour le libelle |
hidden |
bool |
false |
Masquer le champ |
readOnly |
bool? |
null |
Rendre le champ en lecture seule |
style |
FieldStyle? |
Variable | Configuration de style specifique au champ |
onChanged |
Function(dynamic)? |
null |
Callback de changement de valeur |
Champs texte
Field.text("Name")
Field.text("Name",
value: "John",
validator: FormValidator.notEmpty(),
autofocus: true,
)
Type de style : FieldStyleTextField
Champs numeriques
Field.number("Age")
// Decimal numbers
Field.number("Score", decimal: true)
Le parametre decimal controle si la saisie decimale est autorisee. Type de style : FieldStyleTextField
Champs mot de passe
Field.password("Password")
// With visibility toggle
Field.password("Password", viewable: true)
Le parametre viewable ajoute un bouton afficher/masquer. Type de style : FieldStyleTextField
Champs email
Field.email("Email", validator: FormValidator.email())
Configure automatiquement le type de clavier email et filtre les espaces. Type de style : FieldStyleTextField
Champs URL
Field.url("Website", validator: FormValidator.url())
Configure le type de clavier URL. Type de style : FieldStyleTextField
Champs zone de texte
Field.textArea("Description")
Saisie de texte multiligne. Type de style : FieldStyleTextField
Champs numero de telephone
Field.phoneNumber("Mobile Phone")
Formate automatiquement la saisie du numero de telephone. Type de style : FieldStyleTextField
Majuscule aux mots
Field.capitalizeWords("Full Name")
Met en majuscule la premiere lettre de chaque mot. Type de style : FieldStyleTextField
Majuscule aux phrases
Field.capitalizeSentences("Bio")
Met en majuscule la premiere lettre de chaque phrase. Type de style : FieldStyleTextField
Champs date
Field.date("Birthday")
Field.date("Birthday",
dummyData: "1990-01-01",
style: FieldStyleDateTimePicker(
firstDate: DateTime(1900),
lastDate: DateTime.now(),
),
)
Ouvre un selecteur de date. Type de style : FieldStyleDateTimePicker
Champs date et heure
Field.datetime("Check in Date")
Field.datetime("Appointment", dummyData: "2025-01-01 10:00")
Ouvre un selecteur de date et d'heure. Type de style : FieldStyleDateTimePicker
Champs saisie masquee
Field.mask("Phone", mask: "(###) ###-####")
Field.mask("Credit Card", mask: "#### #### #### ####")
Field.mask("Custom Code",
mask: "AA-####",
match: r'[\w\d]',
maskReturnValue: true, // Returns the formatted value
)
Le caractere # dans le masque est remplace par la saisie de l'utilisateur. Utilisez match pour controler les caracteres autorises. Lorsque maskReturnValue est true, la valeur retournee inclut le formatage du masque.
Champs devise
Field.currency("Price", currency: "usd")
Le parametre currency est requis et determine le format de la devise. Type de style : FieldStyleTextField
Champs case a cocher
Field.checkbox("Accept Terms")
Field.checkbox("Agree to terms",
header: Text("Terms and conditions"),
footer: Text("You must agree to continue."),
validator: FormValidator.booleanTrue(message: "You must accept the terms"),
)
Type de style : FieldStyleCheckbox
Champs interrupteur
Field.switchBox("Enable Notifications")
Type de style : FieldStyleSwitchBox
Champs selecteur
Field.picker("Category",
options: FormCollection.from(["Electronics", "Clothing", "Books"]),
)
// With key-value pairs
Field.picker("Country",
options: FormCollection.fromMap({
"us": "United States",
"ca": "Canada",
"uk": "United Kingdom",
}),
)
Le parametre options necessite un FormCollection (pas une liste brute). Voir FormCollection pour plus de details. Type de style : FieldStylePicker
Champs bouton radio
Field.radio("Newsletter",
options: FormCollection.fromMap({
"Yes": "Yes, I want to receive newsletters",
"No": "No, I do not want to receive newsletters",
}),
)
Le parametre options necessite un FormCollection. Type de style : FieldStyleRadio
Champs chip
Field.chips("Tags",
options: FormCollection.from(["Featured", "Sale", "New"]),
)
// With key-value pairs
Field.chips("Engine Size",
options: FormCollection.fromMap({
"125": "125cc",
"250": "250cc",
"500": "500cc",
}),
)
Permet la selection multiple via des widgets chip. Le parametre options necessite un FormCollection. Type de style : FieldStyleChip
Champs curseur
Field.slider("Rating",
label: "Rate us",
validator: FormValidator.minValue(4, message: "Rating must be at least 4"),
style: FieldStyleSlider(
min: 0,
max: 10,
divisions: 10,
activeColor: Colors.blue,
inactiveColor: Colors.grey,
),
)
Type de style : FieldStyleSlider -- configurez min, max, divisions, les couleurs, l'affichage de la valeur, et plus encore.
Champs curseur de plage
Field.rangeSlider("Price Range",
style: FieldStyleRangeSlider(
min: 0,
max: 1000,
divisions: 20,
activeColor: Colors.blue,
inactiveColor: Colors.grey,
),
)
Retourne un objet RangeValues. Type de style : FieldStyleRangeSlider
Champs personnalises
Utilisez Field.custom() pour fournir votre propre widget avec etat :
Field.custom("My Field",
child: MyCustomFieldWidget(),
)
Le parametre child necessite un widget qui etend NyFieldStatefulWidget. Cela vous donne un controle total sur le rendu et le comportement du champ.
Champs widget
Utilisez Field.widget() pour integrer n'importe quel widget dans le formulaire sans qu'il soit un champ de formulaire :
Field.widget(child: Divider())
Field.widget(child: Text("Section Header", style: TextStyle(fontSize: 18)))
Les champs widget ne participent pas a la validation ni a la collecte de donnees. Ils sont purement destines a la mise en page.
FormCollection
Les champs selecteur, radio et chip necessitent un FormCollection pour leurs options. FormCollection fournit une interface unifiee pour gerer differents formats d'options.
Creer un FormCollection
// From a list of strings (value and label are the same)
FormCollection.from(["Red", "Green", "Blue"])
// Same as above, explicit
FormCollection.fromArray(["Red", "Green", "Blue"])
// From a map (key = value, value = label)
FormCollection.fromMap({
"us": "United States",
"ca": "Canada",
})
// From structured data (useful for API responses)
FormCollection.fromKeyValue([
{"value": "en", "label": "English"},
{"value": "es", "label": "Spanish"},
])
FormCollection.from() detecte automatiquement le format des donnees et delegue au constructeur approprie.
FormOption
Chaque option dans un FormCollection est un FormOption avec les proprietes value et label :
FormOption option = FormOption(value: "us", label: "United States");
print(option.value); // "us"
print(option.label); // "United States"
Interroger les options
FormCollection options = FormCollection.fromMap({"us": "United States", "ca": "Canada"});
options.getByValue("us"); // FormOption(value: us, label: United States)
options.getLabelByValue("us"); // "United States"
options.containsValue("ca"); // true
options.searchByLabel("can"); // [FormOption(value: ca, label: Canada)]
options.values; // ["us", "ca"]
options.labels; // ["United States", "Canada"]
Validation de formulaire
Ajoutez la validation a n'importe quel champ en utilisant le parametre validator avec FormValidator :
// Named constructor
Field.email("Email", validator: FormValidator.email())
// Chained rules
Field.text("Username",
validator: FormValidator()
.notEmpty()
.minLength(3)
.maxLength(20)
)
// Password with strength level
Field.password("Password",
validator: FormValidator.password(strength: 2)
)
// Boolean validation
Field.checkbox("Terms",
validator: FormValidator.booleanTrue(message: "You must accept the terms")
)
// Custom inline validation
Field.number("Age",
validator: FormValidator.custom(
message: "Age must be between 18 and 100",
validate: (data) {
int? age = int.tryParse(data.toString());
return age != null && age >= 18 && age <= 100;
},
)
)
Lors de la soumission d'un formulaire, tous les validateurs sont verifies. Si l'un echoue, une erreur toast affiche le premier message d'erreur et le callback onFailure est appele.
Voir aussi : Pour une liste complete des validateurs disponibles, consultez la page Validation.
Gestion des donnees du formulaire
Donnees initiales
Il existe deux facons de definir les donnees initiales d'un formulaire.
Option 1 : Surcharger le getter init dans votre classe de formulaire
class EditAccountForm extends NyFormWidget {
EditAccountForm({super.key, super.submitButton, super.onSubmit, super.onFailure});
@override
Function()? get init => () async {
final user = await api<ApiService>((request) => request.getUserData());
return {
"First Name": user?.firstName,
"Last Name": user?.lastName,
};
};
@override
fields() => [
Field.text("First Name"),
Field.text("Last Name"),
];
static NyFormActions get actions => const NyFormActions('EditAccountForm');
}
Le getter init peut retourner soit un Map synchrone, soit un Future<Map> asynchrone. Les cles sont associees aux noms de champs en utilisant la normalisation snake_case, donc "First Name" correspond a un champ avec la cle "First Name".
Option 2 : Passer initialData au widget du formulaire
EditAccountForm(
initialData: {
"first_name": "John",
"last_name": "Doe",
},
)
Definir les valeurs des champs
Utilisez NyFormActions pour definir les valeurs des champs depuis n'importe ou :
// Set a single field value
EditAccountForm.actions.updateField("First Name", "Jane");
Definir les options des champs
Mettez a jour les options des champs selecteur, chip ou radio de maniere dynamique :
EditAccountForm.actions.setOptions("Category", FormCollection.from(["New Option 1", "New Option 2"]));
Lire les donnees du formulaire
Les donnees du formulaire sont accessibles via le callback onSubmit lors de la soumission du formulaire, ou via le callback onChanged pour les mises a jour en temps reel :
EditAccountForm(
onSubmit: (data) {
// data is a Map<String, dynamic>
// {first_name: "Jane", last_name: "Doe", email: "jane@example.com"}
print(data);
},
onChanged: (Field field, dynamic value) {
print("${field.key} changed to: $value");
},
)
Effacer les donnees
// Clear all fields
EditAccountForm.actions.clear();
// Clear a specific field
EditAccountForm.actions.clearField("First Name");
Mettre a jour les champs
// Update a field value
EditAccountForm.actions.updateField("First Name", "Jane");
// Refresh the form UI
EditAccountForm.actions.refresh();
// Refresh form fields (re-calls fields())
EditAccountForm.actions.refreshForm();
Bouton de soumission
Passez un submitButton et un callback onSubmit lors de la construction du formulaire :
UserInfoForm(
submitButton: Button.primary(text: "Submit"),
onSubmit: (data) {
print(data);
},
onFailure: (errors) {
print(errors.first.rule.getMessage());
},
)
Le submitButton est automatiquement affiche sous les champs du formulaire. Vous pouvez utiliser n'importe lequel des styles de boutons integres ou un widget personnalise.
Vous pouvez egalement utiliser n'importe quel widget comme bouton de soumission en le passant comme footer :
UserInfoForm(
onSubmit: (data) {
print(data);
},
footer: ElevatedButton(
onPressed: () {
UserInfoForm.actions.submit(
onSuccess: (data) {
print(data);
},
);
},
child: Text("Submit"),
),
)
Disposition du formulaire
Placez les champs cote a cote en les enveloppant dans une List :
@override
fields() => [
// Single field (full width)
Field.text("Title"),
// Two fields in a row
[
Field.text("First Name"),
Field.text("Last Name"),
],
// Another single field
Field.textArea("Bio"),
// Slider and range slider in a row
[
Field.slider("Rating", style: FieldStyleSlider(min: 0, max: 10)),
Field.rangeSlider("Budget", style: FieldStyleRangeSlider(min: 0, max: 1000)),
],
// Embed a non-field widget
Field.widget(child: Divider()),
Field.email("Email"),
];
Les champs dans une List sont affiches dans un Row avec des largeurs Expanded egales. L'espacement entre les champs est controle par le parametre crossAxisSpacing sur NyFormWidget.
Visibilite des champs
Affichez ou masquez les champs par programmation en utilisant les methodes hide() et show() sur Field. Vous pouvez acceder aux champs a l'interieur de votre classe de formulaire ou via le callback onChanged :
// Inside your NyFormWidget subclass or onChanged callback
Field nameField = ...;
// Hide the field
nameField.hide();
// Show the field
nameField.show();
Les champs masques ne sont pas affiches dans l'interface utilisateur mais existent toujours dans la liste des champs du formulaire.
Style des champs
Chaque type de champ possede une sous-classe FieldStyle correspondante pour le style :
| Type de champ | Classe de style |
|---|---|
| Text, Email, Password, Number, URL, TextArea, PhoneNumber, Currency, Mask, CapitalizeWords, CapitalizeSentences | FieldStyleTextField |
| Date, DateTime | FieldStyleDateTimePicker |
| Picker | FieldStylePicker |
| Checkbox | FieldStyleCheckbox |
| Switch Box | FieldStyleSwitchBox |
| Radio | FieldStyleRadio |
| Chip | FieldStyleChip |
| Slider | FieldStyleSlider |
| Range Slider | FieldStyleRangeSlider |
Passez un objet de style au parametre style de n'importe quel champ :
Field.text("Name",
style: FieldStyleTextField(
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
prefixIcon: Icon(Icons.person),
),
)
Field.slider("Rating",
style: FieldStyleSlider(
min: 0,
max: 10,
divisions: 10,
activeColor: Colors.blue,
showValue: true,
),
)
Field.chips("Tags",
options: FormCollection.from(["Sale", "New", "Featured"]),
style: FieldStyleChip(
selectedColor: Colors.blue,
checkmarkColor: Colors.white,
spacing: 8.0,
runSpacing: 8.0,
),
)
Methodes statiques NyFormWidget
NyFormWidget fournit des methodes statiques pour interagir avec les formulaires par nom depuis n'importe ou dans votre application :
| Methode | Description |
|---|---|
NyFormWidget.submit(name, onSuccess:, onFailure:, showToastError:) |
Soumettre un formulaire par son nom |
NyFormWidget.stateRefresh(name) |
Rafraichir l'etat de l'interface du formulaire |
NyFormWidget.stateSetValue(name, key, value) |
Definir la valeur d'un champ par nom de formulaire |
NyFormWidget.stateSetOptions(name, key, options) |
Definir les options d'un champ par nom de formulaire |
NyFormWidget.stateClearData(name) |
Effacer tous les champs par nom de formulaire |
NyFormWidget.stateRefreshForm(name) |
Rafraichir les champs du formulaire (re-appelle fields()) |
// Submit a form named "LoginForm" from anywhere
NyFormWidget.submit("LoginForm", onSuccess: (data) {
print(data);
});
// Update a field value remotely
NyFormWidget.stateSetValue("LoginForm", "Email", "new@email.com");
// Clear all form data
NyFormWidget.stateClearData("LoginForm");
Conseil : Preferez utiliser
NyFormActions(voir ci-dessous) au lieu d'appeler ces methodes statiques directement -- c'est plus concis et moins sujet aux erreurs.
Reference du constructeur NyFormWidget
Lors de l'extension de NyFormWidget, voici les parametres du constructeur que vous pouvez passer :
LoginForm(
Key? key,
double crossAxisSpacing = 10, // Horizontal spacing between row fields
double mainAxisSpacing = 10, // Vertical spacing between fields
Map<String, dynamic>? initialData, // Initial field values
Function(Field field, dynamic value)? onChanged, // Field change callback
Widget? header, // Widget above the form
Widget? submitButton, // Submit button widget
Widget? footer, // Widget below the form
double headerSpacing = 10, // Spacing after header
double submitButtonSpacing = 10, // Spacing after submit button
double footerSpacing = 10, // Spacing before footer
LoadingStyle? loadingStyle, // Loading indicator style
bool locked = false, // Makes form read-only
Function(dynamic data)? onSubmit, // Called with form data on successful validation
Function(dynamic error)? onFailure, // Called with errors on failed validation
)
Le callback onChanged recoit le Field qui a change et sa nouvelle valeur :
LoginForm(
onChanged: (Field field, dynamic value) {
print("${field.key} changed to: $value");
},
)
NyFormActions
NyFormActions fournit un moyen pratique d'interagir avec un formulaire depuis n'importe ou dans votre application. Definissez-le comme un getter statique sur votre classe de formulaire :
class LoginForm extends NyFormWidget {
LoginForm({super.key, super.submitButton, super.onSubmit, super.onFailure});
@override
fields() => [
Field.email("Email", validator: FormValidator.email()),
Field.password("Password", validator: FormValidator.password()),
];
static NyFormActions get actions => const NyFormActions('LoginForm');
}
Actions disponibles
| Methode | Description |
|---|---|
actions.updateField(key, value) |
Definir la valeur d'un champ |
actions.clearField(key) |
Effacer un champ specifique |
actions.clear() |
Effacer tous les champs |
actions.refresh() |
Rafraichir l'etat de l'interface du formulaire |
actions.refreshForm() |
Re-appeler fields() et reconstruire |
actions.setOptions(key, options) |
Definir les options sur les champs selecteur/chip/radio |
actions.submit(onSuccess:, onFailure:, showToastError:) |
Soumettre avec validation |
// Update a field value
LoginForm.actions.updateField("Email", "new@email.com");
// Clear all form data
LoginForm.actions.clear();
// Submit the form
LoginForm.actions.submit(
onSuccess: (data) {
print(data);
},
);
Surcharges NyFormWidget
Methodes que vous pouvez surcharger dans votre sous-classe NyFormWidget :
| Surcharge | Description |
|---|---|
fields() |
Definir les champs du formulaire (requis) |
init |
Fournir les donnees initiales (synchrone ou asynchrone) |
onChange(field, data) |
Gerer les changements de champs en interne |
Reference de tous les types de champs
| Constructeur | Parametres cles | Description |
|---|---|---|
Field.text() |
-- | Saisie de texte standard |
Field.email() |
-- | Saisie email avec type de clavier |
Field.password() |
viewable |
Mot de passe avec bouton de visibilite optionnel |
Field.number() |
decimal |
Saisie numerique, decimale optionnelle |
Field.currency() |
currency (requis) |
Saisie formatee en devise |
Field.capitalizeWords() |
-- | Saisie de texte en casse titre |
Field.capitalizeSentences() |
-- | Saisie de texte en casse phrase |
Field.textArea() |
-- | Saisie de texte multiligne |
Field.phoneNumber() |
-- | Numero de telephone formate automatiquement |
Field.url() |
-- | Saisie URL avec type de clavier |
Field.mask() |
mask (requis), match, maskReturnValue |
Saisie de texte masquee |
Field.date() |
-- | Selecteur de date |
Field.datetime() |
-- | Selecteur de date et heure |
Field.checkbox() |
-- | Case a cocher booleenne |
Field.switchBox() |
-- | Interrupteur a bascule booleen |
Field.picker() |
options (FormCollection requis) |
Selection unique dans une liste |
Field.radio() |
options (FormCollection requis) |
Groupe de boutons radio |
Field.chips() |
options (FormCollection requis) |
Chips a selection multiple |
Field.slider() |
-- | Curseur a valeur unique |
Field.rangeSlider() |
-- | Curseur a plage de valeurs |
Field.custom() |
child (NyFieldStatefulWidget requis) |
Widget avec etat personnalise |
Field.widget() |
child (Widget requis) |
Integrer n'importe quel widget (non-champ) |