Flutter uses Chanel to communicate with native

In Flutter, Platform Channel allows two-way communication between Flutter and native platforms (such as Android and iOS) to pass messages and call functions between Flutter applications and native code.

The following are the general steps to use Platform Channel to communicate with native:

1. Create a MethodChannel object on the Flutter side to send messages to the native platform. MethodChannel is usually created in the initialization method of Flutter Widget.

import 'package:flutter/services.dart';

// 创建MethodChannel对象
MethodChannel _channel = MethodChannel('com.example.channelName');

2. To send a message to the native platform on the Flutter side, you can use the invokeMethod method of MethodChannel.

// 发送消息给原生平台
dynamic result = await _channel.invokeMethod('methodName', arguments);

3. Implement corresponding methods on native platforms (such as Android and iOS) to receive messages sent by Flutter.

For the Android platform, you can register MethodChannel in MainActivityor Applicationclass and implement the corresponding method.

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();
                    }
                });
    }
}

For the iOS platform, register MethodChannel in the AppDelegate.m file and implement the corresponding method.

#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];
}

Through the above steps, you can perform two-way communication between the Flutter side and the native code.

The following is a research code block from the beginning of my project:

The code of 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"),
            ),
          ),
        ),
      ),
    );
  }
}

Let's take a look at the implementation code on the Android side: firstly, to open the channel, you need the engine, channel, and the unification with the flutter side;

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)
    }

}

The specific activity must inherit FlutterActivity, and this class must be configured in the main configuration list 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?) {
    
  }
}

Then we can call our operation in the onNext method;

/**
 * flutter 调用原生的方法,原生写法
 * */
 
override fun onNext(str: String?) {

  val flutterIntent = Intent(this, FlutterToAndroidActivity::class.java)
  flutterIntent.putExtra("extraString", str)
  startActivity(flutterIntent)
}

Finally, flutter jumps to FlutterToAndroidActivity successfully, and flutter successfully calls the native Android method.

experience:

I have been using pure flutter for development before. When native plus flutter development is required, the cost is actually greater.

He needs both Android to write the bridging code, and ios to write a set, and the flutter side is a set.

why? Because the ship is too big to turn around, some large existing projects cannot change the technology stack in a short time, and some companies will use part of it to test the waters. Partial code migration. Lay the foundation for future projects.

It will be more troublesome when encountering some special requirements. For example, we need to obtain network data on the native side, request the interface, and then pass the data to flutter, and which interface to request at the beginning, and which parameters need to be passed by flutter For native, Android and ios both ends to achieve.

So in the project we used the plugin of Pigeon, so that we don’t need to write too much code, Pigeon will automatically generate it for you during the communication process, you can see my next article for practice!

https://juejin.cn/post/7270861556031700992

Guess you like

Origin blog.csdn.net/qq_28563283/article/details/132524658