跳到主要内容

เนื้อหาภาษาไทย / 泰语内容

系统指南 内容已审核

การใช้งาน FutureBuilder และ StreamBuilder อย่างละเอียด

บทความนี้อธิบายการใช้งาน `FutureBuilder` และ `StreamBuilder` ใน Flutter ซึ่งเป็น Widget ที่ช่วยจัดการข้อมูลแบบ Asynchronous ที่ได้จาก `Future` และ `Stream` โดยจ...

การใช้งาน FutureBuilder และ StreamBuilder อย่างละเอียด

ในโลกของการพัฒนาแอปพลิเคชัน ข้อมูลมักไม่ได้มาทันทีทันใดเสมอไป บ่อยครั้งที่เราต้องรอข้อมูลจากแหล่งภายนอก เช่น API, ฐานข้อมูล หรือแม้แต่การคำนวณที่ใช้เวลานาน Flutter เตรียมเครื่องมือที่ทรงพลังไว้ให้เราจัดการกับข้อมูลที่ไม่พร้อมใช้งานทันที นั่นคือ FutureBuilder และ StreamBuilder บทความนี้จะเจาะลึกการใช้งานเครื่องมือทั้งสองนี้อย่างละเอียด

ทำความเข้าใจข้อมูล Async

ก่อนที่เราจะไปดู FutureBuilder และ StreamBuilder มาทำความเข้าใจกันก่อนว่า Async คืออะไร Async ย่อมาจาก Asynchronous ซึ่งหมายถึง การทำงานที่ไม่จำเป็นต้องรอให้เสร็จสิ้นก่อนที่จะเริ่มทำงานอื่นได้ ตัวอย่างเช่น การเรียก API เพื่อดึงข้อมูล เราไม่จำเป็นต้องรอให้ API ตอบกลับมาก่อนถึงจะสามารถทำงานอื่นได้ เราสามารถเริ่มทำงานอื่นไปก่อน แล้วค่อยกลับมาจัดการข้อมูลเมื่อ API ตอบกลับมา

ใน Flutter, Async operation มักจะ return เป็น Future หรือ Stream

  • Future: เป็นตัวแทนของค่าที่อาจจะยังไม่พร้อมใช้งานในตอนนี้ แต่จะพร้อมใช้งานในอนาคต (คล้ายๆ สัญญาว่าจะให้ค่าในอนาคต)
  • Stream: เป็นลำดับของข้อมูลที่อาจจะยังไม่พร้อมใช้งานในตอนนี้ แต่จะทยอยพร้อมใช้งานในอนาคต (คล้ายๆ ท่อน้ำที่ข้อมูลไหลเข้ามาเรื่อยๆ)

FutureBuilder: จัดการข้อมูลจาก Future

FutureBuilder เป็น Widget ที่ช่วยให้เราสร้าง UI ตามสถานะของ Future ได้ง่ายขึ้น มันจะ rebuild UI ทุกครั้งที่สถานะของ Future เปลี่ยนไป

ส่วนประกอบหลักของ FutureBuilder

  • future: Future ที่เราต้องการติดตามสถานะ
  • builder: Function ที่รับ BuildContext และ AsyncSnapshot เป็น argument แล้ว return Widget

AsyncSnapshot คืออะไร?

AsyncSnapshot เป็น Object ที่เก็บข้อมูลเกี่ยวกับสถานะของ Future มันมี properties ที่สำคัญดังนี้:

  • connectionState: สถานะของการเชื่อมต่อ (กำลังรอ, กำลังโหลด, เสร็จสิ้น, หรือมีข้อผิดพลาด)
  • data: ข้อมูลที่ Future return (ถ้ามี)
  • error: ข้อผิดพลาดที่เกิดขึ้น (ถ้ามี)

ConnectionState

connectionState เป็น Enum ที่บอกสถานะของการเชื่อมต่อ มีค่าที่เป็นไปได้ดังนี้:

  • ConnectionState.none: ยังไม่มี Future เริ่มทำงาน
  • ConnectionState.waiting: Future กำลังทำงาน
  • ConnectionState.active: Stream กำลังส่งข้อมูล
  • ConnectionState.done: Future หรือ Stream ทำงานเสร็จสิ้น

ตัวอย่างการใช้งาน FutureBuilder

สมมติว่าเรามี function ที่ชื่อว่า fetchData() ซึ่ง return Future<String> ที่จำลองการดึงข้อมูลจาก API:

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2)); // จำลองการรอ 2 วินาที
  return 'Hello from the Future!';
}

เราสามารถใช้ FutureBuilder เพื่อแสดงผล UI ตามสถานะของ fetchData() ได้ดังนี้:

class FutureBuilderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('FutureBuilder Example')),
      body: Center(
        child: FutureBuilder<String>(
          future: fetchData(),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return CircularProgressIndicator(); // แสดง Loading Indicator
            } else if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}'); // แสดงข้อผิดพลาด
            } else if (snapshot.hasData) {
              return Text('Data: ${snapshot.data}'); // แสดงข้อมูล
            } else {
              return Text('No data available'); // กรณีอื่นๆ
            }
          },
        ),
      ),
    );
  }
}

