setState() เป็นเมธอดหลักของ State ใน Flutter ที่แจ้งให้เฟรมเวิร์กทราบเกี่ยวกับการเปลี่ยนแปลงข้อมูลและกระตุ้นการสร้างอินเทอร์เฟซใหม่ ตามเอกสารทางการของ Flutter (Flutter.dev, 2026) setState เป็นกลไกปฏิกิริยาหลักใน StatefulWidget: หากไม่มีการเรียกใช้ UI จะไม่ทราบเกี่ยวกับการเปลี่ยนแปลงของฟิลด์ State และจะคงอยู่ในสถานะก่อนหน้า เมธอดนี้รับ VoidCallback ซึ่งภายในนั้นนักพัฒนาแก้ไขฟิลด์ที่เปลี่ยนแปลงได้ หลังจากนั้น Flutter จะเรียก build โดยอัตโนมัติเพื่อสร้างวิดเจ็ตใหม่
ประเด็นสำคัญ
setState() เป็นเมธอดในตัวของคลาส State ใน Flutter ที่ออกแบบมาเพื่อแจ้งให้เฟรมเวิร์กทราบว่าสถานะภายในของวิดเจ็ตเปลี่ยนแปลงและจำเป็นต้องสร้าง UI ใหม่ หากไม่เรียก setState Flutter จะไม่ทราบเกี่ยวกับการเปลี่ยนแปลง — แม้ว่าฟิลด์ State จะถูกแก้ไขแล้ว อินเทอร์เฟซก็จะไม่เปลี่ยนแปลงจนกว่าจะมีการสร้างใหม่โดยบังคับครั้งถัดไปโดยวิดเจ็ตแม่
ลายเซ็นเมธอด: void setState(VoidCallback fn) Callback ถูกดำเนินการแบบประสานเวลาภายใน setState และหลังจากเสร็จสิ้นเท่านั้น State จึงถูกทำเครื่องหมายว่าสกปรก สิ่งนี้รับประกันว่าการเปลี่ยนแปลงทั้งหมดถูกนำไปใช้แบบอะตอมมิกก่อนการสร้างใหม่ ตามข้อกำหนดภาษา Dart (Dart Team, 2026) ความเป็นอะตอมมิกของ setState ป้องกันสภาวะการแข่งขันที่ build อาจเห็นสถานะที่ถูกอัปเดตบางส่วน
setState ไม่รับอาร์กิวเมนต์ ไม่คืนค่า และไม่สามารถแทนที่ได้ เป็นเมธอด final (ปิดผนึก) ของคลาส State นักพัฒนาไม่สามารถเปลี่ยนพฤติกรรมของมันได้ — ใช้ได้ตามวัตถุประสงค์เท่านั้น การพยายามเรียก setState นอก State (เช่น จากคลาสอื่น) เป็นไปไม่ได้เนื่องจากเมธอดถูกประกาศในคลาส State
ความเข้าใจผิด ที่พบบ่อยคือคิดว่า setState เปลี่ยนสถานะด้วยตัวเอง สิ่งนี้ไม่เป็นความจริง setState เพียงเรียก callback ที่ส่งเข้าไป (ซึ่งนักพัฒนาแก้ไขฟิลด์) จากนั้นส่งสัญญาณให้เฟรมเวิร์กทราบถึงความจำเป็นของ build Callback เป็นสิ่งจำเป็น — การส่ง null หรือ callback ว่างจะทำให้เกิดข้อผิดพลาด
กลไกการทำงานของ setState() สามารถแบ่งออกเป็นสี่ขั้นตอน ขั้นแรก — เรียกเมธอดพร้อม callback ขั้นที่สอง — การดำเนินการ callback แบบประสานเวลา ซึ่งภายในนั้นฟิลด์ State จะถูกแก้ไข ขั้นที่สาม — State ถูกทำเครื่องหมายว่าสกปรกในฟิลด์พิเศษ _dirty ขั้นที่สี่ — เมื่อสิ้นสุดไมโครทาสก์ปัจจุบัน Flutter จะวนผ่านองค์ประกอบสกปรกทั้งหมดและเรียก build ตามลำดับที่ปรากฏในทรี
รายละเอียดสำคัญ: setState ไม่เรียก build ทันที Flutter ใช้กลยุทธ์การอัปเดตแบบแบตช์: องค์ประกอบสกปรกทั้งหมดถูกรวบรวมและสร้างใหม่ในเฟรมเดียว ซึ่งหมายความว่าหาก setState ถูกเรียกหลายครั้งภายในบล็อกประสานเวลาเดียวกัน build จะดำเนินการเพียงครั้งเดียว — หลังจากการเปลี่ยนแปลงทั้งหมดเสร็จสมบูรณ์ การปรับปรุงนี้ป้องกันการสร้างใหม่หลายครั้งต่อเฟรม
ตาม Flutter Engine Team (Google, 2025) กลไกแฟล็กสกปรกขึ้นอยู่กับการผ่าน BuildOwner._dirtyElements แต่ละ StatefulElement ที่สกปรกจะถูกเพิ่มในรายการและประมวลผลในขั้นตอนการอัปเดตเฟรม หากวิดเจ็ตถูกลบออกจากทรีก่อนการประมวลผล มันจะถูกแยกออกจากรายการองค์ประกอบสกปรกโดยอัตโนมัติ
ตัวอย่างพื้นฐานของ setState() พร้อมการเพิ่มตัวนับ แสดงการใช้ที่ถูกต้อง: การแก้ไขฟิลด์ภายใน callback:
class _CounterState extends State<CounterWidget> {
int _count = 0;
void _increment() {
setState(() {
_count++; // การแก้ไขฟิลด์ภายใน callback
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _increment,
child: Text('$_count'),
);
}
}
ตัวอย่างพร้อมฟิลด์ข้อความและตัวควบคุม — setState() สำหรับการจัดการการมองเห็นรหัสผ่าน:
class _PasswordFieldState extends State<PasswordField> {
bool _obscured = true;
final _controller = TextEditingController();
void _toggleVisibility() {
setState(() {
_obscured = !_obscured;
});
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _controller,
obscureText: _obscured,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(_obscured ? Icons.visibility : Icons.visibility_off),
onPressed: _toggleVisibility,
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
ในตัวอย่างนี้ setState() เปลี่ยนเฉพาะฟิลด์บูลีน _obscured ซึ่งกระตุ้นการสร้าง TextField ใหม่พร้อมไอคอนใหม่และโหมดการแสดงผล ตัวควบคุมข้อความไม่ได้ถูกสร้างใหม่ — มันถูกเริ่มต้นครั้งเดียวใน initState และปล่อยใน dispose
หากคุณต้องการเปลี่ยนหลายฟิลด์ การเปลี่ยนแปลงทั้งหมดควรทำภายใน setState เดียว สิ่งนี้รับประกันว่า build จะเห็นสถานะที่สอดคล้องกัน:
setState(() {
_isLoading = false;
_items = newItems;
_error = null;
});
สามฟิลด์ถูกเปลี่ยนใน callback เดียว — build จะดำเนินการหนึ่งครั้งและเห็นการเปลี่ยนแปลงทั้งหมดพร้อมกัน หากแต่ละการเรียกเป็น setState แยกกัน build ก็ยังคงดำเนินการเพียงครั้งเดียวเนื่องจากการประมวลผลแบบแบตช์ขององค์ประกอบสกปรก
หนึ่งในความละเอียดอ่อนที่สำคัญที่สุดของ setState() คือพฤติกรรมกับ การดำเนินการแบบไม่ประสานเวลา Callback ของ setState ถูกดำเนินการแบบประสานเวลา แต่ถ้า await ถูกเรียกภายในนั้น โค้ดหลัง await จะถูกดำเนินการหลังจาก setState ทำงานเสร็จแล้ว ซึ่งหมายความว่าการเปลี่ยนแปลงฟิลด์หลัง await จะไม่ถูกจับโดย setState ปัจจุบัน
วิธีการที่ถูกต้อง: ดำเนินการแบบไม่ประสานเวลาทำงานนอก setState และ setState จะถูกเรียกหลังจากเสร็จสมบูรณ์ โค้ดทั้งหมดระหว่างการรับผลลัพธ์และการเรียก setState จะทำงานในบริบทแบบประสานเวลาหลัง await:
// ถูกต้อง: await นอก setState
Future<void> _loadData() async {
final result = await ApiService.fetchData();
setState(() {
_data = result;
_isLoading = false;
});
}
// ผิด: await ภายใน setState — ไม่รับประกันการอัปเดต
void _loadDataWrong() {
setState(() async {
_data = await ApiService.fetchData(); // setState คืนค่าก่อน await เสร็จสมบูรณ์
_isLoading = false; // โค้ดนี้ไม่ถูกจับโดย setState
});
}
ตามเอกสาร Flutter (Dart async patterns, 2026) การส่ง callback แบบไม่ประสานเวลาให้ setState เป็นสิ่งที่ไม่ควรทำเพราะ setState คาดหวัง VoidCallback (ฟังก์ชันแบบประสานเวลา) ในขณะที่ฟังก์ชันแบบไม่ประสานเวลาคืนค่า Future ซึ่งถูกละเว้น การเปลี่ยนแปลงหลัง await แรกใน callback ดังกล่าวจะไม่ได้รับการจัดการอย่างถูกต้องโดยเฟรมเวิร์ก
ก่อนเรียก setState() หลังดำเนินการแบบไม่ประสานเวลา ให้ตรวจสอบ mounted เสมอ:
if (mounted) {
setState(() => _data = data);
}
หากวิดเจ็ตถูกลบออกจากทรีระหว่างดำเนินการแบบไม่ประสานเวลา mounted จะกลายเป็น false และ setState จะไม่ถูกเรียก สิ่งนี้ป้องกันข้อยกเว้นและการรั่วไหลของทรัพยากร
setState() เป็นกลไกที่สะดวกแต่มีต้นทุนสูงหากใช้โดยไม่คิด แต่ละครั้งที่เรียก setState จะสร้างวิดเจ็ตทั้งหมดและลูกหลานทั้งหมดใหม่ (หากไม่ใช่ const) ในทรีลึกหรือการเรียกบ่อยครั้ง สิ่งนี้อาจทำให้ FPS ลดลง
กลยุทธ์การปรับปรุงหลัก: ลดพื้นที่การสร้างใหม่ (แยกส่วน UI ที่เปลี่ยนแปลงได้เป็น StatefulWidget แยกต่างหาก) ใช้ const สำหรับลูกที่ไม่เปลี่ยนแปลง และหลีกเลี่ยงการเรียก setState ในวิดเจ็ตแม่หากมีรายละเอียด UX เล็กน้อยเท่านั้นที่เปลี่ยนไป หากสถานะถูกอัปเดตด้วยความถี่สูง (แอนิเมชัน, สตรีมข้อมูล) ให้พิจารณา AnimatedBuilder หรือ ValueListenableBuilder
ตาม Flutter Performance Best Practices (Flutter.dev, กุมภาพันธ์ 2026) การทำโปรไฟล์แอปพลิเคชันจริงแสดงให้เห็นว่าสูงถึง 40% ของการเรียก setState ทั้งหมดสามารถแทนที่ด้วยวิดเจ็ตลูกแบบ const หรือตัวสร้างแบบปฏิกิริยา (StreamBuilder, FutureBuilder) ซึ่งลดเวลาเฉลี่ยในการสร้างเฟรมลง 15–25%
| สถานการณ์ | ทางเลือก | ข้อดี |
|---|---|---|
| แอนิเมชัน | AnimatedBuilder | สร้างเฉพาะวิดเจ็ตที่เคลื่อนไหวใหม่ |
| สตรีมข้อมูล | StreamBuilder | ตอบสนองต่อแต่ละองค์ประกอบสตรีม |
| ผลลัพธ์ในอนาคต | FutureBuilder | จัดการสถานะโหลด/ข้อผิดพลาด |
| ค่าท้องถิ่น | ValueListenableBuilder | ตอบสนองต่อการเปลี่ยนแปลงค่าเดียว |
แม้จะมีความหลากหลายของ setState() ในโปรเจกต์ขนาดใหญ่จะใช้สำหรับสถานะท้องถิ่นเป็นหลัก สำหรับสถานะทั่วโลกหรือสถานะที่ใช้ร่วมกัน จะใช้โซลูชันเฉพาะทาง ซึ่งแต่ละโซลูชันแทนที่หรือห่อหุ้ม setState
Provider ใช้ ChangeNotifier + notifyListeners เป็นอะนาล็อกของ setState แต่มีความสามารถในการสมัครสมาชิกหลายวิดเจ็ต Bloc ใช้ Streams — สถานะเปลี่ยนแปลงโดยการเพิ่มเหตุการณ์ไปยัง StreamController Riverpod รวมวิธีการต่าง ๆ โดยให้การจัดการทั้งแบบท้องถิ่น (StateProvider) และแบบไม่ประสานเวลา (AsyncNotifier) โดยไม่ผูกมัดกับ StatefulWidget ทั้งสามวิธีขจัดความจำเป็นในการเรียก setState ด้วยตนเอง — การอัปเดต UI เกิดขึ้นโดยอัตโนมัติเมื่อข้อมูลเปลี่ยนแปลง
ตามแบบสำรวจชุมชน Flutter 2025 (Flutter Foundation, ธันวาคม 2025) 74% ของนักพัฒนาใช้เครื่องมือจัดการสถานะอย่างน้อยหนึ่งอย่างนอกเหนือจาก setState ในขณะเดียวกัน 92% ยังคงใช้ setState สำหรับข้อมูลท้องถิ่นของฟิลด์ข้อความ, ช่องทำเครื่องหมาย หรือตัวนับง่าย ๆ — ถือเป็นแนวทางปฏิบัติที่ดีที่สุด
ข้อผิดพลาดแรกและอันตรายที่สุดคือการเรียก setState หลัง dispose การดำเนินการแบบไม่ประสานเวลาเริ่มต้นใน initState ผู้ใช้ออกจากหน้าจอ วิดเจ็ตถูกลบ และ callback แบบไม่ประสานเวลาเรียก setState — แอป crash ด้วยข้อยกเว้น วิธีแก้ไข — ตรวจสอบ mounted ก่อนเรียกเสมอ
ข้อผิดพลาดที่สองคือการเรียก setState ภายใน build สิ่งนี้นำไปสู่ลูปไม่สิ้นสุด: build → setState → สกปรก → build → setState → ... Flutter ไม่บล็อกการเรียกดังกล่าว (คุณจะได้รับ StackOverflowError) setState สามารถเรียกได้เพื่อตอบสนองต่อเหตุการณ์เท่านั้น (กดปุ่ม, Future เสร็จสมบูรณ์, ข้อมูลจากสตรีม)
ข้อผิดพลาดที่สามคือการแก้ไขฟิลด์ State โดยไม่เรียก setState นักพัฒนาเขียน _count++ และคาดหวังให้ UI อัปเดต Flutter ไม่สามารถติดตามการเปลี่ยนแปลงฟิลด์โดยอัตโนมัติ — มันต้องการสัญญาณที่ชัดเจนผ่าน setState นี่คือความแตกต่างพื้นฐานจากเฟรมเวิร์กแบบปฏิกิริยาอย่าง Vue.js ซึ่งการเปลี่ยนแปลงข้อมูลกระตุ้นการอัปเดตโดยอัตโนมัติ
ข้อผิดพลาดที่สี่คือการเรียก setState ด้วย callback แบบไม่ประสานเวลา (async lambda) ตามที่อธิบายในส่วนความไม่ประสานเวลา การเปลี่ยนแปลงหลัง await จะไม่ถูกจับ ซึ่งนำไปสู่บั๊กที่ยากต่อการทำซ้ำ ใช้ callback แบบประสานเวลาและเรียก setState หลัง await
mounted เสมอใน callback แบบไม่ประสานเวลาคำถามที่พบบ่อย
setState() แจ้ง Flutter ว่าข้อมูลภายในของ StatefulWidget เปลี่ยนแปลงและจำเป็นต้องสร้าง UI ใหม่ เมธอดรับ callback ดำเนินการแบบประสานเวลา ทำเครื่องหมายวิดเจ็ตว่าสกปรก และกำหนดการเรียก build ในเฟรมถัดไป
UI จะไม่อัปเดต Flutter ไม่ติดตามการเปลี่ยนแปลงฟิลด์โดยอัตโนมัติ ค่าฟิลด์เปลี่ยนในหน่วยความจำแต่วิดเจ็ตยังคงอยู่ในสถานะก่อนหน้าจนกว่าจะมีการสร้างใหม่โดยบังคับครั้งถัดไปโดยวิดเจ็ตแม่
ไม่ได้ สิ่งนี้นำไปสู่ลูปไม่สิ้นสุด: build เรียก setState ซึ่งทำเครื่องหมายวิดเจ็ตว่าสกปรกและเรียก build อีกครั้ง Flutter ไม่บล็อกสถานการณ์นี้ — แอปจะ crash ด้วย StackOverflowError
build จะดำเนินการ หนึ่งครั้ง Flutter รวบรวมองค์ประกอบสกปรกทั้งหมดและสร้างใหม่เป็นแบตช์เมื่อสิ้นสุดเฟรม การเรียก setState ครั้งที่สองก่อนการประมวลผลเพียงเพิ่มองค์ประกอบในรายการองค์ประกอบสกปรกเดียวกัน — ไม่มีการสร้างใหม่ซ้ำ
mounted เป็นแฟล็กบูลีนที่บ่งชี้ว่าวิดเจ็ตยังอยู่ในทรี หากเรียก setState หลังดำเนินการแบบไม่ประสานเวลาโดยไม่ตรวจสอบ mounted และวิดเจ็ตถูกลบไปแล้ว — แอป crash ด้วยข้อยกเว้น “setState called after dispose”
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