StatefulWidget: คืออะไร วงจรชีวิตและหลักการทำงาน

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

StatefulWidget คือวิดเจ็ต Flutter ที่มีสถานะที่เปลี่ยนแปลงได้ ทำให้ UI สามารถตอบสนองต่อการกระทำของผู้ใช้ เหตุการณ์แบบไม่พร้อมกัน และสตรีมข้อมูล ตามเอกสารอย่างเป็นทางการของ Flutter (Flutter.dev, 2026) StatefulWidget ถูกใช้สำหรับองค์ประกอบเชิงโต้ตอบทั้งหมดของแอปพลิเคชัน: ฟอร์มป้อนข้อมูล แอนิเมชัน ช่องทำเครื่องหมาย สวิตช์ และหน้าจอที่โหลดข้อมูลจากเครือข่าย แตกต่างจาก StatelessWidget มันสร้างออบเจ็กต์ State แยกต่างหากที่คงอยู่ตลอดวงจรชีวิตทั้งหมดและสามารถสร้างใหม่ได้โดยไม่ต้องสร้างวิดเจ็ตใหม่

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

  • StatefulWidget คือวิดเจ็ตที่สามารถเปลี่ยนสถานะระหว่างทำงาน ทำให้เกิดการสร้าง UI ใหม่ผ่าน setState
  • วงจรชีวิต — StatefulWidget ผ่านขั้นตอน createState, initState, didChangeDependencies, build, didUpdateWidget, dispose
  • ออบเจ็กต์ State คือออบเจ็กต์แยกต่างหากที่เก็บสถานะและดำรงอยู่อย่างอิสระจากวิดเจ็ตตลอดอายุของมัน
  • setState เป็นวิธีที่ถูกต้องเพียงวิธีเดียวในการแจ้งให้ Flutter ทราบว่าวิดเจ็ตต้องการสร้างใหม่หลังจากข้อมูลเปลี่ยนแปลง
  • ประสิทธิภาพ — การใช้ StatefulWidget มากเกินไปจะเพิ่มการใช้หน่วยความจำและเวลาในการเรนเดอร์

StatefulWidget คืออะไร?

StatefulWidget คือคลาส Flutter ที่สามารถเปลี่ยนสถานะเพื่อตอบสนองต่อการกระทำของผู้ใช้ เหตุการณ์ของระบบ หรือการดำเนินการแบบไม่พร้อมกัน ต่างจาก StatelessWidget ตรงที่ StatefulWidget ไม่ได้เรนเดอร์โดยตรง — มันสร้างออบเจ็กต์ State ที่รับผิดชอบการเรนเดอร์ การแยกออกเป็นสองคลาส (Widget และ State) ช่วยให้ Flutter สามารถสร้าง UI ใหม่ได้โดยไม่ต้องสร้างวิดเจ็ตใหม่ ซึ่งให้ข้อได้เปรียบด้านประสิทธิภาพอย่างมากระหว่างการอัปเดตบ่อยครั้ง

สถาปัตยกรรมของ StatefulWidget เป็นไปตามรูปแบบ “การแยกสิ่งที่เปลี่ยนแปลงได้และเปลี่ยนแปลงไม่ได้”: วิดเจ็ตเองยังคงเปลี่ยนแปลงไม่ได้ (เช่น StatelessWidget) ในขณะที่สถานะที่เปลี่ยนแปลงได้ทั้งหมดจะถูกเก็บไว้ในออบเจ็กต์ State แยกต่างหาก ซึ่งช่วยให้ Flutter สามารถนำวิดเจ็ตกลับมาใช้ใหม่ได้โดยการเปรียบเทียบตามประเภทและ Key ในขณะเดียวกันก็รักษาสถานะจริงระหว่างการสร้างใหม่

ตามข้อมูลของ Google (Flutter Architectural Overview, 2026) StatefulWidget เหมาะสมที่สุดสำหรับสถานการณ์ที่สถานะเปลี่ยนแปลงมากกว่าหนึ่งครั้งในช่วงอายุของวิดเจ็ต: ฟิลด์ข้อความ แอนิเมชัน ตัวจับเวลา สตรีมข้อมูล การโหลดแบบไม่พร้อมกัน สำหรับการเริ่มต้นครั้งเดียว StatelessWidget ก็เพียงพอ

เมื่อใดที่ต้องใช้ StatefulWidget

