chat_uikit for Flutter

This guide gives a comprehensive overview into chat_uikit. The new chat_uikit is intended to provide developers with an efficient, plug-and-play, and highly customizable UI component library, helping you build complete and elegant IM applications that can easily satisfy most instant messaging scenarios. Please download the demo to try it out.

chat_uikit Guide

Introduction

This guide provides an overview and usage examples of the chat_uikit framework in Flutter development, and presents various components and functions of this UIKit, giving developers a good understanding of how chat_uikit works and how to use it efficiently.

Table of contents

Development Environment

environment:
  sdk: '>=3.0.0 <4.0.0'
  flutter: ">=3.19.0"
  • ios 12+
  • android minSDKVersion 23

Installation

flutter pub add em_chat_uikit

Structure

.
├── chat_uikit.dart                                             // library
├── chat_uikit_alphabet_sort_helper.dart                        // Tool to correct the alphabetical order of contacts
├── chat_uikit_defines.dart                                     // UIKit handler definition class
├── chat_uikit_emoji_data.dart                                  // Message emoji data class
├── chat_uikit_localizations.dart                               // Internationalization tool class
├── chat_uikit_service                                          // Secondary wrapping for the chat SDK wrapper class, used to adapt incompliant functions in the wrapping class to UIKit functions.
├── chat_uikit_settings.dart                                    // Class to set functions, used to turn on or off or configure certain functions
├── chat_uikit_time_formatter.dart                              // Tool to set the displayed time format
├── provider                                                    // User attribute tool
│   ├── chat_uikit_profile.dart                                 // User attribute object, including the user's avatar, nickname, and remarks.
│   └── chat_uikit_provider.dart                                // User attribute provider, used to provide user attribute data in the UIKit. 
├── sdk_service                                                 // Wrapping for the chat SDK, used to wrap APIs in the chat SDK that are available to developers. The UIKit interacts with the wrapping class, instead of calling APIs in the chat SDK.
├── tools                                                       // Internal tool class
│   ├── chat_uikit_context.dart                                 // Data context to store certain states.
│   ├── chat_uikit_conversation_extension.dart                  // Processed class of the conversation list to pre-process certain properties.
│   ├── chat_uikit_file_size_tool.dart                          // Tool to calculate the displayed file size.
│   ├── chat_uikit_helper.dart                                  // Internal class to calculate the border radius
│   ├── chat_uikit_highlight_tool.dart                          // Tool class to calculate the component highlight value
│   ├── chat_uikit_image_loader.dart                            // Image loading tool class
│   ├── chat_uikit_message_extension.dart                       // Message processing class that pre-processes certain properties
│   ├── chat_uikit_time_tool.dart                               // Default time format class
│   ├── chat_uikit_url_helper.dart                              // Tool class for URL preview
│   └── safe_disposed.dart                                      // Internal processing class for ChangeNotifier
├── ui                                                          // UI components
│   ├── components                                              // Components
│   ├── controllers                                             // View/widget controllers
│   ├── custom                                                  // UI customization
│   ├── models                                                  // models
│   ├── route                                                   // Route component in the UIKit
│   ├── views                                                   // Views
│   └── widgets                                                 // Widgets
└── universal                                                   // Internal class

Quick Start

  1. Create a project.
flutter create uikit_quick_start --platforms=android,ios
  1. Add dependencies.
cd uikit_quick_start
flutter pub add em_chat_uikit
flutter pub get
  1. Add permissions.
  • iOS: Add permissions in <project root>/ios/Runner/Info.plist.
NSPhotoLibraryUsageDescription
NSCameraUsageDescription
NSMicrophoneUsageDescription
  1. Initialize the UIKit.
import 'package:em_chat_uikit/chat_uikit.dart';
...

void main() {
  ChatUIKit.instance
      .init(options: Options(appKey: <!--Your AppKey-->, autoLogin: false))
      .then((value) {
    runApp(const MyApp());
  });
}

  1. Log in to the UIKit.
Future<void> login() async {
  try {
    await ChatUIKit.instance.loginWithToken(
        userId: '<!--user id-->',
        token: '<!--user token-->');
  } catch (e) {
    debugPrint('login error: $e');
  }
}
  1. Create the chat page.
class ChatPage extends StatefulWidget {
  const ChatPage({required this.chatterId, super.key});
  final String chatterId;
  @override
  State<ChatPage> createState() => _ChatPageState();
}

class _ChatPageState extends State<ChatPage> {
  @override
  Widget build(BuildContext context) {
    return MessagesView(
      profile: ChatUIKitProfile.contact(
        id: widget.chatterId,
      ),
    );
  }
}

Complete code:

import 'package:flutter/material.dart';
import 'package:em_chat_uikit/chat_uikit.dart';

const String appKey = '';
const String userId = '';
const String token = '';

const String chatterId = '';

void main() {
  ChatUIKit.instance
      .init(options: Options(appKey: appKey, autoLogin: false))
      .then((value) {
    runApp(MyApp());
  });
}

