update flutter uikit to 1.1.0

This commit is contained in:
anonymous 2022-12-27 16:36:17 +08:00
parent 862939e611
commit 5cba5b30cf
68 changed files with 3177 additions and 1254 deletions

View File

@ -1,3 +1,18 @@
## 1.1.0
* Add: Supports two new languages, Japanese and Korean.
* Add: Supports adding new other languages, apart from our default ones, including English, Chinese(Simplified and Traditional), Japanese and Korean, or modifying the translations, refers to [this documentation](https://www.tencentcloud.com/document/product/1047/52154).
* Add: Sticker plug-in has been embedded in TUIKit by default. Now we support three types of stickers, Unicode Emoji, small image emoji and big image stickers, the usage has been optimized, refers to [this documentation](https://www.tencentcloud.com/document/product/1047/52227).
* Optimize: Themes, more customization.
* Optimize: The animation of the input area, keyboard, sticker panel and the more panel.
* Optimize: Emoji, both Unicode and small images, can be inserted to any position in text messages.
* Optimize: Avatar in profile can be previewed with a large image.
* Optimize: UserID in profile can be copied.
* Optimize: Several UI details, including `TIMUIKitAddFriend`, `TIMUIKitAddGroup`, `TIMUIKitGroupProfile` and `TIMUIKitProfile`.
* Optimize: `TIMUIKitGroupProfile` and `TIMUIKitProfile` can update automatically after `ID` changed.
* Optimize: New loading animation when downloading the image/video on `TIMUIKitGroupChat`.
* Fix: Some bugs.
## 1.0.1
* Modify: Remove `groupTRTCTipsItemBuilder` from `MessageItemBuilder`, please use `customMessageItemBuilder` instead.

View File

@ -1,4 +1,41 @@
<style>
.button-9 {
appearance: button;
backface-visibility: hidden;
background-color: #1d52d9;
border-radius: 6px;
border-width: 0;
box-shadow: rgba(50, 50, 93, .1) 0 0 0 1px inset,rgba(50, 50, 93, .1) 0 2px 5px 0,rgba(0, 0, 0, .07) 0 1px 1px 0;
box-sizing: border-box;
color: #fff;
cursor: pointer;
font-family: -apple-system,system-ui,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif;
font-size: 100%;
height: 44px;
line-height: 1.15;
margin: 12px 0 0;
outline: none;
overflow: hidden;
padding: 0 20px;
position: relative;
text-align: center;
text-transform: none;
transform: translateZ(0);
transition: all .2s,box-shadow .08s ease-in;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
}
.button-9:disabled {
cursor: default;
}
.button-9:focus {
box-shadow: rgba(50, 50, 93, .1) 0 0 0 1px inset, rgba(50, 50, 93, .2) 0 6px 15px 0, rgba(0, 0, 0, .1) 0 2px 2px 0, rgba(50, 151, 211, .3) 0 0 0 4px;
}
</style>
<br>
@ -34,6 +71,8 @@ More languages:
<a target="_blank" href="https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html"><button type="button" class="button-9" role="button">Official Documentation</button></a>
<br>
## Experience DEMO
You can experience our Chat and Voice/Video Call modules via the following demos.
@ -59,7 +98,7 @@ You can experience our Chat and Voice/Video Call modules via the following demos
You can use these UI components to build your APP with the In-APP chat module quickly and easily.
<img width="1447" alt="TUIKit screenshots" src="https://user-images.githubusercontent.com/24520036/206137134-98fb5b4f-cfe2-4d38-bcb3-761d416c1a8b.png">
![img](https://qcloudimg.tencent-cloud.cn/raw/f140dd76be01a65abfb7e6ba2bf50ed5.png)
Currently, Flutter [TUIKit](https://www.tencentcloud.com/document/product/1047/50059?from=pub) contains the following main components:
@ -77,29 +116,13 @@ Currently, Flutter [TUIKit](https://www.tencentcloud.com/document/product/1047/5
Also, there are some other useful components and widgets, that can help to build your APP, and meet your business needs, including group entry application list and group member list, etc.
For the source code of the project in the figure above, see [chat-demo-flutter](https://github.com/TencentCloud/chat-demo-flutter). The project is open source and can be used directly.
For the source code of the project in the figure above, see [im-flutter-uikit](https://github.com/TencentCloud/tc-chat-demo-flutter). The project is open source and can be used directly.
### Supported Platforms
## Supported Platforms
- Android
- iOS
- Web(Since version of 0.1.4)
### What's more
In addition to Flutter SDK & TUIKit, we have numerous SDKs that covering all platforms. The following platforms can communicate with each other and provide services across devices and platforms.
[**chat-uikit-android**](https://github.com/TencentCloud/chat-uikit-android)
[**chat-uikit-ios**](https://github.com/TencentCloud/chat-uikit-ios)
[**chat-uikit-vue**](https://github.com/TencentCloud/chat-uikit-vue)
[**chat-uikit-react**](https://github.com/TencentCloud/chat-uikit-react)
[**chat-uikit-uniapp**](https://github.com/TencentCloud/chat-uikit-uniapp)
[**chat-uikit-wechat**](https://github.com/TencentCloud/chat-uikit-wechat)
- Web(After version of 0.1.4)
## Get Started
@ -840,6 +863,21 @@ TIMUIKitSearchMsgDetail(
);
```
## What's more
In addition to Flutter SDK, we have numerous SDKs that covering all platforms. The following platforms can communicate with each other and provide services across devices and platforms.
| Platform | Introduction | Demo | Download | UI Components library |
| --- | --- | --- | --- | --- |
| Android | Compatible with JDK 1.6 and Android SDK version 14 and later | [Get](https://www.tencentcloud.com/document/product/1047/34279) | [Get](https://github.com/TencentCloud/TIMSDK/tree/master/Android/IMSDK) | [Get](https://www.tencentcloud.com/document/product/1047/50062) |
| iOS | Compatible with iOS 8.0 and later | [Get](https://www.tencentcloud.com/document/product/1047/34279) | [Get](https://github.com/TencentCloud/TIMSDK/tree/master/iOS/IMSDK) | [Get](https://www.tencentcloud.com/document/product/1047/50062) |
| Mac | Compatible with OS X 10.10 and later | - | [Get](https://github.com/TencentCloud/TIMSDK/tree/master/Mac/IMSDK) | - |
| Windows | C and C++ are included. Compatible with Windows 7, Windows 8 and 8.1, and Windows 10. Both 32-bit and 64-bit programs can be connected | - | [Get](https://github.com/TencentCloud/TIMSDK/tree/master/Windows/IMSDK) | - |
| Web | Supports Internet Explorer 11+, Chrome 7+, Firefox 3.6+, Opera 12+ and Safari 6+ | [Get](https://www.tencentcloud.com/document/product/1047/34279) | [Get](https://www.npmjs.com/package/tim-js-sdk) | [Get](https://www.tencentcloud.com/document/product/1047/50061) |
| Unity | Supports 2020.2.7f1c1 or later | - | [Get](https://www.tencentcloud.com/document/product/1047/46263) | - |
| Flutter | Supports Flutter 2 & dart 2.12 and later, deploying to Android, iOS, Web, macOS and Windows. | [Get](https://www.tencentcloud.com/document/product/1047/34279) | Here | [Get](https://pub.dev/packages/tencent_cloud_chat_uikit) |
| Electron | Electron SDK | - | [Get](https://github.com/tencentyun/im_electron_demo) | - |
## Contact Us
Please do not hesitate to contact us in the following place, if you have any further questions or tend to learn more about the use cases.
@ -848,4 +886,4 @@ Please do not hesitate to contact us in the following place, if you have any fur
- WhatsApp Group: <https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A>
- QQ Group: 788910197, chat in Chinese
Our Website: <https://www.tencentcloud.com/products/im?from=pub>
Our Website: <https://www.tencentcloud.com/products/im?from=pub>

View File

@ -1,2 +1,172 @@
【企微文档】TIM Flutter国际化方案
https://doc.weixin.qq.com/doc/w3_AWcADQY0AA8u9RhcnhqRSSW6RZc4w?scode=AJEAIQdfAAoaC0QLyWAWcADQY0AA8
腾讯云IM Flutter TUIKit默认自带 英文/简体中文/繁体中文/日语/韩语 语言包,作为界面展示语言。
根据此文档指引,您可以使用默认语言包,也可自定义语言翻译表述,并增添额外的非自带语言的支持。
![](https://qcloudimg.tencent-cloud.cn/raw/2df62f8a62453c063c396cb656dd07bc.png)
## 使用自带语言
如果您的App需要的语言仅包括英语/简体中文/繁体中文/日语/韩语,请参考本部分。
### 跟随系统语言
直接使用TUIKit即可无需额外步骤。插件内部会跟随系统语言自适应。
### 预指定显示的语言
如果您需要在初始化是手动设置TUIKit界面语言请在`TIMUIKitCore.getInstance()`中调用`init`方法时,传入需要的语言。
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
final isInitSuccess = await _coreInstance.init(
language: LanguageEnum.en, // 请在此处定义语言,枚举值见下方
// ...其他配置
);
```
语言可选项,枚举值为:
```dart
enum LanguageEnum {
zhHant, //繁体中文
zhHans, //简体中文
en, // 英文
ko, // 韩语
ja // 日语
}
```
### 实时动态修改
调用 `I18nUtils(null, language);` 即可。此处的 `language` 为 [ISO 639-1 语言代码](#code)。示例如下:
```dart
I18nUtils(null, "en");
```
>? 语言代码清单见[附录](#code)。
## 使用更多语言/自定义翻译表述
如果您需要支持,除 英文/简体中文/繁体中文/日语/韩语 外的更多语言,或更改我们部分词条的翻译,请参考本部分。
>? 本方案仅适用于,目标语言为的阅读方向为从左至右的语言。对于阅读方向从右至左的小语种,如阿拉伯语,请自行在[GitHub fork](https://github.com/TencentCloud/chat-uikit-flutter)一份我们的源码,完成自定义左右镜像开发适配。
### 新增语言词条包
本章节的核心为本部分, 即将您自定义的国际化多语言词条库文件注入腾讯云IM项目内。
#### 获取语言模板
在您的项目中,允许如下代码。
```shell
flutter pub run tencent_im_base
```
根据提示,选择 `A` 选项。
![](https://qcloudimg.tencent-cloud.cn/raw/01215e7861ed2736c0155c456ad2d0d6.png)
此时我们自带的所有语言包以JSON文件模板的形式存储于您项目根目录下`languages/` 路径内。
![](https://qcloudimg.tencent-cloud.cn/raw/2618d546ece854d93cfe21d1ad342ade.png)
请复制一份您熟悉的语言JSON模板文件例如简体中文版`strings_zh-Hans.i18n.json`。
复制一份新的,并命名为 `strings_${语言编码}.i18n.json`。其中,`${语言编码}` 需要替换为 [ISO 639-1 语言代码](#code)。例如,丹麦语,`strings_da.i18n.json`。
如果您需要兼容支持多个新语言,复制多份,并准确指定每一份的语言编码即可。
#### 个性化自定义翻译
此时,您可以修改上一步 **复制新生成的目标语言模板**
打开您复制生成的新文档,**保留不动JSON的md5 key值**将所有的value值替换成对应目标翻译语言。
![](https://qcloudimg.tencent-cloud.cn/raw/540536815ec579ca4343a7013a768178.png)
>? 如果您需要修改默认语言模板的翻译文案,也可直接打开自动生成的语言模板,进行修改。**除简体中文版本外**,其他翻译文案,均可修改。
翻译完成后,`languages/` 内,包含原始提供的自带语言模板,及您复制生成的其他语言词条集。
![](https://qcloudimg.tencent-cloud.cn/raw/0b409d05e26b81b60a4babed07936cda.png)
#### 回装您的语言包
在您项目的根目录下,执行 `flutter pub run tencent_im_base` 命令,并选择 `B` 选项。
代码运行完成后,即可使您的语言包,在当前电脑本地生效。
![](https://qcloudimg.tencent-cloud.cn/raw/7823200ee5f323bc254aad61be122907.png)
>? 如果您是团队协同开发,或使用了远程流水线编译。需要在您同事电脑中或流水线编译命令脚本中,使用同样的方式,执行本章节所述方法。
### 跟随系统语言
直接使用TUIKit即可无需额外步骤。
只要您新增的语言词条包命名[符合标准](#code),插件内部会跟随系统语言自适应。
### 预指定显示的语言
如果您需要在初始化是手动设置TUIKit界面语言请在`TIMUIKitCore.getInstance()`中调用`init`方法时,传入需要的语言。
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
final isInitSuccess = await _coreInstance.init(
extraLanguage: "ja", // 请在此处定义语言ISO 639-1 语言代码 见下方
// ...其他配置
);
```
### 实时动态修改
调用 `I18nUtils(null, language);` 即可。此处的 `language` 为 [ISO 639-1 语言代码](#code)。示例如下:
```dart
I18nUtils(null, "ja");
```
>? 语言代码清单见[附录](#code)。
[](id:code)
## 附录:语言代码表
| 语言 | 代码 | 语言 | 代码 |
|--------|--------|--------|--------|
| 阿拉伯语 | ar | 保加利亚语 | bg |
| 克罗地亚语 | hr | 捷克语 | cs |
| 丹麦语 | da | 德语 | de |
| 希腊语 | el | 英语 | en |
| 爱沙尼亚语 | et | 西班牙语 | es |
| 芬兰语 | fi | 法语 | fr |
| 爱尔兰语 | ga | 印地语 | hi |
| 匈牙利语 | hu | 希伯来语 | he |
| 意大利语 | it | 日语 | ja |
| 朝鲜语/韩语 | ko | 拉脱维亚语 | lv |
| 立陶宛语 | lt | 荷兰语 | nl |
| 挪威语 | no | 波兰语 | pl |
| 葡萄牙语 | pt | 瑞典语 | sv |
| 罗马尼亚语 | ro | 俄语 | ru |
| 塞尔维亚语 | sr | 斯洛伐克语 | sk |
| 斯洛文尼亚语 | sl | 泰语 | th |
| 土耳其语 | tr | 乌克兰语 | uk-UA |
| 中文(简体) | zh-Hans | 中文(繁体) | zh-Hant |
完整版[请见此处](https://quickref.me/iso-639-1)。
## 联系我们[](id:contact)
如果您在接入使用过程中有任何疑问,请通过如下方式联系我们。
- [Telegram Group](https://t.me/+1doS9AUBmndhNGNl)
- [WhatsApp Group](https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A)

169
doc/I18N_en.md Normal file
View File

@ -0,0 +1,169 @@
English, Simplified Chinese, Traditional Chinese, Japanese and Korean have been embedded in Tencent Cloud Chat TUIKit as the default interface languages.
Adding other interface languages or modifying the current language items are available for you, according to the instructions of this tutorial.
![](https://qcloudimg.tencent-cloud.cn/raw/2df62f8a62453c063c396cb656dd07bc.png)
## Using the default languages
If only Chinese(both traditional and simplified), English, Japanese and Korean are needed for your application, please refer to this section.
### Choosing device language
No further steps are needed, as meeting device language can be automatically.
### Pre-set the language manually
If you tend to specify the language manually, please provide the target language Enum to `init()` in `TIMUIKitCore.getInstance()`.
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
final isInitSuccess = await _coreInstance.init(
language: LanguageEnum.en, // Enums as below
// ...Other configurations
);
```
Enum options for language
```dart
enum LanguageEnum {
zhHant, // Chinese, traditional
zhHans, // Chinese, simplified
en, // English
ko, // korean
ja // Japanese
}
```
### Modify language dynamically
Please just invoking `I18nUtils(null, language);`, while the `language` here should be set as the [ISO 639 Language codes](#code).
Example code:
```dart
I18nUtils(null, "en");
```
## Need more languages / customize translation items
Adding languages, apart from English, Simplified Chinese, Traditional Chinese, Japanese and Korean, or modifying some translation items words, can be referred to this section.
>? This solution only works for languages with a left-to-right reading direction. For small languages that read from right to left, such as Arabic, please fork our source code from [our GitHub repository](https://github.com/TencentCloud/chat-uikit-flutter) to complete the custom left and right mirroring Development adaptation, and import to your project manually.
### Adding language translation files
The key of this section is this part, that is, inject your custom internationalized language file into the Tencent Cloud Chat.
#### Get the language template
Run the following command, and choose `A` as instruction.
```shell
flutter pub run tencent_im_base
```
![](https://qcloudimg.tencent-cloud.cn/raw/01215e7861ed2736c0155c456ad2d0d6.png)
Now, all the pre-set default language files, as JSON, have been generated to your project, `languages/` directory.
![](https://qcloudimg.tencent-cloud.cn/raw/2618d546ece854d93cfe21d1ad342ade.png)
Duplicate for language files, based on the template you are most familiar with.
The newly duplicated language files should be named as `strings_${language code}.i18n.json`. While, `${language code}` should be replaced by [ISO 639 Language Codes](#code). Such as, the file containing Danish language items should be named as `strings_da.i18n.json`.
Duplicate multiple language files, if you need to support multiple other languages.
#### Customize translations
Now, you can modify the language files generated in the previous step.
Open each language file, including the files you just duplicated, except `strings_zh-Hans.i18n.json`, translate or modify each `value` to target language, while keeping the md5 key unchanged.
![](https://qcloudimg.tencent-cloud.cn/raw/540536815ec579ca4343a7013a768178.png)
After translation and modification, all the supported languages files, including those you duplicated and default, should be in the `languages/` directory.
![](https://qcloudimg.tencent-cloud.cn/raw/0b409d05e26b81b60a4babed07936cda.png)
#### Activate those language files
Run the following command, and choose `B` as instruction.
```dart
flutter pub run tencent_im_base
```
After the script has finished, those customization languages are activated on your local Flutter environment.
![](https://qcloudimg.tencent-cloud.cn/raw/7823200ee5f323bc254aad61be122907.png)
>? If you are developing with a team collaboratively, or using DevOps pipeline compilation. You also need to execute this solution on your colleague's computer or in the DevOps pipeline compilation command script.
### Choosing device language
No further steps are needed, as meeting device language can be automatically.
### Pre-set the language manually
If you tend to specify the language manually, please provide the [ISO 639 Language Codes](#code) of the language to `init()` in `TIMUIKitCore.getInstance()`.
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
final isInitSuccess = await _coreInstance.init(
extraLanguage: "ja", // ISO 639 Language Codes
// ...Other configurations
);
```
### Modify language dynamically
Please just invoking `I18nUtils(null, language);`, while the `language` here should be set as the [ISO 639 Language codes](#code).
Example code:
```dart
I18nUtils(null, "en");
```
[](id:code)
## Appendix: Language codes
| Language | Code | Language | Code |
|--------|--------|--------|--------|
| Arabic | ar | Bulgarian | bg |
| Croatian | hr | Czech | cs |
| Danish | da | German | de |
| Greek | el | English | en |
| Estonian | et | Spanish | es |
| Finnish | fi | French | fr |
| Irish | ga | Hindi | hi |
| Hungarian | hu | Hebrew | he |
| Italian | it | Japanese | ja |
| Korean | ko | Latvian | lv |
| Lithuanian | lt | Dutch | nl |
| Norwegian | no | Polish | pl |
| Portuguese | pt | Swedish | sv |
| Romanian | ro | Russian | ru |
| Serbian | sr | Slovak | sk |
| Slovenian | sl | Thai | th |
| Turkish | tr | Ukrainian | uk |
| Chinese (Simplified) | zh-Hans | Chinese (Traditional) | zh-Hant |
## Contact us[](id:contact)
If there's anything unclear or you have more ideas, feel free to contact us!
- [Telegram Group](https://t.me/+1doS9AUBmndhNGNl)
- [WhatsApp Group](https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A)

453
doc/表情.md Normal file
View File

@ -0,0 +1,453 @@
以下为您介绍如何为腾讯云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)

457
doc/表情_en.md Normal file
View File

@ -0,0 +1,457 @@
## Overview
Tencent Cloud Chat Flutter TUIKit provides powerful Emoji and Sticker modules to help you customize the Emoji and Sticker sharing of your app.
Through simple configurations, you can easily choose and integrate those three types of stickers to your app.
| Sticker Type | Message Type | Embed to text | Sending content | Render | Import | Provide by default |
|---------|---------|---------|---------|---------|---------|---------|
| [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) Emoji | Text Message | Yes | [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) |[Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) to Emoji can be compiled automatically by devices, while different devices or platforms have different Emoji rendering | [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) List | Sample [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) List is provided [here](#unicode) |
| Small Image Emoji | Text Message | Yes | Image name | Match local Asset image resources according to name automatically | Image stored in asset, and define `List` | A set of QQ Emoji is provided by default |
| Big Image Sticker | Sticker Message | No | `baseURL` join with image name, as asset path | Render image from asset | Image stored in asset, and define `List` | - |
![](https://qcloudimg.tencent-cloud.cn/raw/023c3c716b481401c8da763e66ba08d1.png)
Now, let's start integrating Emoji and Sticker to your app with TUIKit.
>? The usage of this module has been modified since the 1.1.0 version of [TUIKit](https://pub.dev/packages/tencent_cloud_chat_uikit). Please check all the parts in this tutorial if you upgrade the version.
## STEP 1: Customize Image Emoji and Sticker
>? **This step is optional:**
> This step is necessary only if image stickers, except default QQ emoji one, includes both small and big, are needed.
> QQ Emoji set is provided by default, and is unnecessary to import in this step.
### Import Image File to Project
Please add your image resources file to `assets/custom_face_resource/` of your project, including both small and big images.
In this directory, separate subdirectory with different sticker packages, means each Tabs on Sticker panel. Only one type of sticker is allowed in each Tab(package or subdirectory).
Name those subdirectories differently, and this name will be used as the `name` field of `CustomEmojiFaceData` and `CustomStickerPackage` in the following steps. Please allocate the name as you need.
Also, please make sure that all image resource files do not have the same name.
You can refer to our [sample project](https://github.com/TencentCloud/chat-demo-flutter/tree/main/assets/custom_face_resource), if not clear.
![](https://qcloudimg.tencent-cloud.cn/raw/060d5846a3ad0f5078f40eb05686f9ec.png)
### Add assets to app
Open `pubspec.yaml`, add those following lines to `flutter` => `assets`.
```yaml
flutter:
assets:
- assets/custom_face_resource/
```
### Configure assets list
>? The sample code for this part can be found [here](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/utils/constant.dart), mainly focused on `emojiList`.
Define a static `List<CustomEmojiFaceData>` in your project, aiming for transferring the local image assets to TUIKit, as List.
In this `List`, each item is `CustomEmojiFaceData`, while it constitutes each Tab in the sticker panel.
```dart
CustomEmojiFaceData(
{
String name, // The name of the package and subdirectory.
String icon, // The file name of the icon on the Tab.
List<String> list, // The list of the files name.
bool isEmoji //Whether it contains small image emojis, default is big image stickers.
}
);
```
Sample Code:
```dart
static final List<CustomEmojiFaceData> emojiList = [
// Small Image Emoji, embedded in text messages.
CustomEmojiFaceData(
name: '4349',
icon: "aircraft.png",
isEmoji: true,
list: [
"aircraft.png",
"alarmClock.png",
"anger.png",
// ...
]),
// Big image stickers, sent as sticker messages independently.
CustomEmojiFaceData(
name: '4350',
icon: "menu@2x.png",
list: [
"yz00@2x.png",
// ...
]),
]
```
## Step 2: Customize the Unicode Emoji List
>? **This step is optional:**
> This step is necessary only if Unicode Emoji is needed.
Define a static `List<Map<String, Object>>` of Unicode in your project, you can build it based on the [sample list we provided](#unicode).
You can add, delete and modify some items in this List, based on the official [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html).
## Step 3: Cache the Emoji and Sticker to memory
>?
> - Sample code for this step [can be found here](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/pages/app.dart), mainly focus on `setCustomSticker` function.
> - QQ Emoji has been embedded by default, and unnecessary to do this step.
Cache those Emoji and Stickers to global `Provider`, memory, **just after your app launched, and before the first `TIMUIKitChat` shows**.
**This steps should only be done once.** Aiming for reducing the load of memory IO, as rendering each sticker is a high frequency event, and will cost a lot.
The instance of each sticker is generated by the following `CustomSticker` class. It will show Unicode Emoji if `unicode` is not null, otherwise it shows an image.
```dart
class CustomSticker {
int? unicode; // Unicode int value。It will show Unicode Emoji if this field is not null, otherwise it shows an image.
String name; // The name of the sticker
int index; // The index of the sticker
bool isEmoji; // Whether it is a small image emoji, while a big image sticker is as default.
}
```
The instance of each Tab on sticker panel, each sticker package, is generated by the `CustomStickerPackage` class.
```dart
class CustomStickerPackage { // Each Tab on sticker panel, each sticker package
String name; // The name of this sticker package, subdirectory, and the Tab.
String? baseUrl; // Sticker package baseUrlrecommend specify as "assets/custom_face_resource/${package name}"
List<CustomSticker> stickerList; // The list of the image files name
CustomSticker menuItem; // The file name of the icon of Tab
bool isEmoji; // Whether it contains small image emojis, while big image stickers are as default.
}
```
For the classes shown above, we provide sample codes as follows, for the code you may need to write.
`Solution A` shows the usage of Unicode Emoji while `Solution B` shows the usage of image stickers. You can choose all or part of them as needed.
```dart
setCustomSticker() async {
// Define a list to store sticker packages.
List<CustomStickerPackage> customStickerPackageList = [];
// Solution A: Use Emoji Unicode list. Can be added to text messages.
// `emojiData` comes from step 2.
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]));
// Solution B: Use the image sticker.
// Please make sure `customEmojiPackage.name` is the name of the subdirectory.
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;
}
```
## STEP 4: Adding those stickers to TIMUIKitChat
>?
> - Sample code for this step [can be found here](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/chat.dart), mainly focus on `renderCustomStickerPanel`, `customStickerPanel` and `customEmojiList`.
Copy the following codes to the class that contains the `TIMUIKitChat` widget directly.
```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);
}
```
### STEP 4.1: Render Small Image Emoji
>? **This step is optional:**
> - This step is necessary only if small images emoji are needed for your app, except the QQ Emoji we provided by default.
> - Unicode Emoji and small image emoji are similar, it is not recommended to integrate these two types of emoji at the same time.
- STEP 4.1(a) shows the usage of using custom small image emoji.
- STEP 4.1(b) shows the usage of using default QQ emojis.
It is recommended to choose one of them.
If you tend to use both of them, please make sure those image resource files do not have the same name.
#### STEP 4.1(a): Render custom small image emoji
Add a `List customEmojiList` field to the `build` function of the `Widget` that contains `TIMUIKitChat`, storing the list of small image emoji.
```dart
List customEmojiList =
Const.emojiList.where((element) => element.isEmoji == true).toList();
```
And transferring this list to `customEmojiStickerList` of `TIMUIKitChat`.
```dart
return TIMUIKitChat(
customEmojiStickerList: customEmojiList,
// ......
);
```
>?
> If this widget is a `StatefulWidget`, choosing to place this list to state, and execute the `where` method once, to improve the performance are recommended.
#### STEP 4.1(b): Enable QQ Emoji
Enable the `isUseDefaultEmoji` of `TIMUIKitChatConfig` from `TIMUIKitChat` to `true`. Meanwhile, a Tab shows the default QQ Emoji will occur on the left of the sticker panel.
```dart
return TIMUIKitChat(
config: TIMUIKitChatConfig(
isUseDefaultEmoji: true,
// ......
),
// ......
);
```
![](https://qcloudimg.tencent-cloud.cn/raw/ed14b886c08cb1c0e8371ba54925bd71.png)
### STEP 4.2: Add the sticker panel to TIMUIKitChat
Transfer the function, you copied in this step, to the `customStickerPanel` field of `TIMUIKitChat`.
```dart
return TIMUIKitChat(
customStickerPanel: renderCustomStickerPanel,
// ......
);
```
That's all you need to integrate Emoji and Sticker modules to your app, with Tencent Cloud Chat Flutter TUIKit.
[](id:unicode)
## Appendix: Sample list of Emoji Unicodes
The list is for sample and presentation purposes only, you can modify it as you need.
```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)
## Contact Us
If there's anything unclear or you have more ideas, feel free to contact us!
- Telegram Group: https://t.me/+1doS9AUBmndhNGNl
- WhatsApp Group: https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A

454
doc/表情_国际中文.md Normal file
View File

@ -0,0 +1,454 @@
以下为您介绍如何为腾讯云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/023c3c716b481401c8da763e66ba08d1.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/060d5846a3ad0f5078f40eb05686f9ec.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/ed14b886c08cb1c0e8371ba54925bd71.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)
## 联系我们
If there's anything unclear or you have more ideas, feel free to contact us!
- Telegram Group: https://t.me/+1doS9AUBmndhNGNl
- WhatsApp Group: https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A

View File

@ -64,13 +64,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.1"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
cached_network_image:
dependency: transitive
description:
@ -141,13 +134,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
chewie:
dependency: transitive
description:
@ -211,20 +197,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.4"
dio:
dependency: transitive
description:
name: dio
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.6"
disk_space:
dependency: transitive
description:
@ -455,13 +427,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.2"
i18n:
dependency: transitive
description:
name: i18n
url: "https://pub.dartlang.org"
source: hosted
version: "3.4.1"
image_gallery_saver:
dependency: transitive
description:
@ -833,20 +798,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
quick_log:
dependency: transitive
description:
name: quick_log
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.1"
quiver:
dependency: transitive
description:
@ -931,6 +882,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
shell:
dependency: transitive
description:
name: shell
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
sky_engine:
dependency: transitive
description: flutter
@ -1005,14 +963,14 @@ packages:
name: tencent_cloud_chat_sdk
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.4"
version: "5.0.6"
tencent_cloud_chat_uikit:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "1.0.1"
version: "1.1.0"
tencent_extended_text:
dependency: transitive
description:
@ -1040,14 +998,14 @@ packages:
name: tencent_im_base
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.7"
version: "1.0.14"
tencent_im_sdk_plugin_platform_interface:
dependency: transitive
description:
name: tencent_im_sdk_plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.9"
version: "0.3.10"
tencent_im_sdk_plugin_web:
dependency: "direct main"
description:
@ -1055,6 +1013,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
tencent_keyboard_visibility:
dependency: transitive
description:
name: tencent_keyboard_visibility
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
tencent_open_file:
dependency: transitive
description:
@ -1075,7 +1040,7 @@ packages:
name: tencent_wechat_camera_picker
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.2+5"
version: "3.6.5"
term_glyph:
dependency: transitive
description:
@ -1090,6 +1055,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.12"
tim_ui_kit_sticker_plugin:
dependency: transitive
description:
name: tim_ui_kit_sticker_plugin
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
transparent_image:
dependency: transitive
description:

View File

@ -532,7 +532,6 @@ class TUIChatGlobalModel extends ChangeNotifier with TIMUIKitClass {
return;
}
if (!(_c2cMessageFromUserActiveMap[toUser] ?? false)) {
print("to user is not online");
return;
}
V2TimMsgCreateInfoResult? res = await _messageService.createCustomMessage(

View File

@ -0,0 +1,26 @@
import 'package:flutter/cupertino.dart';
import 'package:shared_preferences/shared_preferences.dart';
class TUISettingModel extends ChangeNotifier {
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
final String keyTitle = "tencent_chat_uikit_";
double? _keyboardHeight;
double get keyboardHeight => _keyboardHeight ?? 0;
set keyboardHeight(double value) {
if(value > 40 && _keyboardHeight != value){
_keyboardHeight = value;
updateLocalSetting("${keyTitle}keyboardHeight", value.toString());
}
}
updateLocalSetting(String key, String value) async {
SharedPreferences prefs = await _prefs;
prefs.setString(key, value);
}
init() async {
SharedPreferences prefs = await _prefs;
_keyboardHeight = double.parse(prefs.getString("${keyTitle}keyboardHeight") ?? "0");
}
}

View File

@ -5,6 +5,31 @@ import 'package:tencent_cloud_chat_uikit/data_services/core/tim_uikit_config.dar
enum AppStatus { foreground, background }
enum LanguageEnum {
/// Chinese, Traditional
zhHant,
/// Chinese, Simplified
zhHans,
/// English
en,
/// Korean
ko,
/// Japanese
ja,
}
const languageEnumToString = {
LanguageEnum.zhHant: "zh-Hant",
LanguageEnum.zhHans: "zh-Hans",
LanguageEnum.en: "en",
LanguageEnum.ja: "ja",
LanguageEnum.ko: "ko",
};
abstract class CoreServices {
Future<bool?> init({
required int sdkAppID,

View File

@ -2,6 +2,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_setting_model.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/listener_model/tui_group_listener_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
@ -65,7 +66,9 @@ class CoreServicesImpl with CoreServices {
setGlobalConfig(TIMUIKitConfig? config) {
final TUISelfInfoViewModel selfInfoViewModel =
serviceLocator<TUISelfInfoViewModel>();
final TUISettingModel settingModel = serviceLocator<TUISettingModel>();
selfInfoViewModel.globalConfig = config;
settingModel.init();
}
addIdentifier() {
@ -83,12 +86,17 @@ class CoreServicesImpl with CoreServices {
required LogLevelEnum loglevel,
required V2TimSDKListener listener,
LanguageEnum? language,
String? extraLanguage,
TIMUIKitConfig? config,
VoidCallback? onWebLoginSuccess}) async {
addIdentifier();
if (language != null) {
if(extraLanguage != null){
Future.delayed(const Duration(milliseconds: 1), () {
I18nUtils(null, language);
I18nUtils(null, extraLanguage);
});
}else if (language != null) {
Future.delayed(const Duration(milliseconds: 1), () {
I18nUtils(null, languageEnumToString[language]);
});
}
if (onTUIKitCallbackListener != null) {
@ -132,12 +140,17 @@ class CoreServicesImpl with CoreServices {
ValueChanged<TIMCallback>? onTUIKitCallbackListener,
LanguageEnum? language,
TIMUIKitConfig? config,
String? extraLanguage,
required String userId,
}) async {
_userID = userId;
if (language != null) {
if(extraLanguage != null){
Future.delayed(const Duration(milliseconds: 1), () {
I18nUtils(null, language);
I18nUtils(null, extraLanguage);
});
}else if (language != null) {
Future.delayed(const Duration(milliseconds: 1), () {
I18nUtils(null, languageEnumToString[language]);
});
}
if (onTUIKitCallbackListener != null) {
@ -340,7 +353,6 @@ class CoreServicesImpl with CoreServices {
@override
Future<V2TimCallback> setOfflinePushConfig({
// ignore: todo
required String token,
bool isTPNSToken = false,
int? businessID,

View File

@ -4,6 +4,7 @@ import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversation_view_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_friendship_view_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_setting_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services.dart';
import 'package:tencent_cloud_chat_uikit/data_services/conversation/conversation_services_implements.dart';
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
@ -22,8 +23,10 @@ bool boolIsInitailized = false;
void setupServiceLocator() {
if (!boolIsInitailized) {
// services
// setting
serviceLocator.registerSingleton<TUISettingModel>(TUISettingModel());
// services
serviceLocator.registerSingleton<CoreServicesImpl>(CoreServicesImpl());
serviceLocator
.registerSingleton<TUISelfInfoViewModel>(TUISelfInfoViewModel());

File diff suppressed because one or more lines are too long

View File

@ -513,5 +513,148 @@
"k_0gl51l6": "[左车头]@2x.png",
"k_0iflllk": "[左哼哼]@2x.png",
"k_0g1y3ir": "[左太极]@2x.png",
"k_026hiq5": "消息列表加载中"
"k_026hiq5": "消息列表加载中",
"k_003tu8k": "爱你",
"k_003myvp": "傲慢",
"k_003kddw": "白眼",
"k_039yfhv": "棒棒糖",
"k_003nu3p": "抱抱",
"k_003nijr": "抱拳",
"k_003mg88": "爆筋",
"k_002v17e": "鄙视",
"k_003qhy4": "闭嘴",
"k_003l5fq": "鞭炮",
"k_003uacl": "便便",
"k_003oq1g": "擦汗",
"k_003qvey": "彩带",
"k_003jci7": "彩球",
"k_003pyu1": "菜刀",
"k_003q97d": "差劲",
"k_003po5d": "车厢",
"k_03eadb2": "打哈欠",
"k_003pnuf": "大兵",
"k_003kg57": "蛋糕",
"k_003mxkt": "得意",
"k_003onu3": "灯泡",
"k_002uv8s": "凋谢",
"k_003kqy0": "调皮",
"k_003tyum": "多云",
"k_003pv9u": "发呆",
"k_036o6mu": "发抖t",
"k_003nogx": "飞机",
"k_003q7wg": "飞吻",
"k_003m0jd": "奋斗",
"k_002ult9": "风车",
"k_003r8gt": "尴尬",
"k_003qy4u": "勾引",
"k_003mnoa": "鼓掌",
"k_003lmw8": "害羞",
"k_003mb30": "憨笑",
"k_03bj41g": "红灯笼",
"k_03dxw2f": "红双喜",
"k_003mk57": "坏笑",
"k_003nmvf": "挥手",
"k_003r2i7": "回头",
"k_002s6f3": "饥饿",
"k_003qd0t": "激动",
"k_002vgi4": "街舞",
"k_003nz33": "惊恐",
"k_002wh4p": "惊讶",
"k_003ozpu": "咖啡",
"k_003qvs4": "磕头",
"k_003l3wb": "可爱",
"k_003nuwm": "可怜",
"k_002rw1q": "抠鼻",
"k_002tujb": "骷髅",
"k_00030eq": "酷",
"k_03i8ath": "快哭了",
"k_000421h": "困",
"k_003l5i7": "蜡烛",
"k_003j72g": "篮球",
"k_003ofwl": "冷汗",
"k_02mw65v": "礼品袋",
"k_003ku40": "礼物",
"k_003ookz": "流汗",
"k_003on72": "流泪",
"k_003rjy0": "麻将",
"k_003q2f8": "猫咪",
"k_03et393": "么么哒",
"k_003j7j2": "玫瑰",
"k_002sr0b": "米饭",
"k_003nnza": "面条",
"k_003jef9": "奶瓶",
"k_002umn0": "难过",
"k_002rjib": "闹钟",
"k_0003zcn": "怒",
"k_003jzwq": "怄火",
"k_003j4js": "皮球",
"k_002r5ir": "啤酒",
"k_002ubu4": "瓢虫",
"k_003ppo6": "撇嘴",
"k_003ty3o": "乒乓",
"k_002vxwe": "汽车",
"k_00043hb": "强",
"k_003nmbo": "敲打",
"k_002tfhq": "青蛙",
"k_03i7lrn": "糗大了",
"k_003r03m": "拳头",
"k_00043h0": "弱",
"k_000345z": "色",
"k_003qmp9": "沙发",
"k_003it8a": "闪电",
"k_003pxow": "胜利",
"k_003kw8e": "示爱",
"k_003n99g": "手枪",
"k_00035cl": "衰",
"k_002vl3h": "睡觉",
"k_002rgqk": "太阳",
"k_003m9d1": "跳绳",
"k_002vobp": "跳跳",
"k_003mkoz": "偷笑",
"k_00041px": "吐",
"k_003rjh5": "委屈",
"k_003j36u": "西瓜",
"k_002re92": "下雨",
"k_00041py": "吓",
"k_003q06o": "献吻",
"k_002ubjp": "香蕉",
"k_003o2tr": "象棋",
"k_03ie6pa": "心碎了",
"k_003rao5": "信封",
"k_003l3us": "熊猫",
"k_000424d": "嘘",
"k_00033yi": "药",
"k_002qtyy": "疑问",
"k_002qe0o": "阴险",
"k_03gu7us": "右车头",
"k_03ere8m": "右哼哼",
"k_003uqk3": "雨伞",
"k_003tzdv": "月亮",
"k_0003z00": "晕",
"k_002vdrd": "再见",
"k_003ra1w": "炸弹",
"k_003lcad": "折磨",
"k_003q7sz": "纸巾",
"k_002thn9": "咒骂",
"k_003qx7f": "猪头",
"k_003l044": "抓狂",
"k_003qg4h": "转圈",
"k_003kb97": "龇牙",
"k_03gu53l": "左车头",
"k_03erd1f": "左哼哼",
"k_003nyvl": "爱情",
"k_003r85z": "爱心",
"k_003mk8j": "钞票",
"k_003pwfj": "大哭",
"k_00042w5": "刀",
"k_003nmtr": "握手",
"k_03c529p": "右太极",
"k_003n4mk": "钻戒",
"k_03c5488": "左太极",
"k_1llp7tu": "该用户不存在",
"k_0tbyqyb": "加载中…",
"k_0td1p3f": "保存中…",
"k_1klqdh1": "仅限汉字、英文、数字和下划线",
"k_03el5lp": "未填写",
"k_1ui0gai": "搜索指定内容"
}

File diff suppressed because one or more lines are too long

View File

@ -8,6 +8,9 @@ export 'data_services/core/core_services_implements.dart';
export 'package:tencent_im_base/theme/tui_theme.dart';
export 'package:tencent_im_base/theme/color.dart';
// Sticker
export 'package:tim_ui_kit_sticker_plugin/tim_ui_kit_sticker_plugin.dart';
// Widgets
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitConversation/tim_uikit_conversation.dart';
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_chat.dart';
@ -47,6 +50,9 @@ export 'package:tencent_cloud_chat_uikit/ui/controller/tim_uikit_profile_control
export 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_chat_config.dart';
export 'package:permission_handler/permission_handler.dart';
// Utils
export 'package:tencent_cloud_chat_uikit/ui/utils/common_utils.dart';
class TIMUIKitCore {
static CoreServicesImpl getInstance() {
setupServiceLocator();

View File

@ -1,139 +0,0 @@
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},
];

View File

@ -52,9 +52,10 @@ class TIMUIKitProfileController {
String title,
String tips,
void Function(String) onSubmitted,
TUITheme theme
) {
TextInputBottomSheet.showTextInputBottomSheet(
context, title, tips, onSubmitted);
context, title, tips, onSubmitted, theme);
}
/// Load the profile data

View File

@ -0,0 +1,7 @@
class TencentUtils{
static bool isTextNotEmpty(String? text){
return text != null && text.isNotEmpty;
}
}

View File

@ -1,153 +0,0 @@
import 'custom_emoji_face_data_class.dart';
class ConstData {
static final List<CustomEmojiFaceData> emojiList = [
CustomEmojiFaceData(
name: '4349',
icon: "[调皮]@2x.png",
isEmoji: true,
list: [
"[爱你]@2x.png",
"[爱情]@2x.png",
"[爱心]@2x.png",
"[傲慢]@2x.png",
"[白眼]@2x.png",
"[棒棒糖]@2x.png",
"[抱抱]@2x.png",
"[抱拳]@2x.png",
"[爆筋]@2x.png",
"[鄙视]@2x.png",
"[闭嘴]@2x.png",
"[鞭炮]@2x.png",
"[便便]@2x.png",
"[擦汗]@2x.png",
"[彩带]@2x.png",
"[彩球]@2x.png",
"[菜刀]@2x.png",
"[差劲]@2x.png",
"[钞票]@2x.png",
"[车厢]@2x.png",
"[打哈欠]@2x.png",
"[大兵]@2x.png",
"[大哭]@2x.png",
"[蛋糕]@2x.png",
"[刀]@2x.png",
"[得意]@2x.png",
"[灯泡]@2x.png",
"[凋谢]@2x.png",
"[调皮]@2x.png",
"[多云]@2x.png",
"[发呆]@2x.png",
"[发抖]@2x.png",
"[飞机]@2x.png",
"[飞吻]@2x.png",
"[奋斗]@2x.png",
"[风车]@2x.png",
"[尴尬]@2x.png",
"[勾引]@2x.png",
"[鼓掌]@2x.png",
"[害羞]@2x.png",
"[憨笑]@2x.png",
"[红灯笼]@2x.png",
"[红双喜]@2x.png",
"[坏笑]@2x.png",
"[挥手]@2x.png",
"[回头]@2x.png",
"[饥饿]@2x.png",
"[激动]@2x.png",
"[街舞]@2x.png",
"[惊恐]@2x.png",
"[惊讶]@2x.png",
"[咖啡]@2x.png",
"[磕头]@2x.png",
"[可爱]@2x.png",
"[可怜]@2x.png",
"[抠鼻]@2x.png",
"[骷髅]@2x.png",
"[酷]@2x.png",
"[快哭了]@2x.png",
"[困]@2x.png",
"[蜡烛]@2x.png",
"[篮球]@2x.png",
"[冷汗]@2x.png",
"[礼品袋]@2x.png",
"[礼物]@2x.png",
"[流汗]@2x.png",
"[流泪]@2x.png",
"[麻将]@2x.png",
"[麦克风]@2x.png",
"[猫咪]@2x.png",
"[么么哒]@2x.png",
"[玫瑰]@2x.png",
"[米饭]@2x.png",
"[面条]@2x.png",
"[奶瓶]@2x.png",
"[难过]@2x.png",
"[闹钟]@2x.png",
"[怒]@2x.png",
"[怄火]@2x.png",
"[皮球]@2x.png",
"[啤酒]@2x.png",
"[瓢虫]@2x.png",
"[撇嘴]@2x.png",
"[乒乓]@2x.png",
"[汽车]@2x.png",
"[强]@2x.png",
"[敲打]@2x.png",
"[青蛙]@2x.png",
"[糗大了]@2x.png",
"[拳头]@2x.png",
"[弱]@2x.png",
"[色]@2x.png",
"[沙发]@2x.png",
"[删除]@2x.png",
"[闪电]@2x.png",
"[胜利]@2x.png",
"[示爱]@2x.png",
"[手枪]@2x.png",
"[衰]@2x.png",
"[睡觉]@2x.png",
"[太阳]@2x.png",
"[跳绳]@2x.png",
"[跳跳]@2x.png",
"[偷笑]@2x.png",
"[吐]@2x.png",
"[委屈]@2x.png",
"[握手]@2x.png",
"[西瓜]@2x.png",
"[下雨]@2x.png",
"[吓]@2x.png",
"[献吻]@2x.png",
"[香蕉]@2x.png",
"[象棋]@2x.png",
"[心碎了]@2x.png",
"[信封]@2x.png",
"[熊猫]@2x.png",
"[嘘]@2x.png",
"[药]@2x.png",
"[疑问]@2x.png",
"[阴险]@2x.png",
"[右车头]@2x.png",
"[右哼哼]@2x.png",
"[右太极]@2x.png",
"[雨伞]@2x.png",
"[月亮]@2x.png",
"[晕]@2x.png",
"[再见]@2x.png",
"[炸弹]@2x.png",
"[折磨]@2x.png",
"[纸巾]@2x.png",
"[咒骂]@2x.png",
"[猪头]@2x.png",
"[抓狂]@2x.png",
"[转圈]@2x.png",
"[龇牙]@2x.png",
"[钻戒]@2x.png",
"[左车头]@2x.png",
"[左哼哼]@2x.png",
"[左太极]@2x.png",
"[NO]@2x.png",
"[OK]@2x.png",
])
];
}

View File

@ -1,19 +0,0 @@
class CustomEmojiFaceData {
CustomEmojiFaceData(
{required this.name,
required this.icon,
required this.list,
this.isEmoji = false});
late String name;
late String icon;
late List<String> list;
late bool isEmoji;
CustomEmojiFaceData.fromJson(Map<String, dynamic> json) {
name = json['name'];
icon = json['icon'];
list = json['list'];
isEmoji = json['isEmoji'] ?? false;
}
}

View File

@ -212,72 +212,7 @@ class MessageUtils {
}
static String handleCustomMessageString(V2TimMessage message) {
final customElem = message.customElem;
final callingMessage = CallingMessage.getCallMessage(customElem);
if (callingMessage != null) {
//
final isCallEnd = CallingMessage.isCallEndExist(callingMessage);
String? option2 = "";
if (isCallEnd) {
option2 = CallingMessage.getShowTime(callingMessage.callEnd!);
}
return isCallEnd
? (TIM_t_para("通话时间:{{option2}}", "通话时间:$option2")(option2: option2))
: (CallingMessage.getActionType(callingMessage.actionType!));
} else {
return TIM_t("自定义消息");
}
}
static handleCustomMessage(V2TimMessage message, context) {
// StringQAQ
final customElem = message.customElem;
final callingMessage = CallingMessage.getCallMessage(customElem);
if (callingMessage != null) {
//
final isCallEnd = CallingMessage.isCallEndExist(callingMessage);
final isVoiceCall = callingMessage.callType == 1;
String? option2 = "";
if (isCallEnd) {
option2 = CallingMessage.getShowTime(callingMessage.callEnd!);
}
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(right: 4),
child: Image.asset(
isVoiceCall ? "images/voice_call.png" : "images/video_call.png",
package: 'tencent_cloud_chat_uikit',
height: 16,
width: 16,
),
),
isCallEnd
? Text(TIM_t_para("通话时间:{{option2}}", "通话时间:$option2")(
option2: option2))
: Text(
CallingMessage.getActionType(callingMessage.actionType!)),
// if (isFromSelf)
// Padding(
// padding: const EdgeInsets.only(left: 4),
// child: Image.asset(
// isVoiceCall
// ? "images/voice_call.png"
// : "images/video_call_self.png",
// package: 'tencent_cloud_chat_uikit',
// height: 16,
// width: 16,
// ),
// ),
],
);
} else {
return const Text("[自定义]");
}
return TIM_t("消息");
}
static Widget wrapMessageTips(Widget child, TUITheme? theme) {

View File

@ -12,7 +12,6 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
class PermissionRequestInfo extends StatefulWidget {
final Function removeOverLay;
final int permissionType;
@ -54,6 +53,8 @@ class _PermissionRequestInfo extends TIMUIKitState<PermissionRequestInfo>
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final theme = value.theme;
final permission = {
1: {
"name": TIM_t("相机"),
@ -79,7 +80,7 @@ class _PermissionRequestInfo extends TIMUIKitState<PermissionRequestInfo>
child: Opacity(
opacity: 0.7,
child: Container(
color: Colors.black,
color: theme.black,
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
@ -103,15 +104,14 @@ class _PermissionRequestInfo extends TIMUIKitState<PermissionRequestInfo>
TIM_t_para(" 申请获取{{option2}}", " 申请获取$option2")(
option2: option2) +
TIM_t("权限"),
style: const TextStyle(color: Colors.white, fontSize: 18),
style: TextStyle(color: theme.white, fontSize: 18),
),
const SizedBox(
height: 20,
),
Text(
permission?["text"] ?? "",
style:
const TextStyle(color: Colors.white70, fontSize: 16),
style: TextStyle(color: theme.white, fontSize: 16),
)
],
),
@ -204,14 +204,22 @@ class Permissions {
return _prefix + appName + _postfixList[value];
}
static Future<bool> checkPermission(BuildContext context, int value,
[bool isShowPermissionPage = true]) async {
static Future<bool> checkPermission(
BuildContext context,
int value, [
TUITheme? theme,
bool isShowPermissionPage = true,
]) async {
final status = await Permission.byValue(value).status;
if (status.isGranted || status.isLimited) {
return true;
}
final bool? shouldRequestPermission =
await showPermissionConfirmDialog(context, value, isShowPermissionPage);
final bool? shouldRequestPermission = await showPermissionConfirmDialog(
context,
value,
theme,
isShowPermissionPage,
);
if (shouldRequestPermission != null && shouldRequestPermission) {
return await Permission.byValue(value).request().isGranted;
}
@ -247,12 +255,8 @@ class Permissions {
}
static Future<bool?> showPermissionConfirmDialog(BuildContext context, value,
[bool isShowPermissionPage = true]) async {
final platformUtils = PlatformUtils();
//
if (!await checkPermissionSetBefore(value)) {
[TUITheme? theme, bool isShowPermissionPage = true]) async {
await setLocalPermission(value);
if (platformUtils.isAndroid && isShowPermissionPage) {
showPermissionRequestInfoDialog(context, value);
}
return true;
@ -303,7 +307,9 @@ class Permissions {
Expanded(
child: TextButton(
child: Text(TIM_t("以后再说"),
style: const TextStyle(color: Colors.black)),
style: TextStyle(
color: theme?.black ?? Colors.black,
)),
onPressed: closeDialog, //
),
),
@ -311,7 +317,9 @@ class Permissions {
Expanded(
child: TextButton(
child: Text(TIM_t("去开启"),
style: const TextStyle(color: Colors.black)),
style: TextStyle(
color: theme?.black ?? Colors.black,
)),
onPressed: getPermission,
),
)

View File

@ -84,8 +84,7 @@ class _TIMUIKitAddFriendState extends TIMUIKitState<TIMUIKitAddFriend> {
model: _selfInfoViewModel)));
},
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(12),
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -122,8 +121,12 @@ class _TIMUIKitAddFriendState extends TIMUIKitState<TIMUIKitAddFriend> {
final noResult = searchResult == null || searchResult.isEmpty;
if (noResult) {
return [
Center(
child: Text(TIM_t("用户不存在")),
Container(
margin: const EdgeInsets.only(top: 20),
child: Center(
child: Text(TIM_t("该用户不存在"),
style: TextStyle(color: theme.weakTextColor, fontSize: 14)),
),
)
];
}
@ -161,23 +164,20 @@ class _TIMUIKitAddFriendState extends TIMUIKitState<TIMUIKitAddFriend> {
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme;
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: _selfInfoViewModel),
],
builder: (BuildContext context, Widget? w) {
final selfInfoModel = Provider.of<TUISelfInfoViewModel>(context);
final userID = selfInfoModel.loginInfo?.userID ?? "";
String option2 = userID;
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Row(
children: [
Expanded(
child: TextField(
autofocus: true,
focusNode: _focusNode,
controller: _controller,
onChanged: (value) {
@ -210,45 +210,24 @@ class _TIMUIKitAddFriendState extends TIMUIKitState<TIMUIKitAddFriend> {
),
),
contentPadding: EdgeInsets.zero,
hintStyle: const TextStyle(
color: Color(0xffAEA4A3),
hintStyle: TextStyle(
color: theme.weakTextColor,
),
fillColor: Colors.white,
fillColor: theme.inputFillColor,
filled: true,
hintText: TIM_t("搜索用户 ID")),
)),
AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: isFocused ? 50 : 0,
child: TextButton(
onPressed: () {
final searchParams = _controller.text;
if (searchParams.trim().isNotEmpty) {
searchFriend(searchParams);
showResult = true;
setState(() {});
}
},
child: Text(
TIM_t("搜索"),
softWrap: false,
style: const TextStyle(color: Colors.black),
)),
)
],
),
),
if (!isFocused)
Center(
child: Text(
TIM_t_para("我的用户ID: {{option2}}", "我的用户ID: $option2")(
option2: option2)),
),
if (showResult)
Expanded(
child: SingleChildScrollView(
child: Column(
children: _searchResultBuilder(searchResult, theme),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: SingleChildScrollView(
child: Column(
children: _searchResultBuilder(searchResult, theme),
),
),
),
)

View File

@ -6,7 +6,6 @@ import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_inf
import 'package:tencent_cloud_chat_uikit/data_services/friendShip/friendship_services.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
@ -58,9 +57,9 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
appBar: AppBar(
title: Text(
TIM_t("添加好友"),
style: const TextStyle(color: Colors.white, fontSize: 17),
style: TextStyle(color: theme.white, fontSize: 17),
),
shadowColor: Colors.white,
shadowColor: theme.white,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [
@ -69,8 +68,8 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
]),
),
),
iconTheme: const IconThemeData(
color: Colors.white,
iconTheme: IconThemeData(
color: theme.white,
),
),
body: SingleChildScrollView(
@ -78,7 +77,7 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Colors.white,
color: theme.white,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
margin: const EdgeInsets.only(bottom: 12),
child: Row(
@ -130,18 +129,18 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
Container(
margin: const EdgeInsets.only(top: 6, bottom: 12),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
color: Colors.white,
color: theme.white,
child: TextField(
// minLines: 1,
maxLines: 4,
controller: _verficationController,
keyboardType: TextInputType.multiline,
decoration: const InputDecoration(
border: InputBorder.none,
hintStyle: TextStyle(
color: Color(0xFFAEA4A3),
),
hintText: '')),
// minLines: 1,
maxLines: 4,
controller: _verficationController,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: TextStyle(color: theme.textgrey),
hintText: '',
),
),
),
Padding(
padding: const EdgeInsets.only(left: 16.0),
@ -151,7 +150,7 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
),
),
Container(
color: Colors.white,
color: theme.white,
padding: const EdgeInsets.symmetric(horizontal: 16),
margin: const EdgeInsets.only(top: 6),
child: Row(
@ -164,14 +163,16 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
SizedBox(
width: 50,
child: TextField(
controller: _nickNameController,
decoration: const InputDecoration(
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
hintStyle: TextStyle(
color: Color(0xFFAEA4A3),
),
hintText: '')),
controller: _nickNameController,
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
hintStyle: TextStyle(
color: theme.textgrey,
),
hintText: '',
),
),
)
],
),
@ -181,7 +182,7 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
),
if (widget.isShowDefaultGroup == true)
Container(
color: Colors.white,
color: theme.white,
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
@ -201,7 +202,7 @@ class _SendApplicationState extends TIMUIKitState<SendApplication> {
),
),
Container(
color: Colors.white,
color: theme.white,
width: double.infinity,
margin: const EdgeInsets.only(top: 10),
child: TextButton(

View File

@ -88,8 +88,7 @@ class _TIMUIKitAddGroupState extends TIMUIKitState<TIMUIKitAddGroup> {
)));
},
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(12),
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
// crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -106,9 +105,6 @@ class _TIMUIKitAddGroupState extends TIMUIKitState<TIMUIKitAddGroup> {
showName,
style: const TextStyle(fontSize: 18),
),
// const SizedBox(
// height: 4,
// ),
Text(
"ID: $groupID",
style: TextStyle(fontSize: 12, color: theme.weakTextColor),
@ -130,8 +126,12 @@ class _TIMUIKitAddGroupState extends TIMUIKitState<TIMUIKitAddGroup> {
final noResult = searchResult != null && searchResult.isEmpty;
if (noResult) {
return [
Center(
child: Text(TIM_t("该群聊不存在")),
Container(
margin: const EdgeInsets.only(top: 20),
child: Center(
child: Text(TIM_t("该群聊不存在"),
style: TextStyle(color: theme.weakTextColor, fontSize: 14)),
),
)
];
}
@ -221,11 +221,12 @@ class _TIMUIKitAddGroupState extends TIMUIKitState<TIMUIKitAddGroup> {
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Row(
children: [
Expanded(
child: TextField(
autofocus: true,
focusNode: _focusNode,
controller: _controller,
onChanged: (value) {
@ -258,39 +259,24 @@ class _TIMUIKitAddGroupState extends TIMUIKitState<TIMUIKitAddGroup> {
),
),
contentPadding: EdgeInsets.zero,
hintStyle: const TextStyle(
color: Color(0xffAEA4A3),
hintStyle: TextStyle(
color: theme.weakTextColor,
),
fillColor: Colors.white,
fillColor: theme.inputFillColor,
filled: true,
hintText: TIM_t("搜索群ID")),
)),
AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: isFocused ? 50 : 0,
child: TextButton(
onPressed: () {
final searchParams = _controller.text;
if (searchParams.trim().isNotEmpty) {
searchGroup(searchParams);
showResult = true;
setState(() {});
}
},
child: Text(
TIM_t("搜索"),
softWrap: false,
style: const TextStyle(color: Colors.black),
)),
)
],
),
),
if (showResult)
Expanded(
child: SingleChildScrollView(
child: Column(
children: _searchResultBuilder(groupResult, theme),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: SingleChildScrollView(
child: Column(
children: _searchResultBuilder(groupResult, theme),
),
),
),
)

View File

@ -6,7 +6,6 @@ import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implem
import 'package:tencent_cloud_chat_uikit/data_services/group/group_services.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
@ -79,9 +78,9 @@ class _SendJoinGroupApplicationState
appBar: AppBar(
title: Text(
TIM_t("进群申请"),
style: const TextStyle(color: Colors.white, fontSize: 17),
style: TextStyle(color: theme.white, fontSize: 17),
),
shadowColor: Colors.white,
shadowColor: theme.white,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [
@ -90,8 +89,8 @@ class _SendJoinGroupApplicationState
]),
),
),
iconTheme: const IconThemeData(
color: Colors.white,
iconTheme: IconThemeData(
color: theme.white,
),
),
body: SingleChildScrollView(
@ -99,7 +98,7 @@ class _SendJoinGroupApplicationState
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Colors.white,
color: theme.white,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
margin: const EdgeInsets.only(bottom: 12),
child: Row(
@ -151,20 +150,20 @@ class _SendJoinGroupApplicationState
Container(
margin: const EdgeInsets.only(top: 6, bottom: 12),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
color: Colors.white,
color: theme.white,
child: TextField(
maxLines: 4,
controller: _verficationController,
keyboardType: TextInputType.multiline,
decoration: const InputDecoration(
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: TextStyle(
color: Color(0xFFAEA4A3),
color: theme.textgrey,
),
hintText: '')),
),
Container(
color: Colors.white,
color: theme.white,
width: double.infinity,
margin: const EdgeInsets.only(top: 10),
child: TextButton(

View File

@ -58,7 +58,7 @@ class _TIMUIKitBlackListState extends TIMUIKitState<TIMUIKitBlackList> {
.deleteFromBlockList([friendInfo.userID]);
},
backgroundColor: theme.cautionColor ?? CommonColor.cautionColor,
foregroundColor: Colors.white,
foregroundColor: theme.white,
label: TIM_t("删除"),
autoClose: true,
)
@ -93,7 +93,7 @@ class _TIMUIKitBlackListState extends TIMUIKitState<TIMUIKitBlackList> {
CommonColor.weakDividerColor))),
child: Text(
showName,
style: const TextStyle(color: Colors.black, fontSize: 18),
style: TextStyle(color: theme.black, fontSize: 18),
),
))
],

View File

@ -1,3 +1,4 @@
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';

View File

@ -581,7 +581,10 @@ class _TIMUIKItHistoryMessageListItemState
? (model.chatConfig.timeDividerConfig?.timestampParser!(timeStamp))!
: TimeAgo().getTimeForMessage(timeStamp),
style: widget.themeData?.timelineTextStyle ??
TextStyle(fontSize: 12, color: theme.weakTextColor),
TextStyle(
fontSize: 12,
color: theme.chatTimeDividerTextColor,
),
),
);
}
@ -589,8 +592,13 @@ class _TIMUIKItHistoryMessageListItemState
bool isRevokable(int timestamp) =>
(DateTime.now().millisecondsSinceEpoch / 1000).ceil() - timestamp < 120;
_onLongPress(c, V2TimMessage message, TUIChatSeparateViewModel model,
TapDownDetails? details) {
_onLongPress(
c,
V2TimMessage message,
TUIChatSeparateViewModel model,
TUITheme theme,
TapDownDetails? details,
) {
if (tooltip != null && tooltip!.isOpen) {
tooltip!.close();
return;
@ -600,7 +608,12 @@ class _TIMUIKItHistoryMessageListItemState
final screenHeight = MediaQuery.of(context).size.height;
if (context.size!.height + 180 > screenHeight && !PlatformUtils().isWeb) {
initTools(
context: c, isLongMessage: true, model: model, details: details);
context: c,
isLongMessage: true,
model: model,
details: details,
theme: theme,
);
if (widget.onScrollToIndexBegin != null) {
widget.onScrollToIndexBegin!(message);
}
@ -608,7 +621,12 @@ class _TIMUIKItHistoryMessageListItemState
tooltip!.show(c);
});
} else {
initTools(context: c, model: model, details: details);
initTools(
context: c,
model: model,
details: details,
theme: theme,
);
tooltip!.show(c, targetCenter: details?.globalPosition);
}
}
@ -633,6 +651,7 @@ class _TIMUIKItHistoryMessageListItemState
{BuildContext? context,
bool isLongMessage = false,
required TUIChatSeparateViewModel model,
TUITheme? theme,
TapDownDetails? details}) {
final isSelf = widget.message.isSelf ?? false;
double arrowTipDistance = 30;
@ -688,8 +707,8 @@ class _TIMUIKItHistoryMessageListItemState
right: right,
left: left,
hasArrow: hasArrow,
borderColor: Colors.white,
backgroundColor: Colors.white,
borderColor: theme?.white ?? Colors.white,
backgroundColor: theme?.white ?? Colors.white,
shadowColor: Colors.black26,
hasShadow: true,
borderWidth: 1.0,
@ -967,7 +986,8 @@ class _TIMUIKItHistoryMessageListItemState
child: Text(
isPeerRead ? TIM_t("已读") : TIM_t("未读"),
style: TextStyle(
color: theme.weakTextColor,
color: theme
.chatMessageItemUnreadStatusTextColor,
fontSize: 12),
),
),
@ -1024,7 +1044,12 @@ class _TIMUIKItHistoryMessageListItemState
if (PlatformUtils().isWeb) {
if (widget.allowLongPress) {
_onLongPress(
context, message, model, details);
context,
message,
model,
theme,
details,
);
}
if (widget.onLongPress != null) {
widget.onLongPress!(context, message);
@ -1034,7 +1059,12 @@ class _TIMUIKItHistoryMessageListItemState
onLongPress: () {
if (widget.allowLongPress) {
_onLongPress(
context, message, model, null);
context,
message,
model,
theme,
null,
);
}
if (widget.onLongPress != null) {
widget.onLongPress!(context, message);

View File

@ -5,6 +5,7 @@ import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
@ -93,6 +94,10 @@ class TIMUIKitMessageTooltipState
final shouldShowRevokeAction = isCanRevoke &&
(widget.message.isSelf ?? false) &&
widget.message.status != MessageStatus.V2TIM_MSG_STATUS_SEND_FAIL;
final shouldShowReplyAction = !(widget.message.customElem?.data != null &&
MessageUtils.isCallingData(widget.message.customElem!.data!));
final shouldShowForwardAction = !(widget.message.customElem?.data != null &&
MessageUtils.isCallingData(widget.message.customElem!.data!));
final tooltipsConfig = widget.toolTipsConfig;
final defaultTipsList = [
{
@ -100,6 +105,7 @@ class TIMUIKitMessageTooltipState
"id": "copyMessage",
"icon": "images/copy_message.png"
},
if(shouldShowForwardAction)
{
"label": TIM_t("转发"),
"id": "forwardMessage",
@ -110,6 +116,7 @@ class TIMUIKitMessageTooltipState
"id": "multiSelect",
"icon": "images/multi_message.png"
},
if (shouldShowReplyAction)
{
"label": TIM_t("引用"),
"id": "replyMessage",

View File

@ -173,7 +173,7 @@ class _TIMUIKitAppBarState extends TIMUIKitState<TIMUIKitAppBar> {
final TUIChatSeparateViewModel chatVM =
Provider.of<TUIChatSeparateViewModel>(context);
return AppBar(
backgroundColor: setAppbar?.backgroundColor,
backgroundColor: setAppbar?.backgroundColor ?? theme.chatHeaderBgColor,
actionsIconTheme: setAppbar?.actionsIconTheme,
foregroundColor: setAppbar?.foregroundColor,
elevation: setAppbar?.elevation,
@ -189,15 +189,18 @@ class _TIMUIKitAppBarState extends TIMUIKitState<TIMUIKitAppBar> {
toolbarTextStyle: setAppbar?.toolbarTextStyle,
textTheme: setAppbar?.textTheme,
flexibleSpace: setAppbar?.backgroundColor == null
? setAppbar?.flexibleSpace ??
Container(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [
theme.lightPrimaryColor ?? CommonColor.lightPrimaryColor,
theme.primaryColor ?? CommonColor.primaryColor
]),
),
)
? theme.chatHeaderBgColor != null
? setAppbar?.flexibleSpace ??
Container(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [
theme.lightPrimaryColor ??
CommonColor.lightPrimaryColor,
theme.primaryColor ?? CommonColor.primaryColor
]),
),
)
: null
: null,
iconTheme: setAppbar?.iconTheme ??
const IconThemeData(

View File

@ -5,7 +5,6 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/avatar.dart';
class TIMUIKitMessageReactionDetail extends StatefulWidget {
@ -99,7 +98,7 @@ class TIMUIKitMessageReactionDetailState
padding: const EdgeInsets.only(top: 20, bottom: 20, right: 28),
child: Text(
showName,
style: const TextStyle(color: Colors.black, fontSize: 18),
style: TextStyle(color: theme.black, fontSize: 18),
),
)),
],

View File

@ -3,7 +3,8 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitMessageItem/TIMUIKitMessageReaction/message_reaction_emoji.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_emoji_panel.dart'
as emoji;
import 'package:tencent_cloud_chat_uikit/ui/widgets/emoji.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/extended_wrap/extended_wrap.dart';
@ -64,7 +65,7 @@ class TIMUIKitMessageReactionEmojiSelectPanelState
onTap: () {
widget.onSelect(item.unicode);
},
child: EmojiItem(
child: emoji.EmojiItem(
name: item.name,
unicode: item.unicode,
),

View File

@ -16,7 +16,6 @@ import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/permission.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
@ -167,15 +166,17 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
return res;
}
downloadFile() async {
if (!await Permissions.checkPermission(context, Permission.storage.value)) {
downloadFile(TUITheme theme) async {
if (!await Permissions.checkPermission(
context, Permission.storage.value, theme)) {
return;
}
await model.downloadFile();
}
tryOpenFile(context, theme) async {
if (!await Permissions.checkPermission(context, Permission.storage.value)) {
if (!await Permissions.checkPermission(
context, Permission.storage.value, theme)) {
return;
}
showOpenFileConfirmDialog(context, filePath, theme);
@ -252,7 +253,7 @@ class _TIMUIKitFileElemState extends TIMUIKitState<TIMUIKitFileElem> {
} else {
await addUrlToWaitingPath();
}
await downloadFile();
await downloadFile(theme);
},
child: Container(
width: 237,

View File

@ -2,8 +2,8 @@
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/message/message_services.dart';
import 'package:universal_html/html.dart' as html;
@ -11,7 +11,6 @@ import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
@ -35,8 +34,6 @@ import 'package:tencent_cloud_chat_uikit/ui/widgets/toast.dart';
import 'package:transparent_image/transparent_image.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
class TIMUIKitImageElem extends StatefulWidget {
final V2TimMessage message;
final bool isShowJump;
@ -85,7 +82,7 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
borderRadius: const BorderRadius.all(Radius.circular(5)),
border: Border.all(
width: 1,
color: Colors.black12,
color: theme?.black ?? Colors.white,
)),
height: 100,
child: Center(
@ -112,7 +109,12 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
}
//
_saveImageToLocal(context, String imageUrl, {bool isAsset = true}) async {
Future<void> _saveImageToLocal(
context,
String imageUrl, {
bool isAsset = true,
TUITheme? theme,
}) async {
if (PlatformUtils().isWeb) {
download(imageUrl) async {
final http.Response r = await http.get(Uri.parse(imageUrl));
@ -131,12 +133,12 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
if (PlatformUtils().isIOS) {
if (!await Permissions.checkPermission(
context, Permission.photosAddOnly.value, false)) {
context, Permission.photosAddOnly.value, theme!, false)) {
return;
}
} else {
if (!await Permissions.checkPermission(
context, Permission.storage.value)) {
context, Permission.storage.value, theme!)) {
return;
}
}
@ -225,6 +227,7 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
infoCode: 6660407));
}
}
return;
}
getImgWidthAndHeight(
@ -236,34 +239,44 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
return {height: curHeight, width: curWidth};
}
void _saveImg() {
Future<void> _saveImg(TUITheme theme) async {
String? path = widget.message.imageElem!.path;
if (path != null && PlatformUtils().isWeb
? true
: File(path!).existsSync()) {
_saveImageToLocal(context, path, isAsset: true);
return await _saveImageToLocal(context, path, isAsset: true, theme: theme);
} else {
String imgUrl = getBigPicUrl();
if (widget.message.imageElem!.imageList![0]!.localUrl != '' &&
widget.message.imageElem!.imageList![0]!.localUrl != null) {
File f = File(widget.message.imageElem!.imageList![0]!.localUrl!);
if (f.existsSync()) {
_saveImageToLocal(
context, widget.message.imageElem!.imageList![0]!.localUrl!,
isAsset: true);
return;
return await _saveImageToLocal(
context,
widget.message.imageElem!.imageList![0]!.localUrl!,
isAsset: true,
theme: theme,
);
}
}
if (widget.message.imageElem!.path != '' &&
widget.message.imageElem!.path != null) {
File f = File(widget.message.imageElem!.path!);
if (f.existsSync()) {
_saveImageToLocal(context, widget.message.imageElem!.path!,
isAsset: true);
return;
return await _saveImageToLocal(
context,
widget.message.imageElem!.path!,
isAsset: true,
theme: theme,
);
}
}
_saveImageToLocal(context, imgUrl, isAsset: false);
return await _saveImageToLocal(
context,
imgUrl,
isAsset: false,
theme: theme,
);
}
}
@ -278,7 +291,7 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
Widget errorPage(theme) => Container(
height: MediaQuery.of(context).size.height,
color: Colors.black,
color: theme.black,
child: GestureDetector(
onTap: () {
Navigator.of(context).pop();
@ -328,7 +341,9 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
imageProvider: FileImage(File(imgPath)),
heroTag: heroTag,
messageID: widget.message.msgID,
downloadFn: () => _saveImg()),
downloadFn: () async {
return await _saveImg(theme!);
}),
),
);
},
@ -386,8 +401,7 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
void setOnlineImageRatio() {
if (networkImagePositionRadio == null) {
V2TimImage? smallImg = getImageFromList(V2TimImageTypesEnum.small);
V2TimImage? originalImg =
getImageFromList(V2TimImageTypesEnum.original);
V2TimImage? originalImg = getImageFromList(V2TimImageTypesEnum.original);
Image image = Image.network(smallImg?.url ?? originalImg?.url ?? "");
image.image
@ -434,7 +448,9 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
),
heroTag: heroTag,
messageID: widget.message.msgID,
downloadFn: () => _saveImg())),
downloadFn: () async {
return await _saveImg(theme!);
})),
);
},
child: Container(
@ -444,7 +460,8 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
tag: heroTag,
child: PlatformUtils().isWeb
? Image.network(
path ?? smallImg?.url ?? originalImg!.url!, fit: BoxFit.contain)
path ?? smallImg?.url ?? originalImg!.url!,
fit: BoxFit.contain)
:
// Image.network(smallImg?.url ?? ""),
CachedNetworkImage(
@ -539,8 +556,7 @@ class _TIMUIKitImageElem extends TIMUIKitState<TIMUIKitImageElem> {
final heroTag =
"${widget.message.msgID ?? widget.message.id ?? widget.message.timestamp ?? DateTime.now().millisecondsSinceEpoch}${widget.isFrom}";
V2TimImage? originalImg =
getImageFromList(V2TimImageTypesEnum.original);
V2TimImage? originalImg = getImageFromList(V2TimImageTypesEnum.original);
V2TimImage? smallImg = getImageFromList(V2TimImageTypesEnum.small);
return TIMUIKitMessageReactionWrapper(
chatModel: widget.chatModel,

View File

@ -192,7 +192,7 @@ class _TIMUIKitTextElemState extends TIMUIKitState<TIMUIKitTextElem> {
}
final defaultStyle = widget.isFromSelf
? theme.lightPrimaryMaterialColor.shade50
: theme.weakBackgroundColor;
: theme.chatMessageItemFromOthersBgColor;
final backgroundColor = isShowJumpState
? const Color.fromRGBO(245, 166, 35, 1)
: (widget.backgroundColor ?? defaultStyle);

View File

@ -22,6 +22,16 @@ class IntlCameraPickerTextDelegate extends CameraPickerTextDelegate {
@override
String get loadFailed => TIM_t('加载失败');
/// Default loading string for the dialog.
///
@override
String get loading => TIM_t('加载中…');
/// Saving string for the dialog.
///
@override
String get saving => TIM_t('保存中…');
/// Semantics fields.
///
/// Fields below are only for semantics usage. For customizable these fields,

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/constant_data.dart';
import 'package:tencent_extended_text/extended_text.dart';
import 'package:tim_ui_kit_sticker_plugin/constant/emoji.dart';
///emoji/image text
class EmojiText extends SpecialText {
@ -18,7 +17,7 @@ class EmojiText extends SpecialText {
InlineSpan finishText() {
final String key = toString();
if (EmojiUitl(
if (EmojiUtil(
isUseDefaultEmoji: isUseDefaultEmoji,
customEmojiStickerList: customEmojiStickerList)
.instance
@ -34,7 +33,7 @@ class EmojiText extends SpecialText {
if (isUseDefaultEmoji == true) {
return ImageSpan(
AssetImage(
EmojiUitl(
EmojiUtil(
isUseDefaultEmoji: isUseDefaultEmoji,
customEmojiStickerList: customEmojiStickerList)
.instance
@ -48,7 +47,7 @@ class EmojiText extends SpecialText {
margin: const EdgeInsets.all(0));
} else {
return ImageSpan(
AssetImage(EmojiUitl(
AssetImage(EmojiUtil(
isUseDefaultEmoji: isUseDefaultEmoji,
customEmojiStickerList: customEmojiStickerList)
.instance
@ -66,29 +65,27 @@ class EmojiText extends SpecialText {
}
}
class EmojiUitl {
EmojiUitl(
class EmojiUtil {
EmojiUtil(
{this.isUseDefaultEmoji = false, this.customEmojiStickerList = const []});
EmojiUitl._(this.isUseDefaultEmoji, this.customEmojiStickerList) {
RegExp exp = RegExp(r"([\u4e00-\u9fa5]+|[a-zA-Z]+)");
EmojiUtil._(this.isUseDefaultEmoji, this.customEmojiStickerList) {
if (isUseDefaultEmoji == true) {
for (int i = 0; i < ConstData.emojiList.length; i++) {
for (int j = 0; j < ConstData.emojiList[i].list.length; j++) {
var emojiPngNameMatch =
exp.firstMatch(ConstData.emojiList[i].list[j]);
String? emojiName = emojiPngNameMatch![0];
String? emojiName = ConstData.emojiList[i].list[j].split('.png')[0];
_emojiMap['[$emojiName]'] =
'$_emojiFilePath/${ConstData.emojiList[i].name}/[$emojiName]@2x.png';
'$_emojiFilePath/${ConstData.emojiList[i].name}/$emojiName.png';
_emojiMap['[${ConstData.emojiMapList[emojiName]}]'] =
'$_emojiFilePath/${ConstData.emojiList[i].name}/$emojiName.png';
}
}
} else {
for (int i = 0; i < customEmojiStickerList.length; i++) {
for (int j = 0; j < customEmojiStickerList[i].list.length; j++) {
var emojiPngNameMatch =
exp.firstMatch(customEmojiStickerList[i].list[j]);
String? emojiName = emojiPngNameMatch![0];
String? emojiName =
customEmojiStickerList[i].list[j].split('.png')[0];
_emojiMap['[$emojiName]'] =
'$_emojiFilePath/${customEmojiStickerList[i].name}/[$emojiName]@2x.png';
'$_emojiFilePath/${customEmojiStickerList[i].name}/$emojiName.png';
}
}
}
@ -101,7 +98,7 @@ class EmojiUitl {
final String _emojiFilePath = 'assets/custom_face_resource';
// EmojiUitl? _instance;
static EmojiUitl? _instance;
EmojiUitl get instance =>
_instance ??= EmojiUitl._(isUseDefaultEmoji, customEmojiStickerList);
static EmojiUtil? _instance;
EmojiUtil get instance =>
_instance ??= EmojiUtil._(isUseDefaultEmoji, customEmojiStickerList);
}

View File

@ -31,7 +31,6 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
// ignore: unnecessary_import
import 'dart:typed_data';
import 'package:universal_html/html.dart' as html;
class MorePanelConfig {
@ -91,7 +90,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
String? fileName;
File? tempFile;
List<MorePanelItem> itemList(TUIChatSeparateViewModel model) {
List<MorePanelItem> itemList(TUIChatSeparateViewModel model, TUITheme theme) {
final config = widget.morePanelConfig ?? MorePanelConfig();
return [
if (!PlatformUtils().isWeb)
@ -99,7 +98,7 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
id: "screen",
title: TIM_t("拍摄"),
onTap: (c) {
_onFeatureTap("screen", c, model);
_onFeatureTap("screen", c, model, theme);
},
icon: Container(
height: 64,
@ -120,7 +119,12 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
id: "photo",
title: TIM_t("照片"),
onTap: (c) {
_onFeatureTap("photo", c, model);
_onFeatureTap(
"photo",
c,
model,
theme,
);
},
icon: Container(
height: 64,
@ -141,7 +145,12 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
id: "image",
title: TIM_t("图片"),
onTap: (c) {
_onFeatureTap("image", c, model);
_onFeatureTap(
"image",
c,
model,
theme,
);
},
icon: Container(
height: 64,
@ -162,7 +171,12 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
id: "video",
title: TIM_t("视频"),
onTap: (c) {
_onFeatureTap("video", c, model);
_onFeatureTap(
"video",
c,
model,
theme,
);
},
icon: Container(
height: 64,
@ -178,7 +192,12 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
id: "file",
title: TIM_t("文件"),
onTap: (c) {
_onFeatureTap("file", c, model);
_onFeatureTap(
"file",
c,
model,
theme,
);
},
icon: Container(
height: 64,
@ -255,12 +274,15 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
context);
}
_sendImageMessage(TUIChatSeparateViewModel model) async {
_sendImageMessage(TUIChatSeparateViewModel model, TUITheme theme) async {
try {
final bool isAndroid = PlatformUtils().isAndroid;
if (!PlatformUtils().isWeb &&
!await Permissions.checkPermission(context,
isAndroid ? Permission.storage.value : Permission.photos.value)) {
!await Permissions.checkPermission(
context,
isAndroid ? Permission.storage.value : Permission.photos.value,
theme,
)) {
return;
}
final convID = widget.conversationID;
@ -291,11 +313,17 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
}
}
_sendImageFromCamera(TUIChatSeparateViewModel model) async {
_sendImageFromCamera(
TUIChatSeparateViewModel model,
TUITheme theme,
) async {
try {
if (PlatformUtils().isIOS &&
!await Permissions.checkPermission(
context, Permission.camera.value)) {
context,
Permission.camera.value,
theme,
)) {
return;
}
final convID = widget.conversationID;
@ -386,9 +414,16 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
}
}
_sendFile(TUIChatSeparateViewModel model) async {
_sendFile(
TUIChatSeparateViewModel model,
TUITheme theme,
) async {
if (!kIsWeb &&
!await Permissions.checkPermission(context, Permission.storage.value)) {
!await Permissions.checkPermission(
context,
Permission.storage.value,
theme,
)) {
return;
}
try {
@ -436,16 +471,20 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
}
_onFeatureTap(
String id, BuildContext context, TUIChatSeparateViewModel model) async {
String id,
BuildContext context,
TUIChatSeparateViewModel model,
TUITheme theme,
) async {
switch (id) {
case "photo":
_sendImageMessage(model);
_sendImageMessage(model, theme);
break;
case "screen":
_sendImageFromCamera(model);
_sendImageFromCamera(model, theme);
break;
case "file":
_sendFile(model);
_sendFile(model, theme);
break;
case "image":
// only for web
@ -478,37 +517,37 @@ class _MorePanelState extends TIMUIKitState<MorePanel> {
child: Wrap(
spacing: (screenWidth - (23 * 2) - 64 * 4) / 3,
runSpacing: 20,
children: itemList(model)
children: itemList(model, theme)
.map((item) => InkWell(
onTap: () {
if (item.onTap != null) {
item.onTap!(context);
}
},
child: widget.morePanelConfig?.actionBuilder != null
? widget.morePanelConfig?.actionBuilder!(item)
: SizedBox(
height: 94,
width: 64,
child: Column(
children: [
Container(
height: 64,
width: 64,
margin: const EdgeInsets.only(bottom: 4),
decoration: const BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(5))),
child: item.icon,
),
Text(
item.title,
style: TextStyle(
fontSize: 12, color: theme.darkTextColor),
)
],
),
)))
onTap: () {
if (item.onTap != null) {
item.onTap!(context);
}
},
child: widget.morePanelConfig?.actionBuilder != null
? widget.morePanelConfig?.actionBuilder!(item)
: SizedBox(
height: 94,
width: 64,
child: Column(
children: [
Container(
height: 64,
width: 64,
margin: const EdgeInsets.only(bottom: 4),
decoration: const BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(5))),
child: item.icon,
),
Text(
item.title,
style: TextStyle(
fontSize: 12, color: theme.darkTextColor),
)
],
),
)))
.toList(),
),
),

View File

@ -291,7 +291,10 @@ class _SendSoundMessageState extends TIMUIKitState<SendSoundMessage> {
onTapDown: (detail) async {
if (!isInit) {
bool hasMicrophonePermission = await Permissions.checkPermission(
context, Permission.microphone.value);
context,
Permission.microphone.value,
theme,
);
if (!hasMicrophonePermission) {
return;
}

View File

@ -1,7 +1,8 @@
import 'dart:async';
import 'dart:math';
import 'package:tencent_cloud_chat_uikit/ui/utils/constant_data.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/custom_emoji_face_data_class.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_setting_model.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart';
import 'package:tencent_extended_text_field/extended_text_field.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -16,7 +17,6 @@ import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_conversa
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/message.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_at_text.dart';
@ -24,6 +24,7 @@ import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_send_sound_message.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/permission.dart';
import 'package:tencent_keyboard_visibility/tencent_keyboard_visibility.dart';
import 'special_text/DefaultSpecialTextSpanBuilder.dart';
enum MuteStatus { none, me, all }
@ -53,13 +54,13 @@ class TIMUIKitInputTextField extends StatefulWidget {
/// show send emoji icon
final bool showSendEmoji;
/// show more pannel
final bool showMorePannel;
/// show more panel
final bool showMorePanel;
/// background color
final Color? backgroundColor;
/// controll input field behavior
/// control input field behavior
final TIMUIKitInputTextFieldController? controller;
/// on text changed
@ -72,7 +73,7 @@ class TIMUIKitInputTextField extends StatefulWidget {
final List customEmojiStickerList;
/// sticker panel customiziation
/// sticker panel customization
final Widget Function(
{void Function() sendTextMessage,
void Function(int index, String data) sendFaceMessage,
@ -93,7 +94,7 @@ class TIMUIKitInputTextField extends StatefulWidget {
this.customStickerPanel,
this.showSendAudio = true,
this.showSendEmoji = true,
this.showMorePannel = true,
this.showMorePanel = true,
this.backgroundColor,
this.controller,
this.onChanged,
@ -108,6 +109,7 @@ class TIMUIKitInputTextField extends StatefulWidget {
class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
final TUIChatGlobalModel globalModel = serviceLocator<TUIChatGlobalModel>();
final TUISettingModel settingModel = serviceLocator<TUISettingModel>();
bool showMore = false;
bool showMoreButton = true;
bool showSendSoundText = false;
@ -116,7 +118,10 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
late FocusNode focusNode;
String zeroWidthSpace = '\ufeff';
String lastText = "";
double lastkeyboardHeight = 0;
String languageType = "";
double? bottomPadding;
Function? setKeyboardHeight;
int? currentCursor;
Map<String, V2TimGroupMemberFullInfo> memberInfoMap = {};
@ -128,21 +133,6 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
int latestSendEditStatusTime = DateTime.now().millisecondsSinceEpoch;
listenKeyBoardStatus() {
final currentKeyboardHeight = MediaQuery.of(context).viewInsets.bottom;
//
if (currentKeyboardHeight - lastkeyboardHeight > 0) {
// showKeyboard为true
setState(() {
showKeyboard = true;
});
///
} else if (currentKeyboardHeight - lastkeyboardHeight < 0) {}
lastkeyboardHeight = MediaQuery.of(context).viewInsets.bottom;
}
Widget _getBottomContainer() {
if (showEmojiPanel) {
return widget.customStickerPanel != null
@ -155,26 +145,27 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
backSpaceText();
},
addText: (int unicode) {
final oldText = textEditingController.text;
final newText = String.fromCharCode(unicode);
textEditingController.text = "$oldText$newText";
addStickerToText(newText);
// handleSetDraftText();
},
addCustomEmojiText: ((String singleEmojiName) {
RegExp exp = RegExp(r"([\u4e00-\u9fa5]+|[a-zA-Z]+)");
var emojiPngNameMatch = exp.firstMatch(singleEmojiName);
String? emojiName = emojiPngNameMatch![0];
final oldText = textEditingController.text;
String? emojiName = singleEmojiName.split('.png')[0];
if (widget.isUseDefaultEmoji &&
languageType == 'zh' &&
ConstData.emojiMapList[emojiName] != null &&
ConstData.emojiMapList[emojiName] != '') {
emojiName = ConstData.emojiMapList[emojiName];
}
final newText = '[$emojiName]';
textEditingController.text = "$oldText$newText";
addStickerToText(newText);
setSendButton();
}),
defaultCustomEmojiStickerList:
widget.isUseDefaultEmoji ? ConstData.emojiList : [])
: EmojiPanel(onTapEmoji: (unicode) {
final oldText = textEditingController.text;
final newText = String.fromCharCode(unicode);
textEditingController.text = "$oldText$newText";
addStickerToText(newText);
setSendButton();
// handleSetDraftText();
}, onSubmitted: () {
@ -191,29 +182,49 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
conversationType: widget.conversationType);
}
return Container();
return const SizedBox(height: 0);
}
void addStickerToText(String sticker){
final oldText = textEditingController.text;
if(currentCursor != null && currentCursor! > -1){
final firstString = oldText.substring(0, currentCursor);
final secondString = oldText.substring(currentCursor!);
currentCursor = currentCursor! + sticker.length;
textEditingController.text = "$firstString$sticker$secondString";
}else{
textEditingController.text = "$oldText$sticker";
}
}
double _getBottomHeight() {
listenKeyBoardStatus();
// if (showKeyboard) {
// // return MediaQuery.of(context).viewInsets.bottom;
// return 0;
// } else
if (showMore || showEmojiPanel) {
return 248.0;
}
//
else if (textEditingController.text.length >= 46 && showKeyboard == false) {
return 25;
if (showKeyboard) {
final currentKeyboardHeight = MediaQuery.of(context).viewInsets.bottom;
double originHeight = settingModel.keyboardHeight;
if (currentKeyboardHeight != 0) {
if (currentKeyboardHeight >= originHeight) {
originHeight = currentKeyboardHeight;
}
if (setKeyboardHeight != null) {
setKeyboardHeight!(currentKeyboardHeight);
}
}
final height = originHeight != 0 ? originHeight : currentKeyboardHeight;
return height;
} else if (showMore || showEmojiPanel) {
return 248.0 + (bottomPadding ?? 0.0);
} else if (textEditingController.text.length >= 46 &&
showKeyboard == false) {
return 25 + (bottomPadding ?? 0.0);
} else {
return 0;
return bottomPadding ?? 0;
}
}
_openMore() {
if (!showMore) {
focusNode.unfocus();
currentCursor = null;
}
setState(() {
showKeyboard = false;
@ -224,6 +235,8 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
}
_openEmojiPanel() {
_onCursorChange();
showKeyboard = showEmojiPanel;
if (showEmojiPanel) {
focusNode.requestFocus();
} else {
@ -231,7 +244,6 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
}
setState(() {
showKeyboard = showEmojiPanel;
showMore = false;
showSendSoundText = false;
showEmojiPanel = !showEmojiPanel;
@ -294,10 +306,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
onTap: () {
widget.model.repliedMessage = null;
},
child: Icon(
Icons.clear,
color: hexToColor("8f959e"),
size: 18),
child: Icon(Icons.clear, color: hexToColor("8f959e"), size: 18),
)
],
),
@ -334,11 +343,6 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
// handleSetDraftText();
}
setSendButton();
// if (originalText.isNotEmpty) {
// text = originalText.characters.skipLast(1);
// textEditingController.text = "$text";
// }
}
// onSubmitted一样
@ -416,6 +420,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
context);
}
textEditingController.clear();
currentCursor = null;
if (showKeyboard) {
focusNode.requestFocus();
}
@ -450,6 +455,7 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
_hideAllPanel() {
focusNode.unfocus();
currentCursor == null;
if (showKeyboard != false || showMore != false || showEmojiPanel != false) {
setState(() {
showKeyboard = false;
@ -461,17 +467,13 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
void onModelChanged() {
if (widget.model.repliedMessage != null) {
showKeyboard = true;
focusNode.requestFocus();
_addDeleteTag();
setState(() {
showKeyboard = true;
});
} else {}
if (widget.model.editRevokedMsg != "") {
showKeyboard = true;
focusNode.requestFocus();
setState(() {
showKeyboard = true;
});
textEditingController.clear();
textEditingController.text = widget.model.editRevokedMsg;
textEditingController.selection = TextSelection.fromPosition(TextPosition(
@ -488,6 +490,11 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
TextPosition(offset: textEditingController.text.length));
}
_onCursorChange() {
final selection = textEditingController.selection;
currentCursor = selection.baseOffset;
}
_handleSoftKeyBoardDelete() {
if (widget.model.repliedMessage != null) {
widget.model.repliedMessage = null;
@ -622,6 +629,11 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
if (widget.initText != null) {
textEditingController.text = widget.initText!;
}
final AppLocale appLocale = I18nUtils.findDeviceLocale();
languageType =
(appLocale == AppLocale.zhHans || appLocale == AppLocale.zhHant)
? 'zh'
: 'en';
}
@override
@ -697,7 +709,13 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
final theme = value.theme;
final TUIChatSeparateViewModel model =
Provider.of<TUIChatSeparateViewModel>(context);
setKeyboardHeight ??= OptimizeUtils.debounce((height) {
settingModel.keyboardHeight = height;
}, const Duration(seconds: 1));
_getMuteType(model);
final debounceFunc = _debounce((value) {
if (isWebDevice() || isAndroidDevice()) {
if (value.isEmpty && showMoreButton != true) {
@ -721,6 +739,12 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
}
}, const Duration(milliseconds: 80));
final MediaQueryData data = MediaQuery.of(context);
EdgeInsets padding = data.padding;
if (bottomPadding == null || padding.bottom > bottomPadding!) {
bottomPadding = padding.bottom;
}
return Selector<TUIChatSeparateViewModel, V2TimMessage?>(
builder: ((context, value, child) {
String? getForbiddenText() {
@ -742,188 +766,201 @@ class _InputTextFieldState extends TIMUIKitState<TIMUIKitInputTextField> {
_buildRepliedMessage(value),
Container(
color: widget.backgroundColor ?? hexToColor("f5f5f6"),
child: SafeArea(
child: Column(
children: [
Container(
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 16),
constraints: const BoxConstraints(minHeight: 50),
child: Row(
children: [
if (forbiddenText != null)
Expanded(
child: Container(
height: 35,
color: theme.weakBackgroundColor,
alignment: Alignment.center,
child: Text(
TIM_t(forbiddenText),
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: theme.weakTextColor,
),
),
)),
if (!PlatformUtils().isWeb &&
widget.showSendAudio &&
forbiddenText == null)
InkWell(
onTap: () async {
if (showSendSoundText) {
focusNode.requestFocus();
setState(() {
showKeyboard = true;
});
}
if (await Permissions.checkPermission(
context, Permission.microphone.value)) {
setState(() {
showEmojiPanel = false;
showMore = false;
showSendSoundText = !showSendSoundText;
});
}
},
child: SvgPicture.asset(
showSendSoundText
? 'images/keyboard.svg'
: 'images/voice.svg',
package: 'tencent_cloud_chat_uikit',
color: const Color.fromRGBO(68, 68, 68, 1),
height: 28,
width: 28,
child: Column(
children: [
Container(
padding: const EdgeInsets.symmetric(
vertical: 8, horizontal: 16),
constraints: const BoxConstraints(minHeight: 50),
child: Row(
children: [
if (forbiddenText != null)
Expanded(
child: Container(
height: 35,
color: theme.weakBackgroundColor,
alignment: Alignment.center,
child: Text(
TIM_t(forbiddenText),
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: theme.weakTextColor,
),
),
if (forbiddenText == null)
const SizedBox(
width: 10,
)),
if (!PlatformUtils().isWeb &&
widget.showSendAudio &&
forbiddenText == null)
InkWell(
onTap: () async {
if (showSendSoundText) {
showKeyboard = true;
focusNode.requestFocus();
}
if (await Permissions.checkPermission(
context,
Permission.microphone.value,
theme,
)) {
setState(() {
showEmojiPanel = false;
showKeyboard = false;
showMore = false;
showSendSoundText = !showSendSoundText;
});
}
},
child: SvgPicture.asset(
showSendSoundText
? 'images/keyboard.svg'
: 'images/voice.svg',
package: 'tencent_cloud_chat_uikit',
color: const Color.fromRGBO(68, 68, 68, 1),
height: 28,
width: 28,
),
if (forbiddenText == null)
Expanded(
child: showSendSoundText
? SendSoundMessage(
onDownBottom: goDownBottom,
conversationID: widget.conversationID,
conversationType:
widget.conversationType)
: ExtendedTextField(
// selectionControls:
// _defaultTextSelectionControls,
maxLines: 4,
minLines: 1,
focusNode: focusNode,
onChanged: debounceFunc,
onTap: () {
goDownBottom();
setState(() {
),
if (forbiddenText == null)
const SizedBox(
width: 10,
),
if (forbiddenText == null)
Expanded(
child: showSendSoundText
? SendSoundMessage(
onDownBottom: goDownBottom,
conversationID: widget.conversationID,
conversationType: widget.conversationType)
: KeyboardVisibility(
child: ExtendedTextField(
maxLines: 4,
minLines: 1,
focusNode: focusNode,
onChanged: debounceFunc,
onTap: () {
showKeyboard = true;
showEmojiPanel = false;
showMore = false;
goDownBottom();
setState(() {
showEmojiPanel = false;
showMore = false;
});
},
keyboardType: TextInputType.multiline,
textInputAction:
PlatformUtils().isAndroid
? TextInputAction.newline
: TextInputAction.send,
onEditingComplete: onSubmitted,
textAlignVertical:
TextAlignVertical.top,
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: const TextStyle(
// fontSize: 10,
color: Color(0xffAEA4A3),
),
fillColor: Colors.white,
filled: true,
isDense: true,
hintText: widget.hintText ?? ''),
controller: textEditingController,
specialTextSpanBuilder: PlatformUtils().isWeb
? null
: DefaultSpecialTextSpanBuilder(
isUseDefaultEmoji:
widget.isUseDefaultEmoji,
customEmojiStickerList: widget
.customEmojiStickerList,
showAtBackground: true,
)
),
onChanged: (bool visibility) {
if (showKeyboard != visibility &&
visibility == false) {
setState(() {
showKeyboard = visibility;
});
},
keyboardType: TextInputType.multiline,
textInputAction:
PlatformUtils().isAndroid
? TextInputAction.newline
: TextInputAction.send,
onEditingComplete: onSubmitted,
textAlignVertical:
TextAlignVertical.center,
decoration: InputDecoration(
border: InputBorder.none,
hintStyle: const TextStyle(
// fontSize: 10,
color: Color(0xffAEA4A3),
),
fillColor: Colors.white,
filled: true,
isDense: true,
hintText: widget.hintText ?? ''),
controller: textEditingController,
specialTextSpanBuilder:
DefaultSpecialTextSpanBuilder(
isUseDefaultEmoji:
widget.isUseDefaultEmoji,
customEmojiStickerList:
widget.customEmojiStickerList,
showAtBackground: true,
)),
}
}),
),
if (forbiddenText == null)
const SizedBox(
width: 10,
),
if (widget.showSendEmoji && forbiddenText == null)
InkWell(
onTap: () {
_openEmojiPanel();
goDownBottom();
},
child: PlatformUtils().isWeb
? Icon(
showEmojiPanel
? Icons.keyboard_alt_outlined
: Icons.mood_outlined,
color: hexToColor("5c6168"),
size: 32)
: SvgPicture.asset(
showEmojiPanel
? 'images/keyboard.svg'
: 'images/face.svg',
package: 'tencent_cloud_chat_uikit',
color:
const Color.fromRGBO(68, 68, 68, 1),
height: 28,
width: 28,
),
),
if (forbiddenText == null)
const SizedBox(
width: 10,
),
if (widget.showMorePanel &&
forbiddenText == null &&
showMoreButton)
InkWell(
onTap: () {
// model.sendCustomMessage(data: "a", convID: model.currentSelectedConv, convType: model.currentSelectedConvType == 1 ? ConvType.c2c : ConvType.group);
_openMore();
goDownBottom();
},
child: PlatformUtils().isWeb
? Icon(Icons.add_circle_outline_outlined,
color: hexToColor("5c6168"), size: 32)
: SvgPicture.asset(
'images/add.svg',
package: 'tencent_cloud_chat_uikit',
color:
const Color.fromRGBO(68, 68, 68, 1),
height: 28,
width: 28,
),
),
if ((isAndroidDevice() || isWebDevice()) &&
!showMoreButton)
SizedBox(
height: 32.0,
child: ElevatedButton(
onPressed: onSubmitted,
child: Text(TIM_t("发送")),
),
if (forbiddenText == null)
const SizedBox(
width: 10,
),
if (widget.showSendEmoji && forbiddenText == null)
InkWell(
onTap: () {
_openEmojiPanel();
goDownBottom();
},
child: kIsWeb
? Icon(
showEmojiPanel
? Icons.keyboard_alt_outlined
: Icons.mood_outlined,
color: hexToColor("5c6168"),
size: 32)
: SvgPicture.asset(
showEmojiPanel
? 'images/keyboard.svg'
: 'images/face.svg',
package: 'tencent_cloud_chat_uikit',
color:
const Color.fromRGBO(68, 68, 68, 1),
height: 28,
width: 28,
),
),
if (forbiddenText == null)
const SizedBox(
width: 10,
),
if (widget.showMorePannel &&
forbiddenText == null &&
showMoreButton)
InkWell(
onTap: () {
// model.sendCustomMessage(data: "a", convID: model.currentSelectedConv, convType: model.currentSelectedConvType == 1 ? ConvType.c2c : ConvType.group);
_openMore();
goDownBottom();
},
child: kIsWeb
? Icon(Icons.add_circle_outline_outlined,
color: hexToColor("5c6168"), size: 32)
: SvgPicture.asset(
'images/add.svg',
package: 'tencent_cloud_chat_uikit',
color:
const Color.fromRGBO(68, 68, 68, 1),
height: 28,
width: 28,
),
),
if ((isAndroidDevice() || isWebDevice()) &&
!showMoreButton)
SizedBox(
height: 32.0,
child: ElevatedButton(
onPressed: onSubmitted,
child: Text(TIM_t("发送")),
),
),
],
),
),
],
),
AnimatedContainer(
duration: const Duration(milliseconds: 150),
height: _getBottomHeight(),
child: _getBottomContainer(),
)
],
),
),
AnimatedContainer(
duration: Duration(
milliseconds:
(showKeyboard && PlatformUtils().isAndroid)
? 200
: 340),
curve: Curves.fastOutSlowIn,
height: max(_getBottomHeight(), 0.0),
child: _getBottomContainer(),
),
],
),
)
],

View File

@ -16,7 +16,6 @@ import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/constants/history_message_constant.dart';
import 'package:tencent_cloud_chat_uikit/ui/controller/tim_uikit_chat_controller.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/custom_emoji_face_data_class.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/frame.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/optimize_utils.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/tim_uikit_multi_select_panel.dart';
@ -318,6 +317,7 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
textFieldController.hideAllPanel();
},
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: TIMUIKitAppBar(
showTotalUnReadCount: widget.showTotalUnReadCount,
config: widget.appBarConfig,
@ -395,7 +395,7 @@ class _TUIChatState extends TIMUIKitState<TIMUIKitChat> {
conversationType: widget.conversationType,
initText: widget.draftText,
hintText: widget.textFieldHintText,
showMorePannel:
showMorePanel:
widget.config?.isAllowShowMorePanel ??
true,
showSendAudio:

View File

@ -5,7 +5,6 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_chat_separate_view_model.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/forward_message_screen.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
@ -50,21 +49,21 @@ class MultiSelectPanel extends TIMUIKitStatelessWidget {
children: [
IconButton(
icon: Image.asset('images/forward.png',
package: 'tencent_cloud_chat_uikit', color: Colors.white),
package: 'tencent_cloud_chat_uikit', color: theme.white),
iconSize: 40,
onPressed: () {
_handleForwardMessage(context, false, model);
},
),
Text(TIM_t("逐条转发"),
style: const TextStyle(color: Colors.white, fontSize: 12))
style: TextStyle(color: theme.white, fontSize: 12))
],
),
Column(
children: [
IconButton(
icon: Image.asset('images/merge_forward.png',
package: 'tencent_cloud_chat_uikit', color: Colors.white),
package: 'tencent_cloud_chat_uikit', color: theme.white),
iconSize: 40,
onPressed: () {
_handleForwardMessage(context, true, model);
@ -72,7 +71,7 @@ class MultiSelectPanel extends TIMUIKitStatelessWidget {
),
Text(
TIM_t("合并转发"),
style: const TextStyle(color: Colors.white, fontSize: 12),
style: TextStyle(color: theme.white, fontSize: 12),
)
],
),
@ -80,7 +79,7 @@ class MultiSelectPanel extends TIMUIKitStatelessWidget {
children: [
IconButton(
icon: Image.asset('images/delete.png',
package: 'tencent_cloud_chat_uikit', color: Colors.white),
package: 'tencent_cloud_chat_uikit', color: theme.white),
iconSize: 40,
onPressed: () {
showCupertinoModalPopup<String>(
@ -121,7 +120,7 @@ class MultiSelectPanel extends TIMUIKitStatelessWidget {
},
),
Text(TIM_t("删除"),
style: const TextStyle(color: Colors.white, fontSize: 12))
style: TextStyle(color: theme.white, fontSize: 12))
],
)
],

View File

@ -187,8 +187,9 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
onPressed: (context) {
_clearHistory(conversationItem);
},
backgroundColor: theme.primaryColor ?? CommonColor.primaryColor,
foregroundColor: Colors.white,
backgroundColor: theme.conversationItemSliderClearBgColor ??
CommonColor.primaryColor,
foregroundColor: theme.conversationItemSliderTextColor,
label: TIM_t("清除聊天"),
spacing: 0,
autoClose: true,
@ -197,16 +198,18 @@ class _TIMUIKitConversationState extends TIMUIKitState<TIMUIKitConversation> {
onPressed: (context) {
_pinConversation(conversationItem);
},
backgroundColor: theme.infoColor ?? CommonColor.infoColor,
foregroundColor: Colors.white,
backgroundColor:
theme.conversationItemSliderPinBgColor ?? CommonColor.infoColor,
foregroundColor: theme.conversationItemSliderTextColor,
label: conversationItem.isPinned! ? TIM_t("取消置顶") : TIM_t("置顶"),
),
ConversationItemSlidablePanel(
onPressed: (context) {
_deleteConversation(conversationItem);
},
backgroundColor: Colors.red,
foregroundColor: Colors.white,
backgroundColor:
theme.conversationItemSliderDeleteBgColor ?? Colors.red,
foregroundColor: theme.conversationItemSliderTextColor,
label: TIM_t("删除"),
)
];

View File

@ -24,14 +24,20 @@ class TIMUIKitDraftText extends TIMUIKitStatelessWidget {
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final TUITheme theme = value.theme;
return Row(children: [
Text(_getDraftShowText(), style: TextStyle(color: theme.cautionColor)),
Text(_getDraftShowText(),
style: TextStyle(
color: theme.conversationItemDraftTextColor,
)),
Expanded(
child: Text(
draftText,
softWrap: true,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(height: 1.5, color: theme.weakTextColor, fontSize: 14),
style: TextStyle(
height: 1.5,
color: theme.conversationItemLastMessageTextColor,
fontSize: 14),
)),
]);
}

View File

@ -85,13 +85,13 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget {
return Text(TimeAgo().getTimeStringForChat(draftTimestamp as int),
style: TextStyle(
fontSize: 12,
color: theme.weakTextColor,
color: theme.conversationItemTitmeTextColor,
));
} else if (lastMsg != null) {
return Text(TimeAgo().getTimeStringForChat(lastMsg!.timestamp as int),
style: TextStyle(
fontSize: 12,
color: theme.weakTextColor,
color: theme.conversationItemTitmeTextColor,
));
}
} catch (err) {}
@ -105,11 +105,17 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget {
return Container(
padding: const EdgeInsets.only(top: 6, bottom: 6, left: 16, right: 16),
decoration: BoxDecoration(
color: isPined ? theme.weakBackgroundColor : Colors.white,
border: Border(
bottom: BorderSide(
color: theme.weakDividerColor ?? CommonColor.weakDividerColor,
width: 1))),
color: isPined
? theme.conversationItemPinedBgColor
: theme.conversationItemBgColor,
border: Border(
bottom: BorderSide(
color: theme.conversationItemBorderColor ??
CommonColor.weakDividerColor,
width: 1,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@ -160,11 +166,12 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget {
textAlign: TextAlign.left,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: const TextStyle(
height: 1,
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.w400),
style: TextStyle(
height: 1,
color: theme.conversationItemTitleTextColor,
fontSize: 18,
fontWeight: FontWeight.w400,
),
)),
_getTimeStringForChatWidget(context, theme),
],
@ -182,7 +189,7 @@ class TIMUIKitConversationItem extends TIMUIKitStatelessWidget {
height: 18,
child: Icon(
Icons.notifications_off,
color: theme.weakTextColor,
color: theme.conversationItemNoNotificationIconColor,
size: 16.0,
),
)

View File

@ -93,6 +93,14 @@ class _TIMUIKitGroupProfileState extends TIMUIKitState<TIMUIKitGroupProfile> {
super.dispose();
}
@override
void didUpdateWidget(covariant TIMUIKitGroupProfile oldWidget) {
super.didUpdateWidget(oldWidget);
if(oldWidget.groupID != widget.groupID){
model.loadData(widget.groupID);
}
}
final List<GroupProfileWidgetEnum> _defaultWidgetOrder = [
GroupProfileWidgetEnum.detailCard,
GroupProfileWidgetEnum.operationDivider,
@ -213,7 +221,7 @@ class _TIMUIKitGroupProfileState extends TIMUIKitState<TIMUIKitGroupProfile> {
if (isAdmin || isGroupOwner) {
return (customBuilder?.groupManage != null
? customBuilder?.groupManage!(toDefaultManagePage)
: TIMUIKitGroupProfileWidget.groupManage(model))!;
: TIMUIKitGroupProfileWidget.groupManage())!;
} else {
return Container();
}

View File

@ -32,8 +32,8 @@ class TIMUIKitGroupProfileWidget {
return GroupProfileNotification();
}
static Widget groupManage(TUIGroupProfileModel model) {
return GroupProfileGroupManage(model);
static Widget groupManage() {
return GroupProfileGroupManage();
}
static Widget searchMessage(Function(V2TimConversation?) onJumpToSearch) {

View File

@ -16,8 +16,7 @@ import 'package:tencent_cloud_chat_uikit/ui/widgets/radio_button.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
class GroupProfileGroupManage extends TIMUIKitStatelessWidget {
final TUIGroupProfileModel model;
GroupProfileGroupManage(this.model, {Key? key}) : super(key: key);
GroupProfileGroupManage({Key? key}) : super(key: key);
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
@ -600,24 +599,17 @@ class _GroupProfileAddAdminState extends TIMUIKitState<GroupProfileAddAdmin> {
onTap: () {
final isChecked = selectedMemberList.contains(e);
if (isChecked) {
selectedMemberList.add(e);
} else {
selectedMemberList.remove(e);
} else {
selectedMemberList.add(e);
}
setState(() {});
},
child: Row(
children: [
CheckBoxButton(
onlyShow: true,
isChecked: selectedMemberList.contains(e),
onChanged: (value) {
if (value) {
selectedMemberList.add(e);
} else {
selectedMemberList.remove(e);
}
setState(() {});
},
),
const SizedBox(
width: 10,

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_group_profile_model.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/text_input_bottom_sheet.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
@ -35,78 +36,12 @@ class GroupProfileNameCard extends TIMUIKitStatelessWidget {
theme.weakDividerColor ?? CommonColor.weakDividerColor))),
child: InkWell(
onTap: () async {
showModalBottomSheet(
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
context: context,
builder: (context) {
return Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.0),
topRight: Radius.circular(10.0))),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Text(TIM_t("修改我的群昵称")),
),
Divider(height: 2, color: theme.weakDividerColor),
Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: controller,
decoration: InputDecoration(
border: InputBorder.none,
fillColor: theme.weakBackgroundColor,
filled: true,
isDense: true,
hintText: ''),
),
const SizedBox(
height: 10,
),
Text(
TIM_t("仅限中文、字母、数字和下划线2-20个字"),
style: TextStyle(
fontSize: 13, color: theme.weakTextColor),
textAlign: TextAlign.left,
),
const SizedBox(
height: 30,
),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
final text = controller.text.trim();
model.setNameCard(text);
Navigator.pop(context);
},
child: Text(TIM_t("确定")),
)),
const SizedBox(
height: 20,
),
Padding(
padding: EdgeInsets.only(
bottom:
MediaQuery.of(context).viewInsets.bottom),
)
],
),
)
],
),
);
});
TextInputBottomSheet.showTextInputBottomSheet(
context, TIM_t("修改我的群昵称"), TIM_t("仅限中文、字母、数字和下划线2-20个字"),
(String nameCard) async {
final text = nameCard.trim();
model.setNameCard(text);
}, theme);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

@ -7,7 +7,6 @@ import 'package:tencent_cloud_chat_uikit/business_logic/separate_models/tui_prof
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_self_info_view_model.dart';
import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitProfile/profile_widget.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitProfile/widget/tim_uikit_profile_widget.dart';
@ -123,6 +122,14 @@ class _TIMUIKitProfileState extends TIMUIKitState<TIMUIKitProfile> {
super.dispose();
}
@override
void didUpdateWidget(covariant TIMUIKitProfile oldWidget) {
super.didUpdateWidget(oldWidget);
if(oldWidget.userID != widget.userID){
_model.loadData(userID: widget.userID, isNeedConversation: !widget.isSelf);
}
}
final List<ProfileWidgetEnum> _defaultWidgetOrder = [
ProfileWidgetEnum.userInfoCard,
ProfileWidgetEnum.operationDivider,
@ -189,14 +196,14 @@ class _TIMUIKitProfileState extends TIMUIKitState<TIMUIKitProfile> {
void handleTapRemarkBar() {
_controller.showTextInputBottomSheet(
context, TIM_t("修改备注名"), TIM_t("仅限中字、字母、数字和下划线"),
context, TIM_t("修改备注名"), TIM_t("仅限汉字、英文、数字和下划线"),
(String remark) async {
final res =
await _controller.updateRemarks(widget.userID, remark);
if (res.code == 0) {
widget.lifeCycle?.didRemarkUpdated(remark);
}
});
}, theme);
}
void handleAddFriend() async {

View File

@ -37,38 +37,45 @@ class TIMUIKitOperationItem extends TIMUIKitStatelessWidget {
final TUITheme theme = value.theme;
return Container(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
margin: const EdgeInsets.only(bottom: 1),
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Container(
margin: const EdgeInsets.only(right: 22),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(operationName),
if (operationDescription != null)
Text(
operationDescription!,
style:
TextStyle(color: theme.weakTextColor, fontSize: 12),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(operationName),
if (operationDescription != null)
Text(
operationDescription!,
style:
TextStyle(color: theme.weakTextColor, fontSize: 12),
)
],
),
),
type == "switch"
? CupertinoSwitch(
value: operationValue ?? false,
onChanged: onSwitchChange,
activeColor: theme.primaryColor,
? Transform.scale(
scale: 0.8,
child: CupertinoSwitch(
value: operationValue ?? false,
onChanged: onSwitchChange,
activeColor: theme.primaryColor,
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Transform.scale(
scale: 0,
child: CupertinoSwitch(
value: false,
onChanged: onSwitchChange,
),
),
ConstrainedBox(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width / 1.6,

View File

@ -45,6 +45,7 @@ class TIMUIKitProfileUserInfoCard extends TIMUIKitStatelessWidget {
height: 48,
child: Avatar(
faceUrl: faceUrl,
isShowBigWhenClick: true,
showName: showName ?? "",
type: 1,
),
@ -66,9 +67,17 @@ class TIMUIKitProfileUserInfoCard extends TIMUIKitStatelessWidget {
),
Container(
margin: const EdgeInsets.symmetric(vertical: 4),
child: Text(
"ID: ${userInfo?.userID ?? ""}",
style: TextStyle(fontSize: 13, color: theme.weakTextColor),
child: Row(
children: [
Text(
"ID: ",
style: TextStyle(fontSize: 13, color: theme.weakTextColor),
),
SelectableText(
userInfo?.userID ?? "",
style: TextStyle(fontSize: 13, color: theme.weakTextColor),
),
],
),
),
Text(signatureText,

View File

@ -146,7 +146,7 @@ class TIMUIKitProfileWidget extends TIMUIKitClass {
child: TIMUIKitOperationItem(
showArrowRightIcon: false,
operationName: TIM_t("账号"),
operationRightWidget: Text(userNum),
operationRightWidget: SelectableText(userNum),
),
);
}
@ -165,7 +165,7 @@ class TIMUIKitProfileWidget extends TIMUIKitClass {
/// gender
static Widget genderBar(int gender) {
Map genderMap = {
0: TIM_t(""),
0: TIM_t("填写"),
1: TIM_t(""),
2: TIM_t(""),
};
@ -181,7 +181,7 @@ class TIMUIKitProfileWidget extends TIMUIKitClass {
/// gender
static Widget genderBarWithArrow(int gender) {
Map genderMap = {
0: TIM_t(""),
0: TIM_t("填写"),
1: TIM_t(""),
2: TIM_t(""),
};
@ -212,7 +212,7 @@ class TIMUIKitProfileWidget extends TIMUIKitClass {
child: TIMUIKitOperationItem(
showArrowRightIcon: false,
operationName: TIM_t("生日"),
operationRightWidget: Text(TIM_t("")),
operationRightWidget: Text(TIM_t("填写")),
),
);
}

View File

@ -0,0 +1,113 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/radio_button.dart';
enum SearchType { contact, group, history }
class TIMUIKitSearchIndicator extends TIMUIKitStatelessWidget {
final List<SearchType> typeList;
final ValueChanged<List<SearchType>> onChange;
TIMUIKitSearchIndicator(
{required this.typeList, required this.onChange, Key? key})
: super(key: key);
final titleMap = {
SearchType.contact: "联系人",
SearchType.group: "群聊",
SearchType.history: "聊天记录"
};
Widget renderItemBox(
IconData icon, SearchType item, bool isSelect, TUITheme theme) {
return InkWell(
onTap: () {
if (isSelect) {
typeList.remove(item);
} else {
typeList.add(item);
}
onChange(typeList);
},
child: Container(
padding: const EdgeInsets.all(6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(
children: [
Padding(
padding: const EdgeInsets.all(6),
child: Icon(
icon,
color: theme.weakTextColor,
size: 30,
),
),
if (isSelect)
Positioned(
right: 0,
bottom: 0,
child: Container(
height: 16,
width: 16,
decoration: BoxDecoration(shape: BoxShape.circle, color: theme.primaryColor),
child: const Icon(
Icons.check,
size: 8,
color: Colors.white,
),
))
],
),
const SizedBox(height: 4),
Text(
TIM_t(titleMap[item]!),
style: TextStyle(color: theme.textColor, fontSize: 13),
)
],
),
),
);
}
@override
Widget tuiBuild(BuildContext context, TUIKitBuildValue value) {
final theme = value.theme;
return Container(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Text(TIM_t("搜索指定内容"),
style: TextStyle(color: theme.weakTextColor, fontSize: 12)),
)
],
),
const SizedBox(height: 1),
Divider(thickness: 0.8, color: theme.weakDividerColor),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
renderItemBox(Icons.person, SearchType.contact,
typeList.contains(SearchType.contact), theme),
renderItemBox(Icons.people, SearchType.group,
typeList.contains(SearchType.group), theme),
renderItemBox(Icons.message, SearchType.history,
typeList.contains(SearchType.history), theme),
],
)
],
),
);
}
}

View File

@ -5,6 +5,7 @@ import 'package:tencent_cloud_chat_uikit/data_services/services_locatar.dart';
import 'package:tencent_cloud_chat_uikit/business_logic/view_models/tui_search_view_model.dart';
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitSearch/pureUI/tim_uikit_search_indicator.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitSearch/tim_uikit_search_friend.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitSearch/pureUI/tim_uikit_search_input.dart';
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitSearch/tim_uikit_search_group.dart';
@ -51,6 +52,11 @@ class TIMUIKitSearchState extends TIMUIKitState<TIMUIKitSearch> {
late TextEditingController textEditingController = TextEditingController();
final model = serviceLocator<TUISearchViewModel>();
GlobalKey<dynamic> inputTextField = GlobalKey();
List<SearchType> searchTypes = [
SearchType.group,
SearchType.contact,
SearchType.history
];
@override
void initState() {
@ -70,12 +76,12 @@ class TIMUIKitSearchState extends TIMUIKitState<TIMUIKitSearch> {
value: serviceLocator<TUISearchViewModel>())
],
builder: (context, w) {
List<V2TimFriendInfoResult>? friendResultList =
Provider.of<TUISearchViewModel>(context).friendList;
List<V2TimMessageSearchResultItem>? msgList =
Provider.of<TUISearchViewModel>(context).msgList;
List<V2TimGroupInfo>? groupList =
Provider.of<TUISearchViewModel>(context).groupList;
List<V2TimFriendInfoResult> friendResultList =
Provider.of<TUISearchViewModel>(context).friendList ?? [];
List<V2TimMessageSearchResultItem> msgList =
Provider.of<TUISearchViewModel>(context).msgList ?? [];
List<V2TimGroupInfo> groupList =
Provider.of<TUISearchViewModel>(context).groupList ?? [];
int totalMsgCount =
Provider.of<TUISearchViewModel>(context).totalMsgCount;
return GestureDetector(
@ -105,27 +111,45 @@ class TIMUIKitSearchState extends TIMUIKitState<TIMUIKitSearch> {
child: SingleChildScrollView(
child: Column(
children: [
TIMUIKitSearchFriend(
if (friendResultList.isEmpty ||
!(searchTypes.contains(SearchType.contact)) &&
(groupList.isEmpty ||
!(searchTypes.contains(SearchType.group))) &&
(totalMsgCount == 0 ||
!(searchTypes.contains(SearchType.history))))
TIMUIKitSearchIndicator(
typeList: searchTypes,
onChange: (list) {
setState(() {
searchTypes = list;
});
},
),
if (searchTypes.contains(SearchType.contact))
TIMUIKitSearchFriend(
onTapConversation: widget.onTapConversation,
friendResultList: friendResultList),
if (searchTypes.contains(SearchType.group))
TIMUIKitSearchGroup(
groupList: groupList,
onTapConversation: widget.onTapConversation),
if (searchTypes.contains(SearchType.history))
TIMUIKitSearchMsg(
onTapConversation: widget.onTapConversation,
friendResultList: friendResultList ?? []),
TIMUIKitSearchGroup(
groupList: groupList ?? [],
onTapConversation: widget.onTapConversation),
TIMUIKitSearchMsg(
onTapConversation: widget.onTapConversation,
keyword: textEditingController.text,
totalMsgCount: totalMsgCount,
msgList: msgList ?? [],
onEnterConversation:
(V2TimConversation conversation, String keyword) {
if (widget.onEnterSearchInConversation != null) {
widget.onEnterSearchInConversation!(
conversation, keyword);
} else if (widget.onEnterConversation != null) {
widget.onEnterConversation!(conversation, keyword);
}
},
)
keyword: textEditingController.text,
totalMsgCount: totalMsgCount,
msgList: msgList,
onEnterConversation:
(V2TimConversation conversation, String keyword) {
if (widget.onEnterSearchInConversation != null) {
widget.onEnterSearchInConversation!(
conversation, keyword);
} else if (widget.onEnterConversation != null) {
widget.onEnterConversation!(
conversation, keyword);
}
},
)
],
),
))

View File

@ -1,5 +1,6 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/image_screen.dart';
import 'package:tencent_im_base/tencent_im_base.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
import 'package:tencent_cloud_chat_uikit/data_services/core/core_services_implements.dart';
@ -9,22 +10,25 @@ import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_base.dart';
class Avatar extends TIMUIKitStatelessWidget {
final String faceUrl;
final String showName;
final bool isFromLocal;
final bool isFromLocalAsset;
final CoreServicesImpl coreService = serviceLocator<CoreServicesImpl>();
final BorderRadius? borderRadius;
final V2TimUserStatus? onlineStatus;
final int? type; // 1 c2c 2 group
final bool isShowBigWhenClick;
Avatar(
{Key? key,
required this.faceUrl,
this.onlineStatus,
required this.showName,
this.isFromLocal = false,
this.isShowBigWhenClick = false,
this.isFromLocalAsset = false,
this.borderRadius,
this.type = 1})
: super(key: key);
Widget _getFaceUrlImageWidget(BuildContext context, TUITheme theme) {
Widget getImageWidget(BuildContext context, TUITheme theme) {
Widget defaultAvatar() {
if (type == 1) {
return Image.asset('images/default_c2c_head.png',
@ -37,7 +41,7 @@ class Avatar extends TIMUIKitStatelessWidget {
// final emptyAvatarBuilder = coreService.emptyAvatarBuilder;
if (faceUrl != "") {
if (isFromLocal) {
if (isFromLocalAsset) {
return Image.asset(faceUrl);
}
return CachedNetworkImage(
@ -45,33 +49,35 @@ class Avatar extends TIMUIKitStatelessWidget {
fadeInDuration: const Duration(milliseconds: 0),
errorWidget: (BuildContext context, String c, dynamic s) {
return defaultAvatar();
// if (emptyAvatarBuilder != null) {
// return emptyAvatarBuilder(context);
// }
// return Container(
// alignment: Alignment.center,
// decoration: BoxDecoration(
// color: theme.primaryColor,
// ),
// child: Text(
// showName.length <= 2 ? showName : showName.substring(0, 2),
// style: const TextStyle(color: Colors.white, fontSize: 14),
// ),
// );
},
);
} else {
return defaultAvatar();
// return Container(
// alignment: Alignment.center,
// decoration: BoxDecoration(
// color: theme.primaryColor,
// ),
// child: Text(
// showName.length <= 2 ? showName : showName.substring(0, 2),
// style: const TextStyle(color: Colors.white, fontSize: 14),
// ),
// );
}
}
ImageProvider getImageProvider() {
ImageProvider defaultAvatar() {
if (type == 1) {
return Image.asset('images/default_c2c_head.png',
package: 'tencent_cloud_chat_uikit')
.image;
} else {
return Image.asset('images/default_group_head.png',
package: 'tencent_cloud_chat_uikit')
.image;
}
}
if (faceUrl != "") {
if (isFromLocalAsset) {
return Image.asset(faceUrl).image;
}
return CachedNetworkImageProvider(
faceUrl,
);
} else {
return defaultAvatar();
}
}
@ -83,10 +89,30 @@ class Avatar extends TIMUIKitStatelessWidget {
fit: StackFit.expand,
clipBehavior: Clip.none,
children: [
ClipRRect(
borderRadius: borderRadius ?? BorderRadius.circular(4.8),
child: _getFaceUrlImageWidget(context, theme),
),
if (isShowBigWhenClick)
GestureDetector(
onTap: () {
Navigator.of(context).push(
PageRouteBuilder(
opaque: false, // set to false
pageBuilder: (_, __, ___) => ImageScreen(
imageProvider: getImageProvider(), heroTag: faceUrl),
),
);
},
child: Hero(
tag: faceUrl,
child: ClipRRect(
borderRadius: borderRadius ?? BorderRadius.circular(4.8),
child: getImageWidget(context, theme),
),
),
),
if (!isShowBigWhenClick)
ClipRRect(
borderRadius: borderRadius ?? BorderRadius.circular(4.8),
child: getImageWidget(context, theme),
),
if (onlineStatus?.statusType != null && onlineStatus?.statusType != 0)
Positioned(
bottom: -4,
@ -102,8 +128,8 @@ class Avatar extends TIMUIKitStatelessWidget {
width: 2.0,
),
color: onlineStatus?.statusType == 1
? hexToColor("43db2b")
: hexToColor("b3b8ba"),
? theme.conversationItemOnlineStatusBgColor
: theme.conversationItemOfflineStatusBgColor,
),
child: null,
),

View File

@ -3,6 +3,7 @@ import 'dart:math';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/center_loading.dart';
import 'package:tencent_cloud_chat_uikit/ui/widgets/gestured_image.dart';
@ -15,15 +16,15 @@ class ImageScreen extends StatefulWidget {
const ImageScreen(
{required this.imageProvider,
required this.heroTag,
required this.downloadFn,
required this.messageID,
this.downloadFn,
this.messageID,
Key? key})
: super(key: key);
final ImageProvider imageProvider;
final String heroTag;
final String? messageID;
final void Function() downloadFn;
final Future<void> Function()? downloadFn;
@override
State<StatefulWidget> createState() {
@ -40,9 +41,10 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
double currentScale = 1.0;
double fittedScale = 1.0;
double initialScale = 1.0;
bool isLoading = false;
GlobalKey<ExtendedImageSlidePageState> slidePagekey =
GlobalKey<ExtendedImageSlidePageState>();
GlobalKey<ExtendedImageGestureState> extendedImageGestureKey =
GlobalKey<ExtendedImageGestureState>();
@ -84,7 +86,9 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
constraints: BoxConstraints.expand(
height: MediaQuery.of(context).size.height,
),
child: Stack(children: [
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
top: 0,
left: 0,
@ -250,19 +254,42 @@ class _ImageScreenState extends TIMUIKitState<ImageScreen>
iconSize: 30,
onPressed: close,
)),
Positioned(
right: 10,
bottom: 50,
child: IconButton(
icon: Image.asset(
'images/download.png',
package: 'tencent_cloud_chat_uikit',
if (widget.downloadFn != null)
Positioned(
right: 10,
bottom: 50,
child: IconButton(
icon: Image.asset(
'images/download.png',
package: 'tencent_cloud_chat_uikit',
),
iconSize: 30,
onPressed: () async {
setState(() {
isLoading = true;
});
await widget.downloadFn!();
Future.delayed(const Duration(milliseconds: 200),(){
setState(() {
isLoading = false;
});
});
},
),
iconSize: 30,
onPressed: widget.downloadFn,
),
),
CenterLoading(messageID: widget.messageID),
if (isLoading)
Container(
child: LoadingAnimationWidget.staggeredDotsWave(
size: 35,
color: Colors.white,
),
padding: const EdgeInsets.all(30),
decoration: const BoxDecoration(
color: Color(0xB22b2b2b),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
),
])),
);
}));

View File

@ -1,3 +1,4 @@
import 'package:tencent_cloud_chat_uikit/ui/utils/platform.dart';
import 'package:tencent_extended_text/extended_text.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
@ -70,7 +71,7 @@ class LinkText extends TIMStatelessWidget {
String _getContentSpan(String text, BuildContext context) {
List<InlineSpan> _contentList = [];
String contentData = '';
String contentData = PlatformUtils().isWeb ? '\u200B' : "";
Iterable<RegExpMatch> matches = LinkUtils.urlReg.allMatches(text);

View File

@ -7,10 +7,14 @@ class CheckBoxButton extends TIMUIKitStatelessWidget {
final bool isChecked;
final Function(bool isChecked)? onChanged;
final bool disabled;
final bool onlyShow;
final double? size;
CheckBoxButton(
{this.disabled = false,
Key? key,
this.size,
this.onlyShow = false,
required this.isChecked,
this.onChanged})
: super(key: key);
@ -31,22 +35,33 @@ class CheckBoxButton extends TIMUIKitStatelessWidget {
const BoxDecoration(shape: BoxShape.circle, color: Colors.grey);
}
return Center(
child: InkWell(
onTap: () {
if (onChanged != null && !disabled) {
onChanged!(!isChecked);
}
},
child: Container(
height: 22,
width: 22,
decoration: boxDecoration,
child: const Icon(
Icons.check,
size: 11.0,
color: Colors.white,
),
),
));
child: onlyShow
? Container(
height: size ?? 22,
width: size ?? 22,
decoration: boxDecoration,
child: Icon(
Icons.check,
size: size != null ? (size! / 2) : 11,
color: Colors.white,
),
)
: InkWell(
onTap: () {
if (onChanged != null && !disabled) {
onChanged!(!isChecked);
}
},
child: Container(
height: size ?? 22,
width: size ?? 22,
decoration: boxDecoration,
child: Icon(
Icons.check,
size: size != null ? (size! / 2) : 11,
color: Colors.white,
),
),
));
}
}

View File

@ -3,7 +3,7 @@ import 'package:tencent_im_base/tencent_im_base.dart';
class TextInputBottomSheet {
static showTextInputBottomSheet(BuildContext context, String title,
String tips, Function(String text) onSubmitted) {
String tips, Function(String text) onSubmitted, TUITheme theme) {
TextEditingController _selectionController = TextEditingController();
showModalBottomSheet(
@ -13,17 +13,21 @@ class TextInputBottomSheet {
return SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(
top: 12,
top: 16,
left: 16,
right: 16,
bottom: MediaQuery.of(context).viewInsets.bottom,
bottom: MediaQuery.of(context).viewInsets.bottom + 30,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(title,
style: const TextStyle(
fontWeight: FontWeight.w500, fontSize: 15)),
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(title,
style: const TextStyle(
fontWeight: FontWeight.w500, fontSize: 16)),
),
Divider(height: 2, color: theme.weakDividerColor),
TextField(
controller: _selectionController,
),
@ -35,7 +39,8 @@ class TextInputBottomSheet {
height: 40,
child: Text(
tips,
style: const TextStyle(color: Colors.grey),
style:
const TextStyle(color: Colors.grey, fontSize: 12),
),
)
],
@ -61,9 +66,6 @@ class TextInputBottomSheet {
},
child: Text(TIM_t("确定"))),
),
const SizedBox(
height: 40,
)
],
),
));

View File

@ -30,12 +30,18 @@ class UnreadMessage extends TIMUIKitStatelessWidget {
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: theme.cautionColor ?? CommonColor.cautionColor,
color: theme.conversationItemUnreadCountBgColor ??
CommonColor.cautionColor,
),
child: unreadText != "0"
? Center(
child: Text(unreadText,
style: TextStyle(color: Colors.white, fontSize: fontSize)),
child: Text(
unreadText,
style: TextStyle(
color: theme.conversationItemUnreadCountTextColor,
fontSize: fontSize,
),
),
)
: null,
);

View File

@ -7,6 +7,7 @@ import 'package:chewie/src/helpers/utils.dart';
import 'package:chewie/src/animated_play_pause.dart';
import 'package:chewie/src/material/material_progress_bar.dart';
import 'package:flutter/material.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_state.dart';
import 'package:tencent_cloud_chat_uikit/base_widgets/tim_ui_kit_statelesswidget.dart';
@ -18,7 +19,7 @@ import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
class VideoCustomControls extends StatefulWidget {
const VideoCustomControls({required this.downloadFn, Key? key})
: super(key: key);
final void Function() downloadFn;
final Future<void> Function() downloadFn;
@override
State<StatefulWidget> createState() {
@ -35,6 +36,7 @@ class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls>
Timer? _showAfterExpandCollapseTimer;
bool _dragging = false;
bool _displayTapped = false;
bool isLoading = false;
final barHeight = 48.0;
final marginSize = 5.0;
@ -72,7 +74,7 @@ class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls>
child: AbsorbPointer(
absorbing: _hideStuff,
child: Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: <Widget>[
if (_latestValue.isBuffering)
const Center(
@ -86,7 +88,19 @@ class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls>
_buildVideoControlBar(context),
_buildBottomBar()
]),
)
),
if (isLoading)
Container(
child: LoadingAnimationWidget.staggeredDotsWave(
size: 35,
color: Colors.white,
),
padding: const EdgeInsets.all(30),
decoration: const BoxDecoration(
color: Color(0xB22b2b2b),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
),
],
),
),
@ -149,7 +163,17 @@ class _VideoCustomControlsState extends TIMUIKitState<VideoCustomControls>
package: 'tencent_cloud_chat_uikit',
),
iconSize: 30,
onPressed: widget.downloadFn,
onPressed: () async {
setState(() {
isLoading = true;
});
await widget.downloadFn();
Future.delayed(const Duration(milliseconds: 200),(){
setState(() {
isLoading = false;
});
});
},
)
],
),

View File

@ -59,7 +59,11 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
}
//
_saveNetworkVideo(context, String videoUrl, {bool isAsset = true}) async {
Future<void> _saveNetworkVideo(
context,
String videoUrl, {
bool isAsset = true,
}) async {
if (PlatformUtils().isWeb) {
RegExp exp = RegExp(r"((\.){1}[^?]{2,4})");
String? suffix = exp.allMatches(videoUrl).last.group(0);
@ -83,12 +87,16 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
}
if (PlatformUtils().isIOS) {
if (!await Permissions.checkPermission(
context, Permission.photosAddOnly.value)) {
context,
Permission.photosAddOnly.value,
)) {
return;
}
} else {
if (!await Permissions.checkPermission(
context, Permission.storage.value)) {
context,
Permission.storage.value,
)) {
return;
}
}
@ -172,33 +180,44 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
infoCode: 6660403));
}
}
return;
}
void _saveVideo() {
Future<void> _saveVideo() async {
if (PlatformUtils().isWeb) {
_saveNetworkVideo(context, widget.videoElement.videoPath!,
isAsset: true);
return;
return await _saveNetworkVideo(
context,
widget.videoElement.videoPath!,
isAsset: true,
);
}
if (widget.videoElement.videoPath != '' &&
widget.videoElement.videoPath != null) {
File f = File(widget.videoElement.videoPath!);
if (f.existsSync()) {
_saveNetworkVideo(context, widget.videoElement.videoPath!,
isAsset: true);
return;
return await _saveNetworkVideo(
context,
widget.videoElement.videoPath!,
isAsset: true,
);
}
}
if (widget.videoElement.localVideoUrl != '' &&
widget.videoElement.localVideoUrl != null) {
File f = File(widget.videoElement.localVideoUrl!);
if (f.existsSync()) {
_saveNetworkVideo(context, widget.videoElement.localVideoUrl!,
isAsset: true);
return;
return await _saveNetworkVideo(
context,
widget.videoElement.localVideoUrl!,
isAsset: true,
);
}
}
_saveNetworkVideo(context, widget.videoElement.videoUrl!, isAsset: false);
return await _saveNetworkVideo(
context,
widget.videoElement.videoUrl!,
isAsset: false,
);
}
double getVideoHeight() {
@ -293,7 +312,9 @@ class _VideoScreenState extends TIMUIKitState<VideoScreen> {
showControlsOnInitialize: false,
allowPlaybackSpeedChanging: false,
aspectRatio: w == 0 || h == 0 ? null : w / h,
customControls: VideoCustomControls(downloadFn: _saveVideo));
customControls: VideoCustomControls(downloadFn: () async{
return await _saveVideo();
}));
setState(() {
videoPlayerController = player;
chewieController = controller;

View File

@ -253,13 +253,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.4"
dio:
dependency: "direct main"
description:
name: dio
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.6"
disk_space:
dependency: "direct main"
description:
@ -506,13 +499,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.2"
i18n:
dependency: "direct main"
description:
name: i18n
url: "https://pub.dartlang.org"
source: hosted
version: "3.4.1"
image_gallery_saver:
dependency: "direct main"
description:
@ -898,13 +884,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
quick_log:
dependency: transitive
description:
name: quick_log
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.1"
quiver:
dependency: transitive
description:
@ -1003,6 +982,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
shell:
dependency: transitive
description:
name: shell
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
sky_engine:
dependency: transitive
description: flutter
@ -1077,7 +1063,7 @@ packages:
name: tencent_cloud_chat_sdk
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.4"
version: "5.0.6"
tencent_extended_text:
dependency: "direct main"
description:
@ -1105,14 +1091,21 @@ packages:
name: tencent_im_base
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.7"
version: "1.0.14"
tencent_im_sdk_plugin_platform_interface:
dependency: transitive
description:
name: tencent_im_sdk_plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.9"
version: "0.3.10"
tencent_keyboard_visibility:
dependency: "direct main"
description:
name: tencent_keyboard_visibility
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
tencent_open_file:
dependency: "direct main"
description:
@ -1133,7 +1126,7 @@ packages:
name: tencent_wechat_camera_picker
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.2+5"
version: "3.6.5"
term_glyph:
dependency: transitive
description:
@ -1141,6 +1134,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
tim_ui_kit_sticker_plugin:
dependency: "direct main"
description:
name: tim_ui_kit_sticker_plugin
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
timing:
dependency: transitive
description:

View File

@ -1,6 +1,6 @@
name: tencent_cloud_chat_uikit
description: UI components library and basic chat business logic for Tencent Cloud Chat service, helping you build In-APP Chat module easily.
version: 1.0.1
version: 1.1.0
homepage: https://www.tencentcloud.com/products/im?from=pub
repository: https://github.com/TencentCloud/tc-chat-uikit-flutter
documentation: https://comm.qq.com/im/doc/flutter/en/TUIKit/readme.html
@ -39,17 +39,15 @@ dependencies:
image_gallery_saver: ^1.7.1
photo_view: ^0.14.0
fluttertoast: ^8.0.8
dio: ^4.0.4
path_provider: ^2.0.8
characters: ^1.1.0
i18n: ^3.4.1
cached_network_image: ^3.2.0
shared_preferences: ^2.0.13
json_annotation: ^4.4.0
js: ^0.6.3
scroll_to_index: ^2.1.1
wechat_assets_picker: ^7.2.0
tencent_wechat_camera_picker: ^3.6.2+5
tencent_wechat_camera_picker: ^3.6.5
flutter_easyrefresh: ^2.2.1
flutter_spinkit: ^5.1.0
extended_image: ^6.0.2+1
@ -64,7 +62,7 @@ dependencies:
url_launcher: ^6.1.4
universal_html: ^2.0.8
link_preview_generator: ^1.2.0
tencent_im_base: ^1.0.7
tencent_im_base: ^1.0.14
disk_space: ^0.2.1
http: ^0.13.5
crypto: ^3.0.2
@ -72,6 +70,8 @@ dependencies:
flutter_image_compress: ^1.1.3
uuid: ^3.0.6
tencent_open_file: ^4.0.9
tencent_keyboard_visibility: ^1.0.1
tim_ui_kit_sticker_plugin: ^1.1.1
dev_dependencies:
flutter_lints: ^1.0.0