StatefulWidget จำเป็นเมื่อวิดเจ็ตต้องตอบสนองต่อเหตุการณ์ภายนอก: การคลิกปุ่ม การเสร็จสิ้นคำขอ HTTP การอัปเดตข้อมูลฐานข้อมูล การสมัครรับข้อมูล WebSocket นอกจากนี้ยังจำเป็นสำหรับวิดเจ็ตที่มีแอนิเมชัน ฟิลด์ข้อความที่มีตัวควบคุม และส่วนประกอบที่จัดการโฟกัส หากวิดเจ็ตเพียงแสดงข้อมูลและไม่สร้างเหตุการณ์ ให้ใช้ StatelessWidget

โครงสร้างภายใน

StatefulWidget ประกอบด้วยสองคลาส: StatefulWidget เอง (เบา ไม่เปลี่ยนแปลงได้) และ State (หนัก เปลี่ยนแปลงได้) เฟรมเวิร์กสร้าง State ผ่านเมธอด createState() ซึ่งเรียกหนึ่งครั้งเมื่อแทรกเข้าไปในทรี State ได้รับการอ้างอิงถึงวิดเจ็ตผ่านคุณสมบัติ widget และสามารถเข้าถึงฟิลด์ของมันได้ตลอดเวลาในวงจรชีวิต

วงจรชีวิตของ StatefulWidget

วงจรชีวิต ของ StatefulWidget ประกอบด้วยหกขั้นตอนหลัก ซึ่งแต่ละขั้นตอนมีเมธอดที่สามารถแทนที่ได้สำหรับการทำงานเฉพาะ การทำความเข้าใจขั้นตอนเหล่านี้มีความสำคัญต่อการจัดการทรัพยากรที่เหมาะสมและการหลีกเลี่ยงการรั่วไหลของหน่วยความจำ

createState

createState เป็นเมธอดแรกของวงจรชีวิต เรียกเมื่อ StatefulWidget ถูกแทรกเข้าไปในทรี มันต้องส่งคืนอินสแตนซ์ State ใหม่ที่เชื่อมโยงกับวิดเจ็ตนี้ เมธอดนี้ถูกเรียกเพียงครั้งเดียวตลอดอายุขององค์ประกอบ สิ่งสำคัญคือต้องไม่ดำเนินการหนักที่นี่ — createState ควรเบาที่สุดเท่าที่จะทำได้

initState

initState ถูกเรียกทันทีหลังจากสร้าง State ก่อนการสร้าง UI ครั้งแรก ที่นี่ดำเนินการ: การเริ่มต้นตัวควบคุม (TextEditingController, AnimationController) การสมัครรับสตรีมข้อมูล (StreamSubscription) การตั้งค่าตัวจับเวลาและการเริ่มต้นฟิลด์เริ่มต้น ตามเอกสาร Flutter (Flutter.dev, 2026) ไม่สามารถเรียก BuildContext.of() ใน initState — ทรียังไม่ได้เมาท์อย่างสมบูรณ์

didChangeDependencies

didChangeDependencies ถูกเรียกหลังจาก initState และทุกครั้งที่การพึ่งพา InheritedWidget เปลี่ยนแปลง นี่คือสถานที่ที่เหมาะสมในการเรียก MediaQuery.of(context) หรือสมัครรับ Theme — ค่าที่อาจเปลี่ยนแปลงระหว่างการทำงานของแอปพลิเคชัน หากวิดเจ็ตใช้ InheritedWidget ตรรกะการเริ่มต้นควรอยู่ที่นี่ ไม่ใช่ใน initState

build และ didUpdateWidget

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

dispose

dispose เป็นขั้นตอนสุดท้ายของวงจรชีวิต ที่นี่ทรัพยากรทั้งหมดจะถูกปลดปล่อย: การสมัครรับสตรีมถูกยกเลิก ตัวควบคุมถูกลบ ตัวจับเวลาถูกยกเลิก การไม่เรียก dispose นำไปสู่การรั่วไหลของหน่วยความจำ หลังจาก dispose State ถือว่าตายแล้ว — การเรียก setState ภายในมันจะโยนข้อยกเว้น

StatefulWidget ทำงานอย่างไร?

