flutter使用Chanel与原生通信

在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平台,可以在MainActivityApplication类中注册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会帮你自动生成,可以看我下一篇文章实践!

https://juejin.cn/post/7270861556031700992

相关推荐
怀君3 小时前
Flutter——数据库Drift开发详细教程(四)
数据库·flutter
JhonKI3 小时前
【MySQL】存储引擎 - CSV详解
android·数据库·mysql
开开心心_Every3 小时前
手机隐私数据彻底删除工具:回收或弃用手机前防数据恢复
android·windows·python·搜索引擎·智能手机·pdf·音视频
yangpipi-4 小时前
10.王道_HTTP
网络协议·http·ios
大G哥4 小时前
Kotlin Lambda语法错误修复
android·java·开发语言·kotlin
鸿蒙布道师8 小时前
鸿蒙NEXT开发动画案例2
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
androidwork8 小时前
Kotlin Android工程Mock数据方法总结
android·开发语言·kotlin
I烟雨云渊T8 小时前
iOS 模块化开发流程
ios
刘小哈哈哈9 小时前
Lost connect to debugger on ‘iphone‘
ios·iphone
I烟雨云渊T9 小时前
iOS蓝牙技术实现及优化
macos·ios·cocoa