การใช้งาน Http ดึงข้อมูลจาก Server มาแสดงใน Flutter

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

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



เนื้อหาตอนต่อไปนี้ เราจะมาดูเกี่ยวกับวิธีการดึงข้อมูลจาก Server
ที่เป็นข้อมูล API มาแสดงใน app ของ flutter ซึ่งจากที่ได้เกริ่นมาแล้ว
ในหลายตอน และได้แนะนำการใช้งาน FutureBuilder สำหรับใช้งาน
ร่วมกับข้อมูล Future ซึ่งเป็นข้อมูลลักษณะเดียวกันที่เราจะได้จากการ
ไปดึงข้อมูลบน server ทบทวนเนื้อหาเกี่ยวกับ FutureBuilder
    การใช้งาน FutureBuilder ที่เป็น Async widgets ใน Flutter http://niik.in/1036
 
    *เนื้อหานี้ใช้เนื้อหาต่อเนื่องจากบทความ http://niik.in/961
 
 

ตัวอย่างการแสดงลิสรายการใน ListView

    ก่อนเข้าไปในรายละเอียดของเนื้อหา มาดูรูปแบบการจัดการ ListView ที่เราจะใช้งาน โดย
จะจำลองข้อมูล วิธีการแสดงข้อมูล ดังนี้
 
    ไฟล์ home.dart
 
import 'dart:math';

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> {

    // จำลองข้อมูล สร้างลิสรายการ 100 รายการ
    List<String> items = List<String>.generate(100, (i) => 'Item ${i+1}');
  
    @override
    void initState() {
      print("initState"); // สำหรับทดสอบ
      super.initState(); 
    }     

    void _refreshData(){
      setState(() {
        print("setState"); // สำหรับทดสอบ
        Random rng = Random(); // ข้อมูล Random
        int rd_number = rng.nextInt(20); // สุ่มค่าจาก 0 - 20
        print(rd_number); // สำหรับทดสอบ
        // สร้างลิสรายการใหม่
        items = List<String>.generate(rd_number, (i) => 'Item ${i+1}');
      });
    }

    @override
    Widget build(BuildContext context) {
        print("build");  // สำหรับทดสอบ
        return Scaffold(
            appBar: AppBar(
                title: Text('Home'),
            ),
            body: Column(
              children: [
                Container( // สร้างส่วน header ของลิสรายการ
                  padding: const EdgeInsets.all(12.0),
                  decoration: BoxDecoration(
                    color: Colors.teal.withAlpha(100),
                //    borderRadius: BorderRadius.circular(8.0),
                  ),                  
                  child: Row(                  
                    children: [
                      Text('Total ${items.length} items'),
                    ],
                  ),
                ),
                Expanded(
                      child: items.length > 0 // กำหนดเงื่อนไขตรงนี้
                      ? ListView.separated( // กรณีมีรายการ แสดงปกติ
                              itemCount: items.length,
                              itemBuilder: (context, index) {
                                  return ListTile(
                                    title: Text('${items[index]}'),
                                  );
                              },
                              separatorBuilder: (BuildContext context, int index) => const Divider(),                    
                        )
                      : const Center(child: Text('No items')), // กรณีไม่มีรายการ
                  ),
              ],
            ),
              floatingActionButton: FloatingActionButton(// ปุ่มสำหรับดึงข้อมูลใหม่
                  onPressed: _refreshData,
                  child: const Icon(Icons.refresh),
              ),
        );
    }

}
 
    ผลลัพธ์ที่ได้
 
 
 
    เกี่ยวกับรายละเอียดการใช้งาน ListView สามารถอ่านเพิ่มได้ที่ http://niik.in/957
    โค้ดตัวอย่างข้างต้น เราจำลองสร้างข้อมูล List<String> เป็นรายการค่าเริ่มต้น 100 รายการ ในส่วน
