Upgrade rongcloud_im_wrapper_plugin: Compatibility with legacy custom messages, onDataReceived, and conversationDigest

Starting from Flutter IM SDK version 5.2.4, you’ll need to upgrade to the new SDK (rongcloud_im_wrapper_plugin).

This guide explains how to manage features from the legacy SDK that cannot be directly replaced.

How to support legacy custom messages

The new SDK (rongcloud_im_wrapper_plugin) has redesigned the implementation of custom message types.

If you’ve upgraded to rongcloud_im_wrapper_plugin but need to maintain compatibility with legacy custom messages, handle this in the Dart, Android, and iOS layers.

:warning: Note: You’ll need to write native layer code. Handling only the Dart layer won’t support custom messages!

Refer to the example for detailed code.

Dart layer handling

  1. Define the Dart layer message content based on your custom message. Refer to example/lib/custom_message/poke_message.dart.

    Import headers:

    import 'package:rongcloud_im_wrapper_plugin/rongcloud_im_wrapper_plugin.dart';
    import 'dart:convert' as json_lib show json;
    

    Define the message class:

    • Your custom message class must extend RCIMIWUserCustomMessage.

    • Define a fromJson constructor and call super.fromJson.

      RCIMDPokeMessage.fromJson(Map<String, dynamic> json) : super.fromJson(json);
      
    • Define your own constructor and call the parent class RCIMIWUserCustomMessage(RCIMIWConversationType type, String targetId).

      RCIMDPokeMessage(RCIMIWConversationType type, String targetId, this.pokeMessage) : super(type, targetId);
      
    • Implement the parent class’s decode and encode methods.

      @override
      void decode(String jsonStr) {
          Map map = json_lib.json.decode(jsonStr.toString());
          // Ensure the key matches the native layer’s key
          pokeMessage = map['content'];
      }
      
      @override
      String encode() {
          Map map = {};
          // Ensure the key matches the native layer’s key
          map['content'] = pokeMessage;
          return json_lib.json.encode(map);
      }
      
    • Implement the parent class’s messageObjectName to return the objectName. Ensure the objectName matches the native layer exactly.

      @override
      String messageObjectName() {
          return "ST:PokeMsg";
      }
      
    • Implement the parent class’s toJson method, create a Map object, and set the 'content' value to the return value of the encode method.

      final Map<String, dynamic> json = super.toJson();
      // Do not modify 'content'
      json['content'] = encode();
      return json;
      
  2. Register the custom message:

    Call the engine’s registerCustomMessage method to register. Modify only the objectName and custom message class.

    e.registerCustomMessage('ST:PokeMsg', (json){
        RCIMDPokeMessage pokeMsg = RCIMDPokeMessage.fromJson(json);
        // Do not modify 'content'
        pokeMsg.decode(json['content']);
        return pokeMsg;
    });
    

Android layer handling

  1. Add the RC native SDK to your project, ensuring the version matches the one in the Flutter SDK. Check the version in android/build.gradle.

  2. Place the native custom message file in your project and register it in MainActivity before initializing the Dart layer.

    import cn.rongcloud.im.wrapper.flutter.RCIMWrapperEngine;
    
    List<Class<? extends MessageContent>> list = new ArrayList<>();
    list.add(PokeMessage.class);
    RCIMWrapperEngine.getInstance().messageContentClassList = list;
    

iOS layer handling

  1. Import the header file:

    #import <rongcloud_im_wrapper_plugin/RCIMWrapperEngine.h>
    
  2. Add the native custom message to your iOS project and register it in AppDelegate before initializing the Dart layer.

    NSMutableArray *marr = [NSMutableArray arrayWithObject:[RCDPokeMessage class]];
    [RCIMWrapperEngine sharedInstance].messageContentClassList = marr.copy;
    

How to implement the legacy conversationDigest feature

With the new custom message optimizations, developers no longer need to handle this in the native layer. To better extend custom message functionality, developers must define their own conversationDigest logic.

Example:

String conversationDigest(RCIMIWMessage message) {
    switch (message.messageType) {
        case RCIMIWMessageType.text:
            return "text";
        case RCIMIWMessageType.voice:
            return "[Voice]";
        case RCIMIWMessageType.userCustom:
            return "[Custom message]";
        case RCIMIWMessageType.command:
            return "[System message]";
    }
}

How to implement the legacy onDataReceived feature

Since onDataReceived isn’t a standard SDK feature, the new SDK no longer supports it. If you need this functionality, refer to the legacy SDK implementation:

  1. Define the onDataReceived method in the Dart plugin layer:

    /// Callback for receiving native data
    ///
    /// [data] The data content
    ///
    /// For push content, use this in main.dart
    static Function(Map? data)? onDataReceived;
    
  2. Handle the channel callback in setMethodCallHandler:

    static void _addNativeMethodCallHandler() {
        _channel.setMethodCallHandler(_methodCallHandler);
    }
    
    static Future<dynamic> _methodCallHandler(MethodCall call) async {
        switch (call.method) {
            case "SendDataToFlutterCallBack":
                if (onDataReceived != null) {
                    Map? map = call.arguments;
                    onDataReceived!(map);
                }
                break;
        }
    }
    
  3. Call the method in the iOS/Android layer.

    Android:

    public void sendDataToFlutter(final Map map) {
        if (map == null) {
            return;
        }
        RCLog.i("sendDataToFlutter start param:" + map.toString());
        mMainHandler.post(new Runnable() {
            @Override
            public void run() {
                mChannel.invokeMethod("SendDataToFlutterCallBack", map);
            }
        });
    }
    

    iOS:

    - (void)sendDataToFlutter:(NSDictionary *)userInfo {
        NSString *LOG_TAG = @"sendDataToFlutter";
        [RCLog i:[NSString stringWithFormat:@"%@,start param:%@",LOG_TAG,userInfo]];
        [self.channel invokeMethod:@"SendDataToFlutterCallBack" arguments:userInfo];
    }