class MyApp extends StatelessWidget {
  MyApp({super.key});
  final ChatUIKitLocalizations _localization = ChatUIKitLocalizations();
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      supportedLocales: _localization.supportedLocales,
      localizationsDelegates: _localization.localizationsDelegates,
      localeResolutionCallback: _localization.localeResolutionCallback,
      locale: _localization.currentLocale,
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: login,
              child: const Text('Login'),
            ),
            ElevatedButton(
              onPressed: chat,
              child: const Text('Chat'),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> login() async {
    try {
      await ChatUIKit.instance.loginWithToken(
        userId: userId,
        token: token,
      );
    } catch (e) {
      debugPrint('login error: $e');
    }
  }

  void chat() {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => const ChatPage(
          chatterId: chatterId,
        ),
      ),
    );
  }
}

class ChatPage extends StatefulWidget {
  const ChatPage({required this.chatterId, super.key});
  final String chatterId;
  @override
  State<ChatPage> createState() => _ChatPageState();
}

class _ChatPageState extends State<ChatPage> {
  @override
  Widget build(BuildContext context) {
    return MessagesView(
      profile: ChatUIKitProfile.contact(
        id: widget.chatterId,
      ),
    );
  }
}

Advanced usage

Provider

Provider is a data provider. If user data or group data needs to be displayed, the UIKit will request data via the Provider and you need to return data to the Provider. The UIKit, once getting your data, will refresh the UI to show your data. Following is an example of Provider (example/lib/tool/user_provider_widget.dart).

Example


class UserProviderWidget extends StatefulWidget {
  const UserProviderWidget({required this.child, super.key});

  final Widget child;

  @override
  State<UserProviderWidget> createState() => _UserProviderWidgetState();
}

class _UserProviderWidgetState extends State<UserProviderWidget>
    with GroupObserver {
  @override
  void initState() {
    super.initState();
    ChatUIKit.instance.addObserver(this);
    // Open DB
    UserDataStore().init(onOpened: onOpened);
    // Set Provider Handler
    ChatUIKitProvider.instance.profilesHandler = onProfilesRequest;
  }

  @override
  void dispose() {
    ChatUIKit.instance.removeObserver(this);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return widget.child;
  }

  void onOpened() async {
    // 1. Fill all stored data into uikit.
    await addAllUserInfoToProvider();
    // 2. Load group information, and check if it has been filled into uikit. If not, fetch data from the server and then fill it into uikit.
    await loadGroupInfos();
    // 2. Load user information, and check if it has been filled into uikit. If not, fetch data from the server and then fill it into uikit.
    await loadUserInfos();
    // 3. Fetch current user information, then fill it into uikit.
    await fetchCurrentUserInfo();
  }

  Future<void> fetchCurrentUserInfo() async {
    try {
      // Do not retrieve your own user data from the db, always fetch the latest data from the server. 
      Map<String, UserInfo> map = await ChatUIKit.instance
          .fetchUserInfoByIds([ChatUIKit.instance.currentUserId!]);
      ChatUIKitProfile profile = ChatUIKitProfile.contact(
        id: map.values.first.userId,
        nickname: map.values.first.nickName,
        avatarUrl: map.values.first.avatarUrl,
      );
      UserDataStore().saveUserData(profile);
      ChatUIKitProvider.instance.addProfiles([profile]);
    } catch (e) {
      debugPrint('fetchCurrentUserInfo error: $e');
    }
  }

  // This method is called when uikit needs to display user information and the cache does not exist;
  // it requires fetching and storing the information in the db based on user attributes.
  List<ChatUIKitProfile>? onProfilesRequest(List<ChatUIKitProfile> profiles) {
    List<String> userIds = profiles
        .where((e) => e.type == ChatUIKitProfileType.contact)
        .map((e) => e.id)
        .toList();
    if (userIds.isNotEmpty) {
      fetchUserInfos(userIds);
    }

    List<String> groupIds = profiles
        .where((e) => e.type == ChatUIKitProfileType.group)
        .map((e) => e.id)
        .toList();
    updateGroupsProfile(groupIds);
    return profiles;
  }

  // When a group is created by yourself, it is necessary to fill the group information into uikit. 
  @override
  void onGroupCreatedByMyself(Group group) async {
    ChatUIKitProfile profile =
        ChatUIKitProfile.group(id: group.groupId, groupName: group.name);

    ChatUIKitProvider.instance.addProfiles([profile]);
    // save to db
    UserDataStore().saveUserData(profile);
  }

  // When the group name is changed by yourself, it is necessary to update the group information in uikit. 
  @override
  void onGroupNameChangedByMeSelf(Group group) {
    ChatUIKitProfile? profile =
        ChatUIKitProvider.instance.getProfileById(group.groupId);

    profile = profile?.copyWith(name: group.name) ??
        ChatUIKitProfile.group(
          id: group.groupId,
          groupName: group.name,
        );

    ChatUIKitProvider.instance.addProfiles([profile]);
    // save to db
    UserDataStore().saveUserData(profile);
  }

  // Fill all stored data into uikit.
  Future<void> addAllUserInfoToProvider() async {
    List<ChatUIKitProfile> list = await UserDataStore().loadAllProfiles();
    ChatUIKitProvider.instance.addProfiles(list);
  }

  // Load group information, and check if it has been filled into uikit. If not, fetch data from the server and then fill it into uikit.
  Future<void> loadGroupInfos() async {
    List<Group> groups = await ChatUIKit.instance.getJoinedGroups();
    List<ChatUIKitProfile> profiles = groups
        .map((e) => ChatUIKitProfile.group(id: e.groupId, groupName: e.name))
        .toList();

    if (profiles.isNotEmpty) {
      UserDataStore().saveUserDatas(profiles);
      ChatUIKitProvider.instance.addProfiles(profiles);
    }
  }

  Future<void> updateGroupsProfile(List<String> groupIds) async {
    List<ChatUIKitProfile> list = [];
    for (var groupId in groupIds) {
      try {
        Group group = await ChatUIKit.instance.fetchGroupInfo(groupId: groupId);
        ChatUIKitProfile profile = ChatUIKitProfile.group(
          id: group.groupId,
          groupName: group.name,
          avatarUrl: group.extension,
        );
        list.add(profile);
      } on ChatError catch (e) {
        if (e.code == 600) {
          // 600 indicates the group does not exist, unable to fetch data, providing default data.
          ChatUIKitProfile profile = ChatUIKitProfile.group(id: groupId);
          list.add(profile);
        }
        debugPrint('loadGroupInfo error: $e');
      }
    }
    UserDataStore().saveUserDatas(list);
    ChatUIKitProvider.instance.addProfiles(list);
  }

  // Load user information, and check if it has been filled into uikit. If not, fetch data from the server and then fill it into uikit.
  Future<void> loadUserInfos() async {
    try {
      Map<String, ChatUIKitProfile> map =
          ChatUIKitProvider.instance.profilesCache;
      List<Contact> contacts = await ChatUIKit.instance.getAllContacts();
      contacts.removeWhere((element) => map.keys.contains(element.userId));
      if (contacts.isNotEmpty) {
        List<String> userIds = contacts.map((e) => e.userId).toList();
        fetchUserInfos(userIds);
      }
    } catch (e) {
      debugPrint('loadUserInfos error: $e');
    }
  }

  void fetchUserInfos(List<String> userIds) async {
    try {
      Map<String, UserInfo> map =
          await ChatUIKit.instance.fetchUserInfoByIds(userIds);
      List<ChatUIKitProfile> list = map.values
          .map((e) => ChatUIKitProfile.contact(
              id: e.userId, nickname: e.nickName, avatarUrl: e.avatarUrl))
          .toList();

      if (list.isNotEmpty) {
        UserDataStore().saveUserDatas(list);
        ChatUIKitProvider.instance.addProfiles(list);
      }
    } catch (e) {
      debugPrint('fetchUserInfos error: $e');
    }
  }
}

