Flutter: TextField vs TextFormField ใช้ตัวไหนดีนะ?
Published on
Authored by Pete. Pittawat Taveekitworachai.
TextField
ถือเป็นหนึ่งใน Element ที่สำคัญในการพัฒนา Application ของเรา เพราะเป็นวิธีที่ผู้ใช้งานจะสามารถเพิ่มข้อมูลบางอย่างเข้ามาใน Application เราได้ ใน Flutter เองก็มี Widget ที่สนับสนุนเกี่ยวกับการสร้าง Form อย่างมากมาย รวมไปถึงยังสนับสนุนการเขียนโค้ดได้หลากหลายรูปแบบแล้วแต่ความถนัดของนักพัฒนาคนนั้น ๆ
อย่างไรก็ตามหลาย ๆ คนที่อยากจะรับข้อความสั้น ๆ จากผู้ใช้งาน อาจจะรู้สึกสับสนว่าจะใช้ Widget ใน Flutter เนื่องจาก มีทั้ง TextFormField
และ TextField
ให้เลือกใช้งาน ในบทความนี้เราจะมาหาคำตอบกัน
TLDR;
TextField
= Text Field ตามหลัก Material
TextFormField
= TextField
ที่ถูก Wrap ไว้ใน FormField
TextField
TextField
เป็น Widget สำหรับแสดงผลช่องรับข้อมูลนำเข้าที่มีรูปแบบเป็นข้อความสั้น ๆ หรือตัวเลขให้ผู้ใช้งานได้กรอกตามหลักของ Material Design (เพราะฉะนั้นถ้าจะใช้ TextField
ต้องมี Ancestor Widget อย่างน้อยหนึ่งตัวที่ Inherit มาจาก Material
แต่โดยทั่วไปเรามักจะไม่ค่อยเจอปัญหานี้กันหากพัฒนา Application ด้วย Material
อยู่แล้ว ยกเว้นกรณีที่มีการ Custom Widget ขึ้นมาเอง หรือใช้ Cupertino (iOS Style Widget)) โดยที่เจ้า Widget ตัวนี้ก็มาพร้อมความสามารถที่เราสามารถแปะ Controller เข้าไปได้ ซึ่งเมื่อมี Controller จะทำให้เรามี Flexibility มากขึ้นในการ Observe ค่า หรือกำหนด Initial Value ต่าง ๆ อย่างไรก็ตามหากใช้งาน Controller (TextEditingController
) เมื่อใช้งานเสร็จสิ้นก็อย่าลืม dispose เพื่อคืน Resource ด้วย
ตัวอย่างโค้ด
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController _controller;
void initState() {
super.initState();
_controller = TextEditingController();
}
void dispose() {
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextField(
controller: _controller,
onSubmitted: (value) {
print(value);
},
),
),
);
}
}
TextFormField
โดยทั่วไปแล้ว Input Field ต่าง ๆ ใน Flutter เรามักไม่ใช้งานกันเดี่ยว ๆ เนื่องจากเราต้องการทำ Validation ของ Field นั้น ๆ ด้วย รวมไปถึงบางครั้งเอาต้องการที่จะได้ค่าของ Input ทุก Field ที่เกี่ยวข้องกัน เพื่อทำอะไรบางอย่าง ทำให้เรามักจะ Wrap Widgets ทั้งหมดที่เป็น Input และเกี่ยวข้องกันไว้ใน Form
ซึ่งเป็น Widget ที่จะมาช่วยเราจัดการในส่วนนี้ ซึ่ง Form
จะกำหนดไว้ว่า Widget ที่เป็น Input และจะใช้งานกับ Form
ต้องถูก Wrap ไว้ใน FormField
ซึ่งตัว FormField
เองนั้น จริง ๆ แล้วสามารถใช้งานเดี่ยว ๆ ไม่ต้องใช้กับ Form
ก็ได้ โดย FormField
จะเพิ่มในส่วนของ Validation มาให้เราทำงานได้ง่ายยิ่งขึ้น และลด Boilerplate Code ในกรณีที่เราต้องจัดการ Validation เองทั้งหมด
ข้อสังเกตนี้เองที่ TextField
มักถูกใช้คู่กับ FormField
บ่อย ๆ ทำให้ทาง Flutter จับ Pack มาด้วยกันเป็น Widget ใหม่ที่ชื่อว่า TextFormField
นั่นเอง
ตัวอย่างโค้ด
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample',
home: Scaffold(
body: HomePage(),
),
);
}
}
class HomePage extends StatefulWidget {
@override
HomePageState createState() {
return HomePageState();
}
}
class HomePageState extends State<HomePage> {
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
validator: (value) =>
value.isEmpty ? 'Input cannot be empty!' : null,
),
MaterialButton(
color: Colors.blue,
onPressed: () {
if (_formKey.currentState.validate()) {
print('Form Complete');
}
},
child: Text('Submit'),
),
],
),
);
}
}
Summary
หากต้องการทำ Input ที่เป็น UI เฉย ๆ หรือมีแค่ TextField
เดี่ยว ๆ ไม่ต้องการทำ Validation อะไร สามารถใช้ TextField ได้เลย แต่หากต้องการใช้งานคู่กับ Form หรือ FormField
ควรใช้ TextFormField