Flutter与Native如何对话?

概念参考Flutter中文网

Flutter与Native间通信,是通过平台通道向Native(Android/IOS)发送消息.Navtive通过平台通道接收到消息,然后Native处理消息,然后将响应消息返回给Flutter.这样就完成了Flutter与Native的一次对话.

可以想象为:通俗的来讲就是Flutter拨打Native的电话号码,平台通道就相当于基站(BTS),通过电话号码向Native发送信号进行呼叫,呼叫成功后Native接通电话,然后Flutter可以向Native指定定义好的消息,Navtive给出响应.响应完毕后自动挂断.完成一次通话.

消息和响应是以异步.

既然是传递消息肯定有类型限制,不能什么消息都传递. 可以传递的消息类型如下:

Dart Android iOS
null null nil (NSNull when nested)
bool java.lang.Boolean NSNumber numberWithBool:
int java.lang.Integer NSNumber numberWithInt:
int, if 32 bits not enough java.lang.Long NSNumber numberWithLong:
double java.lang.Double NSNumber numberWithDouble:
String java.lang.String NSString
Uint8List byte[] FlutterStandardTypedData typedDataWithBytes:
Int32List int[] FlutterStandardTypedData typedDataWithInt32:
Int64List long[] FlutterStandardTypedData typedDataWithInt64:
Float64List double[] FlutterStandardTypedData typedDataWithFloat64:
List java.util.ArrayList NSArray
Map java.util.HashMap NSDictionary

当你发送和接收值时,会自动进行序列化和反序列化

如何实现通信?

本篇以Android为例实现一个语音识别的插件(使用百度语音的SDK)

Flutter要与Navtive通信,就需要构建通道(MethodChannel),构建通道完毕后,还需要知道一个电话号码(与Native定义好的一个唯一的通道名,电话号码也是唯一的)比如“asr_plugin”,这是唯一的通道名

class AsrPlugin {
  //通过MethodChannel 调用原生的方法
  static const MethodChannel _channel = const MethodChannel('asr_plugin');
}

上述代码,已经建立好通道了,并且尝试连接Native.要想连接Native,Native则要接受通道,接受的通道名就是Flutter定义好的通道名,这样才能与Native完整的建立通道

 public static void registerWith(Registrar registrar) {
        //确保通道名称一致
        final MethodChannel channel = new MethodChannel(registrar.messenger(), "asr_plugin");
    }

完成上述代码,Flutter已经连接上了Native,这时Flutter只要发送消息,就可以操作Native了.

代码如下,Flutter向Native发送消息,分别是识别语音、停止、取消、销毁

注意:需要将方法设置为异步的async 发送和接受响应以确保界面保持响应

static Future<String> start({Map params}) async {
    return _channel.invokeMethod("start", params ?? {});
  }

  /// 与native通信的方法 调用native 的停止录音
  static Future<String> stop() async {
    return _channel.invokeMethod("stop");
  }

  /// 与native通信的方法 调用native 的取消录音
  static Future<String> cancel() async {
    return _channel.invokeMethod("cancel");
  }

  static Future<String> release() async {
    return _channel.invokeMethod("release");
  }

Native如何接受消息呢? 需要实现MethodCallHandler接口,onMethodCall方法中可以接受到Flutter发送到消息.

final MethodChannel channel = new MethodChannel(registrar.messenger(), "asr_plugin");
//设置方法处理 MethodCallHandler MethodChannel 是flutter调用原生的方法
channel.setMethodCallHandler(this);
 @Override
    public void onMethodCall(MethodCall call, Result result) {
        //需要初始化权限
        initPermission();
        if (call.method.equals("getPlatformVersion")) {
            result.success("Android " + android.os.Build.VERSION.RELEASE);
        } else if (call.method.equals("start")) {
            start(call, result);
        } else if (call.method.equals("stop")) {
            stop(call, result);
        } else if (call.method.equals("cancel")) {
            cancel(call, result);
        } else if (call.method.equals("release")) {
            release(call, result);
        } else {
            result.notImplemented();
        }
    }

还有一个问题,如果将Native识别到语音.返回给Flutter呢? onMethodCall(MethodCall call, Result result)在onMethodCall方法中有一个Result对象,通过Result返回给Flutter

成功的返回mResult.success(o);

失败的返回 mResult.error(s, s1, o);

Flutter处理响应:

AsrPlugin.start().then((text) {
      print("------------start:" + text);
      if (text != null && text.length > 0) {
        setState(() {
          _result = text;
        });
      }
    }).catchError((e) {
      print("------------catch:" + e.toString());
    });

实现结果如下,已经识别出了语音.

实现结果

Native如何集成Flutter?

  • 创建flutter module

  • 添加flutter module依赖

  • Java/object-c 调用flutter module

  • flutter 作为原生开发的独立的页面

  • flutter 作为原生页面的一部分

Add Flutter to existing apps 此处为官方的文档,解释的非常详细,如果英语好的话可以直接看官方的文档.

这里简单介绍一下集成的步骤

通过命令行的方式创建 Flutter module

> flutter create -t module flutter_module

运行完上面的命令就会生成一个名字为flutter_module的module项目,然后将这个module放到原生项目同级的目录下.

原生项目的配置:

setting.gradle 添加如下代码

setBinding(new Binding([gradle: this])) // newevaluate(new File( // new 

settingsDir.parentFile, // new 

'flutter_hybrid/.android/include_flutter.groovy' // new 

)) 

注意flutter_hybrid需要改成你用命令创建的module的名字如:flutter_module

app.gradle中配置如下

android { 

...... 

compileOptions { 

sourceCompatibility 1.8 

targetCompatibility 1.8 

} 

} 

dependencies { 

implementation project(':flutter') 

} 

build 一下项目就将Flutter module 集成进来了

打开Flutter module

需要修改 main.dart 定义好跳转的Route页面

import 'dart:ui'; 

void main() => runApp(_widgetForRoute(window.defaultRouteName)); 

Widget _widgetForRoute(String route) { 

switch (route) { 

case 'list': 

return _listWidget(); 

case 'item': 

return _itemWidget(); 

case 'page': 

return _pageWidget(); 

} 

} 

Widget _listWidget() { 

return Container( 

child: ListView.builder( 

itemBuilder: (context, index) { 

return _itemWidget(); 

}, 

itemCount: 10, 

), 

); 

} 

Widget _itemWidget() { 

return Container( 

child: Text(//text 必须要有 textDirection TextDirection.ltr 否则会报错 

'This is Flutter route item attach new', 

textDirection: TextDirection.ltr, 

), 

color: Color(0x00fff122), 

); 

} 

Widget _pageWidget() { 

return MyApp(); 

} 


原生项目中如何调用flutter页面?

  • 将flutter页面作为某个view的一部分 ,通过定义好的Route名称来加载某个widget,同时需要将lifecycle生命周期传递进去
val createView = Flutter.createView(this, lifecycle, "item") 

fl_load.addView(createView) 
  • 将flutter页面作为独立的页面
val beginTransaction = supportFragmentManager.beginTransaction() 

beginTransaction.replace(R.id.fl_only, Flutter.createFragment("page")) 

beginTransaction.commit() 

如何如何调试和热重载flutter module

如何热重载flutter module?

当打开flutter module时,并没有运行在当前的module,而是运行在原生的项目中,如果改动了module中的UI 如何热重载呢?在上面的官方文档中同样有详细的讲解

在当前的module中,点击Terminal 输入如下命令 ,就可以实现flutter module的热重载

flutter attach

本篇文章主要讲解了flutter与native通信和flutter module作为native的部分界面