前端的flutter之路(二):專案前期準備

ma125120 發表於 2019-11-28

最近時間比較多,就把專案中可能用到的知識用 flutter 大概寫了一遍,所以就分享出來增強下印象。

1. 路由庫:fluro

安裝

官方文件直接使用版本號的這個部分說錯了,版本號不需要帶引號的,所以正確的方式是在 pubspec.yaml檔案中的dependencies的下方加入

fluro: ^1.5.1
複製程式碼

之後編輯器即可根據 flutter 命令自行安裝依賴,或者在專案根目錄執行 flutter pub get,具體流程見 flutter 官網

使用

官方文件說的比較簡略,我看了 flutter go 的程式碼以後,總結了這個路由庫的使用方式如下:

  1. 定義一個類,實現一個方法 對傳入的 Router 例項進行擴充套件。程式碼在這裡
  2. 確保執行程式碼的 一開始 進行呼叫上述的類方法進行路由的初始化,比如 runApp 方法執行之前或者在 根類 MyApp 的建構函式 中,我選擇了後者。router 例項最好是全域性變數,這樣在其他檔案中也能訪問程式碼在這裡
  3. MaterialApponGenerateRoute 改寫為以下程式碼。在這裡用到了 router,所以一定要在第 2 步中確保路由已經被正確初始化。
onGenerateRoute: (settings) {
  return Application.router.generator(settings);
}
複製程式碼
  1. 在頁面中進行跳轉的時候,可以使用該庫所提供的方法,router.navigateTo(context, "/users/1234", transition: TransitionType.fadeIn);,也可以使用 flutter 提供的路由跳轉方式,Navigator.of(context).pushNamed("/users/1234", arguments: {"from": "ui"})

需要修改頁面轉場動畫使用前者,需要攜帶額外的引數建議使用後者。因為轉場動畫可以在第 2 步中進行設定,並且前者只提供了動態引數的傳遞,如果需要傳遞 Map資料還得編碼解碼,所以我更人更推薦後者。

2. webviewk 庫: webview_flutter

安裝

webview_flutter: ^0.3.16
複製程式碼

使用

程式碼在這裡

  1. 在目錄 ios/Runner/Info.plistdict內新增 <key>io.flutter.embedded_views_preview</key> <string>YES</string>
  2. 在檔案內引入包之後就可以直接使用 WebView了,使用方式相對於外掛的方式要簡單的多,在定義元件時可以提供方法供 js 使用,向 js 傳值則需要控制器的 evaluateJavascript 方法。
WebView(
  // webview的url
  initialUrl: url ?? 'https://www.baidu.com/',
  // 監聽頁面url的變化,根據返回值決定跳轉還是阻止
  navigationDelegate: (request) {
    if (request.url.startsWith('https://www.youtube.com/')) {
      print('blocking navigation to $request}');
      return NavigationDecision.prevent;
    }

    return NavigationDecision.navigate;
  },
  // flutter向js注入全域性變數,然後js就可以呼叫flutter的方法
  // 例子中js的window會多出一個 Print 物件,js呼叫Print.postMessage('我是js')時,在flutter中則會列印 js傳過來的值: 我是js
  javascriptChannels: {
    JavascriptChannel(
      name: 'Print',
      onMessageReceived: (JavascriptMessage msg) {
        print('js傳過來的值: ' + msg.message);
      }
    ),
  },
  // 是否允許執行js
  javascriptMode: JavascriptMode.unrestricted,
  // webview 建立後膚將控制器賦值給例項變數,方便控制
  onWebViewCreated: (webViewController) {
    // _controller.complete(webViewController);
    _webViewController = webViewController;
  },
  // 頁面載入完成
  onPageFinished: (String msg) {
    // print('載入完成');
  },
)
複製程式碼
  1. 在後退時,預設使用的是 flutter 的後退方式,如果需要在 webview 內進行回退,則需要在外面包一層 WillPopScope來監聽頁面的返回,從而進行更改。
Future<bool> back() async {
  bool canBack = await _webViewController?.canGoBack();
  if (canBack) {
    _webViewController.goBack();
  } else {
    Navigator.of(context).pop();
  }

  return false;
}
複製程式碼
  1. 使用控制器的 evaluateJavascript可以通過 flutter 執行 js,從而得到 js 返回的值或是向 js 中注入變數(比如使用者資訊等),但這是有限制的。在安卓,執行後返回的值是 JSON 格式化後的字串。而在 iOS 中,只有基本型別的字串,陣列型別的字串可以被返回,其他非基本型別則不被支援並且該Future 會以 error 形式返回。

3. HTTP 請求庫:dio

安裝

dio: ^3.0.1
複製程式碼

使用

dio 的使用方式跟前端平時使用的 axios 區別不是很大,所以我在這裡就只說一下我之前遇到的困難。

  1. dio 的 interceptors 問題。其實這個主要還是 dart 學習的不好,在函式或者方法以外,只能宣告變數,但是不能執行例項的方法,所以下述的程式碼會報錯。
Dio dio = new Dio();
dio.interceptors.add(DioCacheManager(CacheConfig(baseUrl: baseUrl)).interceptor);
複製程式碼

所以要封裝某一個類 A 的時候,得新建一個類 B,然後在 B 的某一個方法中執行 A 的方法,進行操作後再進行返回。程式碼在這裡

  1. 在進行 http 請求時,根據引數來選擇是否顯示 loading 框。看了文件以後,發現 extra 引數在請求和響應中都存在,但是文件中並沒有提示 extra 是怎麼傳入的。後來看了型別提示,發現 extra 引數是包含在請求引數的 options 中的。其實,沒有必要通過 extra 傳遞 loading 這個引數,可以直接在請求的地方進行封裝。關於 loading 框的程式碼在這裡

  2. 處理響應資料,根據後端的錯誤碼進行相應處理。本來想在 interceptors 中對響應資料進行處理的,但是如果這麼處理之後,返回資料的型別提示就不正確了,所以最後還是在拿到響應資料之後才進行處理的。

4. json 轉 dart 類:json_model

安裝

dependencies:
  json_annotation: ^2.0.0
dev_dependencies:
  json_model: ^0.0.2
  build_runner: ^1.0.0
  json_serializable: ^2.0.0
複製程式碼

使用

  1. 專案根目錄下新建 jsons 資料夾
  2. 在該資料夾下新建 json 檔案
  3. 執行 flutter packages pub run json_model

5. http 請求快取:dio-http-cache

安裝

dependencies:
  dio_http_cache: ^0.2.0
複製程式碼

dio-http-cache 的 0.2.0 版本與 json_model 庫依賴衝突,為解決依賴衝突依賴衝突

json_model的依賴目前是
  json_annotation: ^2.2.0
  json_serializable: ^2.0.0

為了相容http_cache,需要升級這兩個依賴包的版本
  json_annotation: ^3.0.0
  json_serializable: ^3.0.0
複製程式碼

使用

  1. 在dio中新增 dio-http-cache interceptor
dio.interceptors.add(DioCacheManager(CacheConfig(baseUrl: "http://www.google.com")).interceptor);
複製程式碼
  1. 在請求options中新增
options: buildCacheOptions(
  Duration(days: 7),
  forceRefresh: true,
  options: Options(
    headers: headers,
    method: method,
    extra: {
      'needLoading': needLoading ?? true,
      ...(extra ?? {}),
    },
  ),
)
複製程式碼

6. toast 庫:bot_toast

為什麼 toast 還需要使用單獨的庫呢?因為在 flutter 中,要顯示 toast 的話,一般都需要使用到 BuildContext context,而使用這個庫的話,只需要用 BotToastInit 控制元件包裹一次,之後就可以直接使用了;否則就需要建立一個全域性變數的 context(不知道是否可行)或是每次都傳入 context。

flutter的initState 宣告週期中,可以得到 context,但無法真正使用它,因為框架還沒有完全將其與 state 關聯。直到 initState() 方法執行完成,State 物件就被初始化並且 context 變為可用。所以在 initState 方法中進行 http 請求時,不能直接顯示 toast。

// 第一幀 build 結束時觸發,此時context可用
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((callback){

  });
}
複製程式碼

安裝

dependencies:
  bot_toast: ^2.1.0
複製程式碼

使用

  1. 匯入 BotToast 庫
import 'package:bot_toast/bot_toast.dart';
複製程式碼
  1. 初始化 BotToast
// 1.使用BotToastInit直接包裹MaterialApp
BotToastInit(
  child: MaterialApp(
    title: 'BotToast Demo',
    // 2.註冊路由觀察者
    navigatorObservers: [BotToastNavigatorObserver()],
    home: XxxxPage(),
  )
);
複製程式碼
  1. 使用 BotToast
BotToast.showText(text:"xxxx");  //彈出一個文字框;
複製程式碼
BotToast.showSimpleNotification(title: "init"); //彈出簡單通知Toast
複製程式碼
BotToast.showLoading(); //彈出一個載入動畫
複製程式碼

7. 上拉載入下拉重新整理:flutter_easyrefresh

安裝

dependencies:
  flutter_easyrefresh: ^2.0.4
複製程式碼

使用

程式碼在這裡

不得不說,這個庫真是相當的方便,比 web 端寫起來要簡單的多了。官方文件寫的已經很詳細了,我就不寫了。

8. 輪播圖:flutter_swiper

安裝

dependencies:
  flutter_swiper: ^1.1.6
複製程式碼

使用

程式碼在這裡

這個庫也是很強大了,基本上常用的功能都有。

如果要實現 tab 與 swiper 聯動的話,

// swiper onIndexChanged
Swiper(
  itemCount: 2,
  itemBuilder: (context, index) {
    return Container(
      color: Color(0xFF82B1FF),
      child: Center(child: Text('$index',)),
    );
  },
  onIndexChanged: (index) {
    _tabController.index = index;
  },
  controller: _swiperController,
)

// tab onTap
TabBar(
  tabs: tabs,
  labelPadding: EdgeInsets.only(bottom: 12.0),
  indicatorSize: TabBarIndicatorSize.label,
  indicatorWeight: 3.0,
  onTap: (int index) {
    _swiperController.move(index);
  },
  controller: _tabController,
)
複製程式碼

9. 狀態管理:Event Bus

flutter 的狀態管理庫還是挺多的,比如官方推薦的 Provider,還有 redux,mobx,bloc,rxdart 等,主要是目前的專案還很小,需要狀態共享的地方不多,我就用 event bus 先湊合了。

安裝

dependencies:
  event_bus: 1.1.0
複製程式碼

使用

  1. 建立一個例項
import 'package:event_bus/event_bus.dart';

EventBus eventBus = EventBus();
複製程式碼
  1. 定義事件類
class PieceEvent {
  bool isBlack;
  PieceEvent({ this.isBlack = true });
}
複製程式碼
  1. 監聽該事件
eventBus.on<PieceEvent>().listen((event) {
  setState(() {
    isBlack = event.isBlack;
  });
});
複製程式碼
  1. 觸發該事件
eventBus.fire(PieceEvent(isBlack: isBlack));
複製程式碼

10. 其他

  1. [譯] Flutter 核心概念詳解: Widget、State、Context 及 InheritedWidget
  2. Stateful Widget Lifecycle
  3. material icon
  4. emoji

程式碼地址: github.com/ma125120/fl…