Android工程內嵌Flutter,跨平臺的漸進式解決方案 [復制鏈接]

2019-1-2 11:37
liujh 閱讀:537 評論:0 贊:0
Tag:  
Android工程內嵌Flutter,跨平臺的漸進式解決方案


其實2017年的時候就已經接觸Flutter了,但也只是寫了個HelloWorld,一方面是Flutter在那時候還只是preview版本,另一方面ReactNative在那時候非常火熱,忙于用ReactNative重構項目,錯過了入坑Flutter的第一梯隊。

在谷歌的2018IO大會上Flutter再一次成為了跨平臺方案的焦點,而ReactNative也在隨著Airbnb的棄用熱度逐漸冷卻,其實在寫下這篇文章的時候我已經再次入坑了不短的一段時間,Flutter的各種特性也基本上都接觸到了,demo項目也寫了一些,但致使我迫不及待的寫下這篇文章的直接原因是Flutter的這個能力:

Flutter能夠無感知的嵌入到Android工程中,不管是從開發者角度還是用戶角度,你甚至可以只從一個view開始來讓Flutter參與到你的項目中去,接著替換或者開發某一個頁面甚至功能,然后你就會對它愛不釋手,讓你會有用它重構項目和開發新項目的沖動。

  • 用戶:毫秒級的加載速度,無論是view還是頁面,基本上和原生無異。
  • 開發:只作為一個module引入工程,代碼入侵極小,Android工程和Flutter工程互不相干。
Android工程內嵌Flutter,跨平臺的漸進式解決方案

注意:當前日期是2018-07-29,flutter的beta版本還沒有加入這個新功能,使用命令flutter channel [分支]切換到dev或master分支才能使用,如果你閱讀本篇文章離這個時間點是很久之后可以忽略這段。

創建一個Android工程模擬你的現有工程

為了讓Android工程和Flutter工程互不干擾,這里不再以Android工程為工程的跟目錄,而是讓Android工程和平級的Flutter工程的公共目錄作為根目錄。

最終的目錄結構應該是下面這樣的

你的項目根目錄(隨便什么你喜歡的地方)
├── 原生安卓工程(FlutterInAndroid)
└── Flutter工程 (my_flutter)

所以首先在你的項目根目錄下用AS創建一個新的Android原生項目,可以勾選上kotlin支持,這樣更舒服。

創建完成后你會得到一個這樣的結構

你的項目根目錄(隨便什么你喜歡的地方)
└── FlutterInAndroid


FlutterInAndroid目錄內是一個完整的Android工程

module模式創建Flutter工程

接下來使用Flutter命令來創建module工程,在你的項目根目錄下執行:

flutter create -t module my_flutter


創建完成后你會得到一個這樣的結構

你的項目根目錄(隨便什么你喜歡的地方)
├── FlutterInAndroid
└── my_flutter

my_flutter是一個Flutter的module工程,用來供Android項目引入

在Android工程中引入依賴

在FlutterInAndroid這個Android工程的setting.gradle文件中追加flutter工程的引入

你的項目跟目錄/FlutterInAndroid/setting.gradle

include ':app'
//加入下面配置
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'my_flutter/.android/include_flutter.groovy'
))

在app的build.gradle文件中加入工程依賴

你的項目跟目錄/FlutterInAndroid/app/build.gradle

...
dependencies {
...
// 加入下面配置
implementation project(':flutter')
}

使用AS打開FlutterInAndroid工程,重新構建項目,即可成功的將Flutter加入Android工程。

在Android工程中創建Flutter的View

Flutter提供了兩種方式讓Android工程來引用組件,一種是View,一種是Fragment,這里選用View來進行講解,Fragment同理。

這里我們用兩種方式來引入FLutter,本質是還是是作為一個view引入布局還是將FlutterView作為Activity的根View。

以單個view引入布局

val flutterView = Flutter.createView(this,lifecycle,"route1")


通過上面很簡單的一個方法,我們就能通過Flutter創建出一個view,這個方法提供三個參數,第一個是Activity,第二個參數是一個Lifecycle對象,我們之間取Activity的lifecycle即可,第三個參數是告訴Flutter我們要創建一個什么樣的view,這個字符串參數可以在Flutter工程中獲取得到。

