手撕Flutter開發

前言

基於以前的新版本發佈version已經支持開始支持了Web開發者開發,那這個編程語言的前景已經不言而喻了,不只僅是一門跨雙端的編程語言,更是可能成爲將來的主流語言之一。當前這句話說的有點絕對,可是和React Native這一類語言不一樣的地方偶爾可能也是顯而易見的,基於的底層框架的修改,前者基於的是JS的一種擴展。因此接下來就是對這個編程語言的一種學習了。前端

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

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

逃不過的安裝

材料清單:
1. 操做系統:OSX 10.15.3
2. Flutter版本:flutter_macos_v1.12.13+hotfix.7-stable
3. 軟件:Android Studio
複製代碼

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

項目的源碼將集成在WanAndroid-Flutter中,由於項目是android官網推出的,因此直接講解。github

使用

經過以上的這些基礎內容,咱們基本上就能夠開始初級的開發了。 見於對整個開發代碼的審閱,咱們將基於Flutter Inspector對總體的代碼進行觀察。 macos

開發

  • 網絡部分

見於官方文檔中一樣建議咱們使用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已經作好的數據。json

項目結構

|-- libs
     |-- api (Api接口)
     |-- common (公共類)
     |-- fonts (字體類)
     |-- model (Bean類)
     |-- pages (UI頁面)
     |-- util (工具類)
     |-- widget (自定義組件)
複製代碼

單個模塊分析

由於頁面的邏輯基本一致,其實咱們只用分析單個頁面,就能徹底理解總體代碼的邏輯了。 這裏主講的是一個project也就項目頁。設計模式

拋出顯示效果,其實總體的框架就是一個TabBarRefreshIndicator還有Card的組合其實進入之後也能很清楚的發現。在界面上出現了一些叉叉的符號,那是由於個人刪減程序中並無加入字體庫。api

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的性能優化

相關文章
相關標籤/搜索