从零开始向原生项目集成Flutter以及通信

Flutter最近比较火,很多大厂小厂都在搞,网上的教程也很多,但是很多教程其实是不全面的,也有很多其实都是互相copy的。尤其是关于原生集成Flutter并且实现相互通信这一块。笔者今天就来从零开始,与各位读者,一起实现一个原生项目集成Flutter,并实现相互通信。
#0 安装Flutter环境
这个很简单,教程很多,跟着官方文档来就行了,笔者就不多说了,浪费大家时间。
#1 新建Flutter Module
这里笔者建议使用Android Studio。打开AS,File->New->New Flutter Project,如图:
image.png
接下来,选择Flutter Module,点击Next
image.png
输入项目名称以及项目位置等信息,然后点击Finish:
image.png
OK,很简单,一个Flutter Module就建好了。
#2 集成进现有项目

iOS

在podfile里顶层添加

flutter_application_path = '../path/to/your/flutter/module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
复制代码

然后在如下位置添加

target `Your target` do 
  ...
  install_all_flutter_pods(flutter_application_path)
end
复制代码

接下来进入到项目目录中,pod install一下,就可以了。
这里可以参照官方文档(选项 A)

注意:如果build报错了,先打开flutter项目,在对应iOS机型下面跑一遍项目,然后回到iOS,重新build就可以了

Android

打开现有项目,点击File->New->New Module:
image.png
选择Import Flutter Module,在输入框中选择刚才新建的flutter module路径:
image.png
点击finish,就集成好了。
接下来,需要在manifest里面配置一下:

        <activity
            android:name="io.flutter.embedding.android.FlutterActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:theme="@style/Theme.Flutter_native_android.NoActionBar"
            android:windowSoftInputMode="adjustResize" />
复制代码

#3 显示Flutter界面

iOS

在主页面,或者需要的地方,引入FlutterPluginRegistrant, 并添加如下代码:

    lazy var flutterEngine: FlutterEngine = {
        let flutterEngine = FlutterEngine(name: "Your Engin Name")
        flutterEngine.run()
        GeneratedPluginRegistrant.register(with: flutterEngine)
        if let registerer = flutterEngine.registrar(forPlugin: "FlutterNativePlugin") {
            FlutterNativePlugin.register(with: registerer)
        }
        return flutterEngine
    }()
复制代码

其中”FlutterNativePlugin”实际是后面会创建的用来进行通信的类名,暂时先放着。
接下来创建FlutterViewController:

        let flutterVC = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
        let splashScreenView = UIView()
        splashScreenView.backgroundColor = .white
        flutterVC.splashScreenView = splashScreenView
复制代码

这里可以通过一个属性来持有,也可以在需要的时候再创建,根据需求来定。至于splashScreenView实际是设置加载时的过渡页面,默认是项目的launchscreen,也可以根据具体的需求来修改。
在需要进入Flutter页面的地方,调用push或者present:

        navigationController?.pushViewController(flutterVC, animated: true)
复制代码

接下来,就可以进入Flutter页面了。

Android

Android 也很简单,在需要的地方(比如MainActivity的onCreate里)创建Flutter相关实例对象

        flutterEngine = new FlutterEngine(this);
        flutterEngine.getDartExecutor().executeDartEntrypoint(
                DartExecutor.DartEntrypoint.createDefault()
        );
        FlutterEngineCache
                .getInstance()
                .put("my_engine_id", flutterEngine);
复制代码

在按钮的点击事件中跳转:

startActivity(
        FlutterActivity.withCachedEngine("my_engine_id").build(activity)
);
复制代码

build的参数传入当前activity就可以了。
这里采取的也是官方建议的使用缓存的 FlutterEngine进行加载的方案

#4 相互通信

##flutter
我们先实现flutter部分,这样,后面iOS和Android写好后就可以直接看效果了。
新建一个类,比如叫NativeCaller:

import 'package:flutter/services.dart';
import 'package:flutter_module/Networking/token_manager.dart';
class NativeCaller {
  static const MethodChannel _channel = const MethodChannel("your_channel_name");

  static init() {
    _channel.setMethodCallHandler((call) => handleMethodCall(call));
  }

  static Future<String> greeting(String content) async {
    return await _channel.invokeMethod("greeting", content);
  }

  static Future route(String url) async {
    return await _channel.invokeMethod("route", url);
  }

  static Future goBack() async {
    return await _channel.invokeMethod("goBack");
  }

  static Future handleMethodCall(MethodCall call) async {
    print("Handle method call:${call.method} with arguments:${call.arguments}");
    return Future.value(true);
  }
}

复制代码

