app_forms 0.7.0
app_forms: ^0.7.0 copied to clipboard
App Forms Separate the logic from the user interface by use flutter_form_builder
App Forms #
A powerful Flutter package that separates form logic from UI components using flutter_form_builder. Build maintainable, testable, and reusable forms with clean architecture principles.
โจ Features #
- ๐๏ธ Clean Architecture: Separate form logic from UI components
- ๐ Dependency Injection: Global form management with singleton pattern
- ๐ Reactive State Management: Real-time form state updates and validation
- โก Auto-validation: Configurable automatic field validation with debouncing
- ๐ฏ Type Safety: Generic type support for form fields
- ๐ Field Management: Advanced field operations (setValue, reset, validation)
- ๐ Form State Tracking: Built-in loading, progress, error, and success states
- ๐ญ Lifecycle Hooks: onInit, onSubmit, onValid, and onReset callbacks
- ๐ Performance Optimized: Debounced field changes (250ms) for optimal performance
- ๐ฆ Easy Integration: Works seamlessly with flutter_form_builder
๐ Quick Start #
Installation #
Add the package to your pubspec.yaml:
dependencies:
app_forms: ^0.6.0
flutter_form_builder: ^10.1.0
form_builder_validators: ^11.2.0
Basic Usage #
1. Create Your Form Class
import 'package:app_forms/app_forms.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
class LoginForm extends AppForm {
// Configure auto-validation
@override
bool get autoValidate => true;
// Define form fields with validation and callbacks
final email = AppFormField<String>(
name: 'email',
initialValue: '[email protected]',
validations: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.email(),
]),
onChange: (field) {
print('Email changed: ${field?.value}');
},
onValid: (field) {
print('Email is valid: ${field?.value}');
},
);
final password = AppFormField<String>(
name: 'password',
validations: FormBuilderValidators.compose([
FormBuilderValidators.required(),
FormBuilderValidators.minLength(6),
]),
);
LoginForm() {
setFields([email, password]);
}
@override
Future<void> onInit() async {
// Initialize form data, API calls, etc.
print('Form initialized');
}
@override
Future<void> onSubmit(Map<String, dynamic>? values) async {
// Handle form submission
print('Submitting: $values');
try {
// Simulate API call
await Future.delayed(const Duration(seconds: 2));
setSuccess(true);
} catch (e) {
setHasErrors(true);
}
}
@override
Future<void> onValid(Map<String, dynamic>? values) async {
// Called when form becomes valid
print('Form is valid: $values');
}
@override
void onReset() {
// Handle form reset
print('Form reset');
}
}
2. Inject Forms in main.dart
import 'package:app_forms/app_forms.dart';
void main() {
// Inject your forms globally
AppForms.injectForms([
LoginForm(),
// Add other forms here
]);
runApp(MyApp());
}
3. Build Your UI
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// Form Builder - handles form state and validation
AppFormBuilder<LoginForm>(
builder: (form) {
return Column(
children: [
FormBuilderTextField(
name: form.email.name,
validator: form.email.validations,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
FormBuilderTextField(
name: form.password.name,
validator: form.password.validations,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
),
],
);
},
),
const SizedBox(height: 24),
// Form Listener - reacts to form state changes
AppFormListener<LoginForm>(
builder: (form) {
return Column(
children: [
ElevatedButton(
onPressed: form.progressing ? null : form.submit,
child: form.progressing
? const CircularProgressIndicator()
: const Text('Login'),
),
if (form.hasErrors)
const Text(
'Please fix errors above',
style: TextStyle(color: Colors.red),
),
if (form.success)
const Text(
'Login successful!',
style: TextStyle(color: Colors.green),
),
],
);
},
),
],
),
),
);
}
}
๐ Advanced Features #
Form State Management #
class MyForm extends AppForm {
void customAction() {
// Control form states
setLoading(true); // Show loading state
setHasErrors(false); // Clear errors
setSuccess(false); // Clear success state
// Access form values
final values = getValues();
// Validate programmatically
final isValid = saveAndValidate();
// Reset form
reset();
}
}
Dynamic Field Updates #
class MyForm extends AppForm {
void updateFieldValue() {
// Update field value programmatically
email.value = '[email protected]';
updateFieldsValue(); // Sync with form builder
}
void setServerErrors(Map<String, dynamic> errors) {
// Set validation errors from server
setValidationErrors({
'email': 'Email already exists',
'password': 'Password too weak'
});
}
}
Custom Validation #
final customField = AppFormField<String>(
name: 'username',
validations: (value) {
if (value == null || value.isEmpty) {
return 'Username is required';
}
if (value.length < 3) {
return 'Username must be at least 3 characters';
}
return null; // Valid
},
);
๐๏ธ Architecture #
Core Components #
- AppForm: Abstract base class for all forms
- AppFormField: Type-safe field configuration
- AppForms: Singleton service for dependency injection
- AppFormBuilder: Widget wrapper connecting forms to UI
- AppFormListener: Reactive widget for form state changes
Flow Diagram #
Form Definition โ Dependency Injection โ UI Binding โ State Management
โ โ โ โ
AppForm AppForms.inject AppFormBuilder AppFormListener
๐ง Configuration Options #
Auto-validation Control #
class MyForm extends AppForm {
@override
bool get autoValidate => false; // Disable auto-validation
}
Custom Debouncing #
The package uses a 250ms debouncer by default. This is configured internally but optimized for most use cases.
๐งช Testing #
void main() {
group('LoginForm Tests', () {
late LoginForm form;
setUp(() {
form = LoginForm();
AppForms.injectForms([form]);
});
test('should validate email field', () {
form.email.setValue('invalid-email');
expect(form.saveAndValidate(), false);
form.email.setValue('[email protected]');
expect(form.saveAndValidate(), true);
});
});
}
๐ API Reference #
See the API documentation for detailed information about all available methods and properties.
๐ค Contributing #
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our GitHub repository.
๐ License #
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Support #
- ๐ GitHub Issues
- ๐ฆ Pub.dev Package
- ๐ง Email Support