ข้ามไปยังเนื้อหาหลัก
ProgrammerThailand

Flutter

สร้าง Mobile Application ด้วย Flutter Workshop : แอปติดตามรถไฟไทย (Train Tracker)

หากมีข้อสงสัยสอบถามหรือแสดงความคิดเห็นสามารถโพสต์ได้ที่ สอบถามปัญหาหรือแนะนำจากบทเรียนรู้เรื่องการพัฒนา Application ด้วย Flutter Workshop : ระบบติดตามรถไฟไทย ครับ 

เตรียมเครื่องมือ

เครื่องมือที่ใช้ในการเขียน Application ด้วยภาษา Dart นั้นเราจะใช้ Visual Studio Code และ Android SDK / Android AVD หรือ iPhone Simulator ก็ได้

Flutter

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

หลังจากติดตั้งเสร็จแล้วให้พิมพ์คำสั่ง  

flutter doctor

ระบบจะแสดงรายการต่างๆ หากไม่มี issue ก็สามารถไปขั้นตอนต่อไปได้ แต่ถ้ามี issue ก็ให้แก้ไขให้เรียบร้อยเสียก่อน ดังตัวอย่าง

หากต้องการ upgrade version ของ Flutter ให้พิมพ์คำสั่ง 

flutter upgrade

 

Visual Studio Code

สำหรับ Visual Studio Code ให้ติดตั้ง PlugIn เสริมดังนี้

  • Flutter หลังจากติดตั้งจะสามารถเรียกใช้คำสั่งต่างๆ ใน command palette ได้ เช่น การสร้าง project ใหม่ เป็นต้น โดยมีลักษณะดังนี้

  • Dart เป็น PlugIn ที่รองรับภาษา Dart

Flutter hot reload example

Android SDK

Android AVD

iPhone Simulator

การสร้าง Flutter Project : Train Tracker

ในบทเรียนรู้นี้เรามาทำความเข้าใจในการสร้าง Mobile Application ด้วย Flutter ของ Google กัน โดยในบทเรียนรู้นี้จะพาทุกท่านทำเป็นลักษณะ Workshop แบบ Step by Step ในขั้นตอนนี้เรามาสร้าง Flutter Project (ขั้นตอนการติดตั้ง Flutter สามารถดูเพิ่มเติมได้จาก https://flutter.dev/docs/get-started/install )

ให้ทำการเป็น terminal หรือ cmd แล้ว ไปที่ folder ที่เก็บ project เช่น kongoon/flutter/project จากนั้นพิมพ์คำสั่งเพื่อการสร้าง project ใหม่ โดยตั้งชื่อ project ว่า train_tracker โดยพิมพ์คำสั่ง

flutter create train_tracker

จะปรากฏหน้าจอลักษณะดังนี้

หลังจากนั้นเปิด Project กับโปรแกรม Visual Studio Code แล้วเปิดไฟล์ lib/main.dart จากนั้นไปที่เมนู Debug/Start Debugging จากนั้นเลือก เครื่องสำหรับการ Debug หากยังไม่มีให้สร้างขึ้น (ดูรายละเอียดการสร้าง AVD สำหรับ Android ได้ที่นี่ https://developer.android.com/studio/run/managing-avds ) ในที่นี้จะใช้ iOS Simulator/ iPhone11 จะปรากฏหน้าจอลักษณะดังนี้

 

การปรับแต่ง Theme Color สำหรับ Application

ใน Flutter เราสามารถกำหนดสีที่เป็น Primary Color และ Accent Color ได้ โดยกำหนดที่ไฟล์ main.dart ใน ที่นี้กำหนด Primary Color เป็น Colors.red และ Accent Color เป็น redAccent ลักษณะดังนี้

//....
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Thai Train Tracker',
      theme: ThemeData(
        primarySwatch: Colors.red,
        accentColor: Colors.redAccent
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

//....

เมื่อทดลอง run โดยกด debug -> start debuging จะมีหน้าจอลักษณะดังนี้

 

การสร้างหน้าหลัก Home Page

หน้าหลักเป็นหน้าจอที่เมื่อเปิดเข้า Application แล้วจะแสดงหน้านี้เป็นหน้าเริ่มต้น โดยเราจะสร้าง Folder สำหรับเก็บไฟล์ ​page ต่างๆ ใน folder lib โดยตั้งชื่อ folder ว่า pages และสร้างไฟล์ home_page.dart ลักษณะดังนี้

จากนั้นเขียนโปรแกรมไฟล์ home_page.dart โดยสร้าง Class HomePage ให้เป็นแบบ stateful โดยพิมพ์ stf จะมีเมนูมาให้เลือกจากนั้นกดปุ่ม Enter ได้เลย แล้วใส่ชื่อ Class ว่า HomePage ลักษณะดังนี้ (เมื่อใส่ชื่อ Class เสร็จแล้ว Visual Studio Code จะแสดงเส้นสีแดง ให้นำ cursor มาวางที่ Container จะมีหลอดไฟแสดงขึ้น ให้คลิกที่หลอดไฟแล้วเลือก import material.dart แล้ว error สีแดงจะหายไป)

import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Container(
      
    );
  }
}

จากนั้นเขียนโปรแกรมเพื่อแสดงผลหน้าแรกลักษณะดังนี้

import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Thailand Train Tracking'),
      ),
      body: Center(
        child: Column(
          
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
           Icon(Icons.train, size: 150.0, color: Colors.red),
            Text(
              'Train Tracking',
              style: TextStyle(fontSize: 50.0),
            ),
            Text(
              'Where is your train?', 
              style: TextStyle(fontSize: 20.0),
            ),
          ],
        ),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: FloatingActionButton(
        backgroundColor: Colors.redAccent,
        tooltip: 'Find Train',
        child: Icon(Icons.table_chart),
        onPressed: (){
          Navigator.push(
            context,
            MaterialPageRoute(
              
            )
          );
        },
      ),
      
    );
  }
}

 ปรับแก้ไขไฟล์ main.dart ให้เปิดหน้า HomePage.dart โดยแก้ไขไฟล์ main.dart ลักษณะดังนี้

