其實2017年的時候就已經接觸Flutter了,但也只是寫了個HelloWorld,一方面是Flutter在那時候還只是preview版本,另一方面ReactNative在那時候非常火熱,忙於用ReactNative重構專案,錯過了入坑Flutter的第一梯隊。
在谷歌的2018IO大會上Flutter再一次成為了跨平臺方案的焦點,而ReactNative也在隨著Airbnb的棄用熱度逐漸冷卻,其實在寫下這篇文章的時候我已經再次入坑了不短的一段時間,Flutter的各種特性也基本上都接觸到了,demo專案也寫了一些,但致使我迫不及待的寫下這篇文章的直接原因是Flutter的這個能力:
Flutter能夠無感知的嵌入到Android工程中,不管是從開發者角度還是使用者角度,你甚至可以只從一個view開始來讓Flutter參與到你的專案中去,接著替換或者開發某一個頁面甚至功能,然後你就會對它愛不釋手,讓你會有用它重構專案和開發新專案的衝動。
- 使用者:毫秒級的載入速度,無論是view還是頁面,基本上和原生無異。
- 開發:只作為一個module引入工程,程式碼入侵極小,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(this@FlutterActivity,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工程的比較
區別 | Flutter的module工程中的Android工程 | 純Flutter工程中的Android工程 |
---|---|---|
資料夾名稱 | .android | android |
包含的module | app和Flutter | app |
說明1 | app只提供了入口Activity,Flutter包含了外掛擴充套件及原生工程呼叫的介面 | app包含入口Activity及外掛擴充套件 |
說明2 | app供Flutter自身開發除錯,Flutter作為module供Android原生呼叫 | app作為Android工程執行及打包 |
為了方便描述我們稱前者為module工程,後者為完整工程。
由此可見,雖然module工程中提供了名為Flutter的module供原生工程呼叫,但仍然保留了app工程,這樣非常大程度的方便了flutter工程師來單獨開發flutter專案,無需依賴任何原生的呼叫,自身即可啟動除錯。
參考
官方wiki
相關文章
騰訊NOW直播團隊方案
閒魚團隊方案
美團技術團隊方案
更多幹貨移步我的個人部落格 www.nightfarmer.top/