คำอธิบาย:

  • เราส่ง fetchData() ให้กับ property future ของ FutureBuilder
  • ใน builder function เราตรวจสอบ snapshot.connectionState เพื่อดูสถานะของการเชื่อมต่อ
  • ถ้า connectionState เป็น ConnectionState.waiting เราจะแสดง CircularProgressIndicator
  • ถ้ามีข้อผิดพลาด เราจะแสดงข้อความ error
  • ถ้ามีข้อมูล เราจะแสดงข้อมูล

Error Handling

FutureBuilder มีกลไกในการจัดการ error ในกรณีที่ Future throw exception เราสามารถเข้าถึง error ได้จาก snapshot.error

StreamBuilder: จัดการข้อมูลจาก Stream

StreamBuilder คล้ายกับ FutureBuilder แต่ใช้สำหรับจัดการข้อมูลจาก Stream แทนที่จะเป็น Future มันจะ rebuild UI ทุกครั้งที่ Stream ส่งข้อมูลใหม่

ส่วนประกอบหลักของ StreamBuilder

  • stream: Stream ที่เราต้องการติดตาม
  • initialData: ข้อมูลเริ่มต้นที่จะแสดงก่อนที่ Stream จะส่งข้อมูล (optional)
  • builder: Function ที่รับ BuildContext และ AsyncSnapshot เป็น argument แล้ว return Widget

ตัวอย่างการใช้งาน StreamBuilder

สมมติว่าเรามี function ที่ชื่อว่า numberStream() ซึ่ง return Stream<int> ที่ส่งตัวเลขทุกๆ 1 วินาที:

Stream<int> numberStream() async* {
  for (int i = 1; i <= 5; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

เราสามารถใช้ StreamBuilder เพื่อแสดงผลตัวเลขที่ Stream ส่งมาได้ดังนี้:

class StreamBuilderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('StreamBuilder Example')),
      body: Center(
        child: StreamBuilder<int>(
          stream: numberStream(),
          initialData: 0, // ข้อมูลเริ่มต้น
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return CircularProgressIndicator();
            } else if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}');
            } else if (snapshot.hasData) {
              return Text('Number: ${snapshot.data}');
            } else {
              return Text('Stream completed');
            }
          },
        ),
      ),
    );
  }
}

คำอธิบาย:

  • เราส่ง numberStream() ให้กับ property stream ของ StreamBuilder
  • เรากำหนด initialData เป็น 0 เพื่อแสดงผลก่อนที่ Stream จะส่งข้อมูล
  • ใน builder function เราตรวจสอบ snapshot.connectionState เพื่อดูสถานะของ Stream
  • ถ้า connectionState เป็น ConnectionState.waiting เราจะแสดง CircularProgressIndicator
  • ถ้ามีข้อผิดพลาด เราจะแสดงข้อความ error
  • ถ้ามีข้อมูล เราจะแสดงข้อมูล
  • เมื่อ Stream ทำงานเสร็จสิ้น (ส่งข้อมูลครบ) connectionState จะเป็น ConnectionState.done

การยกเลิก Stream

เมื่อ Stream ไม่จำเป็นต้องใช้งานอีกต่อไป เราควรยกเลิก StreamSubscription เพื่อป้องกัน memory leak เราสามารถทำได้โดยการใช้ dispose() method ใน StatefulWidget:

class StreamBuilderExample extends StatefulWidget {
  @override
  _StreamBuilderExampleState createState() => _StreamBuilderExampleState();
}

class _StreamBuilderExampleState extends State<StreamBuilderExample> {
  StreamSubscription<int>? _streamSubscription;

  @override
  void initState() {
    super.initState();
    _streamSubscription = numberStream().listen((data) {
      // ทำอะไรบางอย่างกับข้อมูล
    });
  }

  @override
  void dispose() {
    _streamSubscription?.cancel(); // ยกเลิก StreamSubscription
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('StreamBuilder Example')),
      body: Center(
        child: StreamBuilder<int>(
          stream: numberStream(), // ใช้ numberStream() โดยตรงใน StreamBuilder
          initialData: 0,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.waiting) {
              return CircularProgressIndicator();
            } else if (snapshot.hasError) {
              return Text('Error: ${snapshot.error}');
            } else if (snapshot.hasData) {
              return Text('Number: ${snapshot.data}');
            } else {
              return Text('Stream completed');
            }
          },
        ),
      ),
    );
  }
}

ข้อควรระวัง: ในตัวอย่างนี้ เราใช้ numberStream() ทั้งใน initState (เพื่อสร้าง StreamSubscription) และใน StreamBuilder ซึ่งไม่จำเป็นและอาจทำให้เกิด side effects ได้ ทางที่ดีควรใช้ numberStream() ใน StreamBuilder เท่านั้น และยกเลิก StreamSubscription ที่สร้างขึ้นภายนอก (ถ้ามี) ใน dispose()

สรุป

FutureBuilder และ StreamBuilder เป็นเครื่องมือที่สำคัญในการจัดการข้อมูล Async ใน Flutter การเข้าใจการทำงานของ ConnectionState และ AsyncSnapshot จะช่วยให้เราสร้าง UI ที่ตอบสนองต่อสถานะของข้อมูลได้อย่างถูกต้องและมีประสิทธิภาพ

ในบทความถัดไป เราจะเรียนรู้เกี่ยวกับการสร้าง Animation ขั้นสูงด้วย Implicit และ Explicit Animations

Office.in.th Custom Service

想基于此结构开发或定制系统?

Office.in.th 承接企业级 Web Application、API Integration 以及政府/企业管理系统的设计与开发,并提供长期支持。

查看软件开发服务