ของการใช้งาน เราใช้ ListView.separated เป็นตัวจัดการ มีเงื่อนไขการแสดงข้อมูล ถ้ามีข้อมูลก็แสดง
เป็นลิสรายการ ถ้าไม่มีก็ให้แสดงข้อความตรงกลางว่า 'No items'  เรามีปุ่มสำหรับ ทำการ random ค่า
เพื่อสร้างลิสรายการใหม่  ด้านบนของลิสรายการเราก็สร้างส่วน header ของลิสรายการ สำหรับแสดง
จำนวนรายการทั้งหมดที่มี ตัวอย่างการใช้งานข้างต้น เป็นรูปแบบที่เราจะนำไปใช้สำหรับการแสดงข้อมูล
ที่ดึงมาจาก api 
 
 

เตรียมความพร้อมก่อนใช้งาน Http ดึงข้อมูลจาก Server

    เกี่ยวกับการใช้งาน Http ในภาษา Dart หากต้องการรู้รายละเอียดเพิ่มเติม สามารถเข้าไปอ่านได้ที่
ลิ้งค์บทความด้านล่างนี้ อย่างไรก็ตาม ในการใช้งานใน Flutter ก็ไม่มีมีขั้นตอนยุ่งยากนัก
    การใช้งาน HTTP Server และ HTTP Client ในภาษา Dart เบื้องต้น http://niik.in/964
 
 

    ติดตั้ง http package

    ก่อนจะใช้งาน http เราต้องทำการเพิ่ม http package เข้ามาใช้งานใน flutter โปรเจ็คของเราก่อนโดย
เข้าไปเพิ่มในส่วนของไฟล์ pubspec.yaml แล้วกำหนดในลักษณะดังนี้
 
dependencies:
  http: ^0.13.4 
 
    ใช้เป็นเวอร์ชั่นล่าสุด เข้าไปดูได้ที่ http package
 
    ตัวอย่างการเพิ่ม รูปเพิ่มผิดที่ ให้เพิ่มใน dependencies

 

 
 
    ในกรณีเขียนโค้ดโดยใช้ VS Code ก็ทำการกดบันทึก ตัวโปรแกรม จะเรียกใช้งานคำสั่ง flutter pub get
เพื่อเพิ่ม package ให้อัตโนมัติ ส่วนกรณีใช้ Android Studio ก็ให้ เรียกใช้คำสั่งผ่าน Terminal ในตัวโปรแกรม
เพิ่อเพิ่ม package เข้ามาในโปรเจ็ค
 
 

    ทำการ Import http package เข้ามาใช้งาน

 
import 'package:http/http.dart' as http;
    

    การกำหนดสิทธิ์การขอใช้งานเครือข่าย INTERNET สำหรับ Android

    เนื่องจาก app ของเราต้องมีการใช้งาน internet เพราะต้องดึงข้อมูลผ่าน server ให้เราทำการเพิ่มการขอสิทธิ์
การอนุญาตใช้งานในไฟล์ AndroidManifest.xml 
    อยู่ใน path -  android > app > src > main
 
    

 
 
    ให้เรากำหนดค่า ด้านล่างแทรกลงไป
 
<uses-permission android:name="android.permission.INTERNET" />
 
    ตามรูปแบบดังนี้
 
<manifest xmlns:android...>
 ...
 <uses-permission android:name="android.permission.INTERNET" />
 <application ...
</manifest>
 

    จัดรูปแบบ Data Model ข้อมูลสำหรับรองรับข้อมูลจาก API

    ก่อนที่เราจะดึงข้อมูลมาแสดง เราต้องรู้ก่อนว่าข้อมูลเราเป็นแบบไหน มีโครงสร้างข้อมูลเป้นอย่างไร
ในที่นี้เราจะใช้ข้อมูลจาก https://jsonplaceholder.typicode.com/posts เป็นข้อมูลจำลองหรือ Fake API
สำหรับใช้ทดสอบโดยเฉพาะ
 
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaeenderit",
    "body": "quia et suscipitnsuscipit recusatecto"
  }
]
 
    จากรูปแบบข้างต้น เราสามารถกำหนด Data Model สำหรับข้อมูลได้เป็นดังนี้
 
