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

相关推荐
大耳猫4 小时前
主动测量View的宽高
android·ui
帅次6 小时前
Android CoordinatorLayout:打造高效交互界面的利器
android·gradle·android studio·rxjava·android jetpack·androidx·appcompat
枯骨成佛7 小时前
Android中Crash Debug技巧
android
iFlyCai8 小时前
Xcode 16 pod init失败的解决方案
ios·xcode·swift
sunly_10 小时前
Flutter:父组件,向子组件传值,子组件向二级页面传值
flutter
kim565912 小时前
android studio 更改gradle版本方法(备忘)
android·ide·gradle·android studio
咸芝麻鱼12 小时前
Android Studio | 最新版本配置要求高,JDK运行环境不适配,导致无法启动App
android·ide·android studio
无所谓จุ๊บ12 小时前
Android Studio使用c++编写
android·c++
csucoderlee13 小时前
Android Studio的新界面New UI,怎么切换回老界面
android·ui·android studio
kim565913 小时前
各版本android studio下载地址
android·ide·android studio