最近一直在探索android flutter的混合開發,一路遇到了不少的坑,接下來便將這些記錄下來,但願能幫助到你們的開發。html
Android Flutter混合開發問題總結
Android Flutter混合開發問題總結(二)java
flutter help create
flutter建立項目的指令。咱們能夠指定建立module仍是project,能夠指定語言,如java、kotlin,同時能夠指定是否生成androidx項目。android
-h, --help Print this usage information.
--[no-]pub Whether to run "flutter pub get" after the project has been created.
(defaults to on)
--[no-]offline When "flutter pub get" is run by the create command, this indicates whether to run it in offline mode or not. In offline mode, it will need to have all dependencies already available in the pub cache to succeed.
--[no-]with-driver-test Also add a flutter_driver dependency and generate a sample 'flutter drive' test.
-t, --template=<type> Specify the type of project to create.
[app] (default) Generate a Flutter application.
[package] Generate a shareable Flutter project containing modular Dart code.
[plugin] Generate a shareable Flutter project containing an API in Dart code with a platform-specific implementation for Android, for iOS code, or for both.
-s, --sample=<id> Specifies the Flutter code sample to use as the main.dart for an application. Implies --template=app. The value should be the sample ID of the desired sample from the API documentation website (http://docs.flutter.dev). An example can
be found at https://master-api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html
--list-samples=<path> Specifies a JSON output file for a listing of Flutter code samples that can created with --sample.
--[no-]overwrite When performing operations, overwrite existing files.
--description The description to use for your new Flutter project. This string ends up in the pubspec.yaml file.
(defaults to "A new Flutter project.")
--org The organization responsible for your new Flutter project, in reverse domain name notation. This string is used in Java package names and as prefix in the iOS bundle identifier.
(defaults to "com.example")
--project-name The project name for this new Flutter project. This must be a valid dart package name.
-i, --ios-language [objc, swift (default)]
-a, --android-language [java, kotlin (default)]
--[no-]androidx Generate a project using the AndroidX support libraries
複製代碼
flutter channelios
查看當前flutter分支web
Flutter channels:
beta
dev
master
* stable
複製代碼
診斷當前flutter配置是否有問題shell
診斷當前flutter配置是否有問題json
切換到指定flutter版本swift
clean當前工程api
運行當前工程bash
分析當前項目dart代碼
構建資源
attach當前項目,一般用於調試
Output command line shell completion setup scripts.
編譯flutter項目
配置flutter設置
查看設備列表
爲當前項目運行驅動測試
查看模擬器列表以及運行
格式化dart代碼
安裝flutter項目
查看運行日誌
對設備進行截圖
運行flutter單元測試
更新flutter 版本
查看flutter 版本
能夠查看一下.android/Flutter/build.gradle的buildTypes中是否包含app的build.gradle中所需的buildTypes
flutter項目默認支持的是armeabi-v7a,因此須要在app的gradle中加入以下配置:
ndk {
abiFilters "armeabi-v7a"
}
複製代碼
若是仍是提示這個錯誤,則須要在gradle.properties中華加入以下配置:
target-platform=android-arm
複製代碼
能夠嘗試flutter clean, 而後flutter run一下項目,再去運行主工程
flutter混淆配置
-keep class io.flutter.app.** {*;}
-keep class io.flutter.plugin.** {*;}
-keep class io.flutter.util.** {*;}
-keep class io.flutter.view.** {*;}
-keep class io.flutter.** {*;}
-keep class io.flutter.plugins.** {*;}
-dontwarn io.flutter.**
複製代碼
跳轉flutter所在activity黑屏
val flutterView = Flutter.createView(this, lifecycle, Gson().toJson(map))
val layout = findViewById<FrameLayout>(R.id.flutter_container)
layout.visibility = View.INVISIBLE
layout.addView(flutterView)
val listeners = arrayOfNulls<FlutterView.FirstFrameListener>(1)
listeners[0] = FlutterView.FirstFrameListener {
layout.visibility = View.VISIBLE
loadingDialog.dismiss()
}
flutterView.addFirstFrameListener(listeners[0])
複製代碼
或者設置當前主題
debug包這種狀況比較明顯,可是release加載很快,能夠仿照一下閒魚,在進入FlutterActivity的時候提供一個加載loading
GeneratedPluginRegistrant.registerWith便可
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
複製代碼
class TestFlutterActivity : FlutterActivity() {
private lateinit var flutterFragment: FlutterFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setContentView(R.layout.activity_flutter_test)
GeneratedPluginRegistrant.registerWith(this)
}
override fun createFlutterView(context: Context?): FlutterView {
val matchParent = WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
val nativeView = this.createFlutterNativeView()
val flutterView = FlutterView(this, null, nativeView)
flutterView.setInitialRoute("web_view_page")
flutterView.layoutParams = matchParent
// flutterView.enableTransparentBackground()
val layout = findViewById<FrameLayout>(R.id.flutter_container)
val listeners = arrayOfNulls<FlutterView.FirstFrameListener>(1)
listeners[0] = FlutterView.FirstFrameListener {
layout.visibility = View.VISIBLE
}
flutterView.addFirstFrameListener(listeners[0])
this.addContentView(flutterView, matchParent)
return flutterView
}
override fun onBackPressed() {
if (flutterView != null) {
flutterView.popRoute()
} else {
super.onBackPressed()
}
}
}
複製代碼
class MainFlutterActivity : FlutterFragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_flutter)
val mFlutterView: View = Flutter.createView(this, lifecycle, "main_flutter")
val mParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT)
addContentView(mFlutterView, mParams)
}
}
複製代碼
class FlutterActivity : AppCompatActivity() {
private lateinit var flutterFragment: FlutterFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_helper_center)
flutterFragment = Flutter.createFragment("web_view_page")
supportFragmentManager.beginTransaction().replace(R.id.flutter_container, flutterFragment).commitAllowingStateLoss()
}
}
複製代碼
能夠經過flutter_boost,可是目前有適配問題,不支持flutter1.七、1.9
有點相似於Eventbus的方式:
flutter.dart中發送事件:
static const platform = const MethodChannel("webview");
void _finish() {
platform.invokeMethod("finish");
}
複製代碼
android接收並處理:
MethodChannel(flutterView, "webview").setMethodCallHandler { methodCall, result ->
if(methodCall.method == "finish"){
finish()
}
}
複製代碼
android跳轉flutter的時候能夠經過路由名字直接傳入值。
import 'dart:ui' as ui;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
static const String HELP_URL =
'https://xxxx';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or press Run > Flutter Hot Reload in a Flutter IDE). Notice that the
// counter didn't reset back to zero; the application is not restarted.
primarySwatch: Colors.blue,
),
home: _widgetRouter(ui.window.defaultRouteName)
);
}
}
Widget _widgetRouter(String json) {
return RouterManager.getInstance().getPageByRouter(json);
}
class RouterManager {
static RouterManager mInstance = new RouterManager();
static RouterManager getInstance() {
return mInstance;
}
/*
根據傳入的route名 找到對應跳轉的路徑
*/
StatefulWidget getPageByRouter(String jsonStr) {
String path = "";
String param = "";
if (jsonStr != null && jsonStr.isNotEmpty && jsonStr != "/") {
var jsonResponse = jsonDecode(jsonStr);
path = jsonResponse["path"];
print("==== router === path = $path");
param = jsonResponse["param"];
print("==== router === param = $param");
path = path != null && path.isNotEmpty ? path : RouterPath.HOME_PAGE;
param = param != null && param.isNotEmpty ? param : "[]";
}
Map<String, dynamic> map = json.decode(param);
Widget widget = _getWidgetPage(path, map);
if (widget == null) {
debugPrint('==== 找不到widget =====');
}
return widget;
}
Widget _getWidgetPage(String path, Map<String, dynamic> map) {
Widget widget;
switch (path) {
case RouterPath.HOME_PAGE:
widget = MyHomePage(title: 'Flutter Demo Home Page');
break;
case RouterPath.WEB_VIEW_PAGE:
widget = WebViewPage(map["title"], map["url"]);
break;
}
return widget;
}
}
class RouterPath {
static const String HOME_PAGE = 'home_page';
static const String WEB_VIEW_PAGE = 'web_view_page';
}
複製代碼
在_getWidgetPage內配置路由跳轉的值及對應界面。
與以前主要的區別在於參數經過hashmap轉換成json形式放入路由名稱中,而後把路由名連同參數一塊兒傳給main.dart
val map = HashMap<String, String>()
val params = HashMap<String, String>()
params.put("title", "幫助中心")
params.put("url", WebUrlConfig.HELP_URL)
map.put("path", "web_view_page")
map.put("param", Gson().toJson(params))
val matchParent = WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
val nativeView = this.createFlutterNativeView()
val flutterView = FlutterView(this, null, nativeView)
flutterView.setInitialRoute(Gson().toJson(map))
flutterView.layoutParams = matchParent
val layout = findViewById<FrameLayout>(R.id.flutter_container)
val listeners = arrayOfNulls<FlutterView.FirstFrameListener>(1)
listeners[0] = FlutterView.FirstFrameListener {
layout.visibility = View.VISIBLE
}
flutterView.addFirstFrameListener(listeners[0])
this.addContentView(flutterView, matchParent)
複製代碼
我這裏對其android部分進行了封裝,須要使用的話直接拷貝代碼便可:
BaseFlutterActivity:
abstract class BaseFlutterActivity : AppCompatActivity() {
lateinit var flutterView: FlutterView
private val loadingDialog: LoadingDialog by lazy {
LoadingDialog(this, "加載中")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_flutter_activity)
loadingDialog.show()
val map = HashMap<String, String>()
map["path"] = routeUrl()
map["param"] = Gson().toJson(routeParams())
flutterView = Flutter.createView(this, lifecycle, Gson().toJson(map))
val layout = findViewById<FrameLayout>(R.id.flutter_container)
layout.addView(flutterView)
val listeners = arrayOfNulls<FlutterView.FirstFrameListener>(1)
listeners[0] = FlutterView.FirstFrameListener {
layout.visibility = View.VISIBLE
loadingDialog.dismiss()
}
flutterView.addFirstFrameListener(listeners[0])
if (setChannel().isNotEmpty()) {
setFlutterMessageHandler()?.let {
MethodChannel(flutterView, setChannel()).setMethodCallHandler(it)
}
}
}
open fun setChannel(): String {
return ""
}
open fun setFlutterMessageHandler(): MethodChannel.MethodCallHandler? {
return null
}
abstract fun routeUrl(): String
abstract fun routeParams(): HashMap<String, String>
}
複製代碼
使用:
下面代碼是跳轉一個webview而且傳入url:
class FlutterWebActivity : BaseAegisFlutterActivity() {
private val title: String by lazy { intent.getStringExtra(TITLE) }
private val url: String by lazy { intent.getStringExtra(URL) }
companion object {
const val TITLE = "title"
const val URL = "url"
fun buildIntent(context: Context?, title: String, url: String): Intent {
val intent = Intent(context, FlutterWebActivity::class.java)
intent.putExtra(TITLE, title)
intent.putExtra(URL, url)
return intent
}
}
override fun routeUrl(): String = "web_view_page"
override fun routeParams(): HashMap<String, String> {
val params = HashMap<String, String>()
params["title"] = title
params["url"] = url
return params
}
override fun setChannel(): String = "webview"
override fun setFlutterMessageHandler(): MethodChannel.MethodCallHandler? {
return MethodChannel.MethodCallHandler { methodCall, _ ->
if (methodCall.method == "finish") {
finish()
}
}
}
}
複製代碼
Android Flutter的混合開發基礎先總結到這裏,但願能幫助到你們。後續內容我會再進行補充。