การใช้งาน Form และ Form Validation ใน Flutter

บทความ เมื่อไม่กี่สัปดาห์ โดย Ninenik Narkdee
flutter textformfield form validation form

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ flutter textformfield form validation form



เนื้อหาตอนต่อไปนี้ เราจะมาดูเกี่ยวกับการใช้งาน form ใน flutter
เริ่มตั้งแต่ สมมติถ้าเราต้องการสร้าง input สำหรับรับข้อความ 
แล้วนำค่าข้อความที่รับเข้ามาไปแสดง ถ้าเราเข้าใจเกี่ยวกับการใช้งาน
form ในเว็บก็จะลักษณะการทำงานคล้ายๆ กัน
 
   *เนื้อหานี้ใช้เนื้อหาต่อเนื่องจากบทความ http://niik.in/961
 
 

ทำความรู้จักการใช้งาน Form เบื้องต้น

    เราจะใช้ไฟล์ contact.dart เป็นตัวเริ่มต้น อย่างไรก็ตามให้เน้นไปส่วนของโค้ดที่
อยู่ใน state เป็นสำคัญ
 

    ไฟล์ contact.dart

 
import 'package:flutter/material.dart';
  
class Contact extends StatefulWidget {
    static const routeName = '/contact';
 
    const Contact({Key? key}) : super(key: key);
  
    @override
    State<StatefulWidget> createState() {
        return _ContactState();
    }
}
  
class _ContactState extends State<Contact> {
  
    // สร้างฟอร์ม key หรือ id ของฟอร์มสำหรับอ้างอิง
    final _formKey = GlobalKey<FormState>();

    @override
    Widget build(BuildContext context) {

        return Scaffold(
            appBar: AppBar(
                title: Text('Contact Us'),
            ),
            body: Form(  // ใช้งาน Form
              key: _formKey, // กำหนด key
              child: Column(
                  children: <Widget>[ // กำหนด widget ที่จะใช้งานกับฟอร์ม
                      TextFormField(
                        validator: (value) {
                          if (value == null || value.isEmpty) {
                            return 'Please enter some text';
                          }
                          return null;
                        },
                      ),
                      ElevatedButton(
                        onPressed: () {
                          // อ้างอิงฟอร์มที่กำลังใช้งาน ตรวจสอบความถูกต้องข้อมูลในฟอร์ม
                          if (_formKey.currentState!.validate()) { //หากผ่าน 
                            // แสดงข้อความจำลอง ใน snackbar
                            ScaffoldMessenger.of(context).showSnackBar(
                              const SnackBar(content: Text('Processing Data')),
                            );
                          }
                        },
                        child: const Text('Submit'),
                      ),
                  ],
              ),
            ),
        );
    }
}
 
    ผลลัพธ์การทำงาน
 


 
 
     หลักการทำงานเบื้องต้น ถ้ายังไม่ได้กรอกข้อมูล เมื่อกดปุ่ม submit ฟอร์มก็จะตรวจสอบข้อมูลหรือก็คือ
ทำการ validate ซึ่งถ้ายังไม่มีข้อมูล ก็ขึ้นแจ้งให้กรอกข้อมูลก่อนส่ง หลังกรอกข้อมูล และกดส่งใหม่อีกครั้ง
การตรวจสอบของฟอร์มก็ผ่าน และก็จำลองการแสดงข้อความใน snackbar
 
 

การใช้งาน TextFormField

    TextFormField ถือเป็น widget หลักที่มีการใช้งานร่วมกับฟอร์ม มาดูองค์ประกอบของการกำหนดส่วน
ต่างๆ ของ TextFormField ว่ามีอะไรบ้าง
 
 TextFormField(
   cursorColor: Theme.of(context).cursorColor,
   initialValue: 'Input text', // ค่าเริ่มต้น
   maxLength: 20, // จำกัดความยาวตัวอักษร
   decoration: InputDecoration(
     icon: Icon(Icons.favorite),
     labelText: 'Label text',
     labelStyle: TextStyle(
       color: Color(0xFF6200EE),
     ),
     helperText: 'Helper text',
     suffixIcon: Icon(
       Icons.check_circle,
     ),
     enabledBorder: UnderlineInputBorder(
       borderSide: BorderSide(color: Color(0xFF6200EE)),
     ),
   ),
 ),
 
    ผลลัพธ์ที่ได้
 


 
 
    TextFormField จากรูปแบบแรก เราแทบจะไม่ได้กำหนด property ใดๆ ยกเว้นการ validate ข้อมูล ส่วนตัว
