Skip to main content

Sending Messages

WKIM.shared.messageManager.sendMessage(WKTextContent('I am a text message'), WKChannel('uid_1', WKChannelType.personal));

Text Messages

// Define text message
WKTextContent text = WKTextContent("Hello, WuKong");
// Send text message
WKIM.shared.messageManager.sendMessage(text, channel);

Image Messages

// Define image message
WKImageContent image = WKImageContent(100, 100);
image.localPath = "xxx"; // Image local path
image.url = "http://xxx.com/xxx.jpg"
// Send image message
WKIM.shared.messageManager.sendMessage(image, channel);

Custom Messages

Reference custom messages: Custom Messages

Message Storage Callback (Not Message Send Status)

When sending messages, the SDK will trigger a storage callback after saving the message to the local database. At this point, the message has not been sent yet, and you can display the message in the UI in this listener. Listen for message storage events:
WKIM.shared.messageManager.addOnMsgInsertedListener((wkMsg) {
      // Display in UI
    });

New Messages

Listen for new message events:
// Listen for new message events
WKIM.shared.messageManager.addOnNewMsgListener('chat', (msgs) {
      // Display in UI
    });

// Remove new message listener
WKIM.shared.messageManager.removeNewMsgListener('chat');
If you receive new messages in a chat page, you need to determine whether the message belongs to the current conversation by checking the channelID and channelType of the message object WKMsg

Message Refresh Listening

When the SDK updates messages, such as: message send status, someone likes a message, message read receipt, message recall, message editing, etc., the SDK will callback the following event. The UI can determine which specific message has changed through the clientMsgNO of the message object WKMsg. Listen for refresh message events:
// Listen for refresh message events
WKIM.shared.messageManager.addOnRefreshMsgListener('chat', (wkMsg) {
      // TODO refresh message
    });

// Remove refresh message listener
WKIM.shared.messageManager.removeOnRefreshMsgListener('chat');

View Chat Information for a Channel

/*
    * Query or sync messages for a channel
    *
    * @param channelId                Channel ID
    * @param channelType              Channel type
    * @param oldestOrderSeq           Last message's large orderSeq, pass 0 for first entry into chat
    * @param contain                  Whether to include the oldestOrderSeq message
    * @param pullMode                 Pull mode 0: pull down 1: pull up
    * @param aroundMsgOrderSeq        Query messages around this message, e.g. aroundMsgOrderSeq=20 returns [16,17,19,20,21,22,23,24,25]
    * @param limit                    Number to get each time
    * @param iGetOrSyncHistoryMsgBack Request callback
    * @param syncBack                 Sync message callback, can show loading through this callback
    */
WKIM.shared.messageManager.getOrSyncHistoryMessages(
        channelID, channelType, oldestOrderSeq, contain, pullMode, limit, aroundMsgOrderSeq, Function(List<WKMsg>)){

        }, Function() syncBack);
Getting history messages is not a synchronous method, as there may be non-continuous data that needs to be synced from the server

Complete Message Management Example

class MessageManager {
  static final MessageManager _instance = MessageManager._internal();
  factory MessageManager() => _instance;
  MessageManager._internal();
  
  final Map<String, List<WKMsg>> _channelMessages = {};
  final StreamController<List<WKMsg>> _newMessagesController = StreamController.broadcast();
  final StreamController<WKMsg> _messageUpdateController = StreamController.broadcast();
  
  // Streams for UI to listen
  Stream<List<WKMsg>> get newMessagesStream => _newMessagesController.stream;
  Stream<WKMsg> get messageUpdateStream => _messageUpdateController.stream;
  
  void initialize() {
    _setupMessageListeners();
  }
  
  void _setupMessageListeners() {
    // Listen for message storage
    WKIM.shared.messageManager.addOnMsgInsertedListener((wkMsg) {
      _handleMessageInserted(wkMsg);
    });
    
    // Listen for new messages
    WKIM.shared.messageManager.addOnNewMsgListener('global', (msgs) {
      _handleNewMessages(msgs);
    });
    
    // Listen for message updates
    WKIM.shared.messageManager.addOnRefreshMsgListener('global', (wkMsg) {
      _handleMessageUpdate(wkMsg);
    });
  }
  
  void _handleMessageInserted(WKMsg message) {
    // Message saved to database, update UI immediately
    final channelKey = '${message.channelID}_${message.channelType}';
    _channelMessages[channelKey] ??= [];
    _channelMessages[channelKey]!.add(message);
    
    // Notify UI
    _newMessagesController.add([message]);
  }
  
  void _handleNewMessages(List<WKMsg> messages) {
    for (var message in messages) {
      final channelKey = '${message.channelID}_${message.channelType}';
      _channelMessages[channelKey] ??= [];
      
      // Check if message already exists
      final existingIndex = _channelMessages[channelKey]!
          .indexWhere((m) => m.clientMsgNO == message.clientMsgNO);
      
      if (existingIndex >= 0) {
        // Update existing message
        _channelMessages[channelKey]![existingIndex] = message;
      } else {
        // Add new message
        _channelMessages[channelKey]!.add(message);
      }
    }
    
    // Notify UI
    _newMessagesController.add(messages);
  }
  
