Forms
- Introduction
- Quick Start
- Creating Forms
- Field Types
- Form Validation
- Form Casts
- Managing Form Data
- Form Styling
- Advanced Features
- Pre-built Components
- API Reference for NyForm
Introduction
Nylo's form system provides:
- Easy form creation and management
- Built-in validation
- Field type casting
- Form state management
- Styling customization
- Data handling utilities
Creating a form
First, run the below metro command from your terminal.
dart run nylo_framework:main make:form LoginForm
# or with Metro alias
metro make:form LoginForm
This will create a new form class lib/app/forms/login_form.dart
E.g. The newly created LoginForm
import 'package:nylo_framework/nylo_framework.dart';
class LoginForm extends NyFormData {
LoginForm({String? name}) : super(name ?? "login");
// Add your fields here
@override
fields() => [
Field.email("Email",
validator: FormValidator.email()
),
Field.password("Password",
validator: FormValidator.password()
),
];
}
Displaying a form
To display a form, you can use the NyForm
widget.
import 'package:nylo_framework/nylo_framework.dart';
import '/app/forms/login_form.dart';
import '/resources/widgets/buttons/buttons.dart';
...
// Create your form
LoginForm form = LoginForm();
// add the form using the NyForm widget
@override
Widget view(BuildContext context) {
return Scaffold(
body: SafeArea(
child: NyForm(
form: form,
footer: Button.primary(child: "Submit", submitForm: (form, (data) {
printInfo(data);
}),
),
),
)
);
}
This is all you need to create and display a form in Nylo.
The UI of this page will now contain two fields, Email
and Password
, and a submit button.
Using the Button
widget to submit the form
Out the box, Nylo provides 8 pre-built buttons that you can use to submit a form.
Each button has a different style and color.
-
Button.primary
-
Button.secondary
-
Button.outlined
-
Button.textOnly
-
Button.icon
-
Button.gradient
-
Button.rounded
-
Button.transparency
They all have the ability to submit a form using the submitForm
parameter.
Button.primary(text: "Submit", submitForm: (form, (data) {
printInfo(data);
}));
Submitting the form via a different widget
To submit a form, you can call the submit
method on a form.
LoginForm form = LoginForm();
@override
Widget view(BuildContext context) {
return Scaffold(
body: SafeArea(
child: NyForm(
form: form,
footer: MaterialButton(
onPressed: () {
form.submit(onSuccess: (data) {
// Do something with the data
});
},
child: Text("Submit"),
)),
)
);
}
When you call form.submit()
, Nylo will validate the form and if the form is valid, it will call the onSuccess
callback with the form data.
That's a quick overview of how to create, display and submit a form in Nylo.
This is just scratching the surface, you can customize your forms even further by adding casts, validation rules, dummy data and global styles.
Creating Forms
Using the Metro CLI
The easiest way to create a new form is using the Metro CLI:
metro make:form LoginForm
# or
dart run nylo_framework:main make:form LoginForm
This creates a new form class in lib/app/forms/login_form.dart
.
Form Structure
Forms in Nylo extend the NyFormData
class:
class ProductForm extends NyFormData {
ProductForm({String? name}) : super(name ?? "product");
@override
fields() => [
// Define form fields here
Field.text("Name"),
Field.number("Price"),
Field.textarea("Description")
];
}
Field Types
Nylo provides multiple ways to define fields, with the recommended approach using static methods for cleaner syntax:
Text Fields
// Recommended approach
Field.text("Name"),
Field.textarea("Description"),
Field.email("Email"),
Field.capitalizeWords("Title"),
Field.url("Website"),
// Alternative approach using constructor with casts
Field("Name", cast: FormCast.text()),
Field("Description", cast: FormCast.textArea())
Numeric Fields
// Recommended approach
Field.number("Age"),
Field.currency("Price", currency: "usd"),
Field.decimal("Score"),
// Alternative approach
Field("Age", cast: FormCast.number()),
Field("Price", cast: FormCast.currency("usd"))
Selection Fields
// Recommended approach
Field.picker("Category", options: ["Electronics", "Clothing", "Books"]),
Field.chips("Tags", options: ["Featured", "Sale", "New"]),
// Alternative approach
Field("Category",
cast: FormCast.picker(
options: ["Electronics", "Clothing", "Books"]
)
)
Boolean Fields
// Recommended approach
Field.checkbox("Accept Terms"),
Field.switchBox("Enable Notifications"),
// Alternative approach
Field("Accept Terms", cast: FormCast.checkbox()),
Field("Enable Notifications", cast: FormCast.switchBox())
Date and Time Fields
// Recommended approach
Field.date("Birth Date",
firstDate: DateTime(1900),
lastDate: DateTime.now()
),
Field.datetime("Appointment"),
// Alternative approach
Field("Birth Date",
cast: FormCast.date(
firstDate: DateTime(1900),
lastDate: DateTime.now()
)
)
Password Fields
// Recommended approach
Field.password("Password", viewable: true)
// Alternative approach
Field("Password", cast: FormCast.password(viewable: true))
Masked Input Fields
// Recommended approach
Field.mask("Phone", mask: "(###) ###-####"),
Field.mask("Credit Card", mask: "#### #### #### ####")
// Alternative approach
Field("Phone",
cast: FormCast.mask(mask: "(###) ###-####")
)
Form Validation
Nylo provides extensive validation capabilities:
Basic Validation
Field.text("Username",
validate: FormValidator()
.notEmpty()
.minLength(3)
.maxLength(20)
)
Combined Validation
Field.password("Password",
validate: FormValidator()
.notEmpty()
.minLength(8)
.password(strength: 2)
)
Custom Validation
Field.number("Age",
validate: FormValidator.custom(
(value) {
if (value < 18) return false;
if (value > 100) return false;
return true;
},
message: "Age must be between 18 and 100"
)
)
Validation Examples
// Email validation
Field.email("Email",
validate: FormValidator.email(
message: "Please enter a valid email address"
)
)
// Password validation with strength levels
Field.password("Password",
validate: FormValidator.password(strength: 2)
)
// Length validation
Field.text("Username",
validate: FormValidator()
.minLength(3)
.maxLength(20)
)
// Phone number validation
Field.phone("Phone",
validate: FormValidator.phoneNumberUs() // US format
// or
validate: FormValidator.phoneNumberUk() // UK format
)
// URL validation
Field.url("Website",
validate: FormValidator.url()
)
// Contains validation
Field.picker("Category",
validate: FormValidator.contains(["Tech", "Health", "Sports"])
)
// Numeric validation
Field.number("Age",
validate: FormValidator()
.numeric()
.minValue(18)
.maxValue(100)
)
// Date validation
Field.date("EventDate",
validate: FormValidator()
.date()
.dateInFuture()
)
// Boolean validation
Field.checkbox("Terms",
validate: FormValidator.isTrue()
)
// Multiple validators
Field.text("Username",
validate: FormValidator()
.notEmpty()
.minLength(3)
.maxLength(20)
.regex(r'^[a-zA-Z0-9_]+$')
)
Available Validators
Validator | Description |
---|---|
notEmpty() |
Ensures field is not empty |
email() |
Validates email format |
minLength(n) |
Minimum length check |
maxLength(n) |
Maximum length check |
numeric() |
Numbers only |
regex(pattern) |
Custom regex pattern |
contains(list) |
Must contain value from list |
dateInPast() |
Date must be in past |
dateInFuture() |
Date must be in future |
password(strength: 1|2) |
Password strength validation |
phoneNumberUs() |
US phone format |
phoneNumberUk() |
UK phone format |
url() |
Valid URL format |
zipcodeUs() |
US zipcode format |
postcodeUk() |
UK postcode format |
isTrue() |
Must be true |
isFalse() |
Must be false |
dateAgeIsYounger(age) |
Age younger than specified |
dateAgeIsOlder(age) |
Age older than specified |
Form Casts
Casts transform field input into specific formats:
Available Casts
Cast | Description | Example |
---|---|---|
FormCast.email() |
Email input | user@example.com |
FormCast.number() |
Numeric input | 42 |
FormCast.currency("usd") |
Currency formatting | $42.99 |
FormCast.capitalizeWords() |
Title case | Hello World |
FormCast.date() |
Date picker | 2024-01-15 |
FormCast.mask() |
Custom input mask | (123) 456-7890 |
FormCast.picker() |
Selection list | - |
FormCast.chips() |
Multi-select chips | - |
FormCast.checkbox() |
Boolean checkbox | - |
FormCast.switchBox() |
Boolean switch | - |
FormCast.textarea() |
Multi-line text | - |
FormCast.password() |
Password input | - |
Custom Casts
Create custom casts in config/form_casts.dart
:
final Map<String, dynamic> formCasts = {
"phone": (Field field, Function(dynamic value)? onChanged) {
return CustomPhoneField(
field: field,
onChanged: onChanged
);
}
};
Managing Form Data
Setting Initial Data
NyForm(
form: form,
loadData: () async {
// final userData = api<ApiService>
;
return {
"name": "John Doe",
"email": "john@example.com"
};
}
)
// or
NyForm(
form: form,
initialData: {
"name": "John Doe",
"email": "john@example.com"
}
)
Updating Data
// Update single field
form.setField("name", "Jane Doe");
// Update multiple fields
form.setData({
"name": "Jane Doe",
"email": "jane@example.com"
});
Clearing Data
// Clear everything
form.clear();
// Clear specific field
form.clearField("name");
Form Styling
Global Styles
Define global styles in app/forms/style/form_style.dart
:
class FormStyle extends NyFormStyle {
@override
FormStyleTextField textField(BuildContext context, Field field) {
return {
'default': (NyTextField textField) => textField.copyWith(
decoration: InputDecoration(
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.grey[100],
),
),
'compact': (NyTextField textField) => textField.copyWith(
decoration: InputDecoration(
border: UnderlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 8,
vertical: 4
),
),
),
};
}
}
Field-Level Styling
Using Style Extension
Field.email("Email",
style: "compact".extend(
labelText: "Email Address",
prefixIcon: Icon(Icons.email),
backgroundColor: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
// Custom decoration states
decoration: (data, inputDecoration) {
return inputDecoration.copyWith(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Colors.blue)
)
);
},
successDecoration: (data, inputDecoration) {
return inputDecoration.copyWith(
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.green)
)
);
},
errorDecoration: (data, inputDecoration) {
return inputDecoration.copyWith(
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red)
)
);
}
)
)
Direct Styling
Field.text("Name",
style: (NyTextField textField) => textField.copyWith(
decoration: InputDecoration(
prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
),
),
)
Advanced Features
Form Layout
fields() => [
// Single field
Field.text("Title"),
// Grouped fields in row
[
Field.text("First Name"),
Field.text("Last Name"),
],
// Another single field
Field.textarea("Bio")
];
Conditional Fields
Field.checkbox("Has Pets",
onChange: (value) {
if (value == true) {
form.showField("Pet Names");
} else {
form.hideField("Pet Names");
}
}
)
Form Events
NyForm(
form: form,
onChanged: (field, data) {
print("$field changed: $data");
},
validateOnFocusChange: true
)
Pre-built Components
Login Form
NyLoginForm loginForm = Forms.login(
emailValidationMessage: "Please enter a valid email",
passwordValidationMessage: "Password is required",
style: "compact"
);
API Reference for NyForm
A widget that manages form state, validation, and submission in Nylo applications.
Constructor
NyForm({
Key? key,
required NyFormData form,
double crossAxisSpacing = 10,
double mainAxisSpacing = 10,
Map<String, dynamic>? initialData,
Function(String field, Map<String, dynamic> data)? onChanged,
bool validateOnFocusChange = false,
Widget? header,
Widget? footer,
Widget? loading,
bool locked = false,
})
Parameters
Required Parameters
Parameter | Type | Description |
---|---|---|
form |
NyFormData |
The form to display and manage. Contains field definitions and validation rules. |
Optional Parameters
Parameter | Type | Default | Description |
---|---|---|---|
key |
Key? |
null |
Controls how one widget replaces another widget in the tree. |
crossAxisSpacing |
double |
10 |
Spacing between fields in the cross axis direction. |
mainAxisSpacing |
double |
10 |
Spacing between fields in the main axis direction. |
initialData |
Map<String, dynamic>? |
null |
Initial values for form fields. Keys should match field names. |
onChanged |
Function(String, Map<String, dynamic>)? |
null |
Callback when any field value changes. Provides field name and complete form data. |
validateOnFocusChange |
bool |
false |
Whether to validate fields when focus changes. |
header |
Widget? |
null |
Widget to display above the form fields. |
footer |
Widget? |
null |
Widget to display below the form fields. |
loading |
Widget? |
null |
Widget to display while the form is loading. Defaults to a skeleton loader if not provided. |
locked |
bool |
false |
When true, makes the form read-only and prevents user input. |
Example Usage
NyForm(
form: LoginForm(),
initialData: {
"email": "user@example.com",
"password": ""
},
header: Text("Login"),
footer: SubmitButton(),
onChanged: (field, data) {
print("Field $field changed. New data: $data");
},
validateOnFocusChange: true,
crossAxisSpacing: 16,
mainAxisSpacing: 20,
)
Notes
- The form parameter automatically initializes with the provided
initialData
if any. - The
loading
widget is only shown when the form is in a loading state. - The
onChanged
callback provides both the changed field name and the complete form data. - When
locked
is true, the form becomes non-interactive but still displays values. -
header
andfooter
widgets are optional and will only be displayed if provided.