When I am working on Flutter with form. For ensuring data integrity, and handling user input errors. I want to show an error message below each TextField, Dropdown, Switch, ... if the user does not input or wrong input. The EzValidator help me to resolve this issue.
In this article, we will explore how to validate user inputs in Flutter with the EzValidator package.
Ensure the VSCode and Flutter are installed on your local. If not installed yet, you should follow up below steps:
Create a new application
$ flutter create ez_validator_example
Navigate to the new project
main.dart
import 'package:EzValidatorExample/app.dart'; import 'package:flutter/material.dart'; void main() { runApp(App()); }
app.dart
import 'package:EzValidatorExample/my_address.dart'; import 'package:flutter/material.dart'; class App extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'EzValidator Example', theme: ThemeData(), home: const MyAddressScreen(title: 'EzValidator Example'), ); } }
Install EzValidator package
$ flutter pub add ez_validator
pubspec.yaml
dependencies: ez_validator: ^0.3.1
Define a new schema object with name is address_schema.dart
import 'package:ez_validator/ez_validator.dart'; class AddressSchema { static EzSchema upsertSchema = EzSchema.shape( { 'email': EzValidator<String>(optional: true) .email("Email không đúng định dạng"), 'fullName': EzValidator<String>().required( "Trường này không được bỏ trống", ), 'province': EzValidator<String>().number().required( "Trường này không được bỏ trống", ), 'district': EzValidator<String>().number().required( "Trường này không được bỏ trống", ), 'ward': EzValidator<String>().number().required( "Trường này không được bỏ trống", ), 'streetAddress': EzValidator<String>().required( "Trường này không được bỏ trống", ), 'phoneNumber': EzValidator<String>().phone(), 'note': EzValidator<String>(), 'isDefault': EzValidator<bool>(defaultValue: false).boolean(), }, ); }
Create my address screen with file name is my_address.dart
import 'package:EzValidatorExample/my_address_state.dart'; import 'package:flutter/material.dart'; class MyAddressScreen extends StatefulWidget { const MyAddressScreen({super.key}); @override State<MyAddressScreen> createState() => MyAddressScreenState(); }
Create screen state my_address_state.dart
import 'package:EzValidatorExample/my_address.dart'; import 'package:EzValidatorExample/address_schema.dart'; import 'package:flutter/material.dart'; class MyAddressScreenState extends State<MyAddressScreen> { bool isButtonDisabled = true; Map<String, dynamic>? errors = {}; List<LocationModel> provinces = []; List<LocationModel> districts = []; List<LocationModel> wards = []; TextEditingController fullNameController = TextEditingController(); TextEditingController phoneNumberController = TextEditingController(); TextEditingController streetAddressController = TextEditingController(); TextEditingController isDefaultController = TextEditingController(); TextEditingController noteController = TextEditingController(); TextEditingController emailController = TextEditingController(); LocationModel province = const LocationModel(); LocationModel district = const LocationModel(); LocationModel ward = const LocationModel(); @override void initState() { super.initState(); _textFieldsListener(); } Map<String, Object?> _getFormValues() { return { 'fullName': fullNameController.text, 'province': province, 'district': district, 'ward': ward, 'streetAddress': streetAddressController.text, 'phoneNumber': phoneNumberController.text, 'email': emailController.text, 'note': noteController.text, 'isDefault': isDefault, }; } void _textFieldsListener() { emailController.addListener(_validate); fullNameController.addListener(_validate); streetAddressController.addListener(_validate); phoneNumberController.addListener(_validate); noteController.addListener(_validate); } void _validate() { final validateRes = AddressSchema.upsertSchema.validateSync(_getFormValues()).$2 as Map<String, dynamic>; setState(() { errors = validateRes; isButtonDisabled = validateRes!.isEmpty == false; }); } Future<void> _saveAddress() async {} Widget item( String lbText, TextEditingController controller, dynamic errorText, ) { return Padding( padding: const EdgeInsets.only(bottom: 16), child: TextField( controller: widget.controller, onTapOutside: (event) { FocusManager.instance.primaryFocus?.unfocus(); }, decoration: InputDecoration( border: const OutlineInputBorder(), alignLabelWithHint: true, labelStyle: const TextStyle(color: Palette.secondary);, labelText: widget.label, errorText: widget.errorText, isDense: true, contentPadding: const EdgeInsets.all(8), focusedBorder: const OutlineInputBorder( borderSide: BorderSide(color: Palette.primary), ), ), ); ); } @override Widget build(BuildContext context) { return Scaffold( appBar: const AppBar( title: 'My Address', ), body: SingleChildScrollView( child: Column( children: [ Container( margin: const EdgeInsets.all(5), padding: const EdgeInsets.all(25), decoration: BoxDecoration( color: Palette.white, borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( color: Palette.secondary.withOpacity(0.2), spreadRadius: 0.1, blurRadius: 0.1, offset: const Offset(0, 0.5), ), ], ), child: Column( children: [ Padding( padding: const EdgeInsets.only( bottom: 16, ), child: PhoneNumberInputComp( controller: phoneNumberController, errorMessage: errors!['phoneNumber'] as String?, ), ), item('Tên người nhận', fullNameController, errors!['fullName']), item('Email', emailController, errors!['email']), itemLocation( 0, 'Tỉnh / Thành phố', provinces, province, ), itemLocation( 1, 'Quận / Huyên', districts, district, ), itemLocation( 2, 'Xã / Phường', wards, ward, ), item('Đia chỉ', streetAddressController, errors!['streetAddress']), item('Ghi chú', noteController, errors!['note']), Row( children: [ const Text('Mặc định'), SwitchComp( value: isDefault, onChanged: (checked) { setState(() { isDefault = checked; }); }, ), ], ), Align( child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: isButtonDisabled ? Palette.secondary : Palette.primary, ), onPressed: isButtonDisabled ? null : _saveAddress, child: const Row( mainAxisSize: MainAxisSize.min, children: [ Text( 'Lưu Thay Đổi', style: TextStyle( color: Palette.white, ), ), SizedBox( width: 5, ), Icon( Icons.save_outlined, size: 24, color: Palette.white, ), ], ), ), ), ], ), ), ], ), ), } }
And that's it on building a form validation in Flutter. Congrats!
If you enjoyed this article, consider sharing it to help other developers. You can also visit my blog to read more articles from me.
Till next time guys, byeeeee!