import 'package:flutter/material.dart';
import 'package:train_tracker/pages/home_page.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.red,
        accentColor: Colors.redAccent
      ),
      home: HomePage(),
    );
  }
}

เมื่อ run application จะได้หน้าจอลักษณะดังนี้

 

 

การสร้างการเชื่อมต่อ API

ขั้นตอนนี้มาสร้างการเชื่อมต่อ API (Application Programming Interface) ไปยังรถไฟไทย

ก่อนอื่นต้องติดตั้ง package สำหรับการใช้งาน http request โดยเปิดไฟล์ pubspec.yaml จากนั้นเพิ่มชื่อ package ลักษณะดังนี้

จากนั้น Visual Studio Code จะทำการติดตั้งให้อัตโนมัติ โดยจะเห็นคำสั่งใน Output ลักษณะดังนี้

[train_tracker] flutter packages get
Running "flutter pub get" in train_tracker...                      27.8s
exit code 0

จากนั้นสร้าง folder เก็บใน lib ดังนี้ lib/api จากนั้นสร้างไฟล์สำหรับการเชื่อมต่อโดยตั้งชื่อว่า train_api.dart

จากนั้นเขียนโปรแกรมในการเชื่มอต่อไปยังรฟท. โดยเขียนโปรแกรมในลักษณะดังนี้

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


class TrainApi {

  static final apiUrl = "http://tts.railway.co.th/ttsmaster/checktrain.php";

  Future<http.Response> getTrainCurrent() async {

    return await http.post(apiUrl, headers: {
        'cache-control' : 'no-cache',
        'Connection' : 'keep-alive',
        'Content-Length' : '0',
        'Accept-Encoding' : 'gzip, deflate',
        'Host' : 'tts.railway.co.th',
        'Cache-Control' : 'no-cache',
        'Accept' : '*/*',
        'User-Agent' : 'ThaiTrainTracker/1.0',
      }, 
      body: {});
    
  }
  
}

 

การสร้างหน้าเพื่อแสดงผลข้อมูลจาก API

