CategoryTagArticle

admin

I'm a Full-stack developer

Tag

Linked List
Data Structure
Chat GPT
Design Pattern
Microservices
API
AWS CDK
ReactJS
AWS Lightsail
Flutter Mobile
Form validator in Flutter with EzValidator
Published date: 04/01/2024

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.


Contents

  1. Prerequisites
  2. Implementation
  3. Demo


Prerequisites

Ensure the VSCode and Flutter are installed on your local. If not installed yet, you should follow up below steps:


  • Download and install VSCode here
  • Download and install Flutter here
  • Set up the VSCode editor to complete, deploy, and debug the app here


Implementation

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,
                                ),
                              ],
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
  }
}


Demo


Wrapping Up

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!

Recommend

TypeScript Design Pattern - Abstract Factory
admin07/08/2023

TypeScript Design Pattern - Abstract Factory
The abstract factory pattern is one of five design patterns in the Creational Design Pattern group. The abstract factory provides an interface for creating families of related or dependent objects without specifying their concrete classes.
TypeScript Design Pattern - Singleton
admin07/08/2023

TypeScript Design Pattern - Singleton
The singleton ensures only a single install is created in a class and provides a method to access this instance everywhere in a codebase.
Flutter push notification
admin14/06/2023

Flutter push notification
Welcome everyone, in this article, we will implement flutter push notification through APNs (Apple Push Notification Service) and FCM (Firebase Cloud Message), and manage messages sent using AWS SNS (Amazon Simple Notification Service).
Newest

State management with redux in Flutter
admin14/06/2023

State management with redux in Flutter
In this article I will show an example to manage state in Flutter application using Redux.
Create Project with Express + TypeScript + ESLint + Auto Reload
admin12/06/2023

Create Project with Express + TypeScript + ESLint + Auto Reload
In this article, I introduce to you how to initialize an Express + TypeScript project.
Part 1: Build a Chat App with ReactJS + Material UI
admin13/09/2023

Part 1: Build a Chat App with ReactJS + Material UI
In this article, I would like to introduce using ReactJS and material UI to build a Chat App.
Đinh Thành Công Blog

My website, where I write blogs on a variety of topics and where I have some experiments with new technologies.

hotlinelinkedinskypezalofacebook
DMCA.com Protection Status
Feedback
Name
Phone number
Email
Content
Download app
hotline

copyright © 2023 - AGAPIFA

Privacy
Term
About