用 Flutter 開發一個 Android App 吧 - Day15

Build Android app with Flutter - Day15

Posted by Bobson Lin on Tuesday, September 24, 2019

Day 15.

本系列同步發表在 第11屆鐵人賽

GitHub API 初體驗

本人在這專案之前是完全沒使用過 GitHub API 的,所以我是以新手之姿來串接它。
如果有理解錯的,希望大家留言告訴我囉~

目前我使用步驟是

  1. 首先要有個 GitHub 帳號(廢話)
  2. 再到個人的設定頁面拿 Personal access token,開發測試時使用。
  3. 最後拿 token 就可以對接 GitHub REST API v3 了。

小提醒:

  • Personal access token 我個人認知是拿來自己開發測試用,之後若有開放出來給別人下載使用的話,應該要建立 Oauth App 的,用 Oauth2 認證方式來拿到別的用戶授權。
  • GitHub API 目前的想法是以搭配 github 這個套件來作串接。

倉庫頁 - 改

接下來我們就直接實際上使用看看。

我先在創建一個新的檔案 lib/services/github_api.dart

import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:github/server.dart';

GitHub githubClient = GitHub(
  auth: Authentication.withToken(DotEnv().env["GITHUB_PERSONAL_ACCESS_TOKEN"]),
);

主要目的是要宣告出一個全域變數 githubClient,方便後續其他頁面也能使用。

這裡和作天一樣我創建了 fetchRepos() 函數,裡頭來獲取我個人的所有 repo。
接著在 initState() 函數裡將 fetchRepos() assign 給 repoList,這樣前置作業就算完成拉~

最後比較複雜的部份,我選擇使用 Flutter 原生提供一個方便的 Widget – FutureBuilder
將最主要的內容部份以這個 Widget 來作些改寫,先來看看程式碼。

...

  @override
  Widget build(BuildContext context) {
    return Scrollbar(
      child: RefreshIndicator(
        child: FutureBuilder(
          future: repoList,
          builder:
              (BuildContext context, AsyncSnapshot<List<Repository>> snapshot) {
            switch (snapshot.connectionState) {
              case ConnectionState.done:
                if (!snapshot.hasError) {
                  return ListView.separated(
                    padding: EdgeInsets.all(0.0),
                    itemCount: snapshot.data.length,
                    itemBuilder: (BuildContext context, int index) {
                      return ListTile(
                        title: Text(
                            "${snapshot.data[index].owner.login}/${snapshot.data[index].name}"),
                        subtitle: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: <Widget>[
                            SizedBox(height: 8.0),
                            snapshot.data[index].description != null
                                ? Text(snapshot.data[index].description)
                                : Text("No description provided."),
                            SizedBox(height: 8.0),
                            Text("★ ${snapshot.data[index].stargazersCount}"),
                          ],
                        ),
                        trailing: snapshot.data[index].language != null
                            ? Text(snapshot.data[index].language)
                            : SizedBox(),
                        contentPadding: EdgeInsets.symmetric(
                            vertical: 8.0, horizontal: 16.0),
                        onTap: () {},
                      );
                    },
                    separatorBuilder: (BuildContext context, int index) =>
                        const Divider(height: 0.0),
                  );
                } else {
                  return Center(child: Text("No Data"));
                }
                break;
              case ConnectionState.none:
              case ConnectionState.active:
              case ConnectionState.waiting:
                return Center(child: CircularProgressIndicator());
            }
          },
        ),
        onRefresh: () async {
          await Future.delayed(Duration(seconds: 1));
          setState(() {
            repoList = fetchRepos();
          });
        },
      ),
    );
  }
  
  Future<List<Repository>> fetchRepos() async {
    ...
  }

程式碼看起來很長,但最主要的概念就是我將原本 ListView.separated 的部份包進了 FutureBuilder 裡。

而使用 FutureBuilder 有兩大重點: * future 屬性需要帶入你要執行的異步任務(這裡就是指獲取 repos) * builder 屬性需要填入你所想要創建的內容,而執行異步任務完的結果,可以在函數第二個參數 AsyncSnapshot snapshot 來作使用。

小提醒:

  • 使用 FutureBuilder 其實還有一個好處,可以在 build 裡面處理 snapshot.connectionState ,比如有 連線異常 或 載入資料中的情況,能直接用 switch case 切換 Widget 來表示這些狀態。
  • 在開發途中發現如果搭配 FutureBuilder 的話,RefreshIndicator.onRefresh 的使用上需作些修改。(參考: HOW TO REFRESH FUTUREBUILDER ON BUTTON CLICK IN FLUTTER)
  • 關於 Personal access token 我使用 flutter_dotenv 這套件來載入我的 .env 設定檔,主要是為些安全上作考量囉~

成果

day15-1.gif

鐵人賽默默的也過了一半了~
這幾天小弟較忙,原諒小弟今天先以簡短的內容做收~