กลไกการทำงานของ StatefulWidget ขึ้นอยู่กับการทำงานร่วมกันของสามเอนทิตี: Widget (คำอธิบายแบบเบา), Element (ชั้นกลาง) และ State (การจัดเก็บข้อมูล) เมื่อ Flutter พบ StatefulWidget ในคำอธิบาย มันจะสร้าง StatefulElement ซึ่งเรียก createState และเก็บการอ้างอิงไปยังออบเจ็กต์ State เมื่อผู้ปกครองสร้างใหม่ Flutter จะเปรียบเทียบวิดเจ็ตใหม่กับ Element ปัจจุบัน — หากประเภทและ Key ตรงกัน Element จะถูกอัปเดตและ State ยังคงเหมือนเดิม

สถานะจะเปลี่ยนแปลงผ่านการเรียก setState เท่านั้น ซึ่งแจ้งให้เฟรมเวิร์กทราบว่าจำเป็นต้องสร้างใหม่ สิ่งสำคัญที่ต้องเข้าใจ: setState ไม่ได้เปลี่ยนสถานะโดยอัตโนมัติ — มันเพียงทำเครื่องหมายวิดเจ็ตเป็น “สกปรก” นักพัฒนาอัปเดตฟิลด์ State อย่างอิสระในคอลแบ็กที่ส่งไปยัง setState หลังจากคอลแบ็กเสร็จสมบูรณ์ Flutter จะเรียก build และอัปเดต UI

ตามทีม Dart/Flutter (Dart Language Specification, 2026) การแยกนี้รับประกันว่าการเปลี่ยนแปลงสถานะทั้งหมดเกิดขึ้นแบบซิงโครนัสก่อนที่จะเรียก build ซึ่งกำจัดสถานการณ์ที่ UI แสดงข้อมูลที่อัปเดตบางส่วน นี่เป็นกลไกสำคัญของความสอดคล้องของอินเทอร์เฟซใน Flutter

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

มาดู StatefulWidget ง่าย ๆ — ตัวนับการคลิกปุ่ม มันสาธิตรูปแบบพื้นฐาน: การสร้าง State การเริ่มต้นฟิลด์ใน initState การเปลี่ยนแปลงผ่าน setState:

dart
class CounterScreen extends StatefulWidget {
  const CounterScreen({super.key});

  @override
  State<CounterScreen> createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  int _count = 0;

  void _increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $_count'),
        ElevatedButton(
          onPressed: _increment,
          child: const Text('Increment'),
        ),
      ],
    );
  }
}

ตัวอย่างการโหลดข้อมูลแบบไม่พร้อมกันและการจัดการวงจรชีวิต StatefulWidget โหลดข้อมูลจากเครือข่ายและแสดงสถานะการโหลด:

dart
class UserProfilePage extends StatefulWidget {
  final String userId;
  const UserProfilePage({super.key, required this.userId});

  @override
  State<UserProfilePage> createState() => _UserProfilePageState();
}

class _UserProfilePageState extends State<UserProfilePage> {
  UserModel? _user;
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _loadUser();
  }

  Future<void> _loadUser() async {
    final user = await UserService.fetchUser(widget.userId);
    setState(() {
      _user = user;
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) return const CircularProgressIndicator();
    return Text('Hello, ${_user!.name}');
  }
}

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

StatefulWidget vs StatelessWidget

การเลือกระหว่าง StatefulWidget และ StatelessWidget ไม่ใช่แค่เรื่องของการมีสถานะ StatefulWidget ให้วงจรชีวิตที่สมบูรณ์พร้อมเมธอด initState, didChangeDependencies, didUpdateWidget และ dispose ซึ่งจำเป็นสำหรับการทำงานกับตัวควบคุม แอนิเมชัน และสตรีม ในทางกลับกัน StatelessWidget ไม่มีเมธอดเหล่านี้และเบากว่าสำหรับเฟรมเวิร์กเสมอ

คำแนะนำของทีม Flutter (Flutter docs, 2026) คือการลดจำนวน StatefulWidget ในแอปพลิเคชันโดยการยกระดับสถานะขึ้นไปในทรี (State Hoisting) หรือใช้โซลูชันการจัดการสถานะ (Riverpod, Bloc, Provider) แต่ละ StatefulWidget สร้างออบเจ็กต์ State ที่มีชีวิตอยู่จนกว่าองค์ประกอบจะถูกลบ — ยิ่งมีวิดเจ็ตประเภทนี้มากเท่าไร ภาระหน่วยความจำก็ยิ่งสูงขึ้นเท่านั้น

