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

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

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

![](https://qcloudimg.tencent-cloud.cn/raw/bf06763d76d0c6e0c1871a1da401f6ab.png)

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

>? [TUIKit](https://pub.dev/packages/tencent_cloud_chat_uikit)在升级至1.1.0版本后，对表情能力用法有较大改动。请您在升级最新版后，根据本文档指引，重新适配接入表情能力。谢谢～

## STEP1: 自定义表情图片资源

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

### 将文件导入项目

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

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

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

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

此路径的表情图片放置引入，可参考我们的[Demo](https://github.com/TencentCloud/chat-demo-flutter/tree/main/assets/custom_face_resource)。

![](https://qcloudimg.tencent-cloud.cn/raw/9cf910304e3161bd17336054de3ba7d9.png)

### 声明表情文件

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

```yaml
flutter:
  assets:
    - assets/custom_face_resource/
```

### 在代码中配置图片资源List

>? 本步骤代码示例请[参考此处](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/utils/constant.dart)，直接看 `emojiList` 即可。

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

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

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

示例代码如下：

```dart
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](https://unicode.org/emoji/charts/full-emoji-list.html) Emoji表情，才需完成本步骤。

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

该列表，我们提供一套示例，在[附录](#unicode)中。

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

## STEP3: 表情资源预读入内存

>?
> 本步骤代码示例请[参考此处](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/pages/app.dart)，直接看 `setCustomSticker` 方法即可。
> QQ小表情包，TUIKit已自带提供，无需在本步骤引入，请关注后续步骤。

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

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

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

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

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

```dart
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表情包，表情项二演示如何使用图片类型（包含大或小）表情包。您可以根据需要，使用全部或部分代码。

```dart
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 组件添加表情解析能力

>?
> 本步骤代码示例请[参考此处](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/chat.dart)，重点浏览 `renderCustomStickerPanel`, `customStickerPanel` 及 `customEmojiList` 即可。

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

```dart
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` 变量，用于存放图片小表情列表。

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

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

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

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

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

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

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

![](https://qcloudimg.tencent-cloud.cn/raw/f096cf79710207a0db6f33936a55bbb0.png)

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

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

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

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

[](id:unicode)

## 附录： Emoji Unicode 列表示例

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

```dart
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},
];
```

[](id:contact)

## 联系我们

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

![](https://qcloudimg.tencent-cloud.cn/raw/e830ae8c7b8d9253eb71e7c3d9f7b2be.png)

