เนื้อหาตอนต่อไปนี้ เราจะมาดูเกี่ยวกับการใช้งาน form ใน flutter
เริ่มตั้งแต่ สมมติถ้าเราต้องการสร้าง input สำหรับรับข้อความ
แล้วนำค่าข้อความที่รับเข้ามาไปแสดง ถ้าเราเข้าใจเกี่ยวกับการใช้งาน
form ในเว็บก็จะลักษณะการทำงานคล้ายๆ กัน
*เนื้อหานี้ใช้เนื้อหาต่อเนื่องจากบทความ http://niik.in/1047
ทำความรู้จักการใช้งาน 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: Colors.red, 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 ดังนี้
lib > validations > 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; } return null; }; } // กำหนดฟังก์ชั่น สำหรับระบุฟิลด์ต้องกรอกตัวเลขต่ำสุดอย่างน้อย 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 now = DateTime.now(); final DateTime firstDate = DateTime(2017, 7, 1); // ช่วงเริ่มต้น final DateTime lastDate = DateTime(2023, 7, 1); // ช่วงสิ้นสิน final DateTime initialDate = now.isAfter(lastDate) ? lastDate : now; final DateTime? newDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: firstDate, lastDate: lastDate, 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 now = DateTime.now(); final DateTime firstDate = DateTime(2017, 7, 1); // ช่วงเริ่มต้น final DateTime lastDate = DateTime(2023, 7, 1); // ช่วงสิ้นสิน final DateTime initialDate = now.isAfter(lastDate) ? lastDate : now; final DateTime? newDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: firstDate, lastDate: lastDate, 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.card_giftcard), ), controller: _text4, // ผูกกับ TextFormField ที่จะใช้ validator: Validators.required('Please enter some text'), keyboardType: TextInputType.number, inputFormatters: <TextInputFormatter>[ FilteringTextInputFormatter.digitsOnly ], ), 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, // การซ่อนหรือแสดงข้อความในรูปแบบรหัสผ่าน ), 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 ก็จะ
ประมาณเท่านี้ เพื่อไม่ให้เนื้อหายาวเกินไป ยังมีการพูดถึงเกียวกับการใช้งานองค์ประกอบอื่นๆ ของฟอร์มเพิ่ม
เติม รอติดตาม