setState() — สาระสำคัญ กลไกการทำงาน และการประยุกต์ใช้

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-07-01 เวลาอ่าน: 9 นาที

setState() เป็นเมธอดหลักของ State ใน Flutter ที่แจ้งให้เฟรมเวิร์กทราบเกี่ยวกับการเปลี่ยนแปลงข้อมูลและกระตุ้นการสร้างอินเทอร์เฟซใหม่ ตามเอกสารทางการของ Flutter (Flutter.dev, 2026) setState เป็นกลไกปฏิกิริยาหลักใน StatefulWidget: หากไม่มีการเรียกใช้ UI จะไม่ทราบเกี่ยวกับการเปลี่ยนแปลงของฟิลด์ State และจะคงอยู่ในสถานะก่อนหน้า เมธอดนี้รับ VoidCallback ซึ่งภายในนั้นนักพัฒนาแก้ไขฟิลด์ที่เปลี่ยนแปลงได้ หลังจากนั้น Flutter จะเรียก build โดยอัตโนมัติเพื่อสร้างวิดเจ็ตใหม่

ประเด็นสำคัญ

  • setState() — เมธอดของ State ที่ทำเครื่องหมายวิดเจ็ตว่าสกปรก (dirty) และกำหนดการสร้าง UI ใหม่ในเฟรมถัดไป
  • Callback — setState รับ VoidCallback ซึ่งภายในนั้นควรทำการเปลี่ยนแปลงฟิลด์ State ทั้งหมดที่ส่งผลต่ออินเทอร์เฟซ
  • ความไม่ประสานเวลา — setTimeout หรือ Future ภายใน setState ไม่รับประกันการประสานเวลา; การกลายพันธุ์หลัง await ควรอยู่ใน setState อื่น
  • ประสิทธิภาพ — ทุกครั้งที่เรียก setState จะสร้างวิดเจ็ตทั้งหมดใหม่; เพื่อลดผลกระทบให้ใช้วิดเจ็ตลูกแบบ const
  • mounted — ก่อนเรียก setState ใน callback แบบไม่ประสานเวลา ให้ตรวจสอบ mounted เสมอ มิฉะนั้น — ข้อยกเว้น

setState() คืออะไร?

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 เปลี่ยนสถานะด้วยตัวเอง สิ่งนี้ไม่เป็นความจริง setState เพียงเรียก callback ที่ส่งเข้าไป (ซึ่งนักพัฒนาแก้ไขฟิลด์) จากนั้นส่งสัญญาณให้เฟรมเวิร์กทราบถึงความจำเป็นของ build Callback เป็นสิ่งจำเป็น — การส่ง null หรือ callback ว่างจะทำให้เกิดข้อผิดพลาด

setState() ทำงานอย่างไร?

กลไกการทำงานของ setState() สามารถแบ่งออกเป็นสี่ขั้นตอน ขั้นแรก — เรียกเมธอดพร้อม callback ขั้นที่สอง — การดำเนินการ callback แบบประสานเวลา ซึ่งภายในนั้นฟิลด์ State จะถูกแก้ไข ขั้นที่สาม — State ถูกทำเครื่องหมายว่าสกปรกในฟิลด์พิเศษ _dirty ขั้นที่สี่ — เมื่อสิ้นสุดไมโครทาสก์ปัจจุบัน Flutter จะวนผ่านองค์ประกอบสกปรกทั้งหมดและเรียก build ตามลำดับที่ปรากฏในทรี

รายละเอียดสำคัญ: setState ไม่เรียก build ทันที Flutter ใช้กลยุทธ์การอัปเดตแบบแบตช์: องค์ประกอบสกปรกทั้งหมดถูกรวบรวมและสร้างใหม่ในเฟรมเดียว ซึ่งหมายความว่าหาก setState ถูกเรียกหลายครั้งภายในบล็อกประสานเวลาเดียวกัน build จะดำเนินการเพียงครั้งเดียว — หลังจากการเปลี่ยนแปลงทั้งหมดเสร็จสมบูรณ์ การปรับปรุงนี้ป้องกันการสร้างใหม่หลายครั้งต่อเฟรม

ตาม Flutter Engine Team (Google, 2025) กลไกแฟล็กสกปรกขึ้นอยู่กับการผ่าน BuildOwner._dirtyElements แต่ละ StatefulElement ที่สกปรกจะถูกเพิ่มในรายการและประมวลผลในขั้นตอนการอัปเดตเฟรม หากวิดเจ็ตถูกลบออกจากทรีก่อนการประมวลผล มันจะถูกแยกออกจากรายการองค์ประกอบสกปรกโดยอัตโนมัติ

