所有源码已上传githubphp
这部分的核心是须要你在Flutter程序的入口添加路由,Android会从Flutter设置的路由里找须要的页面做为View嵌入到Activity里,若是Flutter程序的入口同时配置了routes和home,则优先使用routes里配置的路由,当经过routes和home都没有找到路由的时候,才会从onUnknownRoute寻找。android
window.defaultRouteName
就是Activity里传递的路由名称git
Activity跳转到Flutter其实就是在Activity里嵌入FlutterView而后再跳转到这个包含FlutterView的Activity,分别为下面4种方式github
方法3和方法4是最简单的一种显示FlutterView的方式,不须要本身建立FlutterView,由于内部已经帮你建立好了。也不须要建立包含FlutterView的Fragment。而且可让Flutter与FlutterActivity有交互功能。可是直接跳转FlutterFragmentActivity或FlutterActivity会有短暂的黑屏,优化方式在下面的代码里我也写了,和其余页面的方式优化方式是同样的,都是获取到FlutterView而后调用一下enableTransparentBackground()
方法。方法3和方法4的惟一区别就是方法4继承的是v4包的FragmentActivity,而方法3继承的是普通的Activity。json
我写了点内容,可是仍是直接发完整代码看起来更方便更容易理解,因此就不写多余的文字了,直接把完整代码放出来app
package com.liuhc.myapplication
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import org.jetbrains.anko.startActivity
/** * 描述:首页 * 做者:liuhc * 建立日期:2019-09-05 on 11:21 AM */
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//addContentView方式添加FlutterView
page1.setOnClickListener {
startActivity<Page1Activity>()
}
//普通Fragment方式添加FlutterView
page2.setOnClickListener {
startActivity<Page2Activity>()
}
//使用FlutterFragmentActivity
page3.setOnClickListener {
startActivity<PageFlutterFragmentActivity>()
}
//使用FlutterActivity
page4.setOnClickListener {
startActivity<PageFlutterActivity>()
}
//addContentView方式添加FlutterView并传递参数
page1Param.setOnClickListener {
startActivity<Page1ParamActivity>()
}
//解决debug模式下黑屏的另外一种方式
noBlack.setOnClickListener {
startActivity<DebugNoBlackActivity>()
}
}
}
复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity">
<Button android:id="@+id/page1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="addContentView方式添加FlutterView" android:textAllCaps="false" />
<Button android:id="@+id/page2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="普通Fragment方式添加FlutterView" android:textAllCaps="false" />
<Button android:id="@+id/page3" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="使用FlutterFragmentActivity" android:textAllCaps="false" />
<Button android:id="@+id/page4" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="使用FlutterActivity" android:textAllCaps="false" />
<Button android:id="@+id/page1Param" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="addContentView方式添加FlutterView并传递参数" android:textAllCaps="false" />
<Button android:id="@+id/noBlack" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="解决debug模式下黑屏的另外一种方式" android:textAllCaps="false" />
</LinearLayout>
复制代码
package com.liuhc.myapplication
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.ViewGroup
import android.widget.FrameLayout
import io.flutter.facade.Flutter
/** * 描述:addContentView方式添加FlutterView * 做者:liuhc * 建立日期:2019-09-05 on 11:21 AM */
class Page1Activity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//下面这个setContentView添加不添加的都不要紧
//若是添加了,在debug模式下FlutterView显示有点慢因此会先显示activity_page1再显示FlutterView
//而release模式下速度会很快,看不到activity_page1,会直接显示FlutterView
setContentView(R.layout.activity_page1)
//Activity嵌入FlutterView的方式1
val flutterView = Flutter.createView(
this,
lifecycle,
"page1"
)
//这句话的做用是在debug模式下去除启动Flutter页面的时候的黑屏
flutterView.enableTransparentBackground()
val flutterLayoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
)
flutterLayoutParams.leftMargin = 0
//将下面的值设置为100就能够在FlutterView加载完之后仍然看到TextView
flutterLayoutParams.topMargin = 0
//由于flutterLayoutParams是占满全屏,因此调用addContentView会将flutterView放到R.layout.activity_page1上层
//效果看起来就是flutterView替换了R.layout.activity_page1
addContentView(flutterView, flutterLayoutParams)
//可是若是上面直接使用setContentView就看不到过渡效果了,也就是看不到activity_page1也就看不到TextView显示的文字了
}
}
复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".Page1Activity">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="FlutterView正在加载中..." />
</RelativeLayout>
复制代码
package com.liuhc.myapplication
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import io.flutter.facade.Flutter
/** * 描述:普通Fragment方式添加FlutterView * 做者:liuhc * 建立日期:2019-09-05 on 11:21 AM */
class Page2Activity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_page2)
//Activity嵌入Flutter View的方式2
val tx = supportFragmentManager.beginTransaction()
//下面2种方式均可以,只不过方式2代码里调用了flutterView.enableTransparentBackground(),因此在debug模式下不会闪烁黑屏
//方式1
tx.replace(R.id.frameLayout, Flutter.createFragment("page2"))
//方式2
tx.replace(R.id.frameLayout, Page2Fragment())
tx.commit()
}
class Page2Fragment : Fragment() {
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? {
val flutterView = Flutter.createView(activity!!, lifecycle, "page2")
//这句话的做用是在debug模式下去除启动Flutter页面的时候的黑屏
flutterView.enableTransparentBackground()
return flutterView
}
}
}
复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".Page2Activity">
<FrameLayout android:id="@+id/frameLayout" android:layout_width="match_parent" android:layout_height="match_parent" />
</LinearLayout>
复制代码
package com.liuhc.myapplication
import android.os.Bundle
import android.util.Log
import io.flutter.app.FlutterFragmentActivity
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterMain
import org.json.JSONObject
/** * 描述: * 做者:liuhc * 建立日期:2019-09-04 on 21:07 */
class PageFlutterFragmentActivity : FlutterFragmentActivity() {
private lateinit var methodChannel: MethodChannel
override fun onCreate(savedInstanceState: Bundle?) {
//强烈建议放到Application里初始化,初始化一次便可,放这里只是举个例子
FlutterMain.startInitialization(this)
//intent的参数设置必须在super.onCreate以前,由于super.onCreate里会取这些参数
intent.action = "android.intent.action.RUN"
val channelName = "channelName_PageFlutterFragmentActivity"
val androidMethod = "methodName_PageFlutterFragmentActivity"
val jsonObject = JSONObject()
jsonObject.put("path", "InvokeMethodPage")
jsonObject.put("title", "PageFlutterFragmentActivity")
jsonObject.put("channelName", channelName)
jsonObject.put("androidMethod", androidMethod)
intent.putExtra("route", jsonObject.toString())
super.onCreate(savedInstanceState)
//调用super.onCreate(savedInstanceState)以后flutterView才有值,
//因此若是须要注册插件,则应该放到super.onCreate(savedInstanceState)代码以后才能够
flutterView.enableTransparentBackground()
//若是不须要平台交互的话,只须要上面的代码并在最后加上super.onCreate就能够了
//这里this.registrarFor方法实际调用的是FlutterFragmentActivity里的delegate的registrarFor方法,
//而delegate的registrarFor方法实际调用的是FlutterActivityDelegate里的flutterView.getPluginRegistry().registrarFor方法,
//而FlutterActivityDelegate里的flutterView在调用了这里的super.onCreate(savedInstanceState)才有值,
//因此若是须要注册插件,则应该放到super.onCreate(savedInstanceState)代码以后才能够
val key = "PageFlutterFragmentActivity"
if (this.hasPlugin(key)) return
val registrar = this.registrarFor(key)
methodChannel = MethodChannel(
registrar.messenger(),
channelName
)
methodChannel.setMethodCallHandler { methodCall, result ->
if (methodCall.method == androidMethod) {
Log.e("Android", "接收到了Flutter传递的参数:${methodCall.arguments}")
result.success("$androidMethod ok")
Log.e("Android", "主动调用Flutter的methodInvokeMethodPageState方法")
methodChannel.invokeMethod("methodInvokeMethodPageState", "Android发送给Flutter的参数")
}
}
}
}
复制代码
package com.liuhc.myapplication
import android.os.Bundle
import android.util.Log
import io.flutter.app.FlutterActivity
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterMain
import org.json.JSONObject
/** * 描述: * 做者:liuhc * 建立日期:2019-09-04 on 23:30 */
class PageFlutterActivity : FlutterActivity() {
private lateinit var methodChannel: MethodChannel
override fun onCreate(savedInstanceState: Bundle?) {
//强烈建议放到Application里初始化,初始化一次便可,放这里只是举个例子
FlutterMain.startInitialization(this)
//intent的参数设置必须在super.onCreate以前,由于super.onCreate里会取这些参数
intent.action = "android.intent.action.RUN"
val channelName = "channelName_PageFlutterActivity"
val androidMethod = "methodName_PageFlutterActivity"
val jsonObject = JSONObject()
jsonObject.put("path", "InvokeMethodPage")
jsonObject.put("title", "PageFlutterActivity")
jsonObject.put("channelName", channelName)
jsonObject.put("androidMethod", androidMethod)
intent.putExtra("route", jsonObject.toString())
super.onCreate(savedInstanceState)
//调用super.onCreate(savedInstanceState)以后flutterView才有值,
//因此若是须要注册插件,则应该放到super.onCreate(savedInstanceState)代码以后才能够
flutterView.enableTransparentBackground()
//若是不须要平台交互的话,只须要上面的代码并在最后加上super.onCreate就能够了
//这里this.registrarFor方法实际调用的是FlutterFragmentActivity里的delegate的registrarFor方法,
//而delegate的registrarFor方法实际调用的是FlutterActivityDelegate里的flutterView.getPluginRegistry().registrarFor方法,
//而FlutterActivityDelegate里的flutterView在调用了这里的super.onCreate(savedInstanceState)才有值,
//因此若是须要注册插件,则应该放到super.onCreate(savedInstanceState)代码以后才能够
val key = "PageFlutterActivity"
if (this.hasPlugin(key)) return
val registrar = this.registrarFor(key)
methodChannel = MethodChannel(
registrar.messenger(),
channelName
)
methodChannel.setMethodCallHandler { methodCall, result ->
if (methodCall.method == androidMethod) {
Log.e("Android", "接收到了Flutter传递的参数:${methodCall.arguments}")
result.success("$androidMethod ok")
Log.e("Android", "主动调用Flutter的methodInvokeMethodPageState方法")
methodChannel.invokeMethod("methodInvokeMethodPageState", "Android发送给Flutter的参数")
}
}
}
}
复制代码
Page1ParamActivity.ktless
package com.liuhc.myapplication
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.ViewGroup
import android.widget.FrameLayout
import io.flutter.facade.Flutter
import org.json.JSONObject
/** * 描述:演示在跳转包含FlutterView的页面时如何同时传递参数 * 做者:liuhc * 建立日期:2019-09-03 */
class Page1ParamActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//下面这个setContentView添加不添加的都不要紧
//若是添加了,在debug模式下FlutterView显示有点慢因此会先显示activity_page1再显示FlutterView
//而release模式下速度会很快,看不到activity_page1,会直接显示FlutterView
setContentView(R.layout.activity_page1)
//Activity嵌入FlutterView的方式1
val jsonObject = JSONObject()
jsonObject.put("path", "page1Param")
jsonObject.put("param", "我是参数")
val flutterView = Flutter.createView(
this,
lifecycle,
jsonObject.toString()
)
//这句话的做用是在debug模式下去除启动Flutter页面的时候的黑屏
flutterView.enableTransparentBackground()
val flutterLayoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
)
flutterLayoutParams.leftMargin = 0
//将下面的值设置为100就能够在FlutterView加载完之后仍然看到TextView
flutterLayoutParams.topMargin = 0
//由于flutterLayoutParams是占满全屏,因此调用addContentView会将flutterView放到R.layout.activity_page1上层
//效果看起来就是flutterView替换了R.layout.activity_page1
addContentView(flutterView, flutterLayoutParams)
//可是若是上面直接使用setContentView就看不到过渡效果了,也就是看不到activity_page1也就看不到TextView显示的文字了
}
}
复制代码
package com.liuhc.myapplication
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import io.flutter.facade.Flutter
import org.jetbrains.anko.find
/** * 描述:在debug模式下,直接跳转到使用FlutterView的Activity的时候会有黑屏, * 若是想处理这种状况能够直接调用flutterView.enableTransparentBackground(),参考Page1Activity * 这里提供另外一种解决方式 * * 可是没什么必要,由于release模式下是没有这种问题的 * * 这里要注意隐藏显示window.decorView是不能够的,必须隐藏显示R.layout.xxx里将要包含FlutterView的ViewGroup * * 做者:liuhc * 建立日期:2019-09-03 */
class DebugNoBlackActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//先调用setContentView设置一个layout
setContentView(R.layout.activity_debug_no_black)
//Activity嵌入FlutterView的方式1
val flutterView = Flutter.createView(
this,
lifecycle,
"page1"
)
val flutterLayoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
)
flutterLayoutParams.leftMargin = 0
flutterLayoutParams.topMargin = 0
//1.取出setContentView里将要添加FlutterView的ViewGroup
val flutterViewViewGroup = find<ViewGroup>(R.id.flutterViewViewGroup)
val root = find<ViewGroup>(R.id.root)
val tipTextView = find<View>(R.id.tipTextView)
//2.添加FlutterView
flutterViewViewGroup.addView(flutterView, flutterLayoutParams)
//3.在xml或者调用代码隐藏包含FlutterView的ViewGroup,由于我在xml隐藏了因此下面的方法就不须要调用了
//flutterViewViewGroup.visibility = View.INVISIBLE
//4.当FlutterView绘制了第一帧之后再显示,就解决了debug模式下的黑屏问题
flutterView.addFirstFrameListener {
//删除过渡TextView
root.removeView(tipTextView)
flutterViewViewGroup.visibility = View.VISIBLE
}
}
}
复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".Page1Activity">
<FrameLayout android:id="@+id/flutterViewViewGroup" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" />
<TextView android:id="@+id/tipTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="FlutterView正在加载中..." />
</RelativeLayout>
复制代码
若是你直接运行release的版本,跳转到包含FlutterView的页面是没问题的,可是大部分时候咱们运行的是debug版本,这时候你会发现跳转到包含FlutterView的页面的时候屏幕会黑屏闪烁一下,实际上是不用处理的,由于debug和release模式的编译方式不同,release是没有这个问题的,可是本着刨根问底的态度,咱们能够用DebugNoBlackActivity.kt的方式来解决async
import 'dart:convert';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'invoke_method_page.dart';
import 'my_home_page.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
//方式1
home: _getWidgetByRoute(window.defaultRouteName),
//方式2 若是route相同,优先匹配routes而不是home
routes: <String, WidgetBuilder>{
"page1": (context) => MyHomePage(
title: "匹配到了page1",
message: "经过routes变量",
),
"page2": (context) => MyHomePage(
title: "匹配到了page2",
message: "经过routes变量",
),
"page3": (context) => MyHomePage(
title: "匹配到了page3",
message: "经过routes变量",
),
},
//当经过routes和home的返回值都为null的话,才会从onUnknownRoute寻找
onUnknownRoute: (RouteSettings settings) {
return new PageRouteBuilder(pageBuilder: (BuildContext context, _, __) {
//这里为返回的Widget
return MyHomePage(
title: "没有匹配到",
message: "经过onUnknownRoute变量",
);
});
},
);
}
}
//若是要接收平台层发送的参数,除了使用Channel之外(这种方式不是正常的方式,强烈不推荐),就只能经过window.defaultRouteName了,
//由于routes的route只能提早定义好,没法动态判断
Widget _getWidgetByRoute(String jsonStr) {
print("json=$jsonStr");
String _route;
Map<String, dynamic> jsonMap;
try {
jsonMap = json.decode(jsonStr);
_route = jsonMap["path"];
} catch (e) {
print(e);
_route = jsonStr;
}
switch (_route) {
//接收到了匹配的规则,跳转到flutter指定页面
case 'page1':
return MyHomePage(
title: "匹配到了page1",
message: "经过home变量",
);
case 'page1Param':
return MyHomePage(
title: "匹配到了page1Param",
message: jsonMap["param"],
);
case "InvokeMethodPage":
return InvokeMethodPage(
title: jsonMap["title"],
channelName: jsonMap["channelName"],
androidMethod: jsonMap["androidMethod"],
);
default:
return MyHomePage(
title: "没有匹配到",
message: "经过home变量",
);
}
}
复制代码
invoke_method_page.dartide
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class InvokeMethodPage extends StatefulWidget {
final String _title;
final String _channelName;
final String _androidMethod;
InvokeMethodPage({
@required String title,
@required String channelName,
@required String androidMethod,
}) : this._title = title,
this._channelName = channelName,
this._androidMethod = androidMethod;
@override
_InvokeMethodPageState createState() => _InvokeMethodPageState();
}
class _InvokeMethodPageState extends State<InvokeMethodPage> {
MethodChannel _methodChannel;
@override
void initState() {
_methodChannel = MethodChannel(widget._channelName);
_methodChannel.setMethodCallHandler((methodCall) {
Future future = Future(() {
if (methodCall.method == "methodInvokeMethodPageState") {
print("接收到了Android的主动调用,参数为:${methodCall.arguments}");
}
});
return future;
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget._title),
),
body: RaisedButton(
onPressed: () async {
print("主动调用Android的方法${widget._androidMethod}");
var result = await _methodChannel.invokeMethod(
widget._androidMethod,
"我是来自Flutter的参数",
);
print("接收到了调用Android方法的返回值:$result");
},
child: Text("我是Flutter控件,点击发送参数到Android"),
),
);
}
}
复制代码
my_home_page.dartpost
import 'package:flutter/material.dart';
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title, this.message}) : super(key: key);
final String title;
final String message;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
widget.message,
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
));
}
}
复制代码
到此,本章结束,这章介绍了如何在Android里嵌入FlutterView并跳转,还介绍了如何传递参数到FlutterView,但愿对你们有帮助。
运行Android项目,点击各个页面里的+号,数字会递增,说明刷新页面正常
欢迎加入Flutter开发群457664582,点击加入,你们一块儿学习讨论