Skip to main content

Introduction

The ShortcutFormatter class extends CometChatTextFormatter to handle shortcuts within messages. This guide walks you through implementing shortcut extensions in your CometChat V6 application.

Setup

  1. Create the ShortcutFormatter class by extending CometChatTextFormatter:
import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart';

class ShortcutFormatter extends CometChatTextFormatter {
  @override
  void init() {
    trackingCharacter ??= "!";
  }

  bool isShortcutTracking = false;

  void prepareShortcuts(TextEditingController textEditingController) {
    CometChat.callExtension('message-shortcuts', 'GET', '/v1/fetch', null,
      onSuccess: (map) {
        if (map.isNotEmpty) {
          Map<String, dynamic> data = map["data"];
          if (data.isNotEmpty) {
            Map<String, dynamic> shortcuts = data["shortcuts"];
            if (shortcuts.isNotEmpty) {
              parseData(shortcuts: shortcuts, textEditingController: textEditingController);
            }
          }
        }
      },
      onError: (error) {},
    );
  }

  void parseData({Map<String, dynamic>? shortcuts, required TextEditingController textEditingController}) async {
    if (shortcuts == null || shortcuts.isEmpty) {
      suggestionListEventSink?.add([]);
      if (onSearch != null) onSearch!(null);
      CometChatUIEvents.hidePanel(composerId, CustomUIPosition.composerPreview);
    } else {
      CometChatUIEvents.hidePanel(composerId, CustomUIPosition.composerPreview);
      if (suggestionListEventSink != null && shortcuts.isNotEmpty) {
        List<SuggestionListItem> list = [];
        shortcuts.forEach((key, value) => list.add(SuggestionListItem(
          id: key,
          title: "$key$value",
          onTap: () {
            int cursorPos = textEditingController.selection.base.offset;
            String left = textEditingController.text.substring(0, cursorPos - 1);
            String right = textEditingController.text.substring(cursorPos);
            textEditingController.text = "$left$value $right";
            updatePreviousText(textEditingController.text);
            textEditingController.selection = TextSelection(
              baseOffset: cursorPos - 1 + "$value".length + 1,
              extentOffset: cursorPos - 1 + "$value".length + 1,
            );
            resetMatchTracker();
            isShortcutTracking = false;
            CometChatUIEvents.hidePanel(composerId, CustomUIPosition.composerPreview);
          },
        )));
        suggestionListEventSink?.add(list);
      }
    }
  }

  void updatePreviousText(String text) {
    previousTextEventSink?.add(text);
  }

  void resetMatchTracker() {
    suggestionListEventSink?.add([]);
    if (onSearch != null) onSearch!(null);
  }

  @override
  void onChange(TextEditingController textEditingController, String previousText) {
    var cursorPosition = textEditingController.selection.base.offset;
    if (textEditingController.text.isEmpty) {
      resetMatchTracker();
      return;
    }
    // Handle shortcut tracking logic
    String previousCharacter = cursorPosition == 0 ? "" : textEditingController.text[cursorPosition - 1];
    bool isSpace = cursorPosition == 1 || (textEditingController.text.length > 1 && cursorPosition > 1 &&
        (textEditingController.text[cursorPosition - 2] == " " || textEditingController.text[cursorPosition - 2] == "\n"));

    if (isShortcutTracking) {
      isShortcutTracking = false;
      if (onSearch != null) onSearch!(null);
      CometChatUIEvents.hidePanel(composerId, CustomUIPosition.composerPreview);
    } else if (previousCharacter == trackingCharacter && isSpace) {
      isShortcutTracking = true;
      if (onSearch != null) onSearch!(trackingCharacter);
      CometChatUIEvents.showPanel(composerId, CustomUIPosition.composerPreview,
          (context) => getLoadingIndicator(context, cometChatTheme));
      prepareShortcuts(textEditingController);
    }
  }

  @override
  TextStyle getMessageInputTextStyle(CometChatTheme theme) {
    throw UnimplementedError();
  }

  @override
  void handlePreMessageSend(BuildContext context, BaseMessage baseMessage) {}

  @override
  void onScrollToBottom(TextEditingController textEditingController) {}
}

Usage

Pass the ShortcutFormatter to the textFormatters property of CometChatMessageComposer:
CometChatMessageComposer(
  user: user,
  textFormatters: [ShortcutFormatter()],
)