Usage

A hanlder profilesHandler is required before the use of ChatUIKitProvider. After that, when related information needs to be displayed, UIKit will return you a default ChatUIKitProfile object via the handler and you need to return a ChatUIKitProfile object. In this case, you are advised to return a ChatUIKitProfile object for the placeholding purpose. When you have obtained the correct ChatUIKitProfile object from your server or database, pass it to the UIKit using the ChatUIKitProvider.instance.addProfiles(list) method. Receiving the object, the UIKit refreshes the UI and caches it for subsequent displaying. Take the following steps to use ChatUIKitProvider:

  1. Set profilesHandler upon the app start and login.
    ChatUIKitProvider.instance.profilesHandler = onProfilesRequest;
  1. Set the user data and group data in the database to the UIKit via ChatUIKitProvider.
    List<ChatUIKitProfile> list = await UserDataStore().loadAllProfiles();
    ChatUIKitProvider.instance.addProfiles(list);
  1. When profilesHandler is executed, you can first return ChatUIKitProfile for the placeholding purpose, get data from the server, and then save the data to the database and pass it to the UIKit.
  List<ChatUIKitProfile>? onProfilesRequest(List<ChatUIKitProfile> profiles) {
    List<String> userIds = profiles
        .where((e) => e.type == ChatUIKitProfileType.contact)
        .map((e) => e.id)
        .toList();
    if (userIds.isNotEmpty) {
      fetchUserInfos(userIds);
    }

    List<String> groupIds = profiles
        .where((e) => e.type == ChatUIKitProfileType.group)
        .map((e) => e.id)
        .toList();
    updateGroupsProfile(groupIds);
    return profiles;
  }

Configuration items

ChatUIKit allows easy style customization via ChatUIKitSettings.

import 'chat_uikit.dart';

import 'package:flutter/material.dart';

enum CornerRadius { extraSmall, small, medium, large }

class ChatUIKitSettings {
  /// Specifies the corner radius for the avatars in the uikit.
  static CornerRadius avatarRadius = CornerRadius.medium;

  /// Specifies the corner radius of the search box.
  static CornerRadius searchBarRadius = CornerRadius.small;

  /// Specifies the corner radius of the input box.
  static CornerRadius inputBarRadius = CornerRadius.medium;

