Files
tencent_cloud_chat_uikit/doc/表情.md
T
2025-06-30 16:24:20 +08:00

20 KiB

以下为您介绍,如何为腾讯云IM Flutter TUIKit引入表情能力。

我们的 TIMUIKitChat 组件中,支持发送及接收三种类型的表情:

表情类型 发送形式 是否文字混排 发送内容 解析方式 引入方式 TUIKit默认自带
Unicode Emoji表情 文本消息 是 Unicode编码 设备自动将Unicode编码解析成小表情。不同的设备,对Unicode解析后的图形,略有不同 Unicode List 文档中提供一套默认Unicode列表示例
小图片表情 文本消息 是 表情图片名称 根据名称,自动匹配本地Asset图片资源 图片资源预存于Asset,并定义 List 一套 QQ 同款小表情图库,可直接使用
大图片表情 表情消息 否 baseURL 拼接图片文件名,表情图片Asset路径 通过路径,解析Asset资源 图片资源预存于Asset,并定义 List -

现在,我们就来动手接入TUIKit的表情能力。

? TUIKit在升级至1.1.0版本后,对表情能力用法有较大改动。请您在升级最新版后,根据本文档指引,重新适配接入表情能力。谢谢~

STEP1: 自定义表情图片资源

? 本步骤选做: 如果您需要用到非默认提供的QQ小表情外的其他图片表情,如自定义图片小表情及图片大表情,才需完成本步骤。 QQ小表情包,TUIKit已自带提供,无需在本步骤引入,请关注后续步骤。

将文件导入项目

请将您的表情资源文件,导入项目的 assets/custom_face_resource/ 目录内。无论是图片大表情,亦或是图片小表情,都需按此步骤引入。

该目录内,请使用不同的文件夹,区分表情面板中的不同Tab。每个Tab内表情,仅支持一种类型,图片大表情或图片小表情。

文件夹的名称,请使用该Tab的 name 命名。该命名不会对客户展现,请根据开发需要,自定义即可。

请保证,所有表情资源文件,不要重名。

此路径的表情图片放置引入,可参考我们的Demo。

声明表情文件

打开 pubspec.yaml 文件,在 flutter => assets 中,声明刚刚引入的表情资源文件。

flutter:
  assets:
    - assets/custom_face_resource/

在代码中配置图片资源List

? 本步骤代码示例请参考此处,直接看 emojiList 即可。

在您代码定义静态参数或配置的地方,定义一个 static List<CustomEmojiFaceData>,用于将本地图片资源,转换成TUIKit可以接受的格式,后续以 List 方式传入。

此 List 中,每个item都是 CustomEmojiFaceData,每个 CustomEmojiFaceData 构成一个表情面板中的Tab。具体参数说明如下:

CustomEmojiFaceData(
    {
        String name, // 文件夹目录名称
        String icon, // Tab中的icon资源文件名
        List<String> list, // 每个图片的文件名,以List
        bool isEmoji // 是否为图片小表情,默认为false,即图片大表情
    }
);

示例代码如下:

static final List<CustomEmojiFaceData> emojiList = [
  // 使用图片小表情,支持图文混排,以文本消息形式发送
  CustomEmojiFaceData(
      name: '4349',
      icon: "aircraft.png",
      isEmoji: true,
      list: [
        "aircraft.png",
        "alarmClock.png",
        "anger.png",
        // ...
      ]),

  // 使用图片大表情,不支持图文混排,以表情消息形式发送
  CustomEmojiFaceData(
    name: '4350',
    icon: "menu@2x.png",
    list: [
    "yz00@2x.png",
    // ...
  ]),
]

STEP2: 自定义Emoji Unicode 字符串List

? 本步骤选做: 如果您需要用到Unicode Emoji表情,才需完成本步骤。

在您代码定义静态参数或配置的地方,定义一个 List<Map<String, Object>> Unicode 列表,供传入。

该列表,我们提供一套示例,在附录中。

您看直接复制引入这套示例列表,或基于此修改。

STEP3: 表情资源预读入内存

? 本步骤代码示例请参考此处,直接看 setCustomSticker 方法即可。 QQ小表情包,TUIKit已自带提供,无需在本步骤引入,请关注后续步骤。

