update flutter uikit to 1.1.0
This commit is contained in:
parent
862939e611
commit
5cba5b30cf
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -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.
|
||||
|
|
|
|||
80
README.md
80
README.md
|
|
@ -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">
|
||||

|
||||
|
||||
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>
|
||||
174
doc/I18N.md
174
doc/I18N.md
|
|
@ -1,2 +1,172 @@
|
|||
【企微文档】TIM Flutter国际化方案
|
||||
https://doc.weixin.qq.com/doc/w3_AWcADQY0AA8u9RhcnhqRSSW6RZc4w?scode=AJEAIQdfAAoaC0QLyWAWcADQY0AA8
|
||||
|
||||
腾讯云IM Flutter TUIKit默认自带 英文/简体中文/繁体中文/日语/韩语 语言包,作为界面展示语言。
|
||||
|
||||
根据此文档指引,您可以使用默认语言包,也可自定义语言翻译表述,并增添额外的非自带语言的支持。
|
||||
|
||||

|
||||
|
||||
## 使用自带语言
|
||||
|
||||
如果您的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` 选项。
|
||||
|
||||

|
||||
|
||||
此时,我们自带的所有语言包,以JSON文件模板的形式,存储于您项目根目录下,`languages/` 路径内。
|
||||
|
||||

|
||||
|
||||
请复制一份您熟悉的语言JSON模板文件,例如,简体中文版,`strings_zh-Hans.i18n.json`。
|
||||
|
||||
复制一份新的,并命名为 `strings_${语言编码}.i18n.json`。其中,`${语言编码}` 需要替换为 [ISO 639-1 语言代码](#code)。例如,丹麦语,`strings_da.i18n.json`。
|
||||
|
||||
如果您需要兼容支持多个新语言,复制多份,并准确指定每一份的语言编码即可。
|
||||
|
||||
#### 个性化自定义翻译
|
||||
|
||||
此时,您可以修改上一步 **复制新生成的目标语言模板**。
|
||||
|
||||
打开您复制生成的新文档,**保留不动JSON的md5 key值**,将所有的value值替换成对应目标翻译语言。
|
||||
|
||||

|
||||
|
||||
>? 如果您需要修改默认语言模板的翻译文案,也可直接打开自动生成的语言模板,进行修改。**除简体中文版本外**,其他翻译文案,均可修改。
|
||||
|
||||
翻译完成后,`languages/` 内,包含原始提供的自带语言模板,及您复制生成的其他语言词条集。
|
||||
|
||||

|
||||
|
||||
#### 回装您的语言包
|
||||
|
||||
在您项目的根目录下,执行 `flutter pub run tencent_im_base` 命令,并选择 `B` 选项。
|
||||
|
||||
代码运行完成后,即可使您的语言包,在当前电脑本地生效。
|
||||
|
||||

|
||||
|
||||
>? 如果您是团队协同开发,或使用了远程流水线编译。需要在您同事电脑中或流水线编译命令脚本中,使用同样的方式,执行本章节所述方法。
|
||||
|
||||
### 跟随系统语言
|
||||
|
||||
直接使用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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||

|
||||
|
||||
Now, all the pre-set default language files, as JSON, have been generated to your project, `languages/` directory.
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
After translation and modification, all the supported languages files, including those you duplicated and default, should be in the `languages/` directory.
|
||||
|
||||

|
||||
|
||||
#### 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.
|
||||
|
||||

|
||||
|
||||
>? 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)
|
||||
|
|
@ -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` | - |
|
||||
|
||||

|
||||
|
||||
现在,我们就来动手接入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)。
|
||||
|
||||

|
||||
|
||||
### 声明表情文件
|
||||
|
||||
打开 `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,
|
||||
// ......
|
||||
),
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 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 咨询。
|
||||
|
||||

|
||||
|
||||
|
|
@ -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` | - |
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
### 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 baseUrl,recommend 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,
|
||||
// ......
|
||||
),
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
|
||||
|
||||
|
|
@ -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` | - |
|
||||
|
||||

|
||||
|
||||
现在,我们就来动手接入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)。
|
||||
|
||||

|
||||
|
||||
### 声明表情文件
|
||||
|
||||
打开 `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,
|
||||
// ......
|
||||
),
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
];
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
class TencentUtils{
|
||||
static bool isTextNotEmpty(String? text){
|
||||
return text != null && text.isNotEmpty;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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",
|
||||
])
|
||||
];
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
// 这个函数应该返回String,目前已经切走用不上了,但是不敢删QAQ,就这么留着吧。
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
))
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
)),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
],
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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("删除"),
|
||||
)
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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("未填写")),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
),
|
||||
),
|
||||
])),
|
||||
);
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
50
pubspec.lock
50
pubspec.lock
|
|
@ -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:
|
||||
|
|
|
|||
10
pubspec.yaml
10
pubspec.yaml
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue