最近時間比較多,就把專案中可能用到的知識用 flutter 大概寫了一遍,所以就分享出來增強下印象。
1. 路由庫:fluro
安裝
官方文件直接使用版本號的這個部分說錯了,版本號不需要帶引號的,所以正確的方式是在 pubspec.yaml
檔案中的dependencies
的下方加入
fluro: ^1.5.1
複製程式碼
之後編輯器即可根據 flutter 命令自行安裝依賴,或者在專案根目錄執行 flutter pub get
,具體流程見 flutter 官網
使用
官方文件說的比較簡略,我看了 flutter go 的程式碼以後,總結了這個路由庫的使用方式如下:
- 定義一個類,實現一個方法 對傳入的 Router 例項進行擴充套件。程式碼在這裡
- 確保執行程式碼的 一開始 進行呼叫上述的類方法進行路由的初始化,比如
runApp
方法執行之前或者在 根類 MyApp 的建構函式 中,我選擇了後者。router 例項最好是全域性變數,這樣在其他檔案中也能訪問。程式碼在這裡 - 在
MaterialApp
的onGenerateRoute
改寫為以下程式碼。在這裡用到了 router,所以一定要在第 2 步中確保路由已經被正確初始化。
onGenerateRoute: (settings) {
return Application.router.generator(settings);
}
複製程式碼
- 在頁面中進行跳轉的時候,可以使用該庫所提供的方法,
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
複製程式碼
使用
- 在目錄
ios/Runner/Info.plist
的dict
內新增<key>io.flutter.embedded_views_preview</key> <string>YES</string>
- 在檔案內引入包之後就可以直接使用
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('載入完成');
},
)
複製程式碼
- 在後退時,預設使用的是 flutter 的後退方式,如果需要在 webview 內進行回退,則需要在外面包一層
WillPopScope
來監聽頁面的返回,從而進行更改。
Future<bool> back() async {
bool canBack = await _webViewController?.canGoBack();
if (canBack) {
_webViewController.goBack();
} else {
Navigator.of(context).pop();
}
return false;
}
複製程式碼
- 使用控制器的
evaluateJavascript
可以通過 flutter 執行 js,從而得到 js 返回的值或是向 js 中注入變數(比如使用者資訊等),但這是有限制的。在安卓,執行後返回的值是 JSON 格式化後的字串。而在 iOS 中,只有基本型別的字串,陣列型別的字串可以被返回,其他非基本型別則不被支援並且該Future 會以 error 形式返回。
3. HTTP 請求庫:dio
安裝
dio: ^3.0.1
複製程式碼
使用
dio 的使用方式跟前端平時使用的 axios 區別不是很大,所以我在這裡就只說一下我之前遇到的困難。
- dio 的 interceptors 問題。其實這個主要還是 dart 學習的不好,在函式或者方法以外,只能宣告變數,但是不能執行例項的方法,所以下述的程式碼會報錯。
Dio dio = new Dio();
dio.interceptors.add(DioCacheManager(CacheConfig(baseUrl: baseUrl)).interceptor);
複製程式碼
所以要封裝某一個類 A 的時候,得新建一個類 B,然後在 B 的某一個方法中執行 A 的方法,進行操作後再進行返回。程式碼在這裡
-
在進行 http 請求時,根據引數來選擇是否顯示 loading 框。看了文件以後,發現 extra 引數在請求和響應中都存在,但是文件中並沒有提示 extra 是怎麼傳入的。後來看了型別提示,發現 extra 引數是包含在請求引數的 options 中的。其實,沒有必要通過 extra 傳遞 loading 這個引數,可以直接在請求的地方進行封裝。關於 loading 框的程式碼在這裡
-
處理響應資料,根據後端的錯誤碼進行相應處理。本來想在 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
複製程式碼
使用
- 專案根目錄下新建 jsons 資料夾
- 在該資料夾下新建 json 檔案
- 執行
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
複製程式碼
使用
- 在dio中新增
dio-http-cache interceptor
dio.interceptors.add(DioCacheManager(CacheConfig(baseUrl: "http://www.google.com")).interceptor);
複製程式碼
- 在請求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
複製程式碼
使用
- 匯入 BotToast 庫
import 'package:bot_toast/bot_toast.dart';
複製程式碼
- 初始化 BotToast
// 1.使用BotToastInit直接包裹MaterialApp
BotToastInit(
child: MaterialApp(
title: 'BotToast Demo',
// 2.註冊路由觀察者
navigatorObservers: [BotToastNavigatorObserver()],
home: XxxxPage(),
)
);
複製程式碼
- 使用 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
複製程式碼
使用
- 建立一個例項
import 'package:event_bus/event_bus.dart';
EventBus eventBus = EventBus();
複製程式碼
- 定義事件類
class PieceEvent {
bool isBlack;
PieceEvent({ this.isBlack = true });
}
複製程式碼
- 監聽該事件
eventBus.on<PieceEvent>().listen((event) {
setState(() {
isBlack = event.isBlack;
});
});
複製程式碼
- 觸發該事件
eventBus.fire(PieceEvent(isBlack: isBlack));
複製程式碼
10. 其他
- [譯] Flutter 核心概念詳解: Widget、State、Context 及 InheritedWidget
- Stateful Widget Lifecycle
- material icon
- emoji
程式碼地址: github.com/ma125120/fl…