class Article {
  final int userId;
  final int id;
  final String title;
  final String body;

  Article({
    required this.userId,
    required this.id,
    required this.title,
    required this.body,
  });

  // ส่วนของ name constructor ที่จะแปลง json string มาเป็น Article object
  factory Article.fromJson(Map<String, dynamic> json) {
    return Article(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }

} 
 
    เนื้อหาเกี่ยวกับการจัดการข้อมูล JSON สามารถอ่านเพิ่มเติมได้ที่
    การใช้งาน JSON String Data ในภาษา Dart เบื้องต้น http://niik.in/963
 
    ตอนนี้เราเตรียมส่วนต่างๆ พร้อมแล้วสำหรับการใช้งาน http package ดึงข้อมูล api มาแสดง
 
 
 

เรียกใช้งาน Http package เพื่อดึงข้อมูล API มาแสดง

    มาถึงขั้นตอนการใช้งาน http package เราจะทำการสร้างฟังก์ชั่น ที่จะทำการดึงข้อมูล api มาแสดง
มีรูปแบบการใช้งานไม่ยุ่งยาก ซึ่ง flutter ก็มีเครื่องมาให้ และเราก็ควรใช้งาน นี่คือส่วน package ที่เราจะใช้
ในหน้าดึงข้อมูล
 
import 'dart:async'; // สำหรับจัดการข้อมูลแบบ async
import 'dart:convert'; // สำหรับจัดการข้อมูล JSON data

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';  // มีคำสั่งสำหรับจัดการข้อมูลอยู่ background
import 'package:http/http.dart' as http; // ดึงข้อมูลจัดการข้อมูลบนเครือข่าย internet
 
    ปกติเราสามารถกำหนดคำสั่งจัดการกับข้อมูล JSON ได้อยู่แล้ว แต่ก็อาจจะไม่ครอบคลุม ถ้าข้อมูลนั้นๆ มีขนาด
ที่ค่อนข้างใหญ่ หรือมีหลายรายการ การใช้ตัวช่วยของ flutter จะทำให้สามารถแปลงข้อมูลที่มีจำนวนมากๆ
โดยทำงานอยู่เบื้องหลังให้เรา
 
    มาดูการกำหนดฟังก์ชั่นสำหรับดึงข้อมูล api ด้วย http package และการแปลงข้อมูล api ที่ได้มาในรูป
แบบ JSON data เป็นในรูปแบบ List<Article> โดยใช้ฟังก์ชั่นของ Flutter มาช่วย
 
// สรัางฟังก์ชั่นดึงข้อมูล คืนค่ากลับมาเป็นข้อมูล Future ประเภท List ของ Article
Future<List<Article>> fetchArticle() async {
  // ทำการดึงข้อมูลจาก server ตาม url ที่กำหนด
  final response = await http
      .get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));

  // เมื่อมีข้อมูลกลับมา
  if (response.statusCode == 200) {
    // ส่งข้อมูลที่เป็น JSON String data ไปทำการแปลง เป็นข้อมูล List<Article
    // โดยใช้คำสั่ง compute ทำงานเบื้องหลัง เรียกใช้ฟังก์ชั่นชื่อ parseArticles
    // ส่งข้อมูล JSON String data ผ่านตัวแปร response.body
    return compute(parseArticles, response.body);
  } else { // กรณี error
    throw Exception('Failed to load article');
  }
}

// ฟังก์ชั่นแปลงข้อมูล JSON String data เป็น เป็นข้อมูล List<Article>
List<Article> parseArticles(String responseBody) {
  final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
  return parsed.map<Article>((json) => Article.fromJson(json)).toList();
}
 
    จะมีอยู่สองฟังก์ชั่น คือ ฟังก์ชั่นแรกทำการดึงข้อมูลให้ได้ข้อมูล JSON String data มาแล้ว