  /// Default avatar placeholder image.
  static ImageProvider? avatarPlaceholder;

  /// The corner radius for the dialog.
  static ChatUIKitDialogRectangleType dialogRectangleType =
      ChatUIKitDialogRectangleType.filletCorner;

  /// Default display style of message bubbles.
  static ChatUIKitMessageListViewBubbleStyle messageBubbleStyle =
      ChatUIKitMessageListViewBubbleStyle.arrow;

  /// Whether to show avatars in the conversation list.
  static bool showConversationListAvatar = true;

  /// Whether to show unread message count in the conversation list.
  static bool showConversationListUnreadCount = true;

  // Mute icon displayed in the conversation list.
  static ImageProvider? conversationListMuteImage;

  /// Message long press menu.
  static List<ChatUIKitActionType> msgItemLongPressActions = [
    ChatUIKitActionType.reaction,
    ChatUIKitActionType.copy, // only text message.
    ChatUIKitActionType.forward,
    ChatUIKitActionType.thread, // only group message.
    ChatUIKitActionType.reply,
    ChatUIKitActionType.recall,
    ChatUIKitActionType.edit, // only text message.
    ChatUIKitActionType.multiSelect,
    ChatUIKitActionType.pinMessage,
    ChatUIKitActionType.translate, // only text message.
    ChatUIKitActionType.report,
    ChatUIKitActionType.delete,
  ];

  /// Whether to enable the functionality of input status for one-on-one chat messages.
  static bool enableTypingIndicator = true;

  /// Whether to enable the thread feature.
  static bool enableMessageThread = true;

  /// Whether to enable message translation feature.
  static bool enableMessageTranslation = true;

  /// Message translation target language.
  static String translateTargetLanguage = 'zh-Hans';

  /// Whether to enable message reaction feature.
  static bool enableMessageReaction = true;

  /// Message reaction emojis in the bottom sheet title. These emojis need to be included in the emoji list [ChatUIKitEmojiData.emojiList].
  static List<String> favoriteReaction = [   
    '\u{1F44D}',
    '\u{2764}',
    '\u{1F609}',
    '\u{1F928}',
    '\u{1F62D}',
    '\u{1F389}',
  ];

  /// Regular expression for the default message URL  
  static RegExp defaultUrlRegExp = RegExp(
    r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+',
    caseSensitive: false,
  );

  /// Whether to enable the message pinning feature
  static bool enablePinMsg = true;

  /// Whether to enable message quoting feature
  static bool enableMessageReply = true;

  /// Whether to enable message recall feature
  static bool enableMessageRecall = true;

  /// Time limit for message recall, in seconds
  static int recallExpandTime = 120;

  /// Whether to enable message editing feature
  static bool enableMessageEdit = true;

  /// Whether to enable message reporting feature
  static bool enableMessageReport = true;

  /// Message report tags, can be customized. The reasons for reporting should be written in the localization file, and the key for the reason in the localization file should be consistent with the tag. For example, [ChatUIKitLocal.reportTarget1]
  static List<String> reportMessageTags = [
    'tag1',
    'tag2',
    'tag3',
    'tag4',
    'tag5',
    'tag6',
    'tag7',
    'tag8',
    'tag9',
  ];

  /// Whether to enable message multi-selection feature  
  static bool enableMessageMultiSelect = true;

  /// Whether to enable message forwarding feature
  static bool enableMessageForward = true;

  /// Alphabetical order of `showName` of contacts. If there are Chinese characters, you can redefine the initials using [ChatUIKitAlphabetSortHelper].
  static String sortAlphabetical = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#';
}

Internationalization

UIKit provides the internationalization function. You need to set internationalization information in MaterialApp when integrating the UIKit.

final ChatUIKitLocalizations _localization = ChatUIKitLocalizations();

...

@override
Widget build(BuildContext context) {
  return MaterialApp(
    supportedLocales: _localization.supportedLocales,
    localizationsDelegates: _localization.localizationsDelegates,
    localeResolutionCallback: _localization.localeResolutionCallback,
    locale: _localization.currentLocale,
    ...
  );
}

To add the supported languages, you can first use ChatUIKitLocalizations.addLocales, and then call ChatUIKitLocalizations.resetLocales.

Following is an example of adding French.

_localization.addLocales(locales: const [
  ChatLocal('fr', {
    ChatUIKitLocal.conversationsViewSearchHint: 'Recherche',
  })
]);

_localization.resetLocales();

Theme

UIKit comes with two themes: light and dark, with the former as the default. When setting the theme, you are advised to add ChatUIKitTheme component as root node of the UIKit.

return MaterialApp(
  title: 'Flutter Demo',
  theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
    useMaterial3: true,
  ),
  builder: (context, child) {
    /// add theme support
    return ChatUIKitTheme(
      font: ChatUIKitFont(),
      color: ChatUIKitColor.light(), // ChatUIKitColor.dark()
      child: child!,
    );
  },
  home: const MyHomePage(title: 'Flutter Demo Home Page'),
);

ChatUIKitColor can be customized by adjusting hue. For example, adjust the hue value in light mode.