在您的项目启动后,首个 TIMUIKitChat 组件渲染前,将上一步定义的图片表情资源List,转换成TUIKit表情的实例,放入全局Provider中,存储于内存里。

本步骤方法仅需执行一次,一次性全部读入内存中。 因展示渲染表情资源为高频操作,如每次展示前才动态读入内存,对资源与性能占用比较大。

单个表情内存实例,使用 CustomSticker 类生成。如果传入了 unicode 则为 Unicode Emoji表情,否则为图片类型表情。

class CustomSticker {
  int? unicode; // Unicode int值。如果传入了 `unicode` 则为 Unicode Emoji表情,否则为图片类型表情
  String name; // 表情名称
  int index; // 表情序号
  bool isEmoji; // 是否为图片小表情,默认为图片大表情
}

每个Tab的内存实例,使用 CustomStickerPackage 类生成。

class CustomStickerPackage { // 一个系列的表情包定义为一个package,占据一个表情面板Tab
  String name; // 表情包package name,该Tab文件夹名称。
  String? baseUrl; // 表情包package baseUrl,建议配置成:"assets/custom_face_resource/${表情包文件夹名称 即 表情包package name}"
  List<CustomSticker> stickerList; // 表情资源列表
  CustomSticker menuItem; // 表情面包Tab按钮icon
  bool isEmoji; // 是否为图片小表情,默认为图片大表情
}

综上所述,需要写的代码,我们给出示例版本。

表情项一演示如何使用Emoji Unicode表情包,表情项二演示如何使用图片类型(包含大或小)表情包。您可以根据需要,使用全部或部分代码。

setCustomSticker() async {
  // 定义一个大List来承载各个表情包 package Tab
  List<CustomStickerPackage> customStickerPackageList = [];

  // 表情项一:使用Emoji Unicode表情列表。可以嵌入文字内容中。
  // `emojiData` 来自于STEP2。
  final defEmojiList = emojiData.asMap().keys.map((emojiIndex) {
    final emoji = Emoji.fromJson(emojiData[emojiIndex]);
    return CustomSticker(
        index: emojiIndex, name: emoji.name, unicode: emoji.unicode);
  }).toList();
  customStickerPackageList.add(CustomStickerPackage(
      name: "defaultEmoji",
      stickerList: defEmojiList,
      menuItem: defEmojiList[0]));

  // 表情项二:使用您提供的图片表情包。
  // 务必保证 `customEmojiPackage.name` 为该Tab文件夹名称。
  // `Const.emojiList` 来自于STEP1。
  customStickerPackageList.addAll(Const.emojiList.map((customEmojiPackage) {
    return CustomStickerPackage(
        name: customEmojiPackage.name,
        baseUrl: "assets/custom_face_resource/${customEmojiPackage.name}",
        stickerList: customEmojiPackage.list
            .asMap()
            .keys
            .map((idx) =>
            CustomSticker(index: idx, name: customEmojiPackage.list[idx]))
            .toList(),
        menuItem: CustomSticker(
          index: 0,
          name: customEmojiPackage.icon,
        ));
  }).toList());

  Provider.of<CustomStickerPackageData>(context, listen: false)
      .customStickerPackageList = customStickerPackageList;
}

STEP4: 为 TIMUIKitChat 组件添加表情解析能力

? 本步骤代码示例请参考此处,重点浏览 renderCustomStickerPanel, customStickerPanel 及 customEmojiList 即可。

将以下代码,直接拷贝进入您用于承载 TIMUIKitChat 组件的类中。

Widget renderCustomStickerPanel({
  sendTextMessage,
  sendFaceMessage,
  deleteText,
  addCustomEmojiText,
  addText,
  List<CustomEmojiFaceData> defaultCustomEmojiStickerList = const [],
}) {
  final theme = Provider.of<DefaultThemeData>(context).theme;
  final customStickerPackageList =
      Provider.of<CustomStickerPackageData>(context).customStickerPackageList;
  final defaultEmojiList =
      defaultCustomEmojiStickerList.map((customEmojiPackage) {
    return CustomStickerPackage(
        name: customEmojiPackage.name,
        baseUrl: "assets/custom_face_resource/${customEmojiPackage.name}",
        isEmoji: customEmojiPackage.isEmoji,
        isDefaultEmoji: true,
        stickerList: customEmojiPackage.list
            .asMap()
            .keys
            .map((idx) =>
                CustomSticker(index: idx, name: customEmojiPackage.list[idx]))
            .toList(),
        menuItem: CustomSticker(
          index: 0,
          name: customEmojiPackage.icon,
        ));
  }).toList();
  return StickerPanel(
      sendTextMsg: sendTextMessage,
      sendFaceMsg: (index, data) =>
          sendFaceMessage(index + 1, (data.split("/")[3]).split("@")[0]),
      deleteText: deleteText,
      addText: addText,
      addCustomEmojiText: addCustomEmojiText,
      customStickerPackageList: [
        ...defaultEmojiList,
        ...customStickerPackageList
      ],
      backgroundColor: theme.weakBackgroundColor,
      lightPrimaryColor: theme.lightPrimaryColor);
}

