Flutter项目WanWan之接入融云IM和自定义Plugin

Posted 白玉梁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter项目WanWan之接入融云IM和自定义Plugin相关的知识,希望对你有一定的参考价值。

项目地址 GitHub:https://github.com/baiyuliang/Flutter-WanWan

融云IM-Flutter文档地址:https://docs.rongcloud.cn/v4/5X/views/im/noui/guide/quick/include/flutter.html

集成步骤:

注意:需要先去融云官网注册账号,并创建应用,获取应用appid和appkey!

  • 引入SDK:
rongcloud_im_plugin: ^5.1.3
  • 初始化:
 RongIMClient.init(Url.RONG_IM_KEY);
  • 连接服务器:
RongIMClient.connect(token, (int code, String userId) {
  if (code == 0) {
  }
});
  • 设置监听:
 RongIMClient.onConnectionStatusChange = (int connectionStatus) {}RongIMClient.onMessageReceived = (Message msg, int left) {}
  • 获取会话列表:
RongIMClient.getConversationListByPage([
      RCConversationType.Private,
      RCConversationType.Group,
      RCConversationType.System
    ], 999, 0);
  • 发送消息:
 TextMessage txtMessage = new TextMessage();
    txtMessage.content = "";
    txtMessage.extra = "";
    Message msg = await RongIMClient.sendMessage(RCConversationType.Private, targetId, txtMessage);

消息有多种类型,具体可参考文档!

由于融云的登录,依赖于服务器生成的token,如要正常使用需要自己写后台,这对于完全移动端或前端开发者来说可能无法完成,那么重点来了,本项目中引入了服务端java的serverSDK,放在了android项目中libs,来模拟服务端的注册以及其它相关请求!


这里有一个技巧,我们在同时开发flutter端和native端(这里已android为例)时,可以分别打开flutter项目和flutter项目的android部分:

flutter:

android:

此时,我们就可以在android项目中,完成服务端相关功能,如注册方法:

       val map = HashMap<String, Any>()
       val id = call.argument<String>("id")!!.toString()
       val name = call.argument<String>("name")!!.toString()
       val avatar = call.argument<String>("avatar")!!.toString()
       val user: User = rongCloud.user
       val userModel = UserModel()
           .setId(id)
           .setName(name)
           .setPortrait(avatar)
       Thread {
           try {
               val tokenResult = user.register(userModel)
               if (!TextUtils.isEmpty(tokenResult.token)) {
                   map["code"] = 200
                   map["token"] = tokenResult.token
                   map["error"] = "注册成功"
                   Log.e("RongIMRegPlugin", "注册成功:  $tokenResult")
               } else {
                   map["code"] = -1
                   map["error"] = "注册失败"
               }
           } catch (e: Exception) {
               e.printStackTrace()
               map["code"] = -1
               map["error"] = e.message.toString()
           }
           activity.runOnUiThread {
               result.success(map)
           }
       }.start()

那么问题又来了,如何实现flutter与本地通信?

教大家一个最简单的办法:https://www.baidu.com …此处省略一万字!

开个玩笑!其实很简单,要善于发现,比葫芦画瓢,相信我们在开发过程中,或多或少的引入了其他的第三方库,我们可以打开参观一下他们的Plugin.java,看实现过程,随便打开一个,以为SharedPreferences例:

/** SharedPreferencesPlugin */
public class SharedPreferencesPlugin implements FlutterPlugin {
  private static final String CHANNEL_NAME = "plugins.flutter.io/shared_preferences";
  private MethodChannel channel;
  private MethodCallHandlerImpl handler;

  @SuppressWarnings("deprecation")
  public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) {
    final SharedPreferencesPlugin plugin = new SharedPreferencesPlugin();
    plugin.setupChannel(registrar.messenger(), registrar.context());
  }

  @Override
  public void onAttachedToEngine(FlutterPlugin.FlutterPluginBinding binding) {
    setupChannel(binding.getBinaryMessenger(), binding.getApplicationContext());
  }

  @Override
  public void onDetachedFromEngine(FlutterPlugin.FlutterPluginBinding binding) {
    teardownChannel();
  }

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

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