return MaterialApp(
  title: 'Flutter Demo',
  theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
    useMaterial3: true,
  ),
  builder: (context, child) {
    return ChatUIKitTheme(
      color: ChatUIKitColor.light(
        primaryHue: 203,
        secondaryHue: 155,
        errorHue: 350,
        neutralHue: 203,
        neutralSpecialHue: 220,
      ),
      child: child!,
    );
  },
  home: const MyHomePage(title: 'Flutter Demo Home Page'),
); 

ChatUIKitFont allows you to set the font size. For example, you can pass different types of ChatUIKitFontSize to ChatUIKitFont.fontSize(fontSize: ChatUIKitFontSize.normal) to change the font size of the UIKit.

Route interception and customization

The UIKit uses pushNamed to implement redirection, with the ChatUIKitViewArguments object of the target redirection page passed. You can intercept onGenerateRoute(RouteSettings settings) and parse settings.name to get the target page for redirection. Then, you can reset the ChatUIKitViewArguments parameter for redirection interception and page customization. The name of the target redirection page is specified in chat_uikit_route_names.dart.

For details on route interception, you can refer to example/lib/custom/chat_route_filter.dart.

Event interception and error handling

When the UIKit starts to call the chat SDK, ChatSDKEventsObserver.onChatSDKEventBegin is triggered. When the call ends, ChatSDKEventsObserver.onChatSDKEventEnd is triggered. If an error occurs, ChatError is reported.

class SDKEventHandlerPage extends StatefulWidget {
  const SDKEventHandlerPage({super.key});

  @override
  State<SDKEventHandlerPage> createState() => _SDKEventHandlerPageState();
}

class _SDKEventHandlerPageState extends State<SDKEventHandlerPage>
    with ChatSDKEventsObserver {
  @override
  void initState() {
    ChatUIKit.instance.addObserver(this);
    super.initState();
  }

  @override
  void dispose() {
    ChatUIKit.instance.removeObserver(this);
    super.dispose();
  }

  /// When the call to an SDK method starts, you can display different prompt windows based on different events.
  @override
  void onChatSDKEventBegin(ChatSDKEvent event) {}

  /// When the call to an SDK method call ends, you can end the prompt window display at this time. If there is an error, you can display the corresponding prompt message.
  @override
  void onChatSDKEventEnd(ChatSDKEvent event, ChatError? error) {}

  ...
}

For more information, you can refer to example/lib/tool/toast_page.dart.

For other events than those of the chat SDK, ChatUIKitEventsObservers.onChatUIKitEventsReceived is triggered.

class UIKitEventHandlePage extends StatefulWidget {
  const UIKitEventHandlePage({super.key});

  @override
  State<UIKitEventHandlePage> createState() => _UIKitEventHandlePageState();
}

class _UIKitEventHandlePageState extends State<UIKitEventHandlePage>
    with ChatUIKitEventsObservers {
  @override
  void initState() {
    ChatUIKit.instance.addObserver(this);
    super.initState();
  }

  @override
  void dispose() {
    ChatUIKit.instance.removeObserver(this);
    super.dispose();
  }

  /// This method is used to pass events from ChatUIKit to developers.
  @override
  void onChatUIKitEventsReceived(ChatUIKitEvent event) {}

  ...
}

For more information, see example/lib/tool/toast_page.dart.

Connection status change and login token expiration callback

When the connection status or login status changes, the corresponding event in ChatUIKit.instance.connectHandler is triggered.

ChatUIKit.instance.connectHandler(
  onUserAuthenticationFailed: () {},
  onUserDidChangePassword: () {},
  onUserDidForbidByServer: () {},
  onUserDidLoginFromOtherDevice: (info) {},
  onUserDidLoginTooManyDevice: () {},
  onUserDidRemoveFromServer: () {},
  onUserKickedByOtherDevice: () {},
  onConnected: () {},
  onDisconnected: () {},
  onTokenWillExpire: () {},
  onTokenDidExpire: () {},
  onAppActiveNumberReachLimit: () {},
);

For more information, you can refer to example/lib/tool/token_status_handler_widget.dart.

Message time formatting

UIKit presents time in the default format. You can call ChatUIKitTimeFormatter to alter the way the time is formatted.

ChatUIKitTimeFormatter.instance.formatterHandler = (context, type, time) {
  return 'formatter time'; // return formatted time, e.g. 12:00 PM
};

Correction of the alphabetical order of contacts

For example, if a contact name contains other characters than English letters, you can use ChatUIKitAlphabetSortHelper to sort contacts in the alphabetical order.

ChatUIKitAlphabetSortHelper.instance.sortHandler = (String? groupId, String userId, String showName) {
  // Return the first letter of the showName for sorting, especially useful for sorting Chinese characters
  return PinyinHelper.getFirstWordPinyin(showName);
};

Design guide

For any questions about design guidelines and details, you can add comments to the Figma design draft and mention our designer Stevie Jiang.

Libraries