  void _handleMessageUpdate(WKMsg message) {
    final channelKey = '${message.channelID}_${message.channelType}';
    if (_channelMessages.containsKey(channelKey)) {
      final messages = _channelMessages[channelKey]!;
      final index = messages.indexWhere((m) => m.clientMsgNO == message.clientMsgNO);
      
      if (index >= 0) {
        messages[index] = message;
        _messageUpdateController.add(message);
      }
    }
  }
  
  // Send text message
  Future<void> sendTextMessage(String text, WKChannel channel) async {
    try {
      final textContent = WKTextContent(text);
      await WKIM.shared.messageManager.sendMessage(textContent, channel);
    } catch (e) {
      print('Failed to send text message: $e');
      rethrow;
    }
  }
  
  // Send image message
  Future<void> sendImageMessage(String imagePath, WKChannel channel, {int? width, int? height}) async {
    try {
      final imageContent = WKImageContent(width ?? 0, height ?? 0);
      imageContent.localPath = imagePath;
      
      await WKIM.shared.messageManager.sendMessage(imageContent, channel);
    } catch (e) {
      print('Failed to send image message: $e');
      rethrow;
    }
  }
  
  // Load history messages
  Future<List<WKMsg>> loadHistoryMessages(
    String channelID,
    int channelType, {
    int oldestOrderSeq = 0,
    bool contain = false,
    int pullMode = 0,
    int limit = 20,
    int aroundMsgOrderSeq = 0,
  }) async {
    final completer = Completer<List<WKMsg>>();
    
    WKIM.shared.messageManager.getOrSyncHistoryMessages(
      channelID,
      channelType,
      oldestOrderSeq,
      contain,
      pullMode,
      limit,
      aroundMsgOrderSeq,
      (messages) {
        // Update local cache
        final channelKey = '${channelID}_$channelType';
        _channelMessages[channelKey] = messages;
        
        completer.complete(messages);
      },
      () {
        // Sync callback - show loading
        print('Syncing messages for channel $channelID...');
      },
    );
    
    return completer.future;
  }
  
  // Get messages for a channel
  List<WKMsg> getMessagesForChannel(String channelID, int channelType) {
    final channelKey = '${channelID}_$channelType';
    return _channelMessages[channelKey] ?? [];
  }
  
  // Clear messages for a channel
  void clearMessagesForChannel(String channelID, int channelType) {
    final channelKey = '${channelID}_$channelType';
    _channelMessages.remove(channelKey);
  }
  
  void dispose() {
    _newMessagesController.close();
    _messageUpdateController.close();
    
    // Remove listeners
    WKIM.shared.messageManager.removeNewMsgListener('global');
    WKIM.shared.messageManager.removeOnRefreshMsgListener('global');
  }
}

Offline Messages

Need to implement sync channel message data source Channel Message Data Source Because WuKongIM supports permanent message storage, it will generate massive offline messages. For this, we adopt an on-demand pull mechanism. For example, with 10 conversations each having 100,000 messages, WuKongIM will not pull all 10*100,000=1 million messages to local storage. Instead, it pulls information for these 10 conversations and the corresponding latest 20 messages, which means actually only 200 messages are pulled. Compared to 1 million messages, this greatly improves offline pull speed. Users will only pull messages for a specific conversation when they enter that conversation. These mechanisms are already encapsulated within the SDK, so users don’t need to worry about them. Users only need to focus on recent conversation changes and listen for data retrieval callbacks.

Data Structure Description

Message Class Core Properties

class WKMsg {
  // Message header redDot: whether to show red dot noPersist: whether not to store syncOnce: whether to sync only once
  MessageHeader header = MessageHeader();
  // Message settings receipt: whether receipt, topic: whether topic chat, stream: whether stream message;
  Setting setting = Setting();
  // Server message ID (globally unique, unordered)
  String messageID = "";
  // Server message ID (ordered)
  int messageSeq = 0;
  // Local message ordered ID
  int clientSeq = 0;
  // 10-digit timestamp
  int timestamp = 0;
  // Local unique ID
  String clientMsgNO = "";
  // Sender
  String fromUID = "";
  // Channel ID
  String channelID = "";
  // Channel type
  int channelType = WKChannelType.personal;
  // Message content type e.g. 1:[Text] 2:[Image]...
  int contentType = 0;
  // Message payload
  String content = "";
  // Message status 0.sending 1.success
  int status = 0;
  // Whether deleted 1.yes
  int isDeleted = 0;
  // Sender's profile
  WKChannel? _from;
  // Channel profile
  WKChannel? _channelInfo;
  // Sender's type profile in channel (only for group messages)
  WKChannelMember? _memberOfFrom;
  // Sort number
  int orderSeq = 0;
  // Local extension fields
  dynamic localExtraMap;
  // Remote extension fields, maintained by server
  WKMsgExtra? wkMsgExtra;
  // Message reaction data
  List<WKMsgReaction>? reactionList;
  // Message content body contentType==1.WKTextContent contentType==2.WKImageContent
  WKMessageContent? messageContent;
}

Message Content Body

class WKMessageContent {
  // Message type 1.text 2.image
  var contentType = 0;
  // Message content
  String content = "";
  // Reply message
  WKReply? reply;
  // Message content rendering data
  List<WKMsgEntity>? entities;
  // Mention information
  WKMentionInfo? mentionInfo;
}

Next Steps