在Flutter中,Platform Channel允许Flutter与原生平台(如Android和iOS)之间进行双向通信,以便在Flutter应用程序和原生代码之间传递消息和调用功能。
以下是使用Platform Channel与原生通信的一般步骤:
1. 在Flutter端创建一个MethodChannel对象,用于发送消息给原生平台。通常在Flutter Widget的初始化方法中创建MethodChannel。
import 'package:flutter/services.dart';
// 创建MethodChannel对象
MethodChannel _channel = MethodChannel('com.example.channelName');
2. 在Flutter端发送消息给原生平台,可以使用MethodChannel的invokeMethod方法。
// 发送消息给原生平台
dynamic result = await _channel.invokeMethod('methodName', arguments);
3. 在原生平台(如Android和iOS)实现对应的方法,用于接收Flutter端发送的消息。
对于Android平台,可以在MainActivity
或Application
类中注册MethodChannel,并实现对应的方法。
java
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example.channelName";
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler((call, result) -> {
if (call.method.equals("methodName")) {
// 处理Flutter端发送的消息
String arg = call.arguments.toString();
// 执行相应的操作
// ...
// 将结果返回给Flutter端
result.success("Result from native");
} else {
result.notImplemented();
}
});
}
}
对于iOS平台,在AppDelegate.m文件中注册MethodChannel,并实现对应的方法。
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
FlutterViewController *controller = (FlutterViewController *)self.window.rootViewController;
FlutterMethodChannel *channel = [FlutterMethodChannel
methodChannelWithName:@"com.example.channelName"
binaryMessenger:controller.binaryMessenger];
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"methodName" isEqualToString:call.method]) {
// 处理Flutter端发送的消息
NSString *arg = call.arguments;
// 执行相应的操作
// ...
// 将结果返回给Flutter端
result(@"Result from native");
} else {
result(FlutterMethodNotImplemented);
}
}];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
通过以上步骤,你可以在Flutter端和原生代码之间进行双向通信。
以下是我项目初期的调研代码块:
flutter的代码:
class GetxStatePage extends StatefulWidget {
const GetxStatePage({super.key});
@override
State<StatefulWidget> createState() {
return GetxStatePageState();
}
}
class GetxStatePageState extends State<GetxStatePage> {
late MethodChannel _channel;
@override
void initState() {
super.initState();
//初始化MethodChannel,通道名称"multiple-flutters",Android端ios也要统一
_channel = const MethodChannel('multiple-flutters');
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: SGColors.white,
title: const Text(
'Getx',
style: TextStyle(color: SGColors.textColor),
),
),
body: Center(
child: InkWell(
onTap: () {
String value = "Hello from Flutter";
//这里定义方法和参数
_channel.invokeMethod<void>("nextData", {'data': value});
// 在此处添加您希望执行的点击事件
print('跳转到Android');
},
child: SizedBox(
width: 100,
height: 100,
child: Text("Getx"),
),
),
),
),
);
}
}
我们再看看Android端的实现代码:首先打通通道需要engine,channel,与flutter端的统一就行;
class EngineBindings(
activity: Activity, delegate: EngineBindingsDelegate, entrypoint: String,
initialRoute: String) :
DataModelObserver {
val channel: MethodChannel
val engine: FlutterEngine
val delegate: EngineBindingsDelegate
init {
// This has to be lazy to avoid creation before the FlutterEngineGroup.
val dartEntrypoint =
DartExecutor.DartEntrypoint(
FlutterInjector.instance().flutterLoader().findAppBundlePath(), entrypoint
)
// engine = BaseApplication.getApplication().engines.createAndRunEngine(activity, dartEntrypoint)
engine = BaseApplication.getApplication().engines.createAndRunEngine(activity, dartEntrypoint, initialRoute)
this.delegate = delegate
channel = MethodChannel(engine.dartExecutor.binaryMessenger, "multiple-flutters")
}
/**
* This setups the messaging connections on the platform channel and the DataModel.
*/
fun attach() {
DataModel.instance.addObserver(this)
channel.invokeMethod("setCount", DataModel.instance.counter)
channel.setMethodCallHandler { call, result ->
when (call.method) {
"incrementCount" -> {
DataModel.instance.counter = DataModel.instance.counter + 1
result.success(null)
}
"next" -> {
this.delegate.onNext()
result.success(null)
}
"nextData" -> {
val data: String? = call.argument("data")
this.delegate.onNext(data)
result.success(null)
}
else -> {
result.notImplemented()
}
}
}
}
/**
* This tears down the messaging connections on the platform channel and the DataModel.
*/
fun detach() {
engine.destroy();
DataModel.instance.removeObserver(this)
channel.setMethodCallHandler(null)
}
override fun onCountUpdate(newCount: Int) {
channel.invokeMethod("setCount", newCount)
}
}
具体的activity必须继承FlutterActivity,且这个类必须在主配置清单AndroidMainfest.xml中配置:
<activity
android:name=".ui.demo.SingleFlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" />
class FlutterToAndroidActivity : FlutterActivity(), EngineBindingsDelegate {
var mFlutterApi: SGAndroid2Flutter? = null
private val engineBindings: EngineBindings by lazy {
EngineBindings(activity = this, delegate = this, entrypoint = FlutterRouter.MESSAGE_CENTER_ENTRY_POINTER, initialRoute = "${FlutterRouter.MESSAGE_CENTER_ROUTER}?psId=1234")
}
override fun onDestroy() {
super.onDestroy()
engineBindings.detach()
}
override fun provideFlutterEngine(context: Context): FlutterEngine? {
return engineBindings.engine
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
engineBindings.attach()
setContentView(layout.activity_flutter_to_android)
val extraString = intent.getStringExtra("extraString")
tv_center.text = extraString
val binaryMessenger: BinaryMessenger = provideFlutterEngine(this)!!.getDartExecutor().getBinaryMessenger()
mFlutterApi = SGAndroid2Flutter(binaryMessenger)
tv_center.setOnClickListener {
callFlutterMethod()
}
}
private fun callFlutterMethod() {
val handler = Handler(Looper.getMainLooper())
(0..2)
.map { it.toLong() * 100 }
.forEach {
handler.postDelayed(it) {
mFlutterApi?.sendData(it.toString()) { // 必须在主线程中调用
println("从 Flutter 获取到的值是:$it ") // true,回调在主线程
}
}
}
}
override fun onNext() {
}
override fun onNext(str: String?) {
}
}
就可以在onNext的方法中调用我们的操作;
/**
* flutter 调用原生的方法,原生写法
* */
override fun onNext(str: String?) {
val flutterIntent = Intent(this, FlutterToAndroidActivity::class.java)
flutterIntent.putExtra("extraString", str)
startActivity(flutterIntent)
}
最后执行,flutter跳转FlutterToAndroidActivity成功,flutter成功调用原生Android的方法。
体会:
之前一直使用纯flutter开发,当需要原生加flutter开发的时候,其实成本更大了。
他既需要Android写好桥接代码,又需要ios写一套,flutter端的是一套。
为什么会这样?因为船大难掉头,一些大的现有项目没办法短时间改技术栈,一些公司会拿出一部分试试水。部分代码迁移。为以后的项目打基础。
遇到些特殊的需求,会更麻烦,比如我们这种做法:需要在原生端获取网络数据,去请求接口,然后把数据传递给flutter,而一开始请求哪个接口,传哪些参数又需要flutter传给原生,Android和ios两端去实现。
所以在项目中我们用到了pigeon的插件,这样就不用写太多的代码,通信的过程pigeon会帮你自动生成,可以看我下一篇文章实践!