Flutter探索与实践
Flutter是google近年來新推出的跨平臺移動UI框架,可以在ios和Android系統上快速構建出高質量,體驗較高的原生界面,同時Flutter還將會作為google新一代操作系統Fuchsia的Toolchain,這對Flutter的未來發展前景是一個強有力的支撐。寫這篇文章時,中國 GDG 2018 剛剛落幕,Flutter團隊在大會上發布了release之前的最后一個preview版本。扇貝移動團隊近期也對Flutter如何應用到扇貝產品上做了一番探索和實踐。
Hello Flutter
Flutter項目可以用同一套代碼同時在ios和Android操作系統上運行,在此之前,市面上已經存在了很多跨平臺解決方案,扇貝的移動產品上在跨平臺開發上一直采用Webview加React Native的解決方案。
Webview的優缺點都非常明顯,優勢是可以完全用Web開發的技術棧去實現,app只需要提供webview組件承載即可,同時可以隨時更新頁面,動態性很高,并且這些頁面可以完全交由web開發人員開發迭代,移動端不需要太關心,但是Webview的缺點又很多,渲染的性能一直被開發者詬病,內置的Chrome內核在國內的機型上也會有不兼容的情況發生。
React Native在活動頁面和商品頁面得到了使用,RN相對于Webview來說,性能和渲染效率都得到了不小的提升,它是將編寫好的web界面利用JSCore轉換成原生控件,同時配合動態更新系統可以發布新版本的RN包來實現動態更新界面,但是RN和系統的關聯性較強,很多功能需要依賴系統特性開發,debug成本也相對比較高,移動端的同學也需要對RN有一些了解來配合前端開發。
因此我們將目光投向了Flutter。
Flutter的特點
- Dart可以運行前編譯(AOT),在開發flutter應用的時候布局文件會直接通過源碼編寫node tree,從而避免了大量的解析轉譯時間,使得Dart的效率比JS更高。
- Dart語言同樣支持JIT編譯,因此flutter可以hot reload,為開發周期提速。
- Dart沒有鎖的概念,可以做到對象回收和GC,Dart中的線程叫做isolates,因為不共享內存的原因,同時和js一樣是單線程操作,所以不會出現搶占調度和鎖死的問題。開發者控制線程的時候需要顯式創建線程,最常用的是async和await。
- Flutter用Dart語言開發,因為Flutter主要用來開發用戶界面,Dart語言的特性適合了用戶構建用戶界面時的操作邏輯,沒有像Android的xml文件和前端的html文件這樣的單獨布局文件,使得開發更簡潔,預覽更方便。
- Flutter不再受限于native,自己開發了一套渲染邏輯,因此在未來的性能優化和跨平臺相比RN優勢會更加明顯。
關于Flutter的具體使用可以前往官網學習 flutter.io
Flutter的依賴是在Pub倉庫中管理,項目的依賴在根目錄下的pubspec.yaml中進行配置,例如如下配置:
dependencies:flutter:sdk: flutterjson_annotation: ^1.0.0intl: ^0.15.7 復制代碼關于依賴的package版本可以有兩種寫法
- packagename: ^version 引入某個版本的package
- packagename: ‘>=version’ 引入某個版本之后的package,用來約束最低或最高版本
在pubspec.yaml文件中聲明依賴之后需要執行package get命令,國內開發者可能需要科學上網才能拉下來依賴包,Flutter為國內開發者提供了本地化的網站和鏡像。只需要簡單配置即可。 using Flutter In China
技術預研
在對Flutter方案做技術預研的時候,我們羅列出了一些需要探索和解決的問題。
- 重寫界面的成本
- Flutter和原生互相通信問題
- 圖片資源增加包體積
- Flutter如何構建到現有項目
重寫界面成本
既然開始選用Flutter作為預備跨平臺方案,而且是Android和ios團隊負責開發維護,就需要了解一下Flutter的學習和開發成本。
Dart是Flutter前期在對十幾種語言進行調研之后最后選擇的語言,它天然支持響應式編程,Dart2的升級是進一步優化客戶端的開發,編寫出的代碼結構清晰,有一定程序設計基礎的開發人員不需要經過系統的學習就可以上手進行Flutter開發。
Flutter提供了大量基于Android material design風格和ios Cupertino風格的控件,可以通過組合嵌套的方式構建界面,需要注意的是在Flutter里面屬性也是控件的概念,例如Padding、Center、Align等。具體查看Widgets Catalog。
Flutter和原生互相通信
以Android為例,Dart調用Java代碼可以通過MethodChannel來實現,Java調用dart則用EventChannel來實現。
因為需要用Flutter重寫的頁面有涉及到網絡請求的問題,之前的Android項目里實現了一套完整的網絡請求框架,封裝了項目需要的一些附帶請求信息和處理邏輯等,我們目前還不需要為Flutter項目單獨實現一個完整網絡請求框架,所以將網絡請求通過dart調用java的方式在java層進行網絡請求然后callback到flutter頁面上,有因為網絡請求是異步的,dart的async/await關鍵字可以提供異步支持。
MethodChannel
首先在dart代碼中實例化MethodChannel對象,然后在需要調用java代碼的時候調用invokeMethod方法。
static const methodChannel = const MethodChannel('com.shanbay.shared.data/method'); Future<ProfileModel> _getProfileJson() async {mProfileJson = await platform.invokeMethod("getProfileJson");if (mProfileJson != null) {setState(() {Map profileMap = json.decode(mProfileJson);mProfileData = new ProfileModel.fromJson(profileMap);});}} 復制代碼在java中注冊MethodChannel,注意channel的名字需要相同。
private static final String METHOD_CHANNEL = "com.shanbay.shared.data/method"; private MethodChannel mMethodChannel; protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);GeneratedPluginRegistrant.registerWith(this);mMethodChannel = new MethodChannel(getFlutterView(), METHOD_CHANNEL);...... } mMethodChannel.setMethodCallHandler(MethodChannel.MethodCallHandler() {public void onMethodCall(MethodCall call, final MethodChannel.Result result) {if(call.method.equals("getProfileJson"){//do somethingresult.success(json);} } 復制代碼EventChannel
有時候在頁面接收到了一個event事件要動態刷新或者修改頁面,需要java調用dart。 在dart代碼中實例化EventChannel對象
static const eventChannel = const EventChannel('com.shanbay.shared.data/event'); StreamSubscription _subscription = null;@overridevoid initState() {super.initState();//開啟監聽if (_subscription == null) {_subscription = eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);}}@overridevoid dispose() {super.dispose();//取消監聽if (_subscription != null) {_subscription.cancel();}}void _onEvent(Object event) {setState(() {if (event.toString() == "refresh") {_getProfileJson();}});}void _onError(Object error) {setState(() {print(error);});}復制代碼在java代碼中實例化相同一個EventChannel對象獲得event實例用來調用dart。
private static final String EVENT_CHANNEL = "com.shanbay.shared.data/event"; private EventChannel mChannel; protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);GeneratedPluginRegistrant.registerWith(this);mChannel = new EventChannel(getFlutterView(), EVENT_CHANNEL);mChannel.setStreamHandler(new EventChannel.StreamHandler() {public void onListen(Object arguments, EventChannel.EventSink events) {mEvent = events;}public void onCancel(Object arguments) {}}); }onEvent(event) {mEvent.success("refresh"); } 復制代碼圖片資源增加包體積
在我們現有的Android項目中往往只提供了xxhdpi分辨率的圖片資源,這樣做是為了減小apk包體積,讓其他分辨率的手機通過設備自適應。Flutter對于圖片資源需要提供1x、2x、3x三種分辨率格式,這樣如果需要大量本地圖片資源,會增加一定的包體積。
美團技術發表過一篇文章,他們針對flutter中需要復用的圖片資源采取的方案是對移動app assets中的圖片按屏幕密度縮放并且存放到本地,然后dart中調用本地圖片。
這樣的方案在啟動Flutter頁面之前就需要知道哪些圖片需要被存儲到本地,還要做一個圖片縮放和存儲的操作,我認為這樣可能不能確保圖片的準確性,同時需要Android和ios兩端同時支持,寫兩份代碼,并且還要針對不同的flutter頁面記錄一個資源文件到頁面的映射表,成本有點過高,在我們團隊內實現有些繁瑣,而且我們要實踐的頁面只用到了少量的本地圖片,占用體積也不是很大,針對這種情況,可以直接用官方的三種分辨率,保證了Android、ios的兩端的同步。
Flutter如何構建到現有項目
這個版塊我們計劃是采用將flutter項目打包成aar到Android項目中集成的方式構建,其中踩到了一些坑,正在梳理,《Flutter在混合項目中的構建和集成》會詳細說明。
在個人信息頁的實踐
這個是我們flutter項目結構,host是一個debug工程,可以直接編譯運行,lib文件夾則是dart代碼。最后形成的個人信息頁效果圖如下:
總結
Flutter的控件目前只提供了md風格的基礎組件,想要自定義控件相對于Android和ios來說還是有一些復雜度,但是Flutter團隊通過原生渲染界面確實打破了原有的跨平臺解決方案的思路,性能和效率的提升是顯而易見的,相比RN來說具有很大的優勢。
dart目前在Android Studio上還不能支持代碼塊折疊,同時格式化還要手動點擊,沒有快捷鍵,代碼塊折疊這一點對dart組合嵌套寫界面來說實在是太有必要了,希望可以后續不斷優化開發體驗。
Flutter的引入無疑會增加包體積,preview2的發布,官方宣布release包體積將會再減小30%,一個空的release項目只需要4.7MB 的體積,對于現在流量吃到飽的情況,其實包體積壓縮這個話題可以慢慢的弱化了,優化用戶體驗是最重要的。
Flutter在扇貝聽力上的實踐已經打出了release包等待發布,我們會逐漸完善Flutter項目,在更多跨平臺場景上使用Flutter開發。
參考資料
1.Flutter 文檔
2.Flutter Packages
3.Flutter原理與實踐
總結
以上是生活随笔為你收集整理的Flutter探索与实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进度报告4
- 下一篇: Ehcarts 与 百度地图结合时,如何