การใช้งาน FutureBuilder ที่เป็น Async widgets ใน Flutter

บทความใหม่ เดือนที่แล้ว โดย Ninenik Narkdee
futurebuilder future flutter async widgets

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ futurebuilder future flutter async widgets



เนื้อหาตอนต่อไปนี้ จะมาดูเกี่ยวกับการใช้งาน async widgets
ที่ชื่อว่า FutureBuilder เป็น widget ที่จะจัดการกับข้อมูลสุดท้าย
ที่จะได้รับในอนาคต เกี่ยวกับเรื่องนี้ เราต้องเข้าใจเกี่ยวกับรูปแบบ
การใช้งาน Asynchronous Programming ในภาษา Dart ทบทวน
ได้ที่บทความ 
    การใช้งาน Asynchronous Programming ในภาษา Dart เบื้องต้น http://niik.in/949
 
    *เนื้อหานี้ใช้เนื้อหาต่อเนื่องจากบทความ http://niik.in/961
 
 

จำลองข้อมูลในอนาคตด้วย Future

    เราจะจำลองข้อมูลที่จะได้มาในอนาคต ความหมายที่จะสื่อก็คือ เช่น เราดึงข้อมูล
ผ่านเครือข่าย ก็จำเป็นจะต้องรอสักพักกว่าข้อมูลจะมา ก็เกิดเวลาที่ต้องรอขึ้น 
    ตัวอย่าง
 
// จำลองข้อมูลที่จะได้ หรือจะเกิดในอนาคต
final Future<String> _calculation = Future<String>.delayed(
  const Duration(seconds: 2),
  () => 'Data Loaded',
);
 
    เรากำหนดตัวแปรชื่อ _calculation เป็นข้อมูล Future ที่มีชนิดข้อมูลเป็นข้อความ String โดยสร้าง
จากการกำหนดเวลาหน่วงการคืนค่าจากคำสั่ง Future<String>.delayed() พอครบ 2 วินาทีก็ให้คืนคำว่า
'Data Loaded' กลับมา เหตุผลที่เราต้องรู้จักกับข้อมูล Future ก็เพราะว่า ตัว FutureBuilder widget
ที่เราจะใช้งานในบทความนี้จำเป็นต้องใช้ข้อมูล Future นั่นเอง
 
    ส่วนของไฟล์ home.dart ก่อนกำหนดใช้งาน FutureBuilder
 
class _HomeState extends State<Home> {

    @override
    Widget build(BuildContext context) {
        print("build");// สำหรับทดสอบ
        return Scaffold(
            appBar: AppBar(
                title: Text('Home'),
            ),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                        Text('Home Screen'),
                    ],
                )
            ),
        );
    }
}
 

    รูปแบบการใช้งาน FutureBuilder

 
FutureBuilder<String>( // กำหนดชนิดข้อมูล
  future: _calculation, // ข้อมูล Future
  //builder: (BuildContext context, AsyncSnapshot snapshot) {
  builder: (context, snapshot) { // สร้าง widget เมื่อได้ค่า snapshot ข้อมูลสุดท้าย
    if (snapshot.hasData) { // ถ้าได้ค่าข้อมูลสุดท้าย
      return Text('Completed');
    } else if (snapshot.hasError) { // ถ้ามี error
      return Text('${snapshot.error}');
    }
    // ค่าเริ่มต้น, แสดงตัว Loading.
    return const CircularProgressIndicator();
  },
),  
 
    ส่วนแรกเป็นส่วนของการกำหนดชนิดของข้อมูล Future เราต้องกำหนดให้ถูกต้อง อย่างข้างตันเรา
กำหนดเป็น <String> มาจากข้อมูล Future<String> ถ้าเป็นข้อมูลอื่นเช่น List<String> เราก็จะ
กำหนดเป็น <List<String>> แบบนี้เป็นต้น
    ส่วนที่สองเป็นข้อมูล future ก็ตัวแปรข้อมูล Future หรือจะเรียกฟังก์ชั่นมาใช้เลยก็ได้ ในตัวอย่างเรากำหนด
ค่าเป็น _calculation เป็นค่าที่เราจะใช้งาน
    คำสั่ง builder จะรอทำคำสั่งสร้าง widget โดยอาศัยข้อมูลสุดท้ายที่เรียกว่า snapshot ที่ได้จากข้อมูล
Future เป็นข้อมูล AsyncSnapshot ซึ่งต้องรอข้อมูลหรือมีเวลาที่ต้องรอ จะได้ผลลัพธ์สุดท้าย
    ในคำสั่ง builder เราก็จะ return Widget ที่ต้องการออกไปแสดง โดยมีเงื่อนไข ทำการตรวจสอบข้อมูล
snapshot ใน 3 รูปแบบ หรือจะเขียน 2 รูปแบบตามตัวอย่างด้านบนก็ได้ โดยไม่ต้องมี else ในเงื่อนไขสุดท้าย
คือถ้ามีข้อมูล ก็แสดงด้วย Text ข้อความคำว่า Completed ถ้ามี error ก็แสดงข้อความ error และถ้าไม่เข้า
เงื่อนไขทั้งสอง ก็แสดงตัว loading ด้วย CircularProgressIndicator widget
    ตัวอย่างกรณี error ก็เช่น ในคำสั่งดึงข้อมูล Future เกิด Exception ขึ้น 
 
// จำลองข้อมูลที่จะได้ หรือจะเกิดในอนาคต
final Future<String> _calculation = Future<String>.delayed(
  const Duration(seconds: 5),
  () => throw Exception("ข้อมูลไม่พร้อมใช้งาน"), // 'Data Loaded',
);  
 
    แทนที่จะส่งข้อความคำว่า 'Data Loaded' ก็เกิด error ขึ้นและส่งค่า Exception กลับมา ก็จะเข้าเงื่อนไขที่สอง