STEP4.1: 渲染图片小表情

? 本步骤选做:

  • 如果您的项目需要用到图片小表情,包括自定义图片小表情,或直接使用默认自带 QQ 同款图片小表情,才需完成本步骤。
  • 图片小表情展现形式和Unicode Emoji类似,建议Unicode Emoji和图片小表情选用一个即可。即,如果您选用了Unicode Emoji,可直接跳过本步骤。
  • STEP4.1(a) 为使用自定义图片小表情;
  • STEP4.1(b) 为使用默认自带 QQ 同款图片小表情。

以上方案,建议直接选用一个方案即可。

如果需要同时使用,请保证您的自定义图片小表情名称,不要和我们默认提供的 QQ 同款图片小表情重复。

STEP4.1(a): 添加渲染解析自定义图片小表情的支持

在您用于承载 TIMUIKitChat 组件的 build 方法中,定义一个 List customEmojiList 变量,用于存放图片小表情列表。

List customEmojiList =
    Const.emojiList.where((element) => element.isEmoji == true).toList();

并将此列表,传入 TIMUIKitChat 组件的 customEmojiStickerList 参数内。

return TIMUIKitChat(
    customEmojiStickerList: customEmojiList,
    // ......
);

? 如果您用于承载 TIMUIKitChat 组件的类为 StatefulWidget,您可将 customEmojiList 变量,放如State中,仅在首次build时,才去执行 where 命令,优化性能。

STEP4.1(b): 启用 QQ 小表情包

将 TIMUIKitChat 的 TIMUIKitChatConfig 的 isUseDefaultEmoji 参数,设置为 true 即可。此时,会向表情包面板最左侧,自动生成一个承载 QQ 小表情包的 Tab。

return TIMUIKitChat(
    config: TIMUIKitChatConfig(
        isUseDefaultEmoji: true,
        // ......
    ),
    // ......
);

STEP4.2: 将表情包能力,注入 TIMUIKitChat

将本步骤最开始让您复制的代码方法,传入 TIMUIKitChat 组件的 customStickerPanel 参数内。

return TIMUIKitChat(
    customStickerPanel: renderCustomStickerPanel,
    // ......
);

此时,TUIKit表情能力接入完成。您可正常收发测试。如在接入过程中,有任何问题,欢迎随时联系我们。

附录: Emoji Unicode 列表示例

本列表仅用于示例演示,您可根据需要,增加或修改。

