在Android原生项目中添加flutter
官方文档
1.创建flutter module,有两种方法:
用Android studio创建flutter module;
用命令创建flutter module
cd some/path/(原生项目的根目录)
flutter create -t module --org com.example my_flutter
2.设置 settings.gradle 添加下列代码
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'my_flutter/.android/include_flutter.groovy' // 必须是flutter module的真实路径
))
- 设置app build.gradle
dependencies {
implementation project(':flutter')
}
- 注意事项
//Java 版本号
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
minSdkVersion 16(最低)
- Android 代码加载 flutter 界面
Flutter提供两种引入方法:一个是View,会默认生成一个View,一个是FlutterFragment方式。
package com.example.administrator.shadowapplication.flutter
import android.os.Bundle
import android.widget.FrameLayout
import androidx.fragment.app.FragmentActivity
import com.example.administrator.shadowapplication.flutter.channel.EventChannelPlugin
import io.flutter.facade.Flutter
import io.flutter.facade.FlutterFragment
/**
* native项目加载flutter页面
将flutter页面构建成View,通过addView来显示flutter页面
将flutter页面构建成Fragment,通过对fragment的操作来显示flutter页面
*/
class FlutterActivity : FragmentActivity() {
companion object {
private const val TAG_FLUTTER_FRAGMENT = "flutter_fragment"
private var flutterFragment: FlutterFragment? = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//方法1
/* setContentView(R.layout.activity_flutter)
val fragmentManager: FragmentManager = supportFragmentManager
flutterFragment = fragmentManager.findFragmentByTag(TAG_FLUTTER_FRAGMENT) as FlutterFragment?
if (flutterFragment == null) {
val newFlutterFragment = Flutter.createFragment("这里是flutter页面")
flutterFragment = newFlutterFragment
fragmentManager.beginTransaction()
.add(R.id.flutterContent, newFlutterFragment, TAG_FLUTTER_FRAGMENT)
.commit()
}*/
//方法2
val flutterView = Flutter.createView(this, lifecycle, "route1")
val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
layoutParams.topMargin = 20
addContentView(flutterView, layoutParams)
}
}
- flutter 代码
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp(
initParams: window.defaultRouteName,
));
class MyApp extends StatelessWidget {
final String initParams;
MyApp({Key key, @required this.initParams}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 混合开发',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: _getHomePage(),
);
}
Widget _getHomePage() {
switch (initParams) {
case "route1":
return MyHomePage(
title: 'flutter 混合开发',
initParams: "hello shadow!!",
);
break;
}
}
}
class MyHomePage extends StatefulWidget {
final String initParams;
MyHomePage({Key key, this.title, this.initParams}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState(initParams);
}
class _MyHomePageState extends State<MyHomePage> {
final String initParams;
int _counter = 0;
_MyHomePageState(this.initParams);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'initParams:$initParams',
style: TextStyle(color: Colors.red),
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
);
}
}
flutter 和Android 平台通信
平台通道简介
所谓“平台特定”或“特定平台”,平台指的就是指Flutter运⾏的平台,如Android或IOS,可以认为就是应⽤的原⽣部分。 所以,平台通道正是Flutter和原⽣之间通信的桥梁,它也是Flutter插件的底层基础设施。
Flutter使⽤了⼀个灵活的系统,允许您调⽤特定平台的API,⽆论在Android上的Java或Kotlin代码中,还是iOS上的 ObjectiveC或Swift代码中均可⽤。
Flutter与原⽣之间的通信依赖灵活的消息传递⽅式:
应⽤的Flutter部分通过平台通道(platform channel)将消息发送到其应⽤程序的所在的宿主(iOS或Android)应 ⽤(原⽣应⽤)。 宿主监听平台通道,并接收该消息。然后它会调⽤该平台的API,并将响应发送回客户端,即应⽤程序的Flutter部 分。
平台通道
使⽤平台通道在Flutter(client)和原⽣(host)之间传递消息
1975877-6079b324ae9d7bf7.png
图中可以看到,Flutter 是 Client 端,Native 是 Host,Client 和 host 通信是通过 PlatformChannel,Client 通过 PlatformChannel 向 Host 发送消息,Host 监听 PlatformChannel 并接收消息,然后将响应结果发送给 Client。
消息和响应以异步方式传递,以确保 UI 不阻塞。另外,PlatformChannel 是双工的,这意味着 Flutter 和 Native 可以交替做 Client 和 Host。
Flutter调用原生主要使用插件方式,通过MethodChannel的方式。
Flutter 定义了三种不同类型的 PlatformChannel,它们分别是:
- MethodChannel:用于传递方法调用,是比较常用的 PlatformChannel。
- EventChannel: 用于传递事件。
- BasicMessageChannel:用于传递数据。
BinaryMessenger
BinaryMessenger 是 PlatformChannel 与 Flutter 端的通信的工具,其通信使用的消息格式为二进制格式数据,BinaryMessenger 在 Android 中是一个接口,它的实现类为 FlutterNativeView。
Codec
Codec 是消息编解码器,主要用于将二进制格式的数据转化为 Handler 能够识别的数据,Flutter 定义了两种 Codec:MessageCodec 和 MethodCodec。MessageCodec 用于二进制格式数据与基础数据之间的编解码,BasicMessageChannel 所使用的编解码器是 MessageCodec。MethodChannel 和 EventChannel 所使用的编解码均为 MethodCodec。
Handler
Flutter 定义了三种类型的 Handler,它们与 PlatformChannel 类型一一对应,分别是 MessageHandler、MethodHandler、StreamHandler。
在使用 PlatformChannel 时,会为它注册一个 Handler,PlatformChannel 会将该二进制数据通过 Codec 解码为转化为 Handler 能够识别的数据,并交给 Handler 处理。
当 Handler 处理完消息之后,会通过回调函数返回 result,将 result 通过编解码器编码为二进制格式数据,通过 BinaryMessenger 发送回 Flutter 端。
MethodChannel 可以实现 Flutter 调用 Android,也可以实现 Android 调用 Flutter,这里分别来进行举例。
MethodChannel 实现通信
Android端 代码 实现
package com.example.administrator.shadowapplication.flutter
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import android.os.Bundle
import android.widget.FrameLayout
import androidx.fragment.app.FragmentActivity
import com.example.administrator.shadowapplication.flutter.channel.MethodChannelPlugin
import io.flutter.facade.Flutter
import io.flutter.facade.FlutterFragment
/**
* native项目加载flutter页面
将flutter页面构建成View,通过addView来显示flutter页面
将flutter页面构建成Fragment,通过对fragment的操作来显示flutter页面
*/
class FlutterActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val flutterView = Flutter.createView(this, lifecycle, "route1")
val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
layoutParams.topMargin = 20
addContentView(flutterView, layoutParams)
//生成plateForm,注册 handle
MethodChannelPlugin(flutterView)
}
/**
* 获取当前面 电量
*/
fun getBatteryLevel(): Int {
val batteryLevel: Int
batteryLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
}
return batteryLevel
}
}
//======================== 封装了 MethodChannelPlugin====
package com.example.administrator.shadowapplication.flutter.channel
import android.app.Activity
import com.example.administrator.shadowapplication.flutter.FlutterActivity
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterView
/**
* @author 付影影
* @desc 用于数据流 event streams的通信,持续通信,通常用于native向dart的通信
* 如手机电量变化,网络连接变化,陀螺仪,传感器等
* @date 2020/1/9
*/
class MethodChannelPlugin constructor(flutterView: FlutterView) : MethodChannel.MethodCallHandler {
var activity: Activity = flutterView.context as Activity
private val CHANNEL_NAME = "MethodChannelPlugin"
init {
MethodChannel(flutterView, CHANNEL_NAME).setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"getBatteryLevel" -> {
val batteryLevel = (activity as FlutterActivity).getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
}
else -> {
result.notImplemented()
}
}
}
}
Dart 代码 实现
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp(
initParams: window.defaultRouteName,
));
class MyApp extends StatelessWidget {
final String initParams;
MyApp({Key key, @required this.initParams}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 混合开发',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: _getHomePage(),
);
}
///根据 native 传递的参数,选择要展示的界面
Widget _getHomePage() {
switch (initParams) {
case "route1":
return MyHomePage(
title: 'flutter 混合开发',
initParams: "hello shadow!!",
);
break;
}
}
}
class MyHomePage extends StatefulWidget {
final String initParams;
MyHomePage({Key key, this.title, this.initParams}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState(initParams);
}
class _MyHomePageState extends State<MyHomePage> {
final String initParams;
_MyHomePageState(this.initParams);
///调⽤通道上的⽅法,指定通过字符串标识符调⽤⽅法 getBatteryLevel 。
///该调⽤可能失败(平台不⽀持平台 API,例如在模拟器中运⾏时),所以我们将invokeMethod调⽤包装在try-catch语句中。
///我们使⽤返回的结果,在 setState 中来更新⽤户界⾯状态 batteryLevel
String _batteryLevel = "unknow battery level";
MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
///获取 当前 电量
Future<Null> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await _methodChannel.invokeMethod("getBatteryLevel");
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {
_getBatteryLevel();
},
child: Text(
'FlutterActivity 传递数据:$initParams',
style: TextStyle(color: Colors.red),
),
),
Text(
'$_batteryLevel',
style: Theme.of(context).textTheme.display1,
),
],
),
),
);
}
}
实现 结果
微信图片_20200110105330.pngMethodChannel 实现 在Android 中调用 flutter 方法
有的时候 Flutter 调用 Android 后,Android 还会将结果返回给 Flutter,虽然有时可以用 result 来实现,但 Android 端的处理可能是异步的,result 对象也不能长期的持有,这时就需要 Android 来调用 Flutter。
因为页面 UI 是 Flutter 端绘制的,我们很难在页面中控制 Android 端,要实现 Android 调用 Flutter,可以利用 Android 的 Activty 的生命周期,如果将应用切到后台再切回前台,这样 Activty 的 onResume 方法就会被调用,我们在 onResume 方法中实现调用 Flutter 的功能就可以了。
Android 端代码实现
package com.example.administrator.shadowapplication.flutter
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.FrameLayout
import androidx.fragment.app.FragmentActivity
import com.example.administrator.shadowapplication.flutter.channel.MethodChannelPlugin
import io.flutter.facade.Flutter
import io.flutter.facade.FlutterFragment
import io.flutter.plugin.common.MethodChannel
import io.flutter.view.FlutterView
import java.util.*
/**
* native项目加载flutter页面
将flutter页面构建成View,通过addView来显示flutter页面
将flutter页面构建成Fragment,通过对fragment的操作来显示flutter页面
*/
private lateinit var flutterView: FlutterView;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
flutterView = Flutter.createView(this, lifecycle, "route1")
val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
layoutParams.topMargin = 20
addContentView(flutterView, layoutParams)
}
override fun onResume() {
super.onResume()
val map = HashMap<String, String>()
map["content"] = "Hello 付小影子,我是Android"
val methodChannel = MethodChannel(flutterView, "MethodChannelPlugin")
methodChannel.invokeMethod("showText", map, object : MethodChannel.Result {
//2
override fun success(o: Any?) {
Log.d("hh", o as String?)
}
override fun error(errorCode: String, errorMsg: String?, errorDetail: Any?) {
Log.d("hh", "errorCode:" + errorCode + " errorMsg:" + errorMsg + " errorDetail:" + errorDetail as String?)
}
override fun notImplemented() {
Log.d("hh", "notImplemented")
}
})
}
}
Dart 端 代码实现
class _MyHomePageState extends State<MyHomePage> {
final String initParams;
_MyHomePageState(this.initParams);
//通信方式2
MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
String textContent = " Flutter 初始数据";
@override
void initState() {
super.initState();
_methodChannel.setMethodCallHandler((MethodCall call) async {
switch (call.method) {
case "showText":
String content = await call.arguments["content"];
if (content != null && content.isNotEmpty) {
setState(() {
textContent = content;
});
return "success";
} else {
throw PlatformException(
code: "error", message: "失败", details: "content is null");
}
break;
default:
throw MissingPluginException();
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
child: Text(
'FlutterActivity 传递数据:$initParams',
style: TextStyle(color: Colors.red),
),
),
Text(
'$_batteryLevel',
style: Theme.of(context).textTheme.display1,
),
Text(" Android端数据 -- $textContent")
],
),
),
);
}
}
BasicMessageChannel 实现 通信
Android 代码实现
package com.example.administrator.shadowapplication.flutter.channel
import android.app.Activity
import com.example.administrator.shadowapplication.crash_log.ToastUtil
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.StringCodec
import io.flutter.view.FlutterView
/**
* @author 付影影
* @desc BasicMessageChannel (主要是传递字符串和一些半结构体的数据)
* BasicMessageChannel(@NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MessageCodec<T> codec)
* @date 2020/1/9
*/
class BasicMessageChannelPlugin constructor(flutterView: FlutterView) : BasicMessageChannel.MessageHandler<String> {
lateinit var activity: Activity;
lateinit var messageChannel: BasicMessageChannel<String>;
val CHANNEL_NAME = "BasicMessageChannelPlugin"
/**
* BasicMessageChannel的初始化,入参有三个,
* 第一个是BinaryMessage,
* 第二个参数是定义的channel name,
* 第三个参数是MessageCodec解码器
* 官方提供了四种消息编码器,最终自动转为ByteBuffer
*/
init {
activity = flutterView.context as Activity
messageChannel = BasicMessageChannel<String>(flutterView, CHANNEL_NAME, StringCodec.INSTANCE);
//设置消息处理器,处理来自dart的消息
messageChannel.setMessageHandler(this)
}
/**
* 第二种 使用:dart 主动发起,Android端为接收方(收到dart的消息,并且发送回复)
*/
override fun onMessage(message: String?, reply: BasicMessageChannel.Reply<String>) {
reply.reply("BasicMessageChannel收到:$message") //可以通过reply进行回复
ToastUtil.showMsg(message) //接收到的消息
}
/**
* 第一种 使用:发送消息到Dart ,并且 接收到 dart的回复
*/
fun send(message: String, callBack: BasicMessageChannel.Reply<String>) {
messageChannel.send(message, callBack)
}
}
Dart 端代码实现
class BasicMessageChannelPage extends StatefulWidget {
@override
_BasicMessageChannelPageState createState() =>
_BasicMessageChannelPageState();
}
class _BasicMessageChannelPageState extends State<BasicMessageChannelPage> {
String _receiveData ="flutter 默认数据";
var _messageChannel = BasicMessageChannel<String>("BasicMessageChannelPlugin", StringCodec());
//发送消息 到 Android,并且收到Android的回复
Future<String> sendMessage() async {
String reply = await _messageChannel.send("hello shadow,我是 dart");
print(reply);
return reply;
}
//从Android 端接收消息,并且发送回复
void receiveMessage() {
_messageChannel.setMessageHandler((message) async {
print("接收 Android 端发送的消息,$message");
return "哈哈哈哈,这是我给你的回复";
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Text("EventChanne 接收的Android数据:$_receiveData"),
);
}
}
EventChannel 实现通信
Android 代码实现
package com.example.administrator.shadowapplication.flutter.channel
import io.flutter.plugin.common.EventChannel
import io.flutter.view.FlutterView
/**
* @author 付影影
* @desc 用于数据流 event streams的通信,持续通信,通常用于native向dart的通信
* 如手机电量变化,网络连接变化,陀螺仪,传感器等
* @date 2020/1/9
*/
class EventChannelPlugin constructor(flutterView: FlutterView) : EventChannel.StreamHandler {
private val CHANNEL_NAME = "EventChannelPlugin"
init {
EventChannel(flutterView, CHANNEL_NAME).setStreamHandler(this)
}
/**
* onCancel代表对面不再接收,这里我们应该做一些clean up的事情。
* onListen则代表通道已经建好,Native可以发送数据了。
* 注意onListen里带的EventSink这个参数,后续Native发送数据都是经过EventSink的
*/
var events: EventChannel.EventSink? = null
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
this.events = events
}
override fun onCancel(arguments: Any?) {
events = null
}
/**
* 给 dart端发送消息
*/
fun send(params: Any) {
if (events != null) {
events!!.success(params)
}
}
}
Dart 代码实现
class EventChannelPage extends StatefulWidget {
@override
_EventChannelPageState createState() => _EventChannelPageState();
}
class _EventChannelPageState extends State<EventChannelPage> {
String _receiveData = "Flutter 默认数据";
var CHANNEL_NAME = "EventChannelPlugin";
var __eventChannel = EventChannel("CHANNEL_NAME");
@override
void initState() {
// TODO: implement initState
super.initState();
__eventChannel.receiveBroadcastStream().listen(eventListener);
}
@override
Widget build(BuildContext context) {
return Center(
child: Text("EventChanne 接收的Android数据:$_receiveData"),
);
}
void eventListener(event) {
print("收到的数据:$event");
setState(() {
_receiveData = event;
});
}
}
网友评论