Getting Started
Create your first form in 5 minutes
Getting Started with Vyuh Forms
This guide will walk you through creating your first form with Vyuh Forms. We'll build a simple contact form with validation and submission handling.
Prerequisites
Before starting, ensure you have:
- A Flutter project (Flutter 3.8+)
- Basic understanding of Flutter and Dart
- The
vyuh_feature_formspackage added to your dependencies
Installation
Add vyuh_feature_forms to your pubspec.yaml:
dependencies:
vyuh_feature_forms: ^latest_versionRun:
flutter pub getStep 1: Create a Basic Form
Let's create a simple contact form with name, email, and message fields:
import 'package:flutter/material.dart';
import 'package:vyuh_feature_forms/vyuh_feature_forms.dart';
class ContactFormScreen extends StatefulWidget {
@override
State<ContactFormScreen> createState() => _ContactFormScreenState();
}
class _ContactFormScreenState extends State<ContactFormScreen> {
late final StepForm form;
@override
void initState() {
super.initState();
form = singleFormAsStep(
title: 'Contact Us',
form: FormBuilder(
title: 'Send us a message',
fields: [
TextField(
name: 'name',
label: 'Your Name',
hint: 'Enter your full name',
validators: [required()],
),
TextField(
name: 'email',
label: 'Email Address',
hint: 'your@email.com',
validators: [required(), email()],
),
TextField(
name: 'message',
label: 'Message',
hint: 'Tell us how we can help...',
multiline: true,
maxLines: 5,
validators: [required(), minLength(10)],
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Contact Us')),
body: FormRenderer(
form: form,
onSubmit: _handleSubmit,
),
);
}
Future<void> _handleSubmit(Map<String, dynamic> data) async {
// Simulate API call
await Future.delayed(Duration(seconds: 2));
print('Form submitted with data: $data');
// Show success message
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Message sent successfully!')),
);
}
}
}Step 2: Add Validation
Forms automatically validate when the user submits. You can also validate on change:
TextField(
name: 'email',
label: 'Email',
validators: [
required(message: 'Email is required'),
email(message: 'Please enter a valid email'),
],
validateOnChange: true, // Validate as user types
)Built-in Validators
// Required field
required(message: 'This field is required')
// Email validation
email(message: 'Please enter a valid email')
// Minimum length
minLength(10, message: 'Minimum 10 characters required')
// Maximum length
maxLength(100, message: 'Maximum 100 characters allowed')
// Pattern matching
pattern(
r'^[0-9]{3}-[0-9]{3}-[0-9]{4}$',
message: 'Format: 123-456-7890'
)
// Min/max value (for numbers)
min(0, message: 'Value must be at least 0')
max(100, message: 'Value cannot exceed 100')
// URL validation
url(message: 'Please enter a valid URL')Step 3: Handle Form State
Access form state using the FormStore:
class _ContactFormScreenState extends State<ContactFormScreen> {
late final FormStore formStore;
@override
void initState() {
super.initState();
formStore = FormStore(form);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Observer(
builder: (_) => FormRenderer(
form: form,
store: formStore,
onSubmit: _handleSubmit,
),
),
floatingActionButton: Observer(
builder: (_) => FloatingActionButton.extended(
onPressed: formStore.isValid ? () => formStore.submit() : null,
label: Text('Submit'),
icon: Icon(Icons.send),
),
),
);
}
}Step 4: Add More Field Types
Expand your form with different field types:
FormBuilder(
title: 'User Profile',
fields: [
// Text Input
TextField(
name: 'username',
label: 'Username',
validators: [required()],
),
// Number Input
NumberField(
name: 'age',
label: 'Age',
validators: [required(), min(18), max(120)],
),
// Date Picker
DateField(
name: 'birthdate',
label: 'Birth Date',
validators: [required()],
),
// Dropdown
SelectField(
name: 'country',
label: 'Country',
options: [
Option(value: 'us', label: 'United States'),
Option(value: 'uk', label: 'United Kingdom'),
Option(value: 'ca', label: 'Canada'),
],
validators: [required()],
),
// Checkbox
BooleanField(
name: 'terms',
label: 'I accept the terms and conditions',
validators: [
custom((value) {
if (value != true) {
return 'You must accept the terms';
}
return null;
}),
],
),
// Radio Group
RadioField(
name: 'subscription',
label: 'Subscription Plan',
options: [
Option(value: 'free', label: 'Free'),
Option(value: 'pro', label: 'Pro ($9.99/mo)'),
Option(value: 'enterprise', label: 'Enterprise (Contact us)'),
],
validators: [required()],
),
],
)Step 5: Prefill Form Data
Initialize forms with existing data:
FormRenderer(
form: form,
initialData: {
'name': 'John Doe',
'email': 'john@example.com',
'country': 'us',
},
onSubmit: _handleSubmit,
)Step 6: Handle Errors
Display errors and handle submission failures:
Future<void> _handleSubmit(Map<String, dynamic> data) async {
try {
await api.submitContactForm(data);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Message sent successfully!'),
backgroundColor: Colors.green,
),
);
Navigator.of(context).pop();
}
} catch (error) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${error.toString()}'),
backgroundColor: Colors.red,
),
);
}
}
}Complete Example
Here's the complete contact form:
import 'package:flutter/material.dart';
import 'package:vyuh_feature_forms/vyuh_feature_forms.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class ContactFormScreen extends StatefulWidget {
@override
State<ContactFormScreen> createState() => _ContactFormScreenState();
}
class _ContactFormScreenState extends State<ContactFormScreen> {
late final StepForm form;
late final FormStore formStore;
@override
void initState() {
super.initState();
form = singleFormAsStep(
title: 'Contact Us',
form: FormBuilder(
title: 'Send us a message',
description: 'We\'ll get back to you within 24 hours',
fields: [
TextField(
name: 'name',
label: 'Your Name',
hint: 'Enter your full name',
validators: [required()],
),
TextField(
name: 'email',
label: 'Email Address',
hint: 'your@email.com',
validators: [required(), email()],
),
SelectField(
name: 'topic',
label: 'Topic',
options: [
Option(value: 'support', label: 'Technical Support'),
Option(value: 'sales', label: 'Sales Inquiry'),
Option(value: 'feedback', label: 'Feedback'),
Option(value: 'other', label: 'Other'),
],
validators: [required()],
),
TextField(
name: 'message',
label: 'Message',
hint: 'Tell us how we can help...',
multiline: true,
maxLines: 5,
validators: [required(), minLength(10)],
),
],
),
);
formStore = FormStore(form);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Contact Us'),
centerTitle: true,
),
body: Observer(
builder: (_) => FormRenderer(
form: form,
store: formStore,
onSubmit: _handleSubmit,
),
),
);
}
Future<void> _handleSubmit(Map<String, dynamic> data) async {
try {
// Simulate API call
await Future.delayed(Duration(seconds: 2));
print('Contact form submitted: $data');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Message sent successfully!'),
backgroundColor: Colors.green,
),
);
Navigator.of(context).pop();
}
} catch (error) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${error.toString()}'),
backgroundColor: Colors.red,
),
);
}
}
}
@override
void dispose() {
formStore.dispose();
super.dispose();
}
}What's Next?
Now that you've created your first form, explore more advanced features:
- Field Types - Explore all available field types
- Multi-Step Forms - Create wizard-style forms
- Form Editor - Build forms visually
- Advanced Features - Formula fields, conditional logic
- Examples - See real-world implementations