List<Map<String, Object>> emojiData = [
  {"name": "GRINNING FACE WITH SMILING EYES", "unicode": 128513},
  {"name": "FACE WITH TEARS OF JOY", "unicode": 128514},
  {"name": "SMILING FACE WITH OPEN MOUTH", "unicode": 128515},
  {"name": "SMILING FACE WITH OPEN MOUTH AND SMILING EYES", "unicode": 128516},
  {"name": "SMILING FACE WITH OPEN MOUTH AND COLD SWEAT", "unicode": 128517},
  {
    "name": "SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES",
    "unicode": 128518
  },
  {"name": "WINKING FACE", "unicode": 128521},
  {"name": "SMILING FACE WITH SMILING EYES", "unicode": 128522},
  {"name": "FACE SAVOURING DELICIOUS FOOD", "unicode": 128523},
  {"name": "RELIEVED FACE", "unicode": 128524},
  {"name": "SMILING FACE WITH HEART-SHAPED EYES", "unicode": 128525},
  {"name": "SMIRKING FACE", "unicode": 128527},
  {"name": "UNAMUSED FACE", "unicode": 128530},
  {"name": "FACE WITH COLD SWEAT", "unicode": 128531},
  {"name": "PENSIVE FACE", "unicode": 128532},
  {"name": "CONFOUNDED FACE", "unicode": 128534},
  {"name": "FACE THROWING A KISS", "unicode": 128536},
  {"name": "KISSING FACE WITH CLOSED EYES", "unicode": 128538},
  {"name": "FACE WITH STUCK-OUT TONGUE AND WINKING EYE", "unicode": 128540},
  {
    "name": "FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES",
    "unicode": 128541
  },
  {"name": "DISAPPOINTED FACE", "unicode": 128542},
  {"name": "ANGRY FACE", "unicode": 128544},
  {"name": "POUTING FACE", "unicode": 128545},
  {"name": "CRYING FACE", "unicode": 128546},
  {"name": "PERSEVERING FACE", "unicode": 128547},
  {"name": "FACE WITH LOOK OF TRIUMPH", "unicode": 128548},
  {"name": "DISAPPOINTED BUT RELIEVED FACE", "unicode": 128549},
  {"name": "FEARFUL FACE", "unicode": 128552},
  {"name": "WEARY FACE", "unicode": 128553},
  {"name": "SLEEPY FACE", "unicode": 128554},
  {"name": "TIRED FACE", "unicode": 128555},
  {"name": "LOUDLY CRYING FACE", "unicode": 128557},
  {"name": "FACE WITH OPEN MOUTH AND COLD SWEAT", "unicode": 128560},
  {"name": "FACE SCREAMING IN FEAR", "unicode": 128561},
  {"name": "ASTONISHED FACE", "unicode": 128562},
  {"name": "FLUSHED FACE", "unicode": 128563},
  {"name": "DIZZY FACE", "unicode": 128565},
  {"name": "FACE WITH MEDICAL MASK", "unicode": 128567},
  {"name": "GRINNING CAT FACE WITH SMILING EYES", "unicode": 128568},
  {"name": "CAT FACE WITH TEARS OF JOY", "unicode": 128569},
  {"name": "SMILING CAT FACE WITH OPEN MOUTH", "unicode": 128570},
  {"name": "SMILING CAT FACE WITH HEART-SHAPED EYES", "unicode": 128571},
  {"name": "CAT FACE WITH WRY SMILE", "unicode": 128572},
  {"name": "KISSING CAT FACE WITH CLOSED EYES", "unicode": 128573},
  {"name": "POUTING CAT FACE", "unicode": 128574},
  {"name": "CRYING CAT FACE", "unicode": 128575},
  {"name": "WEARY CAT FACE", "unicode": 128576},
  {"name": "FACE WITH NO GOOD GESTURE", "unicode": 128581},
  {"name": "FACE WITH OK GESTURE", "unicode": 128582},
  {"name": "PERSON BOWING DEEPLY", "unicode": 128583},
  {"name": "SEE-NO-EVIL MONKEY", "unicode": 128584},
  {"name": "HEAR-NO-EVIL MONKEY", "unicode": 128585},
  {"name": "SPEAK-NO-EVIL MONKEY", "unicode": 128586},
  {"name": "HAPPY PERSON RAISING ONE HAND", "unicode": 128587},
  {"name": "PERSON RAISING BOTH HANDS IN CELEBRATION", "unicode": 128588},
  {"name": "PERSON FROWNING", "unicode": 128589},
  {"name": "PERSON WITH POUTING FACE", "unicode": 128590},
  {"name": "PERSON WITH FOLDED HANDS", "unicode": 128591},
  {"name": "BLACK SCISSORS", "unicode": 9986},
  {"name": "WHITE HEAVY CHECK MARK", "unicode": 9989},
  {"name": "AIRPLANE", "unicode": 9992},
  {"name": "ENVELOPE", "unicode": 9993},
  {"name": "RAISED FIST", "unicode": 9994},
  {"name": "RAISED HAND", "unicode": 9995},
  {"name": "VICTORY HAND", "unicode": 9996},
  {"name": "PENCIL", "unicode": 9999},
  {"name": "BLACK NIB", "unicode": 10002},
  {"name": "HEAVY CHECK MARK", "unicode": 10004},
  {"name": "HEAVY MULTIPLICATION X", "unicode": 10006},
  {"name": "SPARKLES", "unicode": 10024},
  {"name": "EIGHT SPOKED ASTERISK", "unicode": 10035},
  {"name": "EIGHT POINTED BLACK STAR", "unicode": 10036},
  {"name": "SNOWFLAKE", "unicode": 10052},
  {"name": "SPARKLE", "unicode": 10055},
  {"name": "CROSS MARK", "unicode": 10060},
  {"name": "NEGATIVE SQUARED CROSS MARK", "unicode": 10062},
  {"name": "BLACK QUESTION MARK ORNAMENT", "unicode": 10067},
  {"name": "WHITE QUESTION MARK ORNAMENT", "unicode": 10068},
  {"name": "WHITE EXCLAMATION MARK ORNAMENT", "unicode": 10069},
  {"name": "HEAVY EXCLAMATION MARK SYMBOL", "unicode": 10071},
  {"name": "HEAVY BLACK HEART", "unicode": 10084},
  {"name": "HEAVY PLUS SIGN", "unicode": 10133},
  {"name": "HEAVY MINUS SIGN", "unicode": 10134},
  {"name": "HEAVY DIVISION SIGN", "unicode": 10135},
  {"name": "BLACK RIGHTWARDS ARROW", "unicode": 10145},
  {"name": "CURLY LOOP", "unicode": 10160},
  {"name": "ROCKET", "unicode": 128640},
  {"name": "RAILWAY CAR", "unicode": 128643},
  {"name": "HIGH-SPEED TRAIN", "unicode": 128644},
  {"name": "HIGH-SPEED TRAIN WITH BULLET NOSE", "unicode": 128645},
  {"name": "METRO", "unicode": 128647},
  {"name": "STATION", "unicode": 128649},
  {"name": "BUS", "unicode": 128652},
  {"name": "BUS STOP", "unicode": 128655},
  {"name": "AMBULANCE", "unicode": 128657},
  {"name": "FIRE ENGINE", "unicode": 128658},
  {"name": "POLICE CAR", "unicode": 128659},
  {"name": "TAXI", "unicode": 128661},
  {"name": "AUTOMOBILE", "unicode": 128663},
  {"name": "RECREATIONAL VEHICLE", "unicode": 128665},
  {"name": "DELIVERY TRUCK", "unicode": 128666},
  {"name": "SHIP", "unicode": 128674},
  {"name": "SPEEDBOAT", "unicode": 128676},
  {"name": "HORIZONTAL TRAFFIC LIGHT", "unicode": 128677},
  {"name": "CONSTRUCTION SIGN", "unicode": 128679},
  {"name": "POLICE CARS REVOLVING LIGHT", "unicode": 128680},
  {"name": "TRIANGULAR FLAG ON POST", "unicode": 128681},
  {"name": "DOOR", "unicode": 128682},
  {"name": "NO ENTRY SIGN", "unicode": 128683},
  {"name": "SMOKING SYMBOL", "unicode": 128684},
  {"name": "NO SMOKING SYMBOL", "unicode": 128685},
  {"name": "BICYCLE", "unicode": 128690},
  {"name": "PEDESTRIAN", "unicode": 128694},
  {"name": "MENS SYMBOL", "unicode": 128697},
  {"name": "WOMENS SYMBOL", "unicode": 128698},
  {"name": "RESTROOM", "unicode": 128699},
  {"name": "BABY SYMBOL", "unicode": 128700},
  {"name": "TOILET", "unicode": 128701},
  {"name": "WATER CLOSET", "unicode": 128702},
  {"name": "BATH", "unicode": 128704},
  {"name": "CIRCLED LATIN CAPITAL LETTER M", "unicode": 9410},
  {"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER A", "unicode": 127344},
  {"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER B", "unicode": 127345},
  {"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER O", "unicode": 127358},
  {"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER P", "unicode": 127359},
  {"name": "NEGATIVE SQUARED AB", "unicode": 127374},
  {"name": "SQUARED CL", "unicode": 127377},
  {"name": "SQUARED COOL", "unicode": 127378},
  {"name": "SQUARED FREE", "unicode": 127379},
  {"name": "SQUARED ID", "unicode": 127380},
  {"name": "SQUARED NEW", "unicode": 127381},
];

联系我们

如果您在接入使用过程中有任何疑问,请扫码加入微信群,或加入QQ群:788910197 咨询。