ที่สองตามโค้ดด้านบน เรากำหนดองค์ประกอบต่างๆ เกือบทั้งหมดลงไป เพื่อให้เห็นภาพรวม ว่าเราสามารถจัดการ
กับ TextFormField อะไรได้บ้าง เราสามารถเพิ่มไอคอน ทั้งหน้า และหลัง TextFormField ได้ โดยอาจจะประยุกต์
ไปใบ้เป็น IconButton เพื่อทำคำสั่งต่างๆ ได้ เช่น การล้างค่า เป็นต้น
    การกำหนด maxLength หรือจำกัดความยาวของข้อความ จะมีตัวนับจำนวนกำกับไว้ในส่วนของ helper text
โดยตรง helper text เราจะใช้กำหนดข้อความแนะนำการกรอกข้อมูล ถ้าหากมีการกำหนดการ validate ก็จะแสดง
เป็นข้อความ error message สีแดงแทนดังรูปด้านล่าง
 
 


 
 
    กำหนดแบบ outline เป็นเส้นขอบนอก สามารถกำหนดสี และเงื่อนไขสีที่แสดงได้เพิ่มเติม
 
TextFormField(
  decoration: InputDecoration(
    border: OutlineInputBorder(),
    hintText: 'Enter a search term',
  ),
),          
SizedBox(height: 5.0,),          
TextFormField(
  decoration: const InputDecoration(
    border: UnderlineInputBorder(),
    labelText: 'Enter your username',
  ),
),
SizedBox(height: 5.0,),
TextFormField(
  decoration: InputDecoration(
    border: OutlineInputBorder(
      borderSide: BorderSide()
    ),
  ),
),
SizedBox(height: 5.0,),
TextFormField(
  decoration: InputDecoration(
    focusedBorder: OutlineInputBorder( // เมื่อ focus
      borderSide: BorderSide(width: 1.0),
    ),
    enabledBorder: OutlineInputBorder( // สถานะปกติ
      borderSide: BorderSide( width: 1.0), // กำหนดสีในนี้ได้
    ), 
    labelText: 'Username*',
    helperText: '*Required',
  ),
),
 
    ผลลัพธ์ที่ได้
 


 
 

    การ Validate ข้อมูลของ TextFormField

    ต่อไปเรามาดูเกี่ยวกับการ validate หรือการตรวจสอบความถูกต้องของข้อมูลใน TextFormField ซึ่งในตัวอย่าง
ข้างต้นเราเห็นวิธีการใช้งาน และการกำหนดคร่าวๆ ไปแล้ว จะมาลงรายละเอียดในหัวข้อนี้
 
TextFormField(
//  autovalidateMode: AutovalidateMode.always,
  validator: (value) {
  if (value == null || value.isEmpty) {
    return 'Please enter some text';
  }
  return null;
  },
),  
 
    การตรวจสอบความถูกต้องของข้อมูลเราจะกำหนด callback ฟังก์ชั่นให้กับ validator property เงื่อนไขการตรวจสอบ
ก็ขึ้นกับรูปแบบตามที่เราต้องการ อย่างตัวอย่าง เป็นการเช็คว่าถ้าเป็นค่า null หรือ เป็นค่าว่าง(ไม่รวมเว้นวรรค) ก็คืนค่า
เป็นข้อความกลับเป็นข้อมูลประเภท FormFieldValidator<String> ไปแสดงตรง error message ของ TextFormField 
แต่ถ้าข้อมูลถูกต้องก็คืนค่าเป็น null ซึ่งหมายถึงตรวจสอบความถูกต้องผ่าน
    callback ฟังก์ชั่น จะทำงานเมื่อฟอร์มมีการตรวจสอบความถูกต้อง หรือก็คือเมื่อใช้คำสั่ง validate()
อย่างในตัวอย่างแรกก็เช่น 
 