เกณฑ์StatefulWidgetStatelessWidget
สถานะเปลี่ยนแปลงได้เปลี่ยนแปลงไม่ได้
วงจรชีวิต6 ขั้นตอนbuild เท่านั้น
ออบเจ็กต์ Stateสร้างแยกต่างหากไม่จำเป็น
setStateใช้ได้ใช้ไม่ได้
การสมัครรับinitState/disposeไม่รองรับ
ตัวสร้าง constจำกัดรองรับเต็มที่
การใช้หน่วยความจำสูงกว่าต่ำกว่า

ประสิทธิภาพและการปรับปรุง

StatefulWidget ต้องการทรัพยากรมากกว่า StatelessWidget เนื่องจากจำเป็นต้องสร้างและรักษาออบเจ็กต์ State อย่างไรก็ตาม การใช้ StatefulWidget อย่างถูกต้องจะไม่ทำให้เกิดปัญหาประสิทธิภาพหากปฏิบัติตามกฎสองสามข้อ หลีกเลี่ยงการซ้อนกันลึกของ StatefulWidget — แต่ละระดับเพิ่มค่าใช้จ่ายในการเดินทรี หัก StatefulWidget ที่ซับซ้อนออกเป็นวิดเจ็ตง่าย ๆ หลายตัว แต่ละตัวรับผิดชอบส่วนของสถานะของตนเอง

จากการวิจัยประสิทธิภาพของ Flutter (Flutter.dev, กุมภาพันธ์ 2026) สาเหตุที่พบบ่อยที่สุดของ FPS ที่ลดลงคือการเรียก setState ในวิดเจ็ตหลักที่สร้างผู้สืบทอดทั้งหมดใหม่ รวมถึง StatelessWidget ที่ไม่ได้เปลี่ยนการแสดงผล วิธีแก้ไขคือแยกส่วนที่เปลี่ยนแปลงได้ของ UI ออกเป็น StatefulWidget แยกต่างหากเพื่อให้ setState สร้างเฉพาะวิดเจ็ตที่จำเป็นขั้นต่ำเท่านั้น

การใช้ const ภายใน State เป็นอีกเทคนิคสำคัญ หากวิดเจ็ตลูกถูกประกาศเป็น const Flutter จะไม่สร้างใหม่เมื่อเรียก setState ในหลัก ซึ่งลดภาระบนเฟรมเวิร์กและลดเวลาในการเรนเดอร์เฟรม

หลีกเลี่ยง setState บ่อยครั้ง

แต่ละครั้งที่เรียก setState จะเริ่มการสร้างวิดเจ็ตใหม่ทั้งหมด หากสถานะเปลี่ยนแปลงด้วยความถี่สูง (เช่น แอนิเมชันหรือสตรีมข้อมูล) ให้พิจารณาใช้ AnimatedBuilder, ValueListenableBuilder หรือ StreamBuilder แทนการเรียก setState ด้วยตนเอง วิดเจ็ตเหล่านี้ปรับการสร้างใหม่ให้เหมาะสม โดยอัปเดตเฉพาะส่วนของ UI ที่เปลี่ยนแปลงจริงเท่านั้น

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

ข้อผิดพลาด ทั่วไปแรกกับ StatefulWidget คือการเรียก setState หลังจาก dispose เมื่อวิดเจ็ตถูกลบออกจากทรี State ถือว่าตายแล้ว และการเรียก setState ใด ๆ จะโยนข้อยกเว้น “setState called after dispose” สิ่งนี้มักเกิดขึ้นเมื่อการดำเนินการแบบไม่พร้อมกันเสร็จสิ้นหลังจากลบวิดเจ็ต วิธีแก้ไขคือตรวจสอบแฟล็ก mounted ก่อนเรียก setState หรือยกเลิกการดำเนินการแบบไม่พร้อมกันใน dispose

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

ข้อผิดพลาดที่สามคือการไม่เรียก super.initState() และ super.dispose() เมื่อแทนที่เมธอดเหล่านี้ นักพัฒนาต้องเรียกการทำงานของผู้ปกครอง หากไม่ทำเช่นนั้น เฟรมเวิร์กจะไม่สามารถจัดการสถานะของ Element ได้อย่างถูกต้อง ซึ่งนำไปสู่บักที่ติดตามยาก