ส่งต่อมาอีกฟังก์ชั่น เพื่อแปลงเป็น List<Article> โดยใช้คำสั่ง compute() ที่ทำงานเบื้องหลัง
จะมีประโยชน์มากๆ กรณีที่ข้อมูลนั้นๆ มีขนาดใหญ่
 
    เมื่อเราได้รูปแบบข้อมูล ได้คำสั่งดึงข้อมูล มาเรียบร้อยแล้ว ต่อไปก็เป็นส่วนของการนำไปแสดง
หรือใช้งานร่วมกับ FutureBuilder 
 
// กำนหดตัวแปรข้อมูล articles
late Future<List<Article>> articles;
 
    เรากำหนดตัวแปรชื่อ articles เป็นข้อมูล Future<List<Article>> ใช้ keyword late เพื่อป้องกัน
กรณีเกิด null safety ประมาณว่า ข้อมูลนี้จะไม่เป็น null แน่นอน แต่ข้อมูลจะมาช้าหน่อย เหมือนสัญญาไว้
และจะทำตามสัญญา (late มาจาก lazy)
    เราจะมากำหนดค่าใน initState() ดังนี้
 
@override
void initState() {
  print("initState"); // สำหรับทดสอบ
  super.initState(); 
  articles = fetchArticle(); // ทำตามสัญญา กำหนดค่า โดยให้ไปดึงจากฟังก์ชั่น
}     
 
    สร้างฟังก์ชั่นสำหรับดึงข้อมูลซ้ำ หรือโหลดข้อมูลใหม่
 
void _refreshData(){
  setState(() {
    print("setState"); // สำหรับทดสอบ
    articles = fetchArticle(); // โหลดข้อมูลใหม่
  });
}
 
    และสุดท้ายก็ส่วนของการประยุกต์ใช้งาน FutureBuilder กับ ListView
 
@override
Widget build(BuildContext context) {
    print("build");  // สำหรับทดสอบ
    return Scaffold(
        appBar: AppBar(
            title: Text('Home'),
        ),
        body: Center(
          child: FutureBuilder<List<Article>>( // ชนิดของข้อมูล
            future: articles, // ข้อมูล Future
            builder: (context, snapshot) {
              print("builder");  // สำหรับทดสอบ
              print(snapshot.connectionState); // สำหรับทดสอบ
              if (snapshot.hasData) {// กรณีมีข้อมูล
                  return Column(
                      children: [
                        Container( // สร้างส่วน header ของลิสรายการ
                          padding: const EdgeInsets.all(5.0),
                          decoration: BoxDecoration(
                            color: Colors.teal.withAlpha(100),
                          ),                  
                          child: Row(                  
                            children: [
                              Text('Total ${snapshot.data!.length} items'), // แสดงจำนวนรายการ
                            ],
                          ),
                        ),
                        Expanded( // ส่วนของลิสรายการ
                              child: snapshot.data!.length > 0 // กำหนดเงื่อนไขตรงนี้
                              ? ListView.separated( // กรณีมีรายการ แสดงปกติ
                                      itemCount: snapshot.data!.length,
                                      itemBuilder: (context, index) {
                                          return ListTile(
                                            title: Text(snapshot.data![index].title),
                                          );
                                      },
                                      separatorBuilder: (BuildContext context, int index) => const Divider(),                    
                                )
                              : const Center(child: Text('No items')), // กรณีไม่มีรายการ
                          ),
                      ],
                    );
              } else if (snapshot.hasError) { // กรณี error
                return Text('${snapshot.error}');
              }
              // กรณีสถานะเป็น waiting ยังไม่มีข้อมูล แสดงตัว loading
              return const CircularProgressIndicator();
            },
          ),  
        ),          
        floatingActionButton: FloatingActionButton( // ปุ่มทดสอบสำหรับดึงข้อมูลซ้ำ
            onPressed: _refreshData,
            child: const Icon(Icons.refresh),
        ),
    );
}
 
    เมื่อเราใช้งาน FutureBuilder ข้อมูลสุดท้ายของเราก็คือ snapshot.data เป็นข้อมูลในรูปแบบ 
List<Article> หรือก็คือ อาเรย์ของ Article object ดังนั้นเวลาเราเรียกใช้งาน เมื่อวนลูปแสดงใน ListView
กฺ็สามารถอ้างอิงได้จากค่า index เช่น เราต้องการแสดง title ก็จะใช้เป็น
 
snapshot.data![index].title
 
    สังเกตว่าหลัง snapshot.data มีเครื่องหมาย ! กำกับไว้ ก็เกี่ยวกับ null safety อีกเหมือนเคย เป็นการบอกว่า
ข้อมูล snapshot.data จะไม่เป็น null เมื่อไม่เป้น null การเรียกใช้งาน ก็จะเรียกใช้งานได้
 
    เราจะได้ไฟล์ home.dart เป็นดังนี้
 
import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
  
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> {

    // กำนหดตัวแปรข้อมูล articles
    late Future<List<Article>> articles;
  
    @override
    void initState() {
      print("initState"); // สำหรับทดสอบ
      super.initState(); 
      articles = fetchArticle();
    }     

    void _refreshData(){
      setState(() {
        print("setState"); // สำหรับทดสอบ
       articles = fetchArticle();
      });
    }

    @override
    Widget build(BuildContext context) {
        print("build");  // สำหรับทดสอบ
        return Scaffold(
            appBar: AppBar(
                title: Text('Home'),
            ),
            body: Center(
              child: FutureBuilder<List<Article>>( // ชนิดของข้อมูล
                future: articles, // ข้อมูล Future
                builder: (context, snapshot) {
                  print("builder");  // สำหรับทดสอบ
                  print(snapshot.connectionState); // สำหรับทดสอบ
                  if (snapshot.hasData) {// กรณีมีข้อมูล
                      return Column(
                          children: [
                            Container( // สร้างส่วน header ของลิสรายการ
                              padding: const EdgeInsets.all(5.0),
                              decoration: BoxDecoration(
                                color: Colors.teal.withAlpha(100),
                              ),                  
                              child: Row(                  
                                children: [
                                  Text('Total ${snapshot.data!.length} items'), // แสดงจำนวนรายการ
                                ],
                              ),
                            ),
                            Expanded( // ส่วนของลิสรายการ
                                  child: snapshot.data!.length > 0 // กำหนดเงื่อนไขตรงนี้
                                  ? ListView.separated( // กรณีมีรายการ แสดงปกติ
                                          itemCount: snapshot.data!.length,
                                          itemBuilder: (context, index) {
                                              return ListTile(
                                                title: Text(snapshot.data![index].title),
                                              );
                                          },
                                          separatorBuilder: (BuildContext context, int index) => const Divider(),                    
                                    )
                                  : const Center(child: Text('No items')), // กรณีไม่มีรายการ
                              ),
                          ],
                        );
                  } else if (snapshot.hasError) { // กรณี error
                    return Text('${snapshot.error}');
                  }
                  // กรณีสถานะเป็น waiting ยังไม่มีข้อมูล แสดงตัว loading
                  return const CircularProgressIndicator();
                },
              ),  
            ),          
            floatingActionButton: FloatingActionButton( // ปุ่มทดสอบสำหรับดึงข้อมูลซ้ำ
                onPressed: _refreshData,
                child: const Icon(Icons.refresh),
            ),
        );
    }

}

// สรัางฟังก์ชั่นดึงข้อมูล คืนค่ากลับมาเป็นข้อมูล Future ประเภท List ของ Article
Future<List<Article>> fetchArticle() async {
  // ทำการดึงข้อมูลจาก server ตาม url ที่กำหนด
  final response = await http
      .get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));

  // เมื่อมีข้อมูลกลับมา
  if (response.statusCode == 200) {
    // ส่งข้อมูลที่เป็น JSON String data ไปทำการแปลง เป็นข้อมูล List<Article
    // โดยใช้คำสั่ง compute ทำงานเบื้องหลัง เรียกใช้ฟังก์ชั่นชื่อ parseArticles
    // ส่งข้อมูล JSON String data ผ่านตัวแปร response.body
    return compute(parseArticles, response.body);
  } else { // กรณี error
    throw Exception('Failed to load article');
  }
}

// ฟังก์ชั่นแปลงข้อมูล JSON String data เป็น เป็นข้อมูล List<Article>
List<Article> parseArticles(String responseBody) {
  final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
  return parsed.map<Article>((json) => Article.fromJson(json)).toList();
} 

// Data models
class Article {
  final int userId;
  final int id;
  final String title;
  final String body;

  Article({
    required this.userId,
    required this.id,
    required this.title,
    required this.body,
  });

  // ส่วนของ name constructor ที่จะแปลง json string มาเป็น Article object
  factory Article.fromJson(Map<String, dynamic> json) {
    return Article(
      userId: json['userId'],
      id: json['id'],
      title: json['title'],
      body: json['body'],
    );
  }

} 
 
    ผลลัพธ์ที่ได้
 


 
 
    เริ่มต้นเราอยู่ที่หน้า About เมื่อมาที่หน้า Home ก็จะทำการโหลดข้อมูลจาก API ขณะโหลด
ก็จะขึ้นตัว loading เพราะต้นนี้ยังไม่มีข้อมูล และเมื่อโหลดข้อมูลเสร็จเรียบร้อยแล้ว ก็แสดงข้อมูลใน
ลิสรายการด้วย ListView พอเรากดที่ปุ่ม refresh ก็จะทำการโหลดข้อมูลใหม่อีกครั้ง แต่ด้วยรูปแบบ
การกำหนดเงื่อนไขข้างต้น เราตรวจสอบโดยใช้ 
 
if (snapshot.hasData){
 
    เมื่อเรากด refresh ค่า snapshot.hasData เดิมยังเเป็น true เพราะมีข้อมูลเก่าแสดงอยู่ทำให้
ไม่ขึ้นตัว loading ใหม่อีกครั้ง ดังนั้น ถ้าเราต้องการให้ขึ้นตัว loading อีกครั้ง เมื่อกำลังโหลดใหม่
ให้เราใส่การตรวจสอบสถานะการเชื่อมต่อเพิ่มเข้าไปด้วย เป็นดังนี้
 
// มีข้อมูล และต้องเป็น done ถึงจะแสดงข้อมูล ถ้าไม่ใช่ ก็แสดงตัว loading 
if (snapshot.hasData &&  snapshot.connectionState == ConnectionState.done) {
 
    เมื่อกำหนดในรูปแบบข้างต้นแล้ว เวลาเรารีเฟรสหรือดึงข้อมูลใหม่ ถึงตัว snapshot.hasData จะเป็น true
แต่ค่า snapshot.connectionState จะเป็น waiting ดังนั้นตัว loading จึงทำการแสดงอีกครั้ง แล้วค่อย
แสดงข้อมูลที่ดึงมา เมื่อได้รับข้อมูลสุดท้าย
 
    ผลลัพธ์ที่ได้
 


 
 
    เกี่ยวกับการใช้งาน http package ในเบื้องต้นก็จะมีเท่านี้ แต่เรายังจะมีเนื้อหาเกี่ยวกับการใช้งานเรื่องนี้อีก
เช่น การเพิ่ม ลบ แก้ไข หรือแสดงข้อมูล 
    หวังว่าเนื้อหานี้จะเป็นแนวทางสำหรับนำไปประยุกต์ใช้งานต่อไป บทความหน้าจะเป็นอะไร รอติดตาม


   เพิ่มเติมเนื้อหา ครั้งที่ 1 วันที่ 25-10-2021


แนวทางการกำหนด Data Model สำหรับใช้งานข้อมูล

 
เมื่อเรารู้โครงสร้างข้อมูลของ JSON String data แล้ว เราสามารถนำมาเป็นแนวทาง
ในการกำหนดรูปแบบของ Data Model เพื่อใช้งานได้ สมมติโครงสร้างข้อมูลที่ได้จาก API
มีรูปแบบ JSON String data ดังนี้
 
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaeenderit",
    "body": "quia et suscipitnsuscipit recusatecto"
  }
]
 
เราจะมากำหนด Data Model ในรูปแบบดังนี้
 
// กำหนด ชื่อ class
class Classname{
  // กำหนด state หรือ property ของ class

  // กำหนด parameter contructor

  // กำนหนด factory constructor

}
 
สมมติเราจะใช้ชื่อว่าคลาสว่า News และมี property อันเดียวคือ id เป็น ก็จะได้เป็น
 
// กำหนด ชื่อ class
class News{
  // กำหนด state หรือ property ของ class
  final String id;

  // กำหนด parameter contructor
  News({
    required this.id
  });

  // กำนหนด factory constructor
  factory News.fromJson(Map<String, dynamic> json) {
    return News(
      id: json['id'],
   );
  }

}
 
โครงสร้างข้อมูล JSON String data ของเรามีด้วยกัน 4 ตัวคือ userID , id , title และ body
เวลาเราสร้าง Data Model ไม่จำเป้นต้องใช้หมดทั้ง 4 ก็ได้ อาจจะกำหนดแค่ id กับ title เพื่อใช้
งานก็ได้
    สมมติเราเพิ่มส่วนของ title และ body เพิ่มลงไป ก็จะได้เป็นดังนี้
 
// กำหนด ชื่อ class
class News{
  // กำหนด state หรือ property ของ class
  final String id;
  final String title;
  final String description;


  // กำหนด parameter contructor
  News({
    required this.id,
    required this.title,
    required this.description
  });

  // กำนหนด factory constructor
  factory News.fromJson(Map<String, dynamic> json) {
    return News(
      id: json['id'],
      title: json['title'],
      description: json['body'],
   );
  }

}
 
    สังเกตว่าข้อมูลส่วนของ body เรากำหนดชื่อใหม่ เพื่อให้เข้าใจมากขึ้น โดยใช้เป็น description
เราจะต้องเปลี่ยนส่วนที่เหลือเป็น description ทั้งใน parameter contructor และ factory constructor
ยกเว้นส่วนที่อ้างอิงจาก JSON เราต้องใช้เป็นค่าเดิม
 
description: json['body'], //ค่า description ต้องมาจาก body
 
    ต่อไป สมมติเรามั่นใจว่าข้อมูล JSON ค่าส่วนของ id และ title ยังไงก็จะมีค่าแน่นอน ไม่เป็นค่าว่าง แต่
ส่วนของ body อาจจะเป็นค่าว่างได้ ดังนั้น เราต้องป้องกันการเกิด null safety หรือ การเรียกใช้งานค่า null
 
// กำหนด ชื่อ class
class News{
  // กำหนด state หรือ property ของ class
  final String id;
  final String title;
  final String? description;


  // กำหนด parameter contructor
  News({
    required this.id,
    required this.title,
    this.description
  });

  // กำนหนด factory constructor
  factory News.fromJson(Map<String, dynamic> json) {
    return News(
      id: json['id'],
      title: json['title'],
      description: json['body'],
   );
  }

}
 
    ส่วนของการกำหนด property ให้เราใส่ ? ต่อเข้าไปในชนิดข้อมูล ของรายการที่จะป้องกัน
 
final String? description;
 
    และในส่วนของ parameter constructor ก็เอาคำว่า required ออก เพื่อเป็นการบอกว่า ค่านี้จะมี
หรือไม่ก็ได้ ดังนั้นเมื่อกำหนดในลักษณะนี้ ตัว description ก็มีโอกาสเป็นค่า null ได้ เวลาเรียกใช้งาน
เช่นดึงมาแสดงผ่านข้อมูล snapshot เราก็ต้องใส่เครื่องหมาย ! ต่อท้ายเพื่อบอกว่า ข้อมูลที่เรียกใช้นี้
อาจจะเป็น null ได้ 
 
snapshot.data![index].description!;    
 
 
    เกี่ยวกับการใช้งาน class ดูเพิ่มเติมได้ที่ http://niik.in/942


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



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









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









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











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