最近一直在探索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的混合开发基础先总结到这里,但愿能帮助到你们。后续内容我会再进行补充。