ขั้นตอนนี้มาสร้างหน้า page สำหรับการแสดงตารางข้อมูลที่ดึงมาจาก รฟท. โดยสร้างไฟล์ page/train_page.dart จากนั้นเขียนโปรแกรมเพื่อแสดงตารางข้อมูลจาก JSON ที่ดึง API มาลักษณะดังนี้

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:json_table/json_table_column.dart';
import 'package:train_tracker/api/train_api.dart';
import 'package:json_table/json_table.dart';

class TrainPage extends StatefulWidget {

  int trainId;
  String name;

  @override
  _TrainPageState createState() => _TrainPageState();
}

class _TrainPageState extends State<TrainPage> {
  List items = [];
  bool isLoading = true;
  List<JsonTableColumn> columns;

  TrainApi trainApi = TrainApi();

  Future trainCurrent() async {
    try{
      var response = await trainApi.getTrainCurrent();

      if(response.statusCode == 200) {
          var jsonResponse  = json.decode(response.body);

          setState(() {
            isLoading = false;
            items = jsonResponse['data'];
          });
      }
    }catch (error) {
      setState(() {
        isLoading = false;
      });
      throw Exception (error.toString());
    }
  }

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

    columns = [
      //JsonTableColumn("new", label: "#", valueBuilder: getUpdated),
      JsonTableColumn("trains_no", label: "Train No."),
      //JsonTableColumn("traintypeid", label: "Train Type"),
      JsonTableColumn("fromen", label: "From"),
      JsonTableColumn("toen", label: "To"),
      JsonTableColumn("line", label: "Line", valueBuilder: getLine),
      JsonTableColumn("train_type_eng", label: "Train Type"),
      //JsonTableColumn("station_code", label: "Current Station Code"),
      JsonTableColumn("station_name_eng", label: "Last Station"),
      //JsonTableColumn("stopno", label: "Status"),
      JsonTableColumn("arrtime", label: "Arrived Time", valueBuilder: getTime),
      //JsonTableColumn("arrlate", label: "Arrived Late"),
      JsonTableColumn("deptime", label: "Deperature Time", valueBuilder: getTime),
      JsonTableColumn("deplate", label: "Late"),
    ];
  }

  String getTime(value) {
    return value == "ผ่าน" ? 'PASS' : (value == "ปลายทาง" ? 'DESTINATION': (value == 'ต้นทาง' ? 'START' :value));
  }

  String getUpdated(value) {
    if (value == "1"){
      return 'UPDATED';
    }
    return '';
  }

  String getLine(value) {
    switch (value) {
      case "1":
        return 'NORTH';
        break;
      case "2":
        return 'NORTH EAST';
        break;
      case "3":
        return 'EAST';
        break;
      case "4":
        return 'SOUTH';
        break;
      default:
        return '';
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Last Train Update'),
        ),
        body: isLoading
            ? Center(
                child: CircularProgressIndicator(),
              )
            : RefreshIndicator(
              onRefresh: trainCurrent,
              child:
              Container(
        padding: EdgeInsets.all(16.0),
        child: ListView(
          children: <Widget>[
              JsonTable(items, columns: columns,
              //showColumnToggle: true
            )
          ]
        )
            )
            )
    );

                    
  }
}

 

จากนั้นแก้ไขไฟล์ main_page.dart เพื่อให้สามารถคลิกที่ปุ่ม floating แล้วเปิดหน้า train_page.dart ลักษณะดังนี้

//...
import 'package:train_tracker/pages/train_page.dart';
//...
MaterialPageRoute(
              builder: (context) => TrainPage()
            )
//...

 

จากนั้นทดลอง run จะได้หน้า application และเมื่อกด floating icon แสดงลักษณะดังนี้

  • การ Signed APK
  • การ Upload ขึ้น Play Store
  • การ Upgrade Version Application ใน Play Store

อยู่ระหว่างการเตรียมเนื้อหา 

ติดปัญหาตอนทำตาม? ถามในชุมชน

ความคิดเห็น (0)

ยังไม่มีความคิดเห็น — เริ่มคุยเป็นคนแรก ถามสิ่งที่สงสัย หรือแชร์ประสบการณ์ของคุณ