การสร้าง Custom Widget ที่มี State ใน Flutter
ในบทความก่อนหน้า เราได้เรียนรู้การใช้งาน Camera API ใน Flutter เพื่อถ่ายรูปและบันทึกวิดีโอ ในบทความนี้ เราจะมาเรียนรู้การสร้าง Custom Widget ที่สามารถเปลี่ยนแปลง State ได้ ซึ่งเป็นหัวใจสำคัญของการสร้างแอปพลิเคชัน Flutter ที่มีความ interactive และ dynamic
StatefulWidget คืออะไร?
ใน Flutter, Widget แบ่งออกเป็นสองประเภทหลักๆ คือ:
- StatelessWidget: Widget ที่ไม่มี state ภายในตัวเอง และไม่สามารถเปลี่ยนแปลงได้เมื่อสร้างขึ้นมาแล้ว ตัวอย่างเช่น
Text,Icon,Image - StatefulWidget: Widget ที่มี state ภายในตัวเอง และสามารถเปลี่ยนแปลงได้ตลอดเวลา ตัวอย่างเช่น
Checkbox,Slider,TextField
StatefulWidget มีความซับซ้อนกว่า StatelessWidget เล็กน้อย เพราะต้องมีคลาส State ที่จัดการ state ของ Widget นั้นๆ
โครงสร้างของ StatefulWidget
StatefulWidget ประกอบด้วยสองส่วนหลักๆ:
- StatefulWidget Class: คลาสที่ extends
StatefulWidgetและสร้าง instance ของStateclass - State Class: คลาสที่ extends
State<StatefulWidget>และจัดการ state ของ Widget รวมถึง UI ที่แสดงผล
ตัวอย่างโครงสร้างพื้นฐานของ StatefulWidget:
import 'package:flutter/material.dart';
class MyCustomWidget extends StatefulWidget {
const MyCustomWidget({Key? key}) : super(key: key);
@override
State<MyCustomWidget> createState() => _MyCustomWidgetState();
}
class _MyCustomWidgetState extends State<MyCustomWidget> {
@override
Widget build(BuildContext context) {
return Container(); // Replace with your actual UI
}
}
ในตัวอย่างนี้:
MyCustomWidgetคือ StatefulWidget class ที่ extendsStatefulWidget_MyCustomWidgetStateคือ State class ที่ extendsState<MyCustomWidget>และจัดการ state ของMyCustomWidgetcreateState()method ในMyCustomWidgetจะสร้าง instance ของ_MyCustomWidgetStatebuild()method ใน_MyCustomWidgetStateจะสร้าง UI ที่แสดงผล โดยใช้ state ที่เก็บไว้
setState() Method
setState() เป็น method ที่สำคัญที่สุดในการจัดการ state ใน StatefulWidget เมื่อ state ของ Widget เปลี่ยนแปลง เราจะต้องเรียก setState() เพื่อบอกให้ Flutter rebuild UI ใหม่ด้วย state ที่อัปเดต
ตัวอย่างการใช้งาน setState():
import 'package:flutter/material.dart';
class MyCounterWidget extends StatefulWidget {
const MyCounterWidget({Key? key}) : super(key: key);
@override
State<MyCounterWidget> createState() => _MyCounterWidgetState();
}
class _MyCounterWidgetState extends State<MyCounterWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Counter App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
ในตัวอย่างนี้:
- เราสร้าง StatefulWidget ชื่อ
MyCounterWidgetที่มี state เป็นตัวแปร_counter - เมื่อกดปุ่ม FloatingActionButton,
_incrementCounter()จะถูกเรียก _incrementCounter()จะเรียกsetState()เพื่ออัปเดตค่าของ_counterและบอกให้ Flutter rebuild UI ใหม่build()method จะใช้ค่า_counterที่อัปเดตเพื่อแสดงผลตัวเลขบนหน้าจอ
สิ่งที่ควรจำ:
- การเรียก
setState()จะทำให้ Flutter rebuild เฉพาะส่วนของ Widget tree ที่เกี่ยวข้องกับการเปลี่ยนแปลง state เท่านั้น ไม่ได้ rebuild ทั้งหน้าจอ ซึ่งช่วยให้แอปพลิเคชันทำงานได้อย่างรวดเร็ว - ควรเรียก
setState()ภายใน State Class เท่านั้น
BuildContext คืออะไร?
BuildContext เป็น interface ที่ให้ข้อมูลเกี่ยวกับตำแหน่งของ Widget ใน Widget tree และช่วยให้ Widget สามารถเข้าถึง resources ต่างๆ เช่น Theme, MediaQuery, Localizations ได้
BuildContext จะถูกส่งผ่านไปยัง build() method ของทุก Widget:
@override
Widget build(BuildContext context) {
// Use context to access resources
final theme = Theme.of(context);
final size = MediaQuery.of(context).size;
return Container(
width: size.width,
height: size.height,
color: theme.primaryColor,
);
}
ในตัวอย่างนี้:
- เราใช้
contextเพื่อเข้าถึงThemeและMediaQuery Theme.of(context)จะคืนค่าThemeDataที่ใช้กับ Widget tree ปัจจุบันMediaQuery.of(context)จะคืนค่าข้อมูลเกี่ยวกับหน้าจอ เช่น ขนาด, orientation, pixel density
BuildContext มีประโยชน์อย่างมากในการสร้าง Widget ที่ปรับตัวตามสภาพแวดล้อมต่างๆ เช่น ขนาดหน้าจอ, theme, ภาษา
ตัวอย่างการสร้าง Custom Widget ที่มี State และใช้ BuildContext
ลองสร้าง Custom Widget ที่แสดงข้อความและเปลี่ยนสีเมื่อกดปุ่ม:
import 'package:flutter/material.dart';
import 'dart:math';
class MyColorChangingText extends StatefulWidget {
const MyColorChangingText({Key? key}) : super(key: key);
@override
State<MyColorChangingText> createState() => _MyColorChangingTextState();
}
class _MyColorChangingTextState extends State<MyColorChangingText> {
Color _textColor = Colors.black;
void _changeColor() {
setState(() {
_textColor = Color((Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0);
});
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _changeColor,
child: Text(
'แตะเพื่อเปลี่ยนสี',
style: TextStyle(
color: _textColor,
fontSize: 20,
),
),
);
}
}
ในตัวอย่างนี้:
- เราสร้าง StatefulWidget ชื่อ
MyColorChangingTextที่มี state เป็นตัวแปร_textColor - เมื่อแตะที่ข้อความ,
_changeColor()จะถูกเรียก _changeColor()จะสร้างสีใหม่แบบสุ่ม และเรียกsetState()เพื่ออัปเดตค่าของ_textColorbuild()method จะใช้ค่า_textColorที่อัปเดตเพื่อแสดงผลข้อความด้วยสีใหม่- เราใช้
GestureDetectorเพื่อตรวจจับการแตะที่ข้อความ
การใช้งาน Widget นี้ในหน้าจอ:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Color Changing App',
home: Scaffold(
appBar: AppBar(
title: const Text('Color Changing App'),
),
body: const Center(
child: MyColorChangingText(),
),
),
);
}
}
สรุป
ในบทความนี้ เราได้เรียนรู้เกี่ยวกับ StatefulWidget, setState(), และ BuildContext ซึ่งเป็นพื้นฐานสำคัญในการสร้างแอปพลิเคชัน Flutter ที่มีความ interactive และ dynamic การเข้าใจ concepts เหล่านี้จะช่วยให้คุณสามารถสร้าง Custom Widget ที่ซับซ้อนและปรับแต่งได้ตามต้องการ
ในบทความถัดไป เราจะเรียนรู้การสร้าง Layout ที่ซับซ้อนด้วย Custom Layouts ซึ่งจะช่วยให้คุณสามารถควบคุมการจัดวาง Widget บนหน้าจอได้อย่างละเอียด