Android与Flutter数据交互
-
先看一下效果图吧(图一):
图一上面是android原生,下面是flutter页面
-
好 让我们一步一步来
- 1.创建flutter module
在android项目同级的目录里按住shift+右键
,然后选择在此处打开 Powershell窗口(s)
。
//或者在dos里cd到指定目录也行
然后输入flutter create -t module my_flutter
创建好后目录如下(图二):
- 2.配置android工程(将Flutter模块作为依赖添加到主项目)
settings.gradle
文件里添加:
setBinding(new Binding([gradle: this]))
//我们的flutter目录
evaluate(new File(
settingsDir.parentFile,
'my_flutter/.android/include_flutter.groovy'
))
app目录的build.gradle
下添加:
dependencies {
...
implementation project(':flutter')
}
添加之后同步下代码,然后我们就可以在android项目里调用flutter页面了
- 3.android添加flutter布局
改一下android:MainActivity.java
里代码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setContentView(R.layout.activity_main)
//获取flutterView
val flutterView = Flutter.createView(this, lifecycle, "route1")
setContentView(flutterView)
}
}
Flutter.createView方法里前两个参数是不变的,后面的"route1"
可以获取指定的flutter页面,后面讲
现在我们先运行一下看一下效果。。。
-
运行的时候报了个错误(图三):
图三
需要在app目录的build.gradle
下指定下jdk版本:
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
再同步下,就可以运行了,运行效果(图四):
图四
获取flutterView还有一种方法:
//获取flutterView 第二种方法
val tx = supportFragmentManager.beginTransaction()
//第一个参数填入flutterView要存放的布局id
tx.replace(R.id.---, Flutter.createFragment("route1"))
tx.commit()
这两种方法都设置了"route1"
这个参数,我们来看一下这个参数在flutter里怎么用吧:
修改flutter main.dart
里代码:
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String route) {
switch (route) {
case 'route1':
return MyApp1();
break;
default:
return MyApp();
break;
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('MyApp'),
),
),
);
}
}
class MyApp1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('MyApp1'),
),
),
);
}
}
这里的window需要导包,现在知道那个参数的作用了吧,我们可以根据那个参数跳转不同的flutter页面
- 到这里android和flutter的基础配置就结束了,接下来我们进行数据传递
- 4.数据传递
写之前我们先修改下android activity_main.xml
的布局(图五):
MainActivity.java
:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//获取flutterView
val flutterView = Flutter.createView(this, lifecycle, "route1")
llMainBottom.addView(flutterView)
}
}
flutter页面的布局也修改下:
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String route) {
switch (route) {
case 'route1':
return MyApp1();
break;
default:
return MyApp();
break;
}
}
//App
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('MyApp'),
),
),
);
}
}
//App1
class MyApp1 extends StatefulWidget {
@override
_MyApp1State createState() => _MyApp1State();
}
class _MyApp1State extends State<MyApp1> {
String _text = '你好 世界!';
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(
alignment: FractionalOffset(0.5, 0.95),
children: <Widget>[
Center(
child: Text(_text),
),
RaisedButton(
child: Text('修改android'),
onPressed: () {
},
),
],
),
),
);
}
}
-
运行效果就和我们最上面的一样了,这里就不贴了,传输数据Flutter向我们提供了3个方法:
-MethodChannel
-EventChannel
-BasicMessageChannel
我这边示例一下MethodChannel
的使用,其他两种方法有点类似 -
我们点击android按钮,然后传输数据到flutter那边
android发送:
//发送msg给flutters
MethodChannel(flutterView, "com.yang.test001").invokeMethod("android","Hello World!")
注
:
-
"com.yang.test001"
相当于唯一标识,flutter那边接受用 -
invokeMethod
方法里传的是键值对,flutter接受时可根据key取值
flutter 接收:
@override
void initState() {
super.initState();
MethodChannel('com.yang.test001').setMethodCallHandler((handler) {
//根据key判断android传过来的值
switch (handler.method) {
case 'android':
setState(() {});
var msg = handler.arguments;
_text = msg.toString();
break;
}
});
}
在initState
方法里进行监听,然后根据key进行判断,再setState(() {});
改变下值就ok了
android接受flutter的值同理...
- 当我们android项目混合了flutter后,可以进行热重载(我这边测试只能更改flutter代码才有效果)
注
:进入我们创建的my_flutter
目录里,再shift+右键
,然后选择在此处打开 Powershell窗口(s)
,然后输入:
flutter attach
会看到如下界面(图六):
图六
然后在android工程里启动我们的app(图七):
图七
启动后在shell窗口会看到如下信息(图八):
图八
之后修改flutter代码,在终端输入r
就可以热重载了,R
是热重启。没用的话先输入一遍R
,再输入r
,应该就可以看到效果了,再没用就反复多试试,感觉有点延迟,还有只能重载flutter代码,所以用处嘛,就一丢丢而已了。。
- 所有的代码也贴一下吧:
android
:
class MainActivity : AppCompatActivity() {
private lateinit var mMethodChannel: MethodChannel //flutter连接
private val METHOD_CHANNER = "com.yang.test001" //与flutter连接的标识
private var mIsSendFlutter = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
initListener()
}
private fun initView() {
//绑定flutterView 第一种方法
val flutterView = Flutter.createView(this, lifecycle, "route1")
llMainBottom.addView(flutterView)
// //第二种方法
// val tx = supportFragmentManager.beginTransaction()
// tx.replace(R.id.llMainBottom, Flutter.createFragment("route1"))
// tx.commit()
//初始化MethodChannel
mMethodChannel = MethodChannel(flutterView, METHOD_CHANNER)
}
private fun initListener() {
//接受flutter消息监听
mMethodChannel.setMethodCallHandler(object : MethodChannel.MethodCallHandler {
override fun onMethodCall(p0: MethodCall, p1: MethodChannel.Result) {
//根据收到的消息key进行接收
when (p0.method) {
"flutter" -> {
val msg = p0.arguments
tvMain.text = msg.toString()
}
else -> {
}
}
}
})
btnMain.setOnClickListener {
//发送msg给flutter
mMethodChannel.invokeMethod("android", if (mIsSendFlutter) {
"你好 世界!"
} else {
"Hello World!"
})
mIsSendFlutter = !mIsSendFlutter
}
}
}
flutter
:
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(_widgetForRoute(window.defaultRouteName));
Widget _widgetForRoute(String route) {
switch (route) {
case 'route1':
return MyApp1();
break;
default:
return MyApp();
break;
}
}
//App1
class MyApp1 extends StatefulWidget {
@override
_MyApp1State createState() => _MyApp1State();
}
class _MyApp1State extends State<MyApp1> {
MethodChannel _methodChannel = MethodChannel('com.yang.test001'); //与android连接
String _text = '你好 世界!';
bool _isSendAndroid = false;
@override
void initState() {
super.initState();
_methodChannel.setMethodCallHandler(_handler);
}
//接收android监听
Future<dynamic> _handler(MethodCall call) {
switch (call.method) {
case 'android':
setState(() {});
var msg = call.arguments;
_text = msg.toString();
break;
}
}
//发送消息给android
void _sendMessage() {
//flutter 相当于key 后面是内容
_methodChannel.invokeListMethod(
'flutter', _isSendAndroid ? 'Hello World!' : '你好 世界!');
_isSendAndroid = !_isSendAndroid;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Stack(
alignment: FractionalOffset(0.5, 0.95),
children: <Widget>[
Center(
child: Text(_text),
),
RaisedButton(
child: Text('修改android'),
onPressed: () {
_sendMessage();
},
),
],
),
),
);
}
}
//默认App
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Text('myApp'),
),
),
);
}
}