คำแนะนำเพื่อหลีกเลี่ยงข้อผิดพลาด

  • ตรวจสอบ mounted ก่อน setState ในคอลแบ็กแบบไม่พร้อมกันเสมอ
  • อย่าลืมเรียก super.initState() และ super.dispose()
  • อย่าทำคำขอ HTTP โดยตรงใน build — ใช้ initState
  • ยกเลิกการสมัครรับทั้งหมดใน dispose
  • ใช้ StatefulWidget จำนวนน้อยที่สุดในโปรเจกต์ของคุณ

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

StatefulWidget แตกต่างจาก StatelessWidget อย่างไร?

StatefulWidget สามารถเปลี่ยนสถานะผ่าน setState มีวงจรชีวิต (initState, dispose) และสร้างออบเจ็กต์ State แยกต่างหาก StatelessWidget ไม่สามารถเปลี่ยนสถานะและไม่มีเมธอดวงจรชีวิต — มันเพียงแสดงข้อมูลที่ส่งมา

createState ถูกเรียกกี่ครั้ง?

createState ถูกเรียกหนึ่งครั้งต่ออินสแตนซ์ StatefulElement แม้ว่าผู้ปกครองจะสร้างใหม่หลายครั้ง ตราบใดที่ประเภทและ Key ของวิดเจ็ตไม่เปลี่ยนแปลง createState จะไม่ถูกเรียก — ใช้ออบเจ็กต์ State ที่มีอยู่

จะเกิดอะไรขึ้นหากไม่เรียก dispose?

ทรัพยากร จะไม่ถูกปลดปล่อย: ตัวควบคุมยังคงทำงานในพื้นหลัง การสมัครรับสตรีมยังคงทำงาน ตัวจับเวลาจะไม่ถูกยกเลิก สิ่งนี้นำไปสู่การรั่วไหลของหน่วยความจำและอาจทำให้เกิดการเรียก setState หลังจาก dispose ซึ่งโยนข้อยกเว้น

StatefulWidget สามารถเป็น const ได้หรือไม่?

ได้ ตัวสร้าง ของ StatefulWidget สามารถเป็น const ได้ อย่างไรก็ตาม สิ่งนี้ไม่ได้ให้ประโยชน์เท่ากับ StatelessWidget — ออบเจ็กต์ State จะยังคงถูกสร้างขึ้นเมื่อแทรกครั้งแรก const มีผลต่อวิดเจ็ตเท่านั้น (ตัวห่อแบบเบา) ไม่ใช่ State

ทำไมต้องใช้เมธอด didUpdateWidget?

didUpdateWidget ถูกเรียกเมื่อผู้ปกครองส่ง StatefulWidget ด้วยพารามิเตอร์ใหม่ สิ่งนี้จำเป็นเพื่อซิงโครไนซ์สถานะกับข้อมูลใหม่ — ตัวอย่างเช่น หาก userId ในพารามิเตอร์เปลี่ยนไป จำเป็นต้องโหลดโปรไฟล์ของผู้ใช้ใหม่

สรุป

  • StatefulWidget คือวิดเจ็ตที่มีสถานะเปลี่ยนแปลงได้ ใช้ออบเจ็กต์ State แยกต่างหากเพื่อเก็บข้อมูลและจัดการวงจรชีวิต
  • วงจรชีวิต ประกอบด้วย createState, initState, didChangeDependencies, build, didUpdateWidget และ dispose แต่ละตัวมีวัตถุประสงค์ของตัวเอง
  • setState เป็นวิธีที่ถูกต้องเพียงวิธีเดียวในการแจ้งเฟรมเวิร์กเกี่ยวกับการเปลี่ยนแปลงสถานะ หลังจากนั้น build จะถูกเรียกโดยอัตโนมัติ
  • mounted เป็นแฟล็กที่ต้องตรวจสอบก่อนเรียก setState ในการดำเนินการแบบไม่พร้อมกันเพื่อหลีกเลี่ยงข้อยกเว้นหลังจาก dispose
  • ประสิทธิภาพ — StatefulWidget ต้องการทรัพยากรมากกว่า StatelessWidget แนะนำให้ลดจำนวนลงโดยยกระดับสถานะไปยังชั้นภายนอก
  • วิดเจ็ตลูก const ภายใน State ช่วยลดปริมาณการสร้างใหม่เมื่อเรียก setState ปรับปรุงประสิทธิภาพ
  • การเลือกที่ถูกต้อง — ใช้ StatefulWidget เฉพาะเมื่อวิดเจ็ตจำเป็นต้องจัดการข้อมูลที่เปลี่ยนแปลงได้หรือการดำเนินการแบบไม่พร้อมกัน

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

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

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

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