显然,该类继承了FlutterPlugin,并重写了两个方法:onAttachedToEngine和onDetachedFromEngine,
在获取MethodChannel实例后:

channel = new MethodChannel(messenger, CHANNEL_NAME);

实现了MethodCallHandler接口,

handler = new MethodCallHandlerImpl(context);
channel.setMethodCallHandler(handler)

再来看MethodCallHandlerImpl:

class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
  @Override
  public void onMethodCall(MethodCall call, MethodChannel.Result result) {
    String key = call.argument("key");
    try {
      switch (call.method) {
        case "setBool":
          commitAsync(preferences.edit().putBoolean(key, (boolean) call.argument("value")), result);
          break;
          ......
}

以上只贴了部分代码,流程已经很清楚了,比葫芦画瓢开始:

  • 步骤一:创建RongIMServerPlugin,继承FlutterPlugin:
class RongIMServerPlugin() : FlutterPlugin{

    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
       
    }
    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
       
    }
}
  • 步骤二:获取MethodChannel实例:
private var channel: MethodChannel? = null

override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
     channel = MethodChannel(binding.binaryMessenger, "rongim/server")
 }

注意,channel的name可以自定义,我将其定义为**“rongim/server”**,flutter在调用时要保持一致!

  • 步骤三:实现MethodChannel.MethodCallHandler接口:
class RongIMServerPlugin() : FlutterPlugin, MethodChannel.MethodCallHandler {

    private var channel: MethodChannel? = null

    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel = MethodChannel(binding.binaryMessenger, "rongim/server")
        channel!!.setMethodCallHandler(this)
    }

    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel!!.setMethodCallHandler(null)
        channel = null
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
       
    }

}

其实我们并不一定要像SharedPreferences中一样,单独写一个MethodCallHandlerImpl类,我们可以像实现View.OnClickListener一样用setMethodCallHandler(this)直接在主类中实现!

  • 步骤四:重写onMethodCall方法,并根据call.method方法名,判断不同调用方法,执行不同操作:
    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        when (call.method) {
            "register" -> {
              
            }
            "user" -> {
         
            }
        }
    }

如果,方法中需要用到context上下文,我们可以在构造方法中引入:

class RongIMServerPlugin(activity: Activity) : FlutterPlugin{
      private var activity: Activity = activity
}

Plugin书写完毕后,在MainActivity中注册:

class MainActivity: FlutterActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        flutterEngine!!.plugins.add(RongIMServerPlugin(this))
    }
}

如果你不明白为什么这么写,可以打开:

flutter端调用:

  static Future<Map> register(String id, String name, String avatar) async {
    var methodChannel = const MethodChannel("rongim/server");
    Map<String, dynamic> params = <String, dynamic>{
      "id": id,
      "name": name,
      "avatar": avatar
    };
    var result = await methodChannel.invokeMethod("register", params);
    return result;
  }

参数以map形式传递,android端onMethodCall方法接收参数:

 val id = call.argument<String>("id")!!.toString()
 val name = call.argument<String>("name")!!.toString()
 val avatar = call.argument<String>("avatar")!!.toString()

详细代码,可参考项目:https://github.com/baiyuliang/Flutter-WanWan

以上是关于Flutter项目WanWan之接入融云IM和自定义Plugin的主要内容,如果未能解决你的问题,请参考以下文章

Flutter项目WanWan之数据库moor

Flutter项目WanWan之数据库moor

iOS即时通讯SDK中,腾讯,网易,环信,融云IM SDK对比,哪个更好

基于融云的IM通讯

融云 Flutter SDK,跨平台开发的真香之选

集成融云 IM 问题总结