最近公司開始推行使用Flutter用於移動端開發,忙活了一個多月的Flutter混合開發迭代端午節後準備上線,寫下此過程的坑以及一些接入流程,以及Flutter技術。android
因爲我主業是搞Android開發的,iOS仍是個菜鳥,先介紹一下Android混合接入流程git
不少狀況下,Flutter的接入都是在原有的移動端項目的基礎上接入,這樣相對於侵入原有項目弱,而且接入的成本低,風險也低。github
flutter create -t module flutter_module
複製代碼
在命令行界面錄入便可,最好是和本來的項目在相同的目錄下,同級文件夾。個人項目基本是原有項目是一個git倉庫,對應的Flutter代碼是在另外一個git倉庫,這樣版本管理也是比較好的json
在原有setting.gradle文件末尾的添加一下代碼bash
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'/flutter_module/.android/include_flutter.groovy'
))
複製代碼
這樣就會引入到對應的Flutter的module資源,對應的編譯腳本,Flutter框架已經寫好,後續有時間能夠讀讀app
在app目錄下的build.gradle文件中添加Flutter依賴框架
implementation project(':flutter')
複製代碼
主要的依賴鏈以下ide
flutter_module/.android/include_flutter.groovy ->
flutter_module/.android/Flutter/build.gradle ->
$flutterRoot/packages/flutter_tools/gradle/flutter.gradle
複製代碼
很方便的完成混合開發的打包操做,接入完以後最好在flutter項目下面的.android文件下,用命令行工具執行如下工具
gradlew assembleDebug
複製代碼
命令完成對應的Android依賴加載佈局
Button open = findViewById(R.id.openBtn);
open.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, MyFlutterActivity.class);
startActivity(intent);
}
});
public class MyFlutterActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flutter);
final FlutterView flutterView = Flutter.createView(
this,
getLifecycle(),
"route1"
);
final FrameLayout layout = findViewById(R.id.flutter_container);
layout.addView(flutterView);
}
}
複製代碼
只須要在Activity中的addView便可,看起來很簡單,不過存在不少問題,跳轉過程可能會有黑屏狀況,以及Flutter視圖的複用也是個問題,以及對應的Flutter 跳轉Native界面也是個問題。因而乎谷歌發現FlutterBoost框架解決了大部分以上的問題。
依照github.com/alibaba/flu…官方的文檔,接入發現有不少的坑,不知道是否是開發人員都反感寫文檔同樣的。
接入的流程以下:
在對應的pubspec.yaml文件中加入依賴,pubspec.yaml就是一個配置文件。
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: '0.0.411'
複製代碼
以後調用Package get,右上角便可查看,以後仍是在.android 文件下執行 gradlew assembleDebug,完成依賴下載。
@override
void initState() {
super.initState();
FlutterBoost.singleton.registerPageBuilders({
//對應的Page的名字,最好是類Http格式
'demoPage': (pageName, params, _) {
return DemoPage();
},
});
FlutterBoost.handleOnStartPage();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Boost example',
builder: FlutterBoost.init(postPush: _onRoutePushed),
home: Container());
}
void _onRoutePushed(
String pageName, String uniqueId, Map params, Route route, Future _) {}
複製代碼
@Override
public void onCreate() {
super.onCreate();
initFlutterBoot();
}
private void initFlutterBoot() {
FlutterBoostPlugin.init(new IPlatform() {
@Override
public Application getApplication() {
return mApplication;
}
@Override
public Activity getMainActivity () {
//這裏返回null,能夠避免跳轉界面的(MainActivity)的頁面總體上移
return null;
}
@Override
public boolean isDebug() {
return AppConfig.IS_DEVELOPING;
}
@Override
public boolean startActivity(Context context, String url, int requestCode) {
//Flutter 跳轉的回調
return PageRouter.openPageByUrl(context,url,requestCode);
}
@Override
public Map getSettings() {
return null;
}
});
}
複製代碼
坑1:getMainActivity 若是返回MainActivity,會致使對應的主頁面的佈局總體上移,發現給null也沒啥問題。
Native-->Fluuter
可使用官方Demo中的PageRouter
public class PageRouter {
public static final String COMMISSION_TASK_PAGE = "gamma://flutter/commissionTaskPage";
public static final String NATIVE_CUST_INFO_PAGE_URL = "gamma://native/custInfo";
public static boolean openPageByUrl(Context context, String url) {
return openPageByUrl(context, url, 0, "{}");
}
public static boolean openPageByUrl(Context context, String url, String json) {
return openPageByUrl(context, url, 0, json);
}
public static boolean openPageByUrl(Context context, String url, int requestCode) {
return openPageByUrl(context, url, requestCode, "{}");
}
public static boolean openPageByUrl(Context context, String url, int requestCode, String json) {
try {
if (url.startsWith(COMMISSION_TASK_PAGE)) {
//貸後任務
Intent intent = new Intent(context, FlutterPageActivity.class);
intent.putExtra("pageName", "gamma://flutter/commissionTaskPage");
intent.putExtra("json", json);
context.startActivity(intent);
return true;
} else if (url.contains(NATIVE_CUST_INFO_PAGE_URL)) {
//打開客戶界面
Map<String, String> params = getUrlParams(url);
AppUIHelper
.showCustomerInfoActivity(context, params.get("custId"));
return true;
}
} catch (Throwable t) {
return false;
}
}
//獲取對應url中的參數
private static Map getUrlParams(String url) {
Map<String, Object> map = new HashMap<>();
url = url.replace("?", ";");
if (!url.contains(";")) {
return map;
}
if (url.split(";").length > 0) {
String[] arr = url.split(";")[1].split("&");
for (String s : arr) {
String key = s.split("=")[0];
String value = s.split("=")[1];
map.put(key, value);
}
return map;
} else {
return map;
}
}
}
複製代碼
這樣相對於好管理,Native頁面能夠設置以Native開頭,Flutter以flutter開頭,這樣好區分。
對應的FlutterPageActivity能夠根據官方Demo自行修改。
坑2:FlutterPageActivity中最好加上如下代碼
@Override
protected void onCreate(Bundle savedInstanceState) {
FlutterMain.startInitialization(this);
super.onCreate(savedInstanceState);
}
複製代碼
否則會運行報錯
Flutter -- >Native
這種類型跳轉和Activity之間互相跳轉有點相似
FlutterBoost.singleton.openPage(
"${NativeUrl.NATIVE_CUST_INFO}?custId=${custId};",
{});
複製代碼
這中類型的跳轉會回調到前面Application中的 FlutterBoostPlugin.init 方法中的startActivity方法中,只會在用PageRouter來接收便可。 以後自行作界面的跳轉
在打release包的時候須要在Flutter的跟目錄下執行如下命令
flutter build apk
複製代碼
以後是常規的Android的打包,否則打包出來的apk跳轉到Flutter頁面就會閃退