創建出這個FlutterView之后就可以按常規的操作來將它加入到任何你想要的布局中去了。

以根view作為Activity

創建一個空的Activity,用Flutter創建一個View作為頁面的根View:

class FlutterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flutter)
val flutterView = Flutter.createView([email protected],lifecycle,"route1")
val layout = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
addContentView(flutterView, layout)
}
}

這里我們并沒有使用setContentView而是是用了addContentView這個方法,原因是這樣的:

雖然FLutter的加載速度非常快,但是這個過程依然存在,在創建FLutterView之前我們先給ContentView設置了一個R.layout.activity_flutter布局,這個布局可以作為FlutterView加載完成之前展示給用戶的界面,當然大部分情況下用戶根本感知不到這個界面Flutter已經加載完成了,但我們仍需要它,因為debug模式下造成Flutter的加載速度并不是非常快,這個界面可以給開發人員看,還有就是如果沒有這個界面的話在Activity的加載過程會出現一個黑色的閃屏,而這個情況對用戶來說并不友好。

在Flutter工程中根據不同的route創建不同的組件

用AndroidStudio在你的項目跟目錄/my_flutter打開Flutter工程,這時候AndroidStudio插件會識別到Flutter工程并以Flutter工程進行加載。

忽略掉.android和.ios文件夾之后你會發現,這個FLutter工程和完整的Flutter工程并沒有任何不同,你依然能夠以完整Flutter工程的流程來進行Flutter開發并啟動調試,這是一個非常人性化的設計。

上面我們在原生Android工程中以View的形式調用了Flutter,而Flutter本質上是只有一個入口的,也就是main.dart文件中的main函數:

void main() => runApp(new MyApp());


我們的目的是根據原生工程的調用讓Flutter生成不同的組件作為View來供原生工程使用,那么我們就可以從這個main函數來入手。

通過文檔我們可以通過window的全局變量中獲取到當前的routeName,這個值正是上面通過原生工程傳給Flutter的標識,有了這個標識就可以簡單的做判斷來進行不同的組件創建了:

import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(_widgetForRoute(window.defaultRouteName));
//根據不同的標識創建不同的組件給原生工程調用
Widget _widgetForRoute(String route) {
switch (route) {
case 'route1':
return SomeWidget(...);
case 'route2':
return SomeOtherWidget(...);
default:
return Center(
child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
);
}
}

讓Flutter模塊支持熱加載

首先在Flutter目錄下啟動監聽服務,在你的項目根目錄/my_flutter下執行:

flutter attach


執行后,監聽服務會等待并監聽debug應用中flutter的狀態

然后在打開FlutterInAndroid項目的AS中以正常方式調試運行,在真機或模擬器中運行app后并不會立即出發flutter的監聽服務,當flutter的view或Fragment激活時才會觸發。

當flutter的監聽服務和app建立連接后,終端會出現如下輸出:

$ flutter attach -d W8
Waiting for a connection from Flutter on PLK UL00...
Done.
Syncing files to device PLK UL00... 8.7s
To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".
An Observatory debugger and profiler on PLK UL00 is available at: http://127.0.0.1:54218/
For a more detailed help message, press "h". To quit, press "q".

這時我們修改flutter工程中的dart代碼文件,保存后在終端中點擊r鍵即可進行熱加載,R鍵進行熱重啟。

簽名打包

引入flutter工程后,對Android原生工程的構建基本上沒有影響,打包按常規操作即可。

Flutter創建的module工程中的Android工程與純Flutter工程的中Android工程的比較

為了方便描述我們稱前者為module工程,后者為完整工程。

由此可見,雖然module工程中提供了名為Flutter的module供原生工程調用,但仍然保留了app工程,這樣非常大程度的方便了flutter工程師來單獨開發flutter項目,無需依賴任何原生的調用,自身即可啟動調試。

分享到:
我來說兩句
facelist
您需要登錄后才可以評論 登錄 | 立即注冊
所有評論(0)

領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

两码中特期期