本系列同步發表在 個人部落格,歡迎大家關注~
題外話
到昨天,本人就撐完此系列的一半了,可喜可賀~
不過在這資訊時代,難免會在意自己文章被別人閱讀的想法或次數,簡單來說就是流量
我是正式開賽後隔一個禮拜才開始發文,一開始覺得這主題(Mobile Development)好像沒太多人關注,好幾個參賽者跟我一樣,最多也才一、兩百瀏覽次數。
結果在 9/17 這主題的熱門文章突然間殺出一篇 5000+ 瀏覽次數的,
我當場直接
傻爆眼 ... ![]()
哇靠! 是一夜之間從哪裡烙這麼多人來點閱的...  莫非是哪位超級大神寫的文 !?
我花這麼長時間準備才寫出一篇 100+ 瀏覽次數,不免有些氣餒阿... ![]()
雖然知道 ithelp 有這方面的 Bug,但我看之前幾屆好像都沒這麼誇張...
後來在 9/20 這主題由 badgameshowtw 發出 [Day 4] Xcode安裝 為什麼有5000觀看??? ithelp觀察實驗 這篇文刷了十幾萬瀏覽後, ithelp 有人開始修正這問題。
好拉,這題外話算是有些抱怨,也希望評分沒有過度參考這數據。
當然期望 ithelp 有更好的機制,也勉勵其他跟我一樣的參賽者能堅持下去,為社群貢獻出優質文章囉~ ![]()
好了,正篇開始
如果你看自己的 github.com 首頁,就會顯示所有的活動。
基本上活動頁就是此頁面的 App 版,研究之後應該是調用 GitHub REST API v3 - Feeds 這個 API。
不過我所使用的 github 套件似乎還沒整合這個 API;內容之後補上。
內容待補
如果一直沒人實現這部份,或許有時間我會發個 PR 給作者吧
接著我直接修改議題頁拉~
其實跟昨天的 [倉庫頁 - 改],差不多概念;一樣使用 FutureBuilder 、修改 issueList 、實現 fetchIssues() 函數。
lib/pages/issue.dart
  ...
  @override
  Widget build(BuildContext context) {
    return Scrollbar(
      child: RefreshIndicator(
        child: FutureBuilder(
          future: issueList,
          builder: (BuildContext context, AsyncSnapshot<List<Issue>> snapshot) {
            switch (snapshot.connectionState) {
              case ConnectionState.done:
                if (!snapshot.hasError) {
                  return ListView.separated(
                    itemCount: snapshot.data.length,
                    itemBuilder: (BuildContext context, int index) {
                      final now = DateTime.now();
                      final difference =
                          now.difference(snapshot.data[index].createdAt);
                      var createTimeAgo =
                          timeago.format(now.subtract(difference));
                      return ListTile(
                        dense: true,
                        leading: CircleAvatar(
                          radius: 18.0,
                          backgroundImage:
                              NetworkImage(snapshot.data[index].user.avatarUrl),
                        ),
                        title: Text(snapshot.data[index].title),
                        subtitle: Text(createTimeAgo),
                        trailing: Icon(Icons.error_outline),
                        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(() {
            issueList = fetchIssues();
          });
        },
      ),
    );
  }
  
  Future<List<Issue>> fetchIssues() async {
    return githubClient.issues.listAll(state: "all").toList();
  }
對比昨天的程式碼是不是 87 分像阿~
小提醒:
- 在顯示議題創建時間上面,我使用了
 timeago這個套件,非常好用~- https://stackoverflow.com/questions/49340116/setstate-called-after-dispose
 
--
成果
大致改完所有首頁的內容後,我覺得應該來講一下 State 的生命週期。
說是「生命週期」,其實就是從(預備)創建畫面、顯示畫面、離開畫面,其中過程調用到的函數。
我直接找別人整理好的附圖,直接看右半邊的部份。
參考自 https://blog.duicode.com/3108.html
左半邊為 Android App 的生命週期。
可能大家可以看得出來繼承 StatefulWidget 的 Widget 在程式碼最大的特點,就是會重寫(override) createState() 這個函數。
從 createState() 開啟 State 的生命週期,而我個人最常用到的函數為 intState()、build() 、 dispose() 和 setState()。
State 物件有著以下的生命週期:
- 框架(Flutter)會藉由調用
 StatefulWidget.createState方法建造一個State物件。- 每個新建出來的
 State物件會永久對應一個BuildContext,但State物件並不會改變BuildContext。此時會將 State 物件標記成為 mounted(mounted=true) 狀態。- 調用
 initState,可藉由重寫(override) initState 來初始化設定。- 調用
 didChangeDependencies。- 此時
 State物件已做好全面初始化,接下來調用build方法來建構 UI。State物件可藉由setState方法來重建或調整 UI。- 在重建或調整 UI 的過程中,
 State物件會先調用didUpdateWidget方法看有沒有對應的 Widget 需要作改變,didUpdateWidget之後一定會再調用build方法(意味著在didUpdateWidget內用setState是沒意義的)。- 在開發過程中,熱重載會調用
 reassemble方法。- 當
 State物件從 Tree 中被移除會調用deactivate方法。- 如果
 State物件有插入至其他 Subtree 會調用build方法,否則會調用dispose方法。- 當調用
 dispose方法後,State物件會被標記成 unmounted (mounted=false),生命週期就此結束。不負責之簡易翻譯自 https://api.flutter.dev/flutter/widgets/State-class.html
--
小提醒:
- State 生命週期過程中其實有很多細節,礙於全部翻譯出來會很冗長,所以我只挑重點部份翻譯,詳細的話還是得要讀官方文件。
 - 小弟也是讀了文件後才更深入的理解 "State"fulWidget 中的祕密。
 
小弟菜鳥,一開始還在想說為什麼 dart lint 一直噴出警示:
給其他一樣是菜鳥的讀者建議,
在 switch case 當中,還是要放個 default 時的 return 值。
case ConnectionState.none:
case ConnectionState.active:
case ConnectionState.waiting:
default:
    return Center(child: CircularProgressIndicator());
感謝作者文章,
因為個人學習方式習慣做中學,因此會挑選實作篇重的文章參考。
這系列看到這,長惹不少知識,感謝大大!
哈哈 能幫助到你是我的榮幸,感謝你給我的系列文肯定