การรับประกันของ setState

  • Callback ถูกดำเนินการ แบบประสานเวลา ก่อนการทำเครื่องหมายสกปรก
  • build ถูกเรียก ไม่เกินหนึ่งครั้ง ต่อเฟรม (แม้จะมีการเรียก setState หลายครั้ง)
  • การอัปเดต UI เกิดขึ้น ในเฟรมถัดไป (โดยทั่วไป ~16ms ที่ 60 FPS)
  • หลัง dispose การเรียก setState ต้องห้าม — จะโยนข้อยกเว้น
  • ระหว่าง build การเรียก setState ต้องห้าม — ลูปไม่สิ้นสุด

ตัวอย่างโค้ด Dart

ตัวอย่างพื้นฐานของ setState() พร้อมการเพิ่มตัวนับ แสดงการใช้ที่ถูกต้อง: การแก้ไขฟิลด์ภายใน callback:

dart
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() สำหรับการจัดการการมองเห็นรหัสผ่าน:

dart
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 เดียว

หากคุณต้องการเปลี่ยนหลายฟิลด์ การเปลี่ยนแปลงทั้งหมดควรทำภายใน setState เดียว สิ่งนี้รับประกันว่า build จะเห็นสถานะที่สอดคล้องกัน:

dart
setState(() {
  _isLoading = false;
  _items = newItems;
  _error = null;
});

สามฟิลด์ถูกเปลี่ยนใน callback เดียว — build จะดำเนินการหนึ่งครั้งและเห็นการเปลี่ยนแปลงทั้งหมดพร้อมกัน หากแต่ละการเรียกเป็น setState แยกกัน build ก็ยังคงดำเนินการเพียงครั้งเดียวเนื่องจากการประมวลผลแบบแบตช์ขององค์ประกอบสกปรก

ความไม่ประสานเวลาและ setState

หนึ่งในความละเอียดอ่อนที่สำคัญที่สุดของ setState() คือพฤติกรรมกับ การดำเนินการแบบไม่ประสานเวลา Callback ของ setState ถูกดำเนินการแบบประสานเวลา แต่ถ้า await ถูกเรียกภายในนั้น โค้ดหลัง await จะถูกดำเนินการหลังจาก setState ทำงานเสร็จแล้ว ซึ่งหมายความว่าการเปลี่ยนแปลงฟิลด์หลัง await จะไม่ถูกจับโดย setState ปัจจุบัน

วิธีการที่ถูกต้อง: ดำเนินการแบบไม่ประสานเวลาทำงานนอก setState และ setState จะถูกเรียกหลังจากเสร็จสมบูรณ์ โค้ดทั้งหมดระหว่างการรับผลลัพธ์และการเรียก setState จะทำงานในบริบทแบบประสานเวลาหลัง await:

dart
// ถูกต้อง: 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 ดังกล่าวจะไม่ได้รับการจัดการอย่างถูกต้องโดยเฟรมเวิร์ก

การตรวจสอบ mounted ในสถานการณ์ไม่ประสานเวลา

ก่อนเรียก setState() หลังดำเนินการแบบไม่ประสานเวลา ให้ตรวจสอบ mounted เสมอ:

dart
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%

เมื่อใดที่ setState ซ้ำซ้อน

สถานการณ์ทางเลือกข้อดี
แอนิเมชันAnimatedBuilderสร้างเฉพาะวิดเจ็ตที่เคลื่อนไหวใหม่
สตรีมข้อมูลStreamBuilderตอบสนองต่อแต่ละองค์ประกอบสตรีม
ผลลัพธ์ในอนาคตFutureBuilderจัดการสถานะโหลด/ข้อผิดพลาด
ค่าท้องถิ่นValueListenableBuilderตอบสนองต่อการเปลี่ยนแปลงค่าเดียว

ทางเลือกของ setState

แม้จะมีความหลากหลายของ 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

  • สถานะถูกใช้โดยวิดเจ็ตเดียวเท่านั้น
  • ค่าบูลีนหรือตัวเลขง่าย ๆ (โฟกัส, การมองเห็น, ตัวนับ)
  • การสร้างต้นแบบและการทดลองอย่างรวดเร็ว
  • ตัวควบคุม (TextEditingController, PageController) ยังคงต้องใช้ StatefulWidget