if (_formKey.currentState!.validate()) { // ตรวจสอบความถูกต้องของข้อมูลในฟอร์ม
 
    คำสั่งนี้จะไปเรียกใช้งาน callback ฟังก์ชั่น ที่กำหนดใน validator
 
    กรณีต้องการให้เรียกใช้งานทันทีที่มีการกรอกข้อมูล สามารถกำหนด
 
autovalidateMode: AutovalidateMode.always,
 
    การกำหนดลักษณะนี้ จะทำให้การกรอกและส่งข้อมูลเป็นไปอย่างรวดเร็ว เพราะหากเงื่อนไขผ่านในขณะกรอก
ข้อมูล ก็ไม่ต้องย้อนกลับมาแก้ไข เหมือนกรณีที่เช็คดด้วย validate() จากฟอร์ม นั่นคือไม่ต้องรอกดปุ่ม submit 
ก็รู้ได้เลยว่าข้อมูลที่กรอกอยู่นั้นผ่านหรือยังไม่ผ่านการตรวจสอบ
 
    การกำหนด callback ฟังก์ชั่นใน validator สามารถแยกออกมาเป็นอีกไฟล์ ที่รวบรวมคำสั่งการตรวจสอบทั้งหมด
เอาไว้ แล้วเรียกใช้งานอีกทีก็ได้ เพื่อให้โค้ดดูเป็นระเบียบเรียบร้อยกรณีมีการตรวจสอบเงื่อนไขจำนวนมากๆ 
    ในการใช้งาน callback ฟังก์ชั่น อาจจะใช้เป็น global ฟังก์ชั่น หรือใช้เป็น provider หรือใช้เป็น class หรือ mixin
ก็ได้ นอกจากนั้น เรายังสามารถใช้งาน package หรือ plugin เสริมมาช่วยจัดการการ validate ข้อมูลแทนการเขียน
ขึ้นมาเองก็ได้เหมือนกัน ที่กล้าวข้างต้น ก็เพื่อให้เข้าใจภาพรวมของการใช้งาน ในที่นี้จะแนะนำในสองวิธีคือ
    - การกำหนดเป็น class แยก
    - การกำหนดโดยใช้ form_field_validator package
 
 

การกำหนด Form Validation ด้วย mixin

    ให้เราสร้างโฟลเดอร์สำหรับเก็บ class ที่จัดการเกี่ยวกับการตรวจสอบความถูกต้องของข้อมูลในฟอร์ม โดยใช้ชื่อ
ว่า validations.dart ดังนี้
    lib > validation > validation.dart
 

    ไฟล์ validation.dart

 
// เนื่องจากข้อมูลข้อความ error ที่ validator ต้องการเป็น FormFieldValidator<String>
// เราจึงต้องกำหนดการใช้งาน FormFieldValidator โดยใช้จาก widgets ดึงมาเฉพาะที่ต้องการโดยใช้
// คำว่า show FormFieldValidator
import 'package:flutter/widgets.dart' show FormFieldValidator;

// เนื่องจากเราไม่ได้กำหนด constructor และ property ใดๆ จึงใช้งานเป็น mixin
// เป็นรูปแบบหนึ่งของ class
mixin Validators {
  // กำหนดฟังก์ชั่น สำหรับระบุฟิลด์ที่ต้องกรอก
  static FormFieldValidator<String> required(String errMsg) {
    return (value) {
        if(value == null){
          return errMsg;
        }else if(value.isEmpty){
          return errMsg;
        }
    };
  }

  // กำหนดฟังก์ชั่น สำหรับระบุฟิลด์ต้องกรอกตัวเลขต่ำสุดอย่างน้อย
  static FormFieldValidator<String> min(int min,String errMsg) {
    return (value) => (int.parse(value!) >= 0 && int.parse(value) < min) ? errMsg : null;
  }  

  // กำหนดฟังก์ชั่น สำหรับระบุฟิลด์ต้องกรอกตัวเลขสูงสุดอย่างน้อย
  static FormFieldValidator<String> max(int max,String errMsg) {
    return (value) => (int.parse(value!) >= 0 && int.parse(value) > max) ? errMsg : null;
  } 

  // กำหนดฟังก์ชั่น สำหรับระบุฟิลด์ต้องกรอกตัวอักษรยาวน้อยสุด
  static FormFieldValidator<String> minLength(int minLength,String errMsg) {
    return (value) => (value!.isNotEmpty && value.length < minLength) ? errMsg : null;
  }  

  // กำหนดฟังก์ชั่น สำหรับระบุฟิลด์ต้องกรอกตัวอักษรยาวมากสุดไม่เกิน
  static FormFieldValidator<String> maxLength(int maxLength,String errMsg) {
    return (value) => (value!.isNotEmpty && value.length > maxLength) ? errMsg : null;
  }   

  // กำหนดฟังก์ชั่น สำหรับระบุฟิลด์ต้องกรอกข้อมูลตามรูปแบบ RegEex
  static FormFieldValidator<String> pattern(RegExp pattern,String errMsg) {
    return (value) => (value!.isNotEmpty && !pattern.hasMatch(value) ) ? errMsg : null;
  } 

  // กำหนดฟังก์ชั่น สำหรับระบุฟิลด์ต้องกรอกข้อมูลอีเมลที่ถูกต้องตามรูปแบบ
  static FormFieldValidator<String> email(String errMsg) {
    final emailPattern = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+");
    return (value) => (value!.isNotEmpty && !emailPattern.hasMatch(value) ) ? errMsg : null;
  }

  // กำหนดฟังก์ชั่น สำหรับระบุฟิลด์ต้องใช้งานการตรวจสอบหลายๆ คำสั่งรวมกัน
  static FormFieldValidator<String> compose(List<FormFieldValidator<String>> validators) {
    return (value) {
      for (final validator in validators) {
        if (validator(value) != null) return validator(value);
      }
      return null;
    };
  }

}
 
    ในขั้นตอนการใช้งาน เราก็ import เข้ามาใช้งานในไฟล์ contact.dart 
 
import '../validations/validation.dart';
 
    เนื่องจาก mixin ที่เรากำหนด เราไม่ได้ต้องการเรียกใช้งานผ่าน class _ContactState โดยตรง แต่จะใช้งาน
ผ่าน mixin Validators ดังนั้นกรณีนี้ เวลานำไปใช้ จะกำหนด with หรือไม่ก็ได้
 
// กรณีนี้ จะกำหนด with หรือไม่ก็ได้
class _ContactState extends State<Contact> with Validators {
 
    ตัวอย่างการใช้งานใน TextFormField
 
TextFormField(
  autovalidateMode: AutovalidateMode.always,
  // validator: Validators.required('Please enter some text'), // แบบกำหนดเงื่อนไขเดียว
  validator: Validators.compose([ // แบบกำหนดหลายเงื่อนไข 
    Validators.required('Please enter some text'),
    // Validators.email('Please enter a valid email'),
    Validators.pattern(RegExp(r'^([0-9])+$'), 'Only numberic'),
    Validators.minLength(4,'Please enter 4 digit'),
    Validators.min(2000,'Please enter a number between 2000 and 3000 '),
    Validators.max(3000,'Please enter a number between 2000 and 3000 '), 
  ])
),
 
    จะเห็นว่าราสามารถกำหนดและเรียกใช้งานได้ในรูปแบบที่กระชับขึ้น การกำหนดแบบหลายเงื่อนไข
เพื่อให้ทำงานแบบมีประสิทธิภาพ เราควรลำดับการตรวจสอบด้วย เช่น สมมติข้างต้นเราต้องการให้
กรอกปี ค.ศ. ตั้งแต่ 2000 - 3000 อย่างแรกก็ต้องให้กรอกข้อมูล ต่อมาต้องเป็นตัวเลข ต่อมาต้องเป็น
ตัวเลข 4 ตัวขึ้นไป ต่อมาต้องเป็น ต่ำสุด และสูงสุด ตามลำดับ เพราะสมมติว่า เราเอา minLength หรือ
จำนวนตัวอักขระที่กรอก 4 ตัวขึ้นก่อนการเช็คว่าเป็นตัวเลข  ก็จะกลายเป็นว่า กรอกเป็นตัวอักษร ก็ผ่าน
แต่ไม่ผ่านต้องเป็นตัวเลข ดังนั้นก็ให้ไม่ผ่านตั้งแต่กรอกตัวอักษรเลย จะเเป็นวิธีที่ถูกต้อง
 
 
 

การกำหนด Form Validation ด้วย form_field_validator package

    การใช้งาน form_field_validator ก็เป็นอีกวิธีที่จะช่วยลดการเขียนโค้ด เพราะมีตัวช่วยในการจัดการการ
ตรวจสอบความถูกต้องของข้อมูลฟอร์มเบื้องต้นมาให้เราเรียกใช้งานได้ทันที
 
    ติดตั้ง form_field_validator
    ให้เราทำการติดตั้ง form_field_validator package ในไฟล์ pubspec.yaml
 
dependencies:
  form_field_validator: ^1.1.0
 
    จากนั้น import เข้ามาใช้งานในหน้าจัดการเกี่ยวกับ ฟอร์ม
 
import 'package:form_field_validator/form_field_validator.dart';
 
 
    ตัวอย่างการเรียกใช้งาน
TextFormField(
  autovalidateMode: AutovalidateMode.always,
  validator: RequiredValidator(errorText: 'this field is required'),
),

// แบบหลายเงื่อนไข

TextFormField(
  autovalidateMode: AutovalidateMode.always,
  validator: MultiValidator([  
    RequiredValidator(errorText: 'Please enter some text'),
    // EmailValidator(errorText: 'Please enter a valid email'),  
    MinLengthValidator(4, errorText: 'Please enter 4 digit'),  
    PatternValidator(r'^([0-9])+$', errorText: 'Only numberic'),
    RangeValidator(min: 2000, max: 3000, errorText: 'Please enter a number between 2000 and 3000'),
 ]),
 
 
การกำหนดเงื่อนไขอื่นๆ
 
LengthRangeValidator(min: 6, max: 14, errorText: 'Password must be character between 6 and 14 long'),   
DateValidator('yyyy-mm-dd', errorText: 'Invalid date format'), // 2021-11-08 valid
 
    ดูเพิ่มเติมที่ form_field_validator
 
 
    เลือกใช้รูปแบบการตรวจสอบความถูกต้องของข้อมูลฟอร์มตามต้องการ
 
 
 

ปัญหาพื้นที่กับการใช้งาน Form ใน Flutter

    การใช้งานฟอร์มใน flutter จะมีในเรื่องของการใช้งานแป้นพิมพ์ เข้ามาเกี่ยวข้องเมื่อมีการกรอกข้อมูล และถ้า
ส่วนของข้อมูลในฟอร์มมีการเปลี่ยนแปลงในเรื่องของขนาดหรือพื้นที่การแสดงข้อมูล ซึ่งอาจจะมาจากการซ่อนหรือ
แสดงข้อความ error หรืออื่นๆ เราจะพบกับปัญหาดังรูป
 
 


 
 
    ดังนั้นเราต้องกำหนดการใช้งาน SingleChildScrollView widget ครอบส่วนของ ฟอร์มอีกที เพื่อให้รองรับการ
ปรับขนาดของพื้นที่ให้สามารถเลื่อนได้ ดัง
 
body: SingleChildScrollView(
  child: Form(  // ใช้งาน Form
    key: _formKey, // กำหนด key
....
 
    ผลลัพธ์ที่ได้ ก็จะไม่เกิดปัญหาในเรื่องของพื้นที่แสดงข้อมูล
 


 
 
 
 

การรับค่าจากข้อมูลจาก TextFormField

    ต่อไปเป็นส่วนของการรับค่าจากข้อมูลที่เรากรอกในฟอร์ม เพื่อนำไปใช้งาน จะมีที่จะกำหนดหลักๆ 
อยู่ 3 - 4  จุด คือ
    1. กำหนดตัวแปรสำหรับ controller หรือเรียกว่าตัวควบคุม
    2. นำ controller ผูกกับฟอร์มฟิลด์ที่ต้องการ
    3. อ้างอิงค่า และใช้งานผ่าน controller
    4. ยกเลิกการใช้งาน
 
    ดูตัวอย่าง
 
class _ContactState extends State<Contact> {
  
    // สร้างฟอร์ม key หรือ id ของฟอร์มสำหรับอ้างอิง
    final _formKey = GlobalKey<FormState>();

    // กำหนดตัวแปรรับค่า
    final _text1 = TextEditingController();

    @override
    void dispose() {
      _text1.dispose(); // ยกเลิกการใช้งานที่เกี่ยวข้องทั้งหมดถ้ามี
      super.dispose();
    }

    @override
    Widget build(BuildContext context) {

        return Scaffold(
            appBar: AppBar(
                title: Text('Contact Us'),
            ),
            body: SingleChildScrollView(
              child: Form(  // ใช้งาน Form
                key: _formKey, // กำหนด key
                child: Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: Column(
                      children: <Widget>[ // กำหนด widget ที่จะใช้งานกับฟอร์ม
                          TextFormField(
                            controller: _text1, // ผูกกับ TextFormField ที่จะใช้
                            validator: Validators.required('Please enter some text'),
                          ), 
                          SizedBox(height: 5.0,),
                          ElevatedButton(
                            onPressed: () {
                              // อ้างอิงฟอร์มที่กำลังใช้งาน ตรวจสอบความถูกต้องข้อมูลในฟอร์ม
                              if (_formKey.currentState!.validate()) { //หากผ่าน 
                                // แสดงข้อความจำลอง ใน snackbar
                                ScaffoldMessenger.of(context).showSnackBar(
                                  // นำค่าข้อมูลไปแสดงหรือใช้งานผ่าน controller
                                   SnackBar(content: Text('You type: ${_text1.text} ')),
                                );
                              }
                            },
                            child: const Text('Submit'),
                          ),
                      ],
                  ),
                ),
              ),
            ),
        );
    }
}
 
    ผลลัพธ์ที่ได้
 


 
 
    เมื่อเรากรอกข้อมูล เราสามารถเรียกใช้ข้อมูลที่ผูกกับ TextFormField ผ่านตัวแปร controller ที่ชื่อ _text1
ในตัวอย่าง เรานำค่าไปแสดงใน snackbar ผ่านการอ้างอิงค่า ${_text1.text} 
 
 
 

การใช้งาน TextFormField ในรูปแบบต่างๆ

 

    การใช้งานในรูปแบบวันที่และเวลา

    ตัวอย่างการใช้งานแบบวันที่ และเวลา ใช้งานร่วมกับ intl package
 

    ไฟล์ contact.dart

 
import 'package:flutter/material.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';

import '../validations/validation.dart';

  
class Contact extends StatefulWidget {
    static const routeName = '/contact';
 
    const Contact({Key? key}) : super(key: key);
  
    @override
    State<StatefulWidget> createState() {
        return _ContactState();
    }
}
  
class _ContactState extends State<Contact> with Validators {
  
    // สร้างฟอร์ม key หรือ id ของฟอร์มสำหรับอ้างอิง
    final _formKey = GlobalKey<FormState>();

    late DateFormat dateFormat; // รูปแบบการจัดการวันที่และเวลา

    // กำหนดตัวแปรรับค่า
    final _text1 = TextEditingController();
    final _text2 = TextEditingController();

    void _selectDate() async {
      final DateTime? newDate = await showDatePicker(
        context: context,
        initialDate: DateTime.now(),
        firstDate: DateTime(2017, 1),
        lastDate: DateTime(2022, 7),
        helpText: 'Select a date',
      );
      if (newDate != null) {
        setState(() {
          _text1.value = TextEditingValue(text: dateFormat.format(newDate).toString());
        });
      }
    }    

    // เกียวกับการใช้เวลา 
    /// แปลงเวลาจากวันที่ TimeOfDay.fromDateTime(DateTime.now())
    /// เวลาปัจจุบัน TimeOfDay.now()
    /// แบบกำหนดเอง TimeOfDay(hour: 7, minute: 15),
    void _selectTime() async {
      final TimeOfDay? newTime = await showTimePicker(
        context: context, 
        initialTime: TimeOfDay.now(), 
      );
      if (newTime != null) {
        setState(() {
          _text2.value = TextEditingValue(text: newTime.format(context));
        });
      }
    }    

    @override
    void initState() {
      // กำหนดรูปแบบการจัดการวันที่และเวลา 
      Intl.defaultLocale = 'en';
      initializeDateFormatting();
      dateFormat = DateFormat('d/MM/y','en');

    }

    @override
    void dispose() {
      _text1.dispose(); // ยกเลิกการใช้งานที่เกี่ยวข้องทั้งหมดถ้ามี
      _text2.dispose(); 
      super.dispose();
    }

    @override
    Widget build(BuildContext context) {

        return Scaffold(
            appBar: AppBar(
                title: Text('Contact Us'),
            ),
            body: SingleChildScrollView(
              child: Form(  // ใช้งาน Form
                key: _formKey, // กำหนด key
                child: Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: Column(
                      children: <Widget>[ // กำหนด widget ที่จะใช้งานกับฟอร์ม
                          TextFormField(
                            decoration: InputDecoration(
                              icon: Icon(Icons.date_range),
                            ),
                            controller: _text1, // ผูกกับ TextFormField ที่จะใช้
                            validator: Validators.required('Please enter some text'),
                            onTap: _selectDate,
                            readOnly: true,
                          ), 
                          SizedBox(height: 5.0,),
                          TextFormField(
                            decoration: InputDecoration(
                              icon: Icon(Icons.alarm),
                            ),                            
                            controller: _text2, // ผูกกับ TextFormField ที่จะใช้
                            validator: Validators.required('Please enter some text'),
                            onTap: _selectTime,
                            readOnly: true,
                          ), 
                          SizedBox(height: 5.0,),                                 
                          ElevatedButton(
                            onPressed: () {
                              // อ้างอิงฟอร์มที่กำลังใช้งาน ตรวจสอบความถูกต้องข้อมูลในฟอร์ม
                              if (_formKey.currentState!.validate()) { //หากผ่าน 
                                // แสดงข้อความจำลอง ใน snackbar
                                ScaffoldMessenger.of(context).showSnackBar(
                                  // นำค่าข้อมูลไปแสดงหรือใช้งานผ่าน controller
                                   SnackBar(content: Text('Date: ${_text1.text} Time: ${_text2.text}')),
                                );
                              }
                            },
                            child: const Text('Submit'),
                          ),
                      ],
                  ),
                ),
              ),
            ),
        );
    }
}
 
    ผลลัพธ์ที่ได้
 
 


 
 
    เรากำหนดให้ input ทั้งสองเป็น readOnly จึงไม่สามารถกรอกข้อมูลผ่าน คีบอร์ดได้ เมื่อแตะเพื่อกรอกข้อมูล
จะเป็นการไปเรียกฟังก์ชั่นแสดงให้เลือกวันที่และเวลาแทน เมื่อเลือกแล้วก้เอาค่าใส่กลับเข้าไปใน input
ผ่านการใช้งาน controller  ในตัวอย่างเราใช้ intl package เข้ามาช่วยเพื่อจัดรูปแบบในเรื่องของวันที่และเวลา
ซึ่งการใช้งานวันที่และเวลาสำหรับการบันทึกควรใช้เป็นภาษาอังกฤษ หรือรูปแบบมาตรฐาน
 
 

    การใช้งานในรูปแบบข้อมูลรหัสผ่าน

    รูปแบบการใช้งานเหมือน TextFormField ทั่วไปปกติ เพียงแค่เพิ่มการกำหนดในส่วนของการแสดงข้อความ
เป็นรูปแบบของรหัสผ่าน โดยกำหนดค่า obscureText: true,
    ตัวอย่างข้างล่างจะประยุกต์ การกำหนดค่า 
 
// กำหนดสถานะการแสดงแบบรหัสผ่าน
bool _isHidden = true;
 
    นำค่าไปใช้งาน เป็นเงื่อนไขกำหนดค่าต่างๆ
 
TextFormField(
  decoration: InputDecoration(
    icon: Icon(Icons.lock),
    suffixIcon: IconButton(
      onPressed: (){
        setState(() {
          _isHidden = !_isHidden; // เมื่อกดก็เปลี่ยนค่าตรงกันข้าม
        });
      }, 
      icon: Icon(
        _isHidden // เงื่อนไขการสลับ icon
        ? Icons.visibility_off 
        : Icons.visibility
      ),
    ),
  ),    
  controller: _text3, // ผูกกับ TextFormField ที่จะใช้                        
  validator: Validators.required('Please enter some text'),
  obscureText: _isHidden, // การซ่อนหรือแสดงข้อความในรูปแบบรหัสผ่าน
), 
 
    ผลลัพธ์ที่ได้
 

 
 
 

    การใช้งานในรูปกรอกตัวเลขอย่างเดียว

    ให้กำหนดค่า keyboardType: TextInputType.number,
 
 
TextFormField(
  decoration: InputDecoration(
    icon: Icon(Icons.card_giftcard),
  ),
  controller: _text1, // ผูกกับ TextFormField ที่จะใช้
  validator: Validators.required('Please enter some text'),
  keyboardType: TextInputType.number,
), 
 
    ผลลัพธ์ที่ได้
 
 


 
 
    หากต้องการจัดรูปแบบเพิ่มเติม ให้ทำการ import 
 
import 'package:flutter/services.dart';
 
    เข้ามาใช้งาน แล้วกำหนดรูปแบบ เช่น
 
  keyboardType: TextInputType.number,
  inputFormatters: <TextInputFormatter>[
      FilteringTextInputFormatter.digitsOnly
  ],   
 
    ในลักษณะนี้จะไม่สามารถกรอก คอมม่า จุด ช่องว่าง เครื่องหมายขีดได้ จะกรอกเได้เฉพาะตัวเลขเท่านั้น
แต่ถ้าให้รับจุดและตัวเลขเท่านั้นก็กำหนดในลักษณะนี้ไปแทนได้
 
FilteringTextInputFormatter.allow(RegExp(r'[0-9\.]')),   
 
    เราสามารถประยุกต์การใช้งาน FilteringTextInputFormatter เพื่อกำหนดให้กรอกได้เฉพาะตัวอักษรหรือ
ตามรูปแบบที่กำหนดได้ แต่ก็ต้องระวังไม่กำหนดซ้ำซ้อนกับการใช้งานการ validate ข้อมูล เพราะจะไม่จำเป็น
 
     ตัวอย่างโค้ดไฟล์ contact.dart
 
import 'package:flutter/material.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';
import 'package:flutter/services.dart';

import '../validations/validation.dart';

  
class Contact extends StatefulWidget {
    static const routeName = '/contact';
 
    const Contact({Key? key}) : super(key: key);
  
    @override
    State<StatefulWidget> createState() {
        return _ContactState();
    }
}
  
class _ContactState extends State<Contact> with Validators {
  
    // สร้างฟอร์ม key หรือ id ของฟอร์มสำหรับอ้างอิง
    final _formKey = GlobalKey<FormState>();

    late DateFormat dateFormat; // รูปแบบการจัดการวันที่และเวลา

    // กำหนดตัวแปรรับค่า
    final _text1 = TextEditingController();
    final _text2 = TextEditingController();
    final _text3 = TextEditingController();
    final _text4 = TextEditingController();    

    // กำหนดสถานะการแสดงแบบรหัสผ่าน
    bool _isHidden = true;

    void _selectDate() async {
      final DateTime? newDate = await showDatePicker(
        context: context,
        initialDate: DateTime.now(),
        firstDate: DateTime(2017, 1),
        lastDate: DateTime(2022, 7),
        helpText: 'Select a date',
      );
      if (newDate != null) {
        setState(() {
          _text1.value = TextEditingValue(text: dateFormat.format(newDate).toString());
        });
      }
    }    

    // เกียวกับการใช้เวลา 
    /// แปลงเวลาจากวันที่ TimeOfDay.fromDateTime(DateTime.now())
    /// เวลาปัจจุบัน TimeOfDay.now()
    /// แบบกำหนดเอง TimeOfDay(hour: 7, minute: 15),
    void _selectTime() async {
      final TimeOfDay? newTime = await showTimePicker(
        context: context, 
        initialTime: TimeOfDay.now(), 
      );
      if (newTime != null) {
        setState(() {
          _text2.value = TextEditingValue(text: newTime.format(context));
        });
      }
    }    

    @override
    void initState() {
      // กำหนดรูปแบบการจัดการวันที่และเวลา 
      Intl.defaultLocale = 'en';
      initializeDateFormatting();
      dateFormat = DateFormat('d/MM/y','en');

    }

    @override
    void dispose() {
      _text1.dispose(); // ยกเลิกการใช้งานที่เกี่ยวข้องทั้งหมดถ้ามี
      _text2.dispose(); 
      _text3.dispose(); 
      _text4.dispose(); 
      super.dispose();
    }

    @override
    Widget build(BuildContext context) {

        return Scaffold(
            appBar: AppBar(
                title: Text('Contact Us'),
            ),
            body: SingleChildScrollView(
              child: Form(  // ใช้งาน Form
                key: _formKey, // กำหนด key
                child: Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: Column(
                      children: <Widget>[ // กำหนด widget ที่จะใช้งานกับฟอร์ม
                          TextFormField(
                            decoration: InputDecoration(
                              icon: Icon(Icons.card_giftcard),
                            ),
                            controller: _text4, // ผูกกับ TextFormField ที่จะใช้
                            validator: Validators.required('Please enter some text'),
                            // keyboardType: TextInputType.number,
                            inputFormatters: <TextInputFormatter>[
                                // FilteringTextInputFormatter.digitsOnly,//
                                FilteringTextInputFormatter.allow(RegExp(r'[0-9\.]')),      
                            ],   
                          ), 
                          SizedBox(height: 5.0,),                                
                          TextFormField(
                            decoration: InputDecoration(
                              icon: Icon(Icons.lock),
                              suffixIcon: IconButton(
                                onPressed: (){
                                  setState(() {
                                    _isHidden = !_isHidden; // เมื่อกดก็เปลี่ยนค่าตรงกันข้าม
                                  });
                                }, 
                                icon: Icon(
                                  _isHidden // เงื่อนไขการสลับ icon
                                  ? Icons.visibility_off 
                                  : Icons.visibility
                                ),
                              ),
                            ),    
                            controller: _text3, // ผูกกับ TextFormField ที่จะใช้                        
                            validator: Validators.required('Please enter some text'),
                            obscureText: _isHidden, // ก่อนซ่อนหรือแสดงข้อความในรูปแบบรหัสผ่าน
                          ), 
                          SizedBox(height: 5.0,),                      
                          TextFormField(
                            decoration: InputDecoration(
                              icon: Icon(Icons.date_range),
                            ),
                            controller: _text1, // ผูกกับ TextFormField ที่จะใช้
                            validator: Validators.required('Please enter some text'),
                            onTap: _selectDate,
                            readOnly: true,
                          ), 
                          SizedBox(height: 5.0,),
                          TextFormField(
                            decoration: InputDecoration(
                              icon: Icon(Icons.alarm),
                            ),                            
                            controller: _text2, // ผูกกับ TextFormField ที่จะใช้
                            validator: Validators.required('Please enter some text'),
                            onTap: _selectTime,
                            readOnly: true,
                          ), 
                          SizedBox(height: 5.0,),                                  
                          ElevatedButton(
                            onPressed: () {
                              // อ้างอิงฟอร์มที่กำลังใช้งาน ตรวจสอบความถูกต้องข้อมูลในฟอร์ม
                              if (_formKey.currentState!.validate()) { //หากผ่าน 
                                // แสดงข้อความจำลอง ใน snackbar
                                ScaffoldMessenger.of(context).showSnackBar(
                                  // นำค่าข้อมูลไปแสดงหรือใช้งานผ่าน controller
                                   SnackBar(content: Text('Date: ${_text1.text} Time: ${_text2.text}')),
                                );
                              }
                            },
                            child: const Text('Submit'),
                          ),
                      ],
                  ),
                ),
              ),
            ),
        );
    }
}
 
    สำหรับเนื้อหาเกี่ยวกับการ validate ข้อมูลในฟอร์มเบื้องต้น และการใช้งานเกี่ยวกับ TextFormField ก็จะ
ประมาณเท่านี้ เพื่อไม่ให้เนื้อหายาวเกินไป ยังมีการพูดถึงเกียวกับการใช้งานองค์ประกอบอื่นๆ ของฟอร์มเพิ่ม
เติม รอติดตาม


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง











เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