这个类持有了一个MethodChannel类型的静态变量,注意MethodChannel初始化方法的参数要和后面原生的保持一致。
其中的greeting,route,goBack三个方法用于调用原生对应方法,返回的future则会通过回调的方式给到原生。
handleMethodCall用于接收原生的调用,需要注意这边:

  static init() {
    _channel.setMethodCallHandler((call) => handleMethodCall(call));
  }
复制代码

需要调用这个方法之后才能开始接收原生的调用,建议写在main里面,如:

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  NativeCaller.init();
  runApp(MyApp());
}
复制代码

iOS

新建一个类,实现FlutterPlugin协议:

class FlutterNativePlugin: NSObject {
        
}

extension FlutterNativePlugin: FlutterPlugin {
    
    static var registrar: FlutterPluginRegistrar?
    static var methodChannel: FlutterMethodChannel?
    
    static func register(with registrar: FlutterPluginRegistrar) {
        self.registrar = registrar
        methodChannel = FlutterMethodChannel(name: "your_channel_name", binaryMessenger: registrar.messenger());
        let plugin = FlutterNativePlugin()
        registrar.addMethodCallDelegate(plugin, channel: methodChannel!)
    }
    
    func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        log.info("Flutter call:\(call.method), params:\(String(describing: call.arguments))");
    }

    static func sendMethod(_ method: SendMethod, with params: Any?, completion: FlutterResult? = nil) {
        log.info("Sending method:\(method), with params:\(String(describing: params))")
        guard let methodChannel = methodChannel else { return }
        methodChannel.invokeMethod(method.rawValue, arguments: params, result: completion)
    }

}
复制代码

其中,handle方法用于接收flutter端的调用,并通过result回调结果给flutter;
sendMethod方法是笔者自定义的方法,用于调用flutter的方法。

接着,在创建FltuterEngine的地方,把这个FlutterIosPlugin注册进去,这样它才能开始接收调用。

        if let registerer = flutterEngine.registrar(forPlugin: "FlutterNativePlugin") {
            FlutterNativePlugin.register(with: registerer)
        }

复制代码

当然,这两行我们在一开始已经写过了,这里只是为了强调一下,必须有这两行,FlutterNativePlugin才能开始工作。

现在,在适当的地方调用FlutterNativePlugin.sendMethod(_ method: SendMethod, with params: Any?, completion: FlutterResult? = nil) 方法,flutter就可以接收到了,同样,在flutter适当的地方调用NativeCaller的对应方法,也可以成功调用到iOS端来了。

Android

Android需要新建两个类:

public class FlutterAndroidPlugin implements FlutterPlugin {

    private static final String CHANNEL_NAME = "flutter.figure";
    private MethodChannel channel;
    private FlutterAndroidCallHandler handler;
    @Override
    public void onAttachedToEngine(@NonNull @NotNull FlutterPluginBinding binding) {
        setupChannel(binding.getBinaryMessenger(), binding.getApplicationContext());
    }

    @Override
    public void onDetachedFromEngine(@NonNull @NotNull FlutterPluginBinding binding) {
        teardownChannel();
    }

    private void setupChannel(BinaryMessenger messenger, Context context) {
        channel = new MethodChannel(messenger, CHANNEL_NAME);
        handler = new FlutterAndroidCallHandler();
        channel.setMethodCallHandler(handler);
    }

    private void teardownChannel() {
        handler = null;
        channel.setMethodCallHandler(null);
        channel = null;
    }

    public void callMethod(@NonNull  String method, @Nullable Object params) {
        channel.invokeMethod(method, params, new MethodChannel.Result() {
            @Override
            public void success(@Nullable @org.jetbrains.annotations.Nullable Object result) {
                Log.d("Plugin", "Success:" + result);
            }

            @Override
            public void error(String errorCode, @Nullable @org.jetbrains.annotations.Nullable String errorMessage, @Nullable @org.jetbrains.annotations.Nullable Object errorDetails) {
                Log.e("Plugin", "Error:" + errorMessage);
            }

            @Override
            public void notImplemented() {
                Log.e("Plugin", "Not implemented!");
            }
        });
    }

}

public class FlutterAndroidCallHandler implements MethodChannel.MethodCallHandler {

    @Override
    public void onMethodCall(@NonNull @NotNull MethodCall call, @NonNull @NotNull MethodChannel.Result result) {
        Log.d("Plugin", "Method:" + call.method + "Params:" + call.arguments);
    }


}
复制代码

然后,在创建FlutterEngin的地方,比如上面提到的MainActivity的onCreate中,将 这个plugin注册起来:

        FlutterAndroidPlugin plugin = new FlutterAndroidPlugin();
        flutterEngine.getPlugins().add(plugin);
复制代码

之后,在适当的地方调用

plugin.callMethod("greeting", "Hello Flutter");
复制代码

在flutter端就可以收到了,同时,flutter端的调用也会在FlutterAndroidCallHandler.onMethodCall中接收到。

Demo 在这里:Demo

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享