手撕Flutter開發

ClericYi發表於2020-02-21

手撕Flutter開發

前言

基於之前的新版本釋出version已經支援開始支援了Web開發者開發,那這個程式語言的前景已經不言而喻了,不僅僅是一門跨雙端的程式語言,更是可能成為未來的主流語言之一。當前這句話說的有點絕對,但是和React Native這一類語言不同的地方偶爾可能也是顯而易見的,基於的底層框架的修改,前者基於的是JS的一種擴充套件。所以接下來就是對這個程式語言的一種學習了。

另外本文應該會呈現4-5天的持續性更新,基於的後臺將是泓洋大神的wanandroid,有興趣的讀者都可以學習一下。

Flutter中文網,安裝步驟及基礎使用等都已經在這份文件裡了。

逃不過的安裝

材料清單:
1. 作業系統:OSX 10.15.3
2. Flutter版本:flutter_macos_v1.12.13+hotfix.7-stable
3. 軟體:Android Studio
複製程式碼

我一頓猛如虎的操作下來基本是沒有問題的,不過Gtihub上拉,確實有點慢,可以直接下載穩定版玩兒。

專案的原始碼將整合在WanAndroid-Flutter中,因為專案是android官網推出的,所以直接講解。

使用

通過以上的這些基礎內容,我們基本上就可以開始初級的開發了。 見於對整個開發程式碼的審閱,我們將基於Flutter Inspector對整體的程式碼進行觀察。

手撕Flutter開發

開發

  • 網路部分

見於官方文件中同樣建議我們使用dio進行網路請求,所以整體的網路請求及資料解析將依靠json_serializable+dio。 建立類似如下模版:

@JsonSerializable()
class User{
  User(this.name, this.email);

  String name;
  String email;
  //不同的類使用不同的mixin即可
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}
複製程式碼

編碼完成以後,執行命令列flutter packages pub run build_runner build,就會自動生成.g.dart檔案就可以進行使用了。 因為沒有類似於Java的反射機制,所以格式化的過程一般會比較麻煩,當然也可以去搜尋解析的框架。 另外在原始碼中,因為書寫方式是鑑於wanandroid一個開放的專案,已經比較符合規範,所以可以選擇直接謄寫。 另外因為資料的原因,其實我還是直接建議呼叫這個demo已經做好的資料。

專案結構

|-- libs
     |-- api (Api介面)
     |-- common (公共類)
     |-- fonts (字型類)
     |-- model (Bean類)
     |-- pages (UI頁面)
     |-- util (工具類)
     |-- widget (自定義元件)
複製程式碼

單個模組分析

因為頁面的邏輯基本一致,其實我們只用分析單個頁面,就能完全理解整體程式碼的邏輯了。 這裡主講的是一個project也就專案頁。

手撕Flutter開發
手撕Flutter開發

丟擲顯示效果,其實整體的框架就是一個TabBarRefreshIndicator還有Card的組合其實進入以後也能很清楚的發現。在介面上出現了一些叉叉的符號,那是因為我的刪減程式中並沒有加入字型庫。

apkLink: ,
author: Mstian,
chapterId: 402,
chapterName: 跨平臺應用,
collect: false,
courseId: 13,
desc: 使用前端跨端框架開發一款安卓版本的玩安卓App,具體實現了登入註冊, 體系, 公眾號, 專案列表功能, 還有導航功能, 收藏文章專案功能等。 還使用了高德地圖api實現機主定位功能。,
envelopePic: https: //www.wanandroid.com/blogimgs/aeb17245-d43c-4ad3-ac35-aae45096aef8.png, 
fresh: false, 
id: 10726,
 link: https://www.wanandroid.com/blog/show/2714, 
niceDate: 2019-12-06 16:02, 
origin: , 
projectLink: https://github.com/Mstian/wanAndroid, 
publishTime: 1575619327000, 
superChapterId: 294, 
superChapterName: 開源專案主Tab, 
tags: [Instance of 'ArticleTagModel'], 
title: 前端框架uniapp版玩安卓客戶端, 
type: 0, 
userId: -1, 
visible: 1, 
zan: 0
複製程式碼

這份json資料其實就是抓取來的資料之一,他所對應的就是Card,也就是我們所看到的卡片,對應的就是ArticleItemPage.dart

而我們看到的列表的呈現、下拉重新整理、上拉載入也只是一個ListViewRefreshIndicator的結合使用,下面使用到原始碼中的一段進行驗證。 程式碼位於ArticleListPage.dart

    listView = ListView.builder(
        physics: AlwaysScrollableScrollPhysics(),
        itemCount: itemCount,
        controller: getControllerForListView(),
        itemBuilder: (context, index) {
          if (index == 0 && null != widget.header) {
            // 當資料為0,並且存在頭部時,就返回頭部
            return widget.header;
          } else if (index - (null == widget.header ? 0 : 1) >=
              _listData.length) {
            // 如果出現index大於資料長度,就出現載入更多
            return _buildLoadMoreItem();
          } else {
            // 正常範圍狀態下,就構建Item
            return _buildListViewItemLayout(
                context, index - (null == widget.header ? 0 : 1));
          }
        });

    var body = NotificationListener<ScrollNotification>(
      onNotification: onScrollNotification,
      child: RefreshIndicator(
        child: listView,
        color: GlobalConfig.colorPrimary,
        onRefresh: handleRefresh,
      ),
    );
複製程式碼

這裡其實也就是我所說的那一系列程式碼的核心了,中間還有一些分頁的功能以及快取的工作,可以詳見ProjectPage.dart中的下述程式碼

因為這部分程式碼和ArticleListPage.dart的耦合,所以放在一起講。

  Widget _buildSinglePage(ProjectClassifyItemModel bean) {
    return ArticleListPage(
      keepAlive: _keepAlive(),
      request: (page) {
        return CommonService().getProjectListData((bean.url == null)
            ? ("${Api.PROJECT_LIST}$page/json?cid=${bean.id}")
            : ("${bean.url}$page/json"));
      },
    );
  }

  bool _keepAlive() {
    if (_cachedPageNum < _maxCachePageNums) {
      _cachedPageNum++;
      return true;
    } else {
      return false;
    }
  }
複製程式碼

在這個dart檔案中,存在一個網路請求和快取數,快取就是通過上述的一個_keepAlive()的函式進行操作的。

網路請求

網路請求部分其實才是整個專案的重點。 筆主將原有的模式,進行了一定的修改,從設計模式上來講就是單例模式中的惡漢式,從java以及我個人的角度來講,因為網路請求是一個一定會被使用到的變數,通過餓漢式的建立方法,其實也是對效能的一種優化。 先呼叫一份dio的使用方法。 在這個專案中,我們頻繁的會看到類似如下程式碼

CommonService().getProjectListData((bean.url == null)
            ? ("${Api.PROJECT_LIST}$page/json?cid=${bean.id}")
            : ("${bean.url}$page/json"))
複製程式碼

這其實是寫程式碼的人對這個網路請求的一個封裝。

而為了通過拿到真實資料我們的一般操作模版如下程式碼所示

  dio.get(Api.PROJECT_CLASSIFY, options: _getOptions()).then((response) {
      callback(ProjectClassifyModel.fromJson(response.data));
    });
複製程式碼

其實原理很簡單,我們還記得自己創造的bean嗎?通過呼叫他的fromJson()函式,我們就能得到了相對的資料實體了。 讓我們在看下Dio中的Reponse

{
  /// Response body. may have been transformed, please refer to [ResponseType].
  T data;
  /// Response headers.
  Headers headers;
  /// The corresponding request info.
  Options request;
  /// Http status code.
  int statusCode;
  /// Whether redirect 
  bool isRedirect;  
  /// redirect info    
  List<RedirectInfo> redirects ;
  /// Returns the final real request uri (maybe redirect). 
  Uri realUri;    
  /// Custom field that you can retrieve it later in `then`.
  Map<String, dynamic> extra;
}
複製程式碼

這個類中的變數很多,不過因為data中儲存的是伺服器傳回來的資料,所以我們也就知道了為什麼一般呼叫是data這個變數了。

另外還有一個點就是我們經常在網路請求中看到的兩個關鍵詞asyncawait,這兩個變數就是為了讓我們進行非同步處理,知道ANR機制的讀者們,想來也就清楚了非同步處理的重要性了。

另外這個專案因為是類似於元件化的開發,不過最近幾年元件化開發興起,確實有著它難以想象的好處,不論是複用機制,還是節省開發成本,他都帶來了難以想象的好處,所以這個開發模式還是很建議學習的。

以上就是我的學習成果,如果有什麼我沒有思考到的地方或是文章記憶體在錯誤,歡迎與我分享。


相關文章推薦: Flutter的效能優化

相關文章