chat_sdk_context/chat_sdk_context
chat_sdk_context/src/context
chat_sdk_service/chat_sdk_service
chat_sdk_service/src/actions/chat_actions
chat_sdk_service/src/actions/contact_actions
chat_sdk_service/src/actions/group_actions
chat_sdk_service/src/actions/notification_actions
chat_sdk_service/src/actions/presence_actions
chat_sdk_service/src/actions/room_actions
chat_sdk_service/src/actions/thread_actions
chat_sdk_service/src/actions/user_info_actions
chat_sdk_service/src/chat_sdk_define
chat_sdk_service/src/chat_sdk_service
chat_sdk_service/src/chat_sdk_service_defines
chat_sdk_service/src/chat_uikit_insert_tools
chat_sdk_service/src/observers/action_event_observer
chat_sdk_service/src/observers/chat_observer
chat_sdk_service/src/observers/connect_observer
chat_sdk_service/src/observers/contact_observer
chat_sdk_service/src/observers/group_observer
chat_sdk_service/src/observers/message_observer
chat_sdk_service/src/observers/multi_observer
chat_sdk_service/src/observers/presence_observer
chat_sdk_service/src/observers/room_observer
chat_sdk_service/src/observers/thread_observer
chat_sdk_service/src/wrappers/chat_wrapper
chat_sdk_service/src/wrappers/connect_wrapper
chat_sdk_service/src/wrappers/contact_wrapper
chat_sdk_service/src/wrappers/group_wrapper
chat_sdk_service/src/wrappers/message_wrapper
chat_sdk_service/src/wrappers/multi_wrapper
chat_sdk_service/src/wrappers/notification_wrapper
chat_sdk_service/src/wrappers/presence_wrapper
chat_sdk_service/src/wrappers/room_wrapper
chat_sdk_service/src/wrappers/thread_wrapper
chat_sdk_service/src/wrappers/user_info_wrapper
chat_uikit
chat_uikit/chat_uikit
chat_uikit/src/chat_uikit_alphabet_sort_helper
chat_uikit/src/chat_uikit_service/actions/chat_uikit_chat_actions
chat_uikit/src/chat_uikit_service/actions/chat_uikit_contact_actions
chat_uikit/src/chat_uikit_service/actions/chat_uikit_events_actions
chat_uikit/src/chat_uikit_service/actions/chat_uikit_group_actions
chat_uikit/src/chat_uikit_service/actions/chat_uikit_notification_actions
chat_uikit/src/chat_uikit_service/actions/chat_uikit_room_actions
chat_uikit/src/chat_uikit_service/chat_uikit_action_events
chat_uikit/src/chat_uikit_service/chat_uikit_service
chat_uikit/src/chat_uikit_service/observers/chat_uikit_chat_observers
chat_uikit/src/chat_uikit_service/observers/chat_uikit_contact_observers
chat_uikit/src/chat_uikit_service/observers/chat_uikit_events_observers
chat_uikit/src/chat_uikit_service/observers/chat_uikit_group_observers
chat_uikit/src/chat_uikit_service/observers/chat_uikit_multi_observers
chat_uikit/src/chat_uikit_service/observers/chat_uikit_thread_observers
chat_uikit/src/chat_uikit_settings
chat_uikit/src/chat_uikit_time_formatter
chat_uikit/src/tools/chat_uikit_extension
chat_uikit/src/tools/chat_uikit_file_size_tool
chat_uikit/src/tools/chat_uikit_highlight_tool
chat_uikit/src/tools/chat_uikit_time_tool
chat_uikit/src/tools/chat_uikit_url_helper
chat_uikit/src/tools/room_time_tool
chat_uikit/src/tools/safe_disposed
chat_uikit/src/ui/components/block_list_view
chat_uikit/src/ui/components/contact_list_view
chat_uikit/src/ui/components/conversation_list_view
chat_uikit/src/ui/components/group_list_view
chat_uikit/src/ui/components/group_member_list_view
chat_uikit/src/ui/components/message_list_view
chat_uikit/src/ui/components/new_requests_list_view
chat_uikit/src/ui/components/pin_message_list_view
chat_uikit/src/ui/components/thread_message_list_view
chat_uikit/src/ui/controllers/block_list_view_controller
chat_uikit/src/ui/controllers/chat_uikit_list_view_controller_base
chat_uikit/src/ui/controllers/contact_list_view_controller
chat_uikit/src/ui/controllers/conversation_list_view_controller
chat_uikit/src/ui/controllers/group_list_view_controller
chat_uikit/src/ui/controllers/group_member_list_view_controller
chat_uikit/src/ui/controllers/messages_view_controller
chat_uikit/src/ui/controllers/new_request_list_view_controller
chat_uikit/src/ui/controllers/pin_message_list_view_controller
chat_uikit/src/ui/controllers/thread_members_view_controller
chat_uikit/src/ui/controllers/thread_messages_view_controller
chat_uikit/src/ui/custom/custom_tab_indicator
chat_uikit/src/ui/custom/share_user_data
chat_uikit/src/ui/models/alphabetical_item_model
chat_uikit/src/ui/models/chat_uikit_list_item_model_base
chat_uikit/src/ui/models/contact_item_model
chat_uikit/src/ui/models/conversation_item_model
chat_uikit/src/ui/models/group_item_model
chat_uikit/src/ui/models/message_model
chat_uikit/src/ui/models/new_request_item_model
chat_uikit/src/ui/models/pin_list_item_model
chat_uikit/src/ui/models/quote_model
chat_uikit/src/ui/models/room_uikit_gift_model
chat_uikit/src/ui/route/chat_uikit_route
chat_uikit/src/ui/route/chat_uikit_route_names
chat_uikit/src/ui/route/chat_uikit_view_observer
chat_uikit/src/ui/route/view_arguments/change_info_view_arguments
chat_uikit/src/ui/route/view_arguments/contact_details_view_arguments
chat_uikit/src/ui/route/view_arguments/contacts_view_arguments
chat_uikit/src/ui/route/view_arguments/conversations_view_arguments
chat_uikit/src/ui/route/view_arguments/create_group_view_arguments
chat_uikit/src/ui/route/view_arguments/current_user_info_view_arguments
chat_uikit/src/ui/route/view_arguments/forward_message_select_view_arguments
chat_uikit/src/ui/route/view_arguments/forward_messages_view_arguments
chat_uikit/src/ui/route/view_arguments/group_add_members_view_arguments
chat_uikit/src/ui/route/view_arguments/group_change_owner_view_arguments
chat_uikit/src/ui/route/view_arguments/group_delete_members_view_arguments
chat_uikit/src/ui/route/view_arguments/group_details_view_arguments
chat_uikit/src/ui/route/view_arguments/group_members_view_arguments
chat_uikit/src/ui/route/view_arguments/group_mention_view_arguments
chat_uikit/src/ui/route/view_arguments/groups_view_arguments
chat_uikit/src/ui/route/view_arguments/messages_view_arguments
chat_uikit/src/ui/route/view_arguments/new_request_details_view_arguments
chat_uikit/src/ui/route/view_arguments/new_requests_view_arguments
chat_uikit/src/ui/route/view_arguments/report_message_view_arguments
chat_uikit/src/ui/route/view_arguments/search_group_members_view_arguments
chat_uikit/src/ui/route/view_arguments/search_history_view_arguments
chat_uikit/src/ui/route/view_arguments/search_view_arguments
chat_uikit/src/ui/route/view_arguments/select_contact_view_arguments
chat_uikit/src/ui/route/view_arguments/show_image_view_arguments
chat_uikit/src/ui/route/view_arguments/show_video_view_arguments
chat_uikit/src/ui/route/view_arguments/thread_members_view_arguments
chat_uikit/src/ui/route/view_arguments/thread_messages_view_arguments
chat_uikit/src/ui/route/view_arguments/threads_view_arguments
chat_uikit/src/ui/route/view_arguments/view_arguments_base
chat_uikit/src/ui/views/change_info_view
chat_uikit/src/ui/views/contact_details_view
chat_uikit/src/ui/views/contacts_view
chat_uikit/src/ui/views/conversations_view
chat_uikit/src/ui/views/create_group_view
chat_uikit/src/ui/views/current_user_info_view
chat_uikit/src/ui/views/forward_message_select_view
chat_uikit/src/ui/views/forward_messages_view
chat_uikit/src/ui/views/group_add_members_view
chat_uikit/src/ui/views/group_change_owner_view
chat_uikit/src/ui/views/group_delete_members_view
chat_uikit/src/ui/views/group_details_view
chat_uikit/src/ui/views/group_members_view
chat_uikit/src/ui/views/group_mention_view
chat_uikit/src/ui/views/groups_view
chat_uikit/src/ui/views/messages_view
chat_uikit/src/ui/views/new_request_details_view
chat_uikit/src/ui/views/new_requests_view
chat_uikit/src/ui/views/report_message_view
chat_uikit/src/ui/views/search_group_members_view
chat_uikit/src/ui/views/search_history_view
chat_uikit/src/ui/views/search_view
chat_uikit/src/ui/views/select_contact_view
chat_uikit/src/ui/views/show_image_view
chat_uikit/src/ui/views/show_video_view
chat_uikit/src/ui/views/thread_members_view
chat_uikit/src/ui/views/thread_messages_view
chat_uikit/src/ui/views/threads_view
chat_uikit/src/ui/widgets/chat_bottom_sheet_background
chat_uikit/src/ui/widgets/chat_uikit_alphabetical_widget
chat_uikit/src/ui/widgets/chat_uikit_app_bar
chat_uikit/src/ui/widgets/chat_uikit_avatar
chat_uikit/src/ui/widgets/chat_uikit_badge
chat_uikit/src/ui/widgets/chat_uikit_bottom_sheet
chat_uikit/src/ui/widgets/chat_uikit_button
chat_uikit/src/ui/widgets/chat_uikit_dialog
chat_uikit/src/ui/widgets/chat_uikit_downloads_helper_widget
chat_uikit/src/ui/widgets/chat_uikit_emoji_panel
chat_uikit/src/ui/widgets/chat_uikit_emoji_rich_text_widget
chat_uikit/src/ui/widgets/chat_uikit_list_view
chat_uikit/src/ui/widgets/chat_uikit_message_reaction_info
chat_uikit/src/ui/widgets/chat_uikit_message_status_widget
chat_uikit/src/ui/widgets/chat_uikit_quote_widget
chat_uikit/src/ui/widgets/chat_uikit_reg_exp_text
chat_uikit/src/ui/widgets/chat_uikit_reply_bar
chat_uikit/src/ui/widgets/chat_uikit_search_widget
chat_uikit/src/ui/widgets/chat_uikit_show_image_widget
chat_uikit/src/ui/widgets/chat_uikit_show_video_widget
chat_uikit/src/ui/widgets/edit_bar/chat_uikit_edit_bar
chat_uikit/src/ui/widgets/input_bar/chat_uikit_input_bar
chat_uikit/src/ui/widgets/input_bar/chat_uikit_input_bar_theme
chat_uikit/src/ui/widgets/input_bar/chat_uikit_selection_controls
chat_uikit/src/ui/widgets/input_bar/custom_text_editing_controller
chat_uikit/src/ui/widgets/list_view_items/chat_uikit_alphabetical_list_view_item
chat_uikit/src/ui/widgets/list_view_items/chat_uikit_contact_list_view_item
chat_uikit/src/ui/widgets/list_view_items/chat_uikit_conversation_list_view_item
chat_uikit/src/ui/widgets/list_view_items/chat_uikit_details_list_view_item
chat_uikit/src/ui/widgets/list_view_items/chat_uikit_group_list_view_item
chat_uikit/src/ui/widgets/list_view_items/chat_uikit_list_view_more_item
chat_uikit/src/ui/widgets/list_view_items/chat_uikit_new_request_list_view_item
chat_uikit/src/ui/widgets/list_view_items/chat_uikit_reaction_widget
chat_uikit/src/ui/widgets/list_view_items/chat_uikit_search_list_view_item
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/chat_uikit_message_bubble_widget
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/chat_uikit_message_list_view_alert_item
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/chat_uikit_message_list_view_message_item
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/chat_uikit_message_reactions_row
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/chat_uikit_message_thread_widget
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/message_widget/chat_uikit_card_bubble_widget
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/message_widget/chat_uikit_combine_bubble_widget
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/message_widget/chat_uikit_file_bubble_widget
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/message_widget/chat_uikit_image_bubble_widget
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/message_widget/chat_uikit_nonsupport_bubble_widget
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/message_widget/chat_uikit_text_bubble_widget
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/message_widget/chat_uikit_video_bubble_widget
chat_uikit/src/ui/widgets/list_view_items/message_list_view_items/message_widget/chat_uikit_voice_bubble_widget
chat_uikit/src/ui/widgets/message_bottom_menu/chat_uikit_message_view_bottom_menu
chat_uikit/src/ui/widgets/popup_menu/chat_uikit_popup_menu
chat_uikit/src/ui/widgets/popup_menu/chat_uikit_position_widget
chat_uikit/src/ui/widgets/record_bar/chat_uikit_record_bar
chat_uikit/src/ui/widgets/record_bar/src/error_code
chat_uikit/src/ui/widgets/record_bar/src/record_bar
chat_uikit/src/ui/widgets/record_bar/src/record_data
chat_uikit/src/ui/widgets/record_bar/src/record_error
chat_uikit/src/ui/widgets/record_bar/src/record_types
chat_uikit/src/ui/widgets/record_bar/src/water_ripple
chat_uikit/src/ui/widgets/room_emoji
chat_uikit/src/ui/widgets/room_gifts_selections_widget
chat_uikit/src/ui/widgets/room_global_message_widget
chat_uikit/src/ui/widgets/room_input_bar
chat_uikit/src/ui/widgets/room_members_widget
chat_uikit/src/ui/widgets/room_message_list_item
chat_uikit/src/ui/widgets/room_messages_widget
chat_uikit/src/ui/widgets/room_show_gift_widget
chat_uikit/src/universal/chat_uikit_action_model
chat_uikit/src/universal/chat_uikit_defines
chat_uikit/src/universal/chat_uikit_tools
chat_uikit/src/universal/defines
chat_uikit_localizations/chat_uikit_localizations
chat_uikit_localizations/src/chat_uikit_localizations
chat_uikit_provider/chat_uikit_provider
chat_uikit_provider/src/chat_uikit_profile
chat_uikit_provider/src/chat_uikit_provider
chat_uikit_universal/chat_uikit_universal
chat_uikit_universal/src/chat_define
chat_uikit_universal/src/chat_enum
chat_uikit_universal/src/chat_uikit_emoji_data
chat_uikit_universal/src/widgets/chat_uikit_image_loader
chatroom_uikit/chatroom_uikit
chatroom_uikit/src/utils/chatroom_image_loader
chatroom_uikit/src/widgets/chatroom_dialog
chatroom_uikit/src/widgets/chatroom_uikit_avatar
chatroom_uikit/src/widgets/chatroom_uikit_bottom_sheet