นอกจากเราจะใช้การตรวจสอบการมีข้อมูลของ snapshot ด้วยคำสั่ง snapshot.hasData ที่จะคืนค่าเป็น true / false
แล้ว เรายังสามารถใช้การตรวจสอบ สถานะการเชื่อมต่อจากค่าของ snapshot.connectionState ได้ โดยจะมีด้วยกัน
สองค่า คือ ConnectionState.waiting และ ConnectionState.done โดยเมื่อเริ่มต้นดึงข้อมูล Future ก็จะขึ้น waiting
ในครั้งแรก และเมื่อได้ข้อมูลกลับมา หรือเกิด error ก็จะขึ้น done
    เราสามารถเขียนตรวจสอบแบบง่ายอย่างนี้ก็ได้
 
if (snapshot.connectionState == ConnectionState.done) {
  return Text('Completed');
}
// ค่าเริ่มต้น, แสดงตัว Loading.สถานะ ConnectionState.waiting
return const CircularProgressIndicator();
 
    อย่างไรก็ตาม ควรใช้แบบวิธีแรกจะครอบคลุมการทำงานมากกว่า เพราะกรณีที่สอง หากมี error ก็ยังมีค่า
connectionState เป็น done เหมือนกัน หากไม่เขียนคำสั่งเพิ่มเพื่อตรวจสอบอีก ก็จะกลายเป็น ส่ง Text 
ข้อความ 'Completed' ออกไป วิธีนี้จึงจะไม่ค่อยครอบคลุมเท่าไหร่
 
    มาดูตัวอย่างโค้ด และผลลัพธ์การทำงาน ไฟล์ home.dart
 
import 'package:flutter/material.dart';
  
class Home extends StatefulWidget {
    static const routeName = '/';
 
    const Home({Key? key}) : super(key: key);
  
    @override
    State<StatefulWidget> createState() {
        return _HomeState();
    }
}
  
class _HomeState extends State<Home> {

    // จำลองข้อมูลที่จะได้ หรือจะเกิดในอนาคต
    final Future<String> _calculation = Future<String>.delayed(
      const Duration(seconds: 5),
      () => 'Data Loaded', // throw Exception("ข้อมูลไม่พร้อมใช้งาน")
    );  

    @override
    Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
                title: Text('Home'),
            ),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      FutureBuilder<String>( // กำหนดชนิดข้อมูล
                        future: _calculation, // ข้อมูล Future
                        //builder: (BuildContext context, AsyncSnapshot snapshot) {
                        builder: (context, snapshot) { // สร้าง widget เมื่อได้ค่า snapshot ข้อมูลสุดท้าย
                          if (snapshot.hasData) { // ถ้าได้ค่าข้อมูลสุดท้าย
                            return Text('Completed');
                          } else if (snapshot.hasError) { // ถ้ามี error
                            return Text('${snapshot.error}');
                          }
                          // ค่าเริ่มต้น, แสดงตัว Loading.สถานะ ConnectionState.waiting
                          return const CircularProgressIndicator();
                        },
                      ),  
                    ],
                )
            ),
        );
    }
}
 
    ผลลัพธ์
 
 


 
 
    เริ่มต้นเราไปอยู่ที่หน้า Settings จากนั้นคลิกมาหน้า Home  ที่เราใช้งาน FutureBuilder ตัว Loading
จะแสดงประมาณ 5 วินาทีตามค่าที่กำหนด แล้วขึ้นข้อความว่า Completed
 
    เรามาจำลองการสร้างข้อมูล Future โดยใช้งานเป็นแบบฟังก์ชั่น ให้เสมือนกับเราจะดึงข้อมูลใน
ฝั่งของ server มาใช้งาน จะได้เป็นดังนี้
 
class _HomeState extends State<Home> {

    @override
    Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
                title: Text('Home'),
            ),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      FutureBuilder<String>( // กำหนดชนิดข้อมูล
                        future: fetchData(), // ข้อมูล Future
                        //builder: (BuildContext context, AsyncSnapshot snapshot) {
                        builder: (context, snapshot) { // สร้าง widget เมื่อได้ค่า snapshot ข้อมูลสุดท้าย
                          if (snapshot.hasData) { // ถ้าได้ค่าข้อมูลสุดท้าย
                            return Text('Completed');
                          } else if (snapshot.hasError) { // ถ้ามี error
                            return Text('${snapshot.error}');
                          }
                          // ค่าเริ่มต้น, แสดงตัว Loading.สถานะ ConnectionState.waiting
                          return const CircularProgressIndicator();
                        },
                      ),  
                    ],
                )
            ),
        );
    }
}

// จำลองใช้เป็นแบบฟังก์ชั่น ให้เสมือนดึงข้อมูลจาก server
Future<String> fetchData() async {
  final response = await Future<String>.delayed(
    const Duration(seconds: 5),
    () => 'Data Loaded', 
  );  
  return response; 
}
 
    เราสร้างฟังก์ชั่นชื่อว่า fetchData() แยกออกมา แล้วเรียกใช้ในส่วนของการกำหนดค่า future property
ของ FutureBuilder widget ผลลัพธ์ที่ได้ก็จะเหมือนกับวิธีแรก แต่ที่เราทำเพิ่มในรูปแบบนี้ก็พี่จำลองการทำงาน
ให้เสมือนการไปดึงข้อมูลในฝั่ง server 
 
    เนื้อหาตอนนี้ ถือได้ว่าเราได้ทำความเข้าใจเบื้องต้นกับการใช้งาน FutureBuilder widget เป็นการปูพื้นฐาน
สำหรับเนื้อหาในลำดับต่อๆ ไป รอติดตาม


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง











เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