Flutter如何嵌入到原生安卓中?
请把Flutter Dart都更新到最新版,否则网上大多数文章就不要看了,纯属浪费时间。
- 新建一个Flutter Module
- 切换config为app,而非默认的main.dart
如图:
以上两步轻松在安卓项目中引入Flutter
你问:如何把Flutter写的view引入到安卓中呢?
我答:第一步在安卓端把Flutter编写的界面作为普通的view,大家都知道,安卓中能看的见的大多数控件都是view,这就很好理解了。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setContentView(R.layout.activity_main)
// 通过FlutterView引入Flutter编写的页面
val flutterView: View = Flutter.createView(this, lifecycle, "route1")
val layout =
FrameLayout.LayoutParams(600, 800)
addContentView(flutterView, layout)
第二步在Flutter端 runApp方法里面来获取window传来的参数
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String url) {
switch (url) {
case 'route1':
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter页面'),
),
body: Center(
child: Text('Flutter页面,route=$url'),
),
),
);
default:
return Center(
child: Text('Unknown route: $url', textDirection: TextDirection.ltr),
);
}
}
运行后的效果:
如果你玩过Webview嵌套前端页面,是不是似曾相识。现在不太好看,我们稍微改一下:主题修改为NoActionBar,再把状态栏也透明起来。
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!--状态栏透明-->
<item name="android:windowTranslucentStatus">true</item>
</style>
再把安卓端的宽高改一下:
设置FlutterView宽高为全屏
val layout: FrameLayout.LayoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
addContentView(flutterView, layout)
效果如图:
上面的方法是其中一种,还有第二种:
首先把xml里面新增一个充满父布局的Framlayout,其次通过代码替换成FlutterFragment
// 通过FlutterFragment引入Flutter编写的页面
val tx: FragmentTransaction = supportFragmentManager.beginTransaction()
tx.replace(R.id.fragment, Flutter.createFragment("route1"))
tx.commit()
好了,现在又要你问我答了.如果我想在显示Flutter写的界面的时候,传一些参数,怎么办?
我答:很简单第一步安卓端:
/**
* 在Flutter路由的后面跟上?+参数
* 像不像get请求,只不过为了后面解析方便,后面跟的是json而已,其实这里我们也可以思考一下,真正的交互底层还是C/S架构的,联想一下http请求
*
*/
val flutterView: View = Flutter.createView(this, lifecycle,"route1?{\"name\":\"StephenCurry\"}")
val layout: FrameLayout.LayoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
addContentView(flutterView, layout)
第二步Flutter端
Widget _widgetForRoute(String url) {
// route名称
String route =
url.indexOf('?') == -1 ? url : url.substring(0, url.indexOf('?'));
// 参数Json字符串
String paramsJson =
url.indexOf('?') == -1 ? '{}' : url.substring(url.indexOf('?') + 1);
// 解析参数
Map<String, dynamic> params = json.decode(paramsJson);
print(params);
这样参数拿到了。这是FlutterView的方式传,那么FlutterFragment呢?其实是一样的。
你问我答,那如何从Flutter跳转到安卓界面呢,并且带参数
Flutter如何跳转到原生界面
安卓端编写接收代码:
class MainActivity : AppCompatActivity() {
private val CHANNEL_NATIVE = "com.example.flutter/native"//与Flutter中channel保持一致即可
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setContentView(R.layout.activity_main)
// 通过FlutterView引入Flutter编写的页面
val flutterView: FlutterView = Flutter.createView(this, lifecycle,"route1?{\"name\":\"StephenCurry\"}")
val layout: FrameLayout.LayoutParams =
FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
addContentView(flutterView, layout)
val nativeChannel = MethodChannel(flutterView, CHANNEL_NATIVE)
nativeChannel.setMethodCallHandler { methodCall, result ->
when (methodCall.method) {
"jumpToNative" -> {
// 跳转原生页面
val jumpToNativeIntent = Intent(
this,
Main2Activity::class.java
)
jumpToNativeIntent.putExtra(
"name",
methodCall.argument<Any>("name") as String?
)
startActivity(jumpToNativeIntent)
}
else -> result.notImplemented()
}
}
新建一个新的Activity用于Flutter跳转过来并接收参数展示
Flutter端发送事件:
const nativeChannel =
const MethodChannel('com.example.flutter/native');//和原生端的ChannleName保持一致即可
RaisedButton(
child: Text('跳转去原生'),
onPressed:(){
// 跳转原生页面
Map<String, dynamic> result = {'name': params['name']};//这里是把Flutter接收到原生的参数,再传回原生,当然也可以是任意Flutter端处理过任意数据
nativeChannel.invokeMethod('jumpToNative', result);
} ,
)
onActivityResult的实现方式
Flutter端发送数据
RaisedButton(
child: Text('返回上一页'),
onPressed: () {
// 返回给上一页的数据
Map<String, dynamic> result = {'message': '我从Flutter页面回来了'};
nativeChannel.invokeMethod('goBackWithResult', result);
}),
安卓端接收并处理
//通过methodCall.argument()获取Flutter的参数,然后自行处理
nativeChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
switch (methodCall.method) {
case "goBackWithResult":
// 返回上一页,携带数据
Intent backIntent = new Intent();
backIntent.putExtra("message", (String) methodCall.argument("message"));
setResult(RESULT_OK, backIntent);
finish();
break;
}
}
});
总结:Flutter端与原生端交互的方式:
- MethodChannel:用于传递方法调用(method invocation),Flutter和平台端进行直接方法调用时候可以使用。
- BasicMessageChannel:用于传递字符串和半结构化的信息,Flutter和平台端进行消息数据交换时候可以使用。
- EventChannel:用于数据流(event streams)的通信,Flutter和平台端进行事件监听、取消等可以使用。
当前只用了其中一种最常用的,后面会继续讲其他方式,其实我们仔细思考一下,这种C/S架构是不是两两通信最朴素的方法。