ข้อผิดพลาดทั่วไป

ข้อผิดพลาดแรกและอันตรายที่สุดคือการเรียก 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

รายการตรวจสอบ setState ที่ปลอดภัย

  • ตรวจสอบ mounted เสมอใน callback แบบไม่ประสานเวลา
  • อย่าเรียก setState ภายใน build
  • อย่าส่ง async lambda ให้ setState
  • อย่าแก้ไขฟิลด์ State นอก setState
  • หากกำลังเปลี่ยนหลายฟิลด์ — ทำใน setState เดียว

คำถามที่พบบ่อย

setState() ใน Flutter ทำอะไร?

setState() แจ้ง Flutter ว่าข้อมูลภายในของ StatefulWidget เปลี่ยนแปลงและจำเป็นต้องสร้าง UI ใหม่ เมธอดรับ callback ดำเนินการแบบประสานเวลา ทำเครื่องหมายวิดเจ็ตว่าสกปรก และกำหนดการเรียก build ในเฟรมถัดไป

จะเกิดอะไรขึ้นหากฉันไม่เรียก setState หลังจากเปลี่ยนฟิลด์?

UI จะไม่อัปเดต Flutter ไม่ติดตามการเปลี่ยนแปลงฟิลด์โดยอัตโนมัติ ค่าฟิลด์เปลี่ยนในหน่วยความจำแต่วิดเจ็ตยังคงอยู่ในสถานะก่อนหน้าจนกว่าจะมีการสร้างใหม่โดยบังคับครั้งถัดไปโดยวิดเจ็ตแม่

สามารถเรียก setState ภายใน build ได้หรือไม่?

ไม่ได้ สิ่งนี้นำไปสู่ลูปไม่สิ้นสุด: build เรียก setState ซึ่งทำเครื่องหมายวิดเจ็ตว่าสกปรกและเรียก build อีกครั้ง Flutter ไม่บล็อกสถานการณ์นี้ — แอปจะ crash ด้วย StackOverflowError

build จะดำเนินการกี่ครั้งเมื่อเรียก setState สองครั้งติดต่อกัน?

build จะดำเนินการ หนึ่งครั้ง Flutter รวบรวมองค์ประกอบสกปรกทั้งหมดและสร้างใหม่เป็นแบตช์เมื่อสิ้นสุดเฟรม การเรียก setState ครั้งที่สองก่อนการประมวลผลเพียงเพิ่มองค์ประกอบในรายการองค์ประกอบสกปรกเดียวกัน — ไม่มีการสร้างใหม่ซ้ำ

mounted คืออะไรและสำคัญต่อ setState อย่างไร?

mounted เป็นแฟล็กบูลีนที่บ่งชี้ว่าวิดเจ็ตยังอยู่ในทรี หากเรียก setState หลังดำเนินการแบบไม่ประสานเวลาโดยไม่ตรวจสอบ mounted และวิดเจ็ตถูกลบไปแล้ว — แอป crash ด้วยข้อยกเว้น “setState called after dispose”

สรุป

  • setState() — เมธอดของ State ที่แจ้ง Flutter เกี่ยวกับการเปลี่ยนแปลงข้อมูลและกระตุ้นการสร้าง UI ใหม่ในเฟรมถัดไป
  • กลไกการทำงาน — การดำเนินการ callback แบบประสานเวลา, การทำเครื่องหมาย State ว่าสกปรก, การสร้างใหม่แบบแบตช์ขององค์ประกอบสกปรกทั้งหมดเมื่อสิ้นสุดเฟรม
  • ความไม่ประสานเวลา — callback แบบไม่ประสานเวลาใน setState ใช้ไม่ได้; await ต้องอยู่ภายนอก และ setState หลังจากได้รับผลลัพธ์
  • mounted — การตรวจสอบที่จำเป็นก่อน setState ในดำเนินการแบบไม่ประสานเวลาเพื่อป้องกันข้อยกเว้น
  • การปรับปรุง — ลดพื้นที่สร้างใหม่ผ่านวิดเจ็ตลูกแบบ const และย้ายแอนิเมชันไปยัง AnimatedBuilder
  • ทางเลือก — สำหรับสถานะทั่วโลกใช้ Riverpod, Bloc หรือ Provider; เก็บ setState ไว้สำหรับข้อมูลท้องถิ่น
  • กฎ — อย่าเรียก setState ภายใน build, อย่าส่ง async lambda, ตรวจสอบ mounted เสมอ

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม