Flutter

Rapid Series: Flutter ดึงข้อมูล Web Services JSON ตกแต่ง Card และ Row ข้อมูล

บทเรียนซีรีย์ Rapid เป็นไว ทำได้ เข้าใจ กับ Flutter กับตัวอย่างหลังจากดึงข้อมูล JSON มาแสดงใน ListView แล้วมาตกแต่ง Row ด้วย Card กันหน่อย

บทเรียนซีรีย์ Rapid เป็นไว ทำได้ เข้าใจ กับ Flutter กับตัวอย่างหลังจากดึงข้อมูล JSON มาแสดงใน ListView แล้วมาตกแต่ง Row ด้วย Card กันหน่อย

ตัวอย่างที่จำเป็นต้องศึกษาก่อนหน้า

จากบทเรียนที่แล้วเราจะได้หน้าจอแอปพลิเคชันของเราเป็นดังนี้:

บทเรียนนี้เราจะมา Decoration กันสักหน่อยในเรื่องของความสวยงามซึ่งเราต้องจัด Padding และ Margin กันหน่อยมาดูกันว่าเราจะเริ่มต้นยังไง

ให้เราสร้าง File ใหม่ขึ้นมาใน folder “lib” ตั้งชื่อว่า items.dart

ใน items.dart เราจะสร้างคลาสที่ชื่อว่า Items() ขึ้นมา โดยรับค่าตัวแปรจาก JSON คือ title และ thumbnail ให้ทำการประกาศคลาส String

class Items {
  final String title;
  final String thumbnail;
  Items(this.title, this.thumbnail) {
    if (title == null) {
      throw ArgumentError("Title can't not be null"
          "Received: '$title'");
    }
    if (thumbnail == null) {
      throw ArgumentError("Thumbnail of Books can't be null. "
          "Received: '$thumbnail'");
    }
  }
}

เป็น คลาสในการทำ Custom Type ง่ายๆ เวลารับค่าก็แค่เรียก Items(title,thumbnail) ก็จบและเช็คค่าว่ามันไม่ใช่ค่าว่างก็เท่านั้น

กลับไปที่ main.dart ให้เพิ่ม Header เข้าไปดังนี้:

import 'items.dart';

เพื่อเรียกใช้งานคลาสของ Items ที่อยู่ในไฟล์ items.dart ได้

ไปที่ ContextState() ใน main.dart ให้เราแก้ไขตัวแปรจากเดิมคือ:

var _books = [];

แก้ไขเป็น:

var _items = <Items>[];

เพิ่ม const ของ style ตัวอักษรให้เป็นสีฟ้าเล็กน้อยดังนี้:

final _blueFont = const TextStyle(color: Colors.blueAccent);

แก้ไขส่วนของ Widget ListView ใหม่จากเดิมคือ:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(Strings.appName),
      ),
      body: ListView.builder(
          padding: const EdgeInsets.all(16.0),
          itemCount: _books.length,
          itemBuilder: (BuildContext context, int position) {
            return _buildRow(position);
          }),
    );
  }

แก้ไขใหม่เป็น:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(Strings.appName),
      ),
      body: ListView.builder(
          itemCount: _items.length * 2,
          itemBuilder: (BuildContext context, int position) {
            if (position.isOdd) return Divider();
            final index = position ~/ 2;
            return _buildRow(index);
          }),
    );
  }

นั่นคือการทำ Divider ของแถวให้มีช่องว่างแก๊บระยะของแต่ละ Item ถูกคั่นด้วย Row เปล่าๆ ที่ไม่สูงมากระหว่างรายการมาคั่นไว้พร้อมเส้นของแถว

แก้ไข setState() ใหม่เป็นดังนี้:

setState(() {
      final itemsJSON = json.decode(response.body);

      for (var data in itemsJSON) {
        final member = Items(data["title"], data["thumbnail"]);
        _items.add(member);
      }
});

นั่นคือเราจะเข้าระหัส URL web Services เก็บที่ itemsJSON หลังจากนั้นไป foreach เอาค่า value ตาม key มาเก็บใน data[“key”] ต่างๆ ส่งไปที่ฟังก์ชันในคลาส Items(title,thumbnail) เก็บลงตัวแปร member สมาชิกข้อมูล แล้วยัด array ค่าลงไปในตัวแปร _items ที่เราสร้างไว้

แก้ไขฟังก์ชันเมธอดของ _buildRow() ใหม่ทั้งหมดเลย โดยเราจะเริ่มที่การสร้าง Row มาคั่นตามด้วย Column ของ Row นั่นคือใน 1 แถวจะถูกแบ่งคอลัมน์เป็น 2 คอลัมน์ฝั่งซ้ายคือหน้าปกหนังสือที่ดึงจาก thumbnail ฝั่งขวาจาก title ซึ่งการจัด Layout อาจจะวุ่นวายและยาวเล็กน้อย

return Container(
      margin: EdgeInsets.symmetric(horizontal: 0.0, vertical: 0.1),
      padding: EdgeInsets.symmetric(horizontal: 5.0, vertical: 0.15),
      height: MediaQuery.of(context).size.height * 0.25,
      child: Card(
          color: Colors.white,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(12.0),
          ),
          child: Row(
            children: <Widget>[
              Expanded(
                child: FittedBox(
                    fit: BoxFit.contain, // otherwise the logo will be tiny
                    child: Image.network(_items[i].thumbnail)),
              ),
              Expanded(
                  child: Column(children: <Widget>[
                Expanded(
                    child: Align(
                  alignment: Alignment.centerLeft,
                  child: Text("${_items[i].title}", style: _biggerFont),
                )),
                Expanded(
                    child: Align(
                  alignment: Alignment.centerLeft,
                  child: Text(
                      "see more detail and click to detail about: ${_items[i].title}",
                      style: _blueFont),
                ))
              ]))
            ],
          )),
    );

ลำดับของ Hierarchy เป็นตามรูปนี้:

ตรวจและแกะกันเอาเองนะครับ ผมแค่ใช้ Container คลุม แล้วสร้าง Card ขอบมนๆ มาระยะ padding ดีๆ ภายใน Card มี Row ที่แบ่ง Expanded (คอลัมน์ซ้าย) ด้วย Image และ ขวาด้วย Columns (แบ่งแถวของฝั่งขวา) เป็น 2 แถวด้วย Text สองตัว ตาม Code

ซึ่งถ้าสังเกตผมจะเรียก _items[i].title แทนการเรียกแบบเก่า ส่วนรูปภาพใช้ Image.Network มารับค่าแทน NetworkImage:

ดังนั้นภาพรวม Source code ของ main.dart เป็นดังนี้:

import 'package:flutter/material.dart';
import 'strings.dart';
import 'items.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

void main() => runApp(MainApp());

class MainApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: Strings.appName,
      home: MainContextApp(),
    );
  }
}

class ContextState extends State<MainContextApp> {
  var _items = <Items>[];

  final _biggerFont = const TextStyle(fontSize: 18.0);
  final _blueFont = const TextStyle(color: Colors.blueAccent);

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

    _loadData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(Strings.appName),
      ),
      body: ListView.builder(
          itemCount: _items.length * 2,
          itemBuilder: (BuildContext context, int position) {
            if (position.isOdd) return Divider();

            final index = position ~/ 2;

            return _buildRow(index);
          }),
    );
  }

  Widget _buildRow(int i) {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 0.0, vertical: 0.1),
      padding: EdgeInsets.symmetric(horizontal: 5.0, vertical: 0.15),
      height: MediaQuery.of(context).size.height * 0.25,
      child: Card(
          color: Colors.white,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(12.0),
          ),
          child: Row(
            children: <Widget>[
              Expanded(
                child: FittedBox(
                    fit: BoxFit.contain, // otherwise the logo will be tiny
                    child: Image.network(_items[i].thumbnail)),
              ),
              Expanded(
                  child: Column(children: <Widget>[
                Expanded(
                    child: Align(
                  alignment: Alignment.centerLeft,
                  child: Text("${_items[i].title}", style: _biggerFont),
                )),
                Expanded(
                    child: Align(
                  alignment: Alignment.centerLeft,
                  child: Text(
                      "see more detail and click to detail about: ${_items[i].title}",
                      style: _blueFont),
                ))
              ]))
            ],
          )),
    );
  }

  _loadData() async {
    String dataURL = "https://enet5-7f9f6.firebaseio.com/Books.json";
    http.Response response = await http.get(dataURL);
    setState(() {
      final itemsJSON = json.decode(response.body);

      for (var data in itemsJSON) {
        final member = Items(data["title"], data["thumbnail"]);
        _items.add(member);
      }
    });
  }
}

class MainContextApp extends StatefulWidget {
  @override
  createState() => ContextState();
}

รันคำสั่ง

$ flutter run

เราจะได้ผลลัพธ์ตามภาพ

ลองไปแกะคำสั่งข้างบนดูอีกทีนะครับ หรือจะ Clean Code ก็ได้ส่วนของ ContextState() ไม่น่าจะยากละ

Asst. Prof. Banyapon Poolsawas

อาจารย์ประจำสาขาวิชาการออกแบบเชิงโต้ตอบ และการพัฒนาเกม วิทยาลัยครีเอทีฟดีไซน์ & เอ็นเตอร์เทนเมนต์เทคโนโลยี มหาวิทยาลัยธุรกิจบัณฑิตย์ ผู้ก่อตั้ง บริษัท Daydev Co., Ltd, (เดย์เดฟ จำกัด)
Back to top button

Adblock Detected

เราตรวจพบว่าคุณใช้ Adblock บนบราวเซอร์ของคุณ,กรุณาปิดระบบ Adblock ก่อนเข้าอ่าน Content ของเรานะครับ, ถือว่าช่วยเหลือกัน