update flutter uikit to 2.0.0

This commit is contained in:
anonymous 2022-11-17 13:41:46 +08:00
parent e66a546d6d
commit e8659b6e4d
379 changed files with 41559 additions and 14 deletions

130
CHANGELOG.md Normal file
View File

@ -0,0 +1,130 @@
## 0.1.8
* Optimize: File batch downloading queue, allow click multiple file messages once.
* Optimize: Group list widgets can be updated automatically.
* Optimize: Camera capture supports relatively lower performance devices, adjust resolution automatically.
* Optimize: Supports customize the color and text style of the app bar, especially on TIMUIKitChat widget.
* Fix: Friend remark or nickname can not show on group tips.
* Fix: Crash on video playing.
* Fix: Several bugs.
## 0.1.7
* Add: Big and RAW images supports, especially for those captured from latest version of iOS and iPhone 14 Pro series, compress and format before sending automatically.
* Optimize: Performance and stability, especially for history message list and launching.
* Optimize: Makes initializing the `TIMUIKitChat` an idempotent operation.
* Optimize: Load latest messages when scrolling back to bottom.
* Optimize: Supports Flutter both 2.x and 3.x series.
* Fix: The issue of select photos permission.
* Fix: Several bugs.
## 0.1.5
* Add: Web supports. Now, you could implement TUIKit on iOS/Android/Web platforms.
* Add: Disk storage checking after log in, and controls in `config` of `init`.
* Add: `timeDividerConfig`, `notificationAndroidSound`, `isSupportMarkdownForTextMessage` and `onTapLink` to `TIMUIKitChatConfig`.
* Remove: The default Emoji list, due to the copyright issues. You can provided your own sticker list to panel by [tim_ui_kit_sticker_plugin](https://pub.dev/packages/tim_ui_kit_sticker_plugin).
* Optimize: You could now choose to disable Markdown parsing for text messages.
* Optimize: You could now choose to disable the shows for @ message in conversation list.
* Optimize: You could now return `null` for `notificationExt`/`notificationBody` in `TIMUIKitChatConfig` and `messageRowBuilder` in `MessageItemBuilder`, to use default value up to your needs in the specific case, means you can control whether or not using customized setting based on the provided situation, without the necessary to re-define the same logic as the TUIKit in your code.
* Optimize: Supports multiple lines for text messages.
* Optimize: Rebuild and improve the experience of `TIMUIKitChat`. While, `TIMUIKitChatController` needs to be specified to `controller` here, like how we shows in [Demo](https://github.com/TencentCloud/TIMSDK/blob/master/Flutter/Demo/im-flutter-uikit/lib/src/chat.dart).
* Fix: Several bugs.
## 0.1.3
* Add: User inputting status.
* Add: Message reactions, with sticker.
* Add: User online status.
## 0.1.2
* Upgrade: flutter_record_plugin_plus to 0.0.4.
## 0.1.1
* Add: Lifecycle hooks for the main widgets, referring to the parameter description for details.
* Add: Mute status display for group chat on the chat page.
* Add: URL enrichment for text messages.
* Add: Callback for global information (Flutter Error, Tips for Reminds, API Error) and you can display toast up to your needs.
* Optimize: Image preview displaying.
* Rebuild: TUIKitGroupProfile and TUIKitProfile, simplified usage.
## 0.1.0-bugfix
* Upgrade: Tencent IM SDK.
## 0.1.0
* Add: Atomization widgets for TIMUIKitChat.
* Add: Updating the UI when the message has been modified.
* Add: The application page for joining the group.
* Add: `updateMessage` API, users can refresh the view after modifying the local message.
* Add: Support for Traditional Chinese.
* Add: Customization for conversation list item.
## 0.0.9
* Add: Offline push along with [tim_ui_kit_push_plugin](https://pub.dev/packages/tim_ui_kit_push_plugin).
* Adapt: Flutter 3.0.0.
* Optimize: Local preview of multimedia files.
## 0.0.8
* Add: Group read receipt module.
* Add: Little tongue on the message list.
* Add: Examples.
* Fix: Several bugs.
## 0.0.7
* Fix: Several bugs.
## 0.0.6
* Add: New `sendMessage` method to the controller `TIMUIKitChatController` for TIMUIKitChat.
* Add: Configuration for TIMUIKitChat, which can control the functions for TIMUIKitChat components.
* Support: Customized for more panel customized ability to TIMUIKitChat.
* Optimize: User authorization standardized.
## 0.0.5
* Add: Several new customized configs, includes, appBarConfig, morePanelConfig, and removed appBarActions config.
* Optimize: Image preview displaying.
* Upgrade: Tencent IM SDK.
* Fix: The issue of conversation item duplication for TIMUIKitConversation.
## 0.0.4
* Optimize: TIMUIKitChat, especially for media files selector.
* Optimize: Previewing of image messages, video messages.
* Optimize: Theme color.
* Optimize: UI for search components.
* Upgrade: Tencent IM SDK.
## 0.0.3
* Add: TIMUIKitSearch and TIMUIKitSearchMsgDetail, supports searching both in conversation and globally.
* Add: TIMUIKitAddFriend.
* Add: TIMUIKitAddGroup.
* Add: Theme style configuration.
* Optimize: Internationalization.
## 0.0.2
* Optimize: TIMUIKitChat.
* Fix: Bugs on Internationalization.
## 0.0.1
The first released of TUIKit for Flutter of Tencent Cloud IM, the component of the first phase includes:
* TIMUIKitCore: The main entrance of the whole TUIKit.
* TIMUIKitConversation: Conversation list.
* TIMUIKitChat: Chat and historical message list.
* TIMUIKitProfile: User detail profile and relationship management.
* TIMUIKitGroupProfile: Group details and management.
* TIMUIKitGroup: Joined group list.
* TIMUIKitBlackList: Blocklist.
* TIMUIKitContact: Contacts list.
* TIMUIKitNewContact: New contact application list.

18
LICENSE
View File

@ -1,6 +1,7 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
@ -175,24 +176,13 @@
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2013-2018 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,

800
README.md Normal file
View File

@ -0,0 +1,800 @@
<br>
<p align="center">
<a href="https://www.tencentcloud.com/products/im">
<img src="https://qcloudimg.tencent-cloud.cn/raw/fd589e1dc7dc3752f320a3b0251189f0.png" width="288px" alt="Tencent Chat Logo" />
</a>
</p>
<h1 align="center"> Tencent Cloud IM Chat UIKIt</h1>
<p align="center">
Globally interconnected In-App Chat, user profile and relationship chains and offline push.
</p>
<p align="center">
More languages:
<a href="https://cloud.tencent.com/document/product/269/70746">简体中文-快速入门</a>
<a href="https://github.com/TencentCloud/TIMSDK/blob/master/Flutter/IMSDK/tim_ui_kit/README_ZH.md">简体中文-README</a>
</p>
![](https://qcloudimg.tencent-cloud.cn/raw/193ec650f17da6bb33edf5df5d978091.png)
<p align="center">
TUIKit has Chat SDK, UI components and basic business logic inside. You can choose our pure Chat SDK <a href="https://pub.dev/packages/tencent_im_sdk_plugin">tencent_im_sdk_plugin</a> if you tend to build the UI yourself.
</p>
## Introduction to TUIKit
TUIKit is a set of official UI components for Tencent Cloud IM Chat SDK, with chat business logic around it. It provides components such as the conversation, chat, relationship chain, and group.
You can use these UI components to build your APP with the In-APP chat module quickly and easily.
![img](https://qcloudimg.tencent-cloud.cn/raw/f140dd76be01a65abfb7e6ba2bf50ed5.png)
Currently, Flutter TUIKit contains the following main components:
- [TIMUIKitCore](https://www.tencentcloud.com/document/product/1047/46297#timuikitcore): Core entry
- [TIMUIKitConversation](https://www.tencentcloud.com/document/product/1047/46297#timuikitconversation): Conversation list
- [TIMUIKitChat](https://www.tencentcloud.com/document/product/1047/46297#timuikitchat): Chat module, includes historical message list and message sending area, with some other features like message reaction and URL preview, etc.
- [TIMUIKitContact](https://www.tencentcloud.com/document/product/1047/46297#timuikitcontact): Contacts list
- [TIMUIKitProfile](https://www.tencentcloud.com/document/product/1047/46297#timuikitprofile): User profile and relationship management
- [TIMUIKitGroupProfile](https://www.tencentcloud.com/document/product/1047/46297#timuikitgroupprofile): Group profile and management
- [TIMUIKitGroup](https://www.tencentcloud.com/document/product/1047/46297#timuikitgroup): The list of group self joined
- [TIMUIKitBlackList](https://www.tencentcloud.com/document/product/1047/46297#timuikitblacklist): The list of user been blocked
- [TIMUIKitNewContact](https://www.tencentcloud.com/document/product/1047/46297#timuikitnewcontact): New contacts application list
- [TIMUIKitSearch](https://pub.dev/documentation/tim_ui_kit/latest/ui_views_TIMUIKitSearch_tim_uikit_search/TIMUIKitSearch-class.html): Search globally
- [TIMUIKitSearchMsgDetail](https://pub.dev/documentation/tim_ui_kit/latest/ui_views_TIMUIKitSearch_tim_uikit_search_msg_detail/TIMUIKitSearchMsgDetail-class.html): Search in specific conversation
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 [im-flutter-uikit](https://github.com/tencentyun/TIMSDK/tree/master/Flutter/Demo/im-flutter-uikit). The project is open source and can be used directly.
## Supported Platforms
- Android
- iOS
- Web(After version of 0.1.4)
## Get Started
**[Please refer this documents](https://www.tencentcloud.com/document/product/1047/45907), for a completed and detailed get started guide.**
## Directions
The following guide describes how to use Flutter TUIKit to build a simple IM APP quickly.
**You may refer to the appendix, if willing to know about the detail and parameter for each widgets.**
### Step 0. Create two accounts for testing
[Signed up](https://www.tencentcloud.com/document/product/378/17985) and [log in](https://www.tencentcloud.com/document/product/378/36004) to the [Tencent IM console](https://console.tencentcloud.com/im).
[Create an application](https://www.tencentcloud.com/document/product/1047/34577) and enter in.
Select [Auxiliary Tools](https://console.cloud.tencent.com/im-detail/tool-usersig) > UserSig Generation and Verification on the left sidebar. Generate two pairs of "UserID" and the corresponding "UserSig", and copy the "key" information. [Refer to here.](https://www.tencentcloud.com/document/product/1047/34580#usersig-generation-and-verification)
Tips: You may create "user1" and "user2" here.
> Note
>
> The correct `UserSig` distribution method is to integrate the calculation code of `UserSig` into your server and provide an application-oriented API. When `UserSig` is needed, your application can send a request to the business server for a dynamic `UserSig`. For more information, see [How do I calculate UserSig on the server?](https://www.tencentcloud.com/document/product/1047/34385).
### Step 1. Create a Flutter app and add permission configuration
Quickly create a Flutter APP by referring to [Flutter documentation](https://docs.flutter.dev/get-started/install).
TUIKit needs the permissions of shooting/album/recording/network for basic messaging function. You need ti declare in the Native file manually to use the relevant capabilities normally.
#### Android
Open `android/app/src/main/AndroidManifest.xml`, add the following lines between `<manifest>` and `</manifest>`.
```xml
<uses-permission
android:name="android.permission.INTERNET"/>
<uses-permission
android:name="android.permission.RECORD_AUDIO"/>
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission
android:name="android.permission.VIBRATE"/>
<uses-permission
android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.CAMERA"/>
```
#### iOS
Open `ios/Podfile`, add the following lines to the end of the file.
```pod
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
'PERMISSION_MICROPHONE=1',
'PERMISSION_CAMERA=1',
'PERMISSION_PHOTOS=1',
]
end
end
end
```
### Step 2. Install dependencies
Add `tim_ui_kit` under `dependencies` in the `pubspec.yaml` file, or run the following command:
```shell
flutter pub add tim_ui_kit
```
It supports Android and iOS as default, if you are also willing to use it on the Web, please refer to the following guide.
#### Web Supports
Version of 0.1.4 or later are required for web supports.
> If your existing Flutter project does not support Web, run `flutter create .` in the project root directory to add web support.
Download the following two JS files from GitHub, placed in the `web` directory of the project.
- [tim-js-friendship.js](https://github.com/TencentCloud/TIMSDK/blob/master/Web/IMSDK/tim-js-friendship.js)
- [Rename this file to `tim-upload-plugin.js`](https://github.com/TencentCloud/TIMSDK/blob/master/Web/IMSDK/tim-upload-plugin/index.js)
Open `web/index.html` , add the following two lines between `<head>` and `</head>` to import them.
```html
<script src='./tim-upload-plugin.js'></script>
<script src="./tim-js-friendship.js"></script>
```
![](https://qcloudimg.tencent-cloud.cn/raw/f88ddfbdc79fb7492f3ce00c2c583246.png)
### Step 3. Initialize TUIKit
Initialize the TUIKit after you app starts.You only need to perform the initialization once for the project to start.
Get the instance of TUIKit first by `TIMUIKitCore.getInstance()`, followed by initializing it, `init()`, with your 'sdkAppID'.
```dart
/// main.dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
@override
void initState() {
_coreInstance.init(
sdkAppID: 0, // Replace 0 with the SDKAppID of your IM application
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
listener: V2TimSDKListener());
super.initState();
}
}
```
> **You may also better to register a callback function for `onTUIKitCallbackListener` here, please refer to the appendix.**
### Step 4. Get the signature and log in
Now, you can log in one of the testing accounts, generated on Step 0, to start the IM module.
Log in by `_coreInstance.login` .
```dart
/// main.dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
_coreInstance.login(userID: userID, userSig: userSig);
```
Caveat: Importing UserSig to your application is ONLY for Debugging purposes and cannot be applied for the Release version. Before publishing your app, you should generate your UserSig from your server. Refers to: <https://www.tencentcloud.com/document/product/1047/34385>
### Step 5. Implementing the conversation list page
You can take the conversation (channel) list page as the homepage of your Chat module, covering the conversation with all users and groups that have chat records.
<img src="https://qcloudimg.tencent-cloud.cn/raw/a27b131d555b1158d150bd9b337c1d9d.png" style="zoom:50%;"/>
You can create a `Conversation` class, with `TIMUIKitConversation` on its `body`, to render the conversation list.
The only parameter you need to provide at least is `onTapItem` callback, aimed at navigating to the Chat page for each conversation. The `Chat` class will be introduced in the next step.
```dart
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class Conversation extends StatelessWidget {
const Conversation({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
"Message",
style: TextStyle(color: Colors.black),
),
),
body: TIMUIKitConversation(
onTapItem: (selectedConv) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chat(
selectedConversation: selectedConv,
),
));
},
),
);
}
}
```
### Step 6. Implementing the chat page
The chat page is composed of the main historical message list and a message sending bar at the bottom.
![](https://qcloudimg.tencent-cloud.cn/raw/09b8b9b54fd0caa47069544343eba461.jpg)
You can create a `Chat` class, with `TIMUIKitChat` on its `body`, to render the chat page.
It is recommended to provide a `onTapAvatar` callback function, for navigating to the profile page for current contact, which will be introduced in the next step.
```dart
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class Chat extends StatelessWidget {
final V2TimConversation selectedConversation;
const Chat({Key? key, required this.selectedConversation}) : super(key: key);
String? _getConvID() {
return selectedConversation.type == 1
? selectedConversation.userID
: selectedConversation.groupID;
}
@override
Widget build(BuildContext context) {
return TIMUIKitChat(
conversationID: _getConvID() ?? '', // groupID or UserID
conversationType: selectedConversation.type ?? 1, // Conversation type
conversationShowName: selectedConversation.showName ?? "", // Conversation display name
onTapAvatar: (_) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfile(userID: userID),
));
}, // Callback for the clicking of the message sender profile photo. This callback can be used with `TIMUIKitProfile`.
);
}
```
### Step 7. Implementing the user profile page
This page can show the profile of a specific user and maintain the relationship between the current login user and it.
![](https://qcloudimg.tencent-cloud.cn/raw/03e88da6f1d63f688d2a8ee446da43ff.png)
Here, you can create a `UserProfile` class, with `TIMUIKitProfile` on its `body`, to render the profile page.
The only parameter you have to provide at least is 'userID', while this component can generate the profile and relationship maintenance page based on the existence of friendship automatically.
> TIPS
>
> Please give priority to use `profileWidgetBuilder`, to customize some profile widgets, with `profileWidgetsOrder`, determine the vertical sequence, if you tend to customize this page. If this method could not meet your business needs, you may consider using `builder` instead.
```dart
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class UserProfile extends StatelessWidget {
final String userID;
const UserProfile({required this.userID, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
"Message",
style: TextStyle(color: Colors.black),
),
),
body: TIMUIKitProfile(
userID: widget.userID,
),
);
}
}
```
Now, your app can send/receive messages, show the conversation list, and deal with the contact friendship.
You can use others components from TUIKit continually to implement the complete IM function quickly and easily.
## FAQs
### Do I need to integrate IM SDK after integrating TUIKit?
No. You don't need to integrate IM SDK again. If you want to use IM SDK related APIs, you can get them via `TIMUIKitCore.getSDKInstance()`. This method is recommended to ensure IM SDK version consistency.
### Why did force quit occur when I sent voice, image, file or other messages?
Check whether you have enabled the **camera**, **mic**, **album**, or other related permissions.
Refers to Step 1 above.
### What should I do if clicking Build And Run for an Android device triggers an error, stating no available device is found?
Check that the device is not occupied by other resources. Alternatively, click Build to generate an APK package, drag it to the simulator, and run it.
### What should I do if an error occurs during the first run for an iOS device?
If an error occurs after the configuration, click **Product > Clean Build Folder** , clean the product, and run `pod install` or `flutter run` again.
![](https://qcloudimg.tencent-cloud.cn/raw/d495b2e8be86dac4b430e8f46a15cef4.png)
### What should I do if an error occurs during debugging on a real iOS device when I am wearing an Apple Watch?
![](https://qcloudimg.tencent-cloud.cn/raw/1ffcfe39a18329c86849d7d3b34b9a0e.png)
Turn on Airplane Mode on your Apple Watch, and go to **Settings > Bluetooth** on your iPhone to turn off Bluetooth.
Restart Xcode (if opened) and run `flutter run` again.
### Issue with Flutter environment?
If you want to check the Flutter environment, run `flutter doctor` to detect whether the Flutter environment is ready.
### What should I do when an error occurs on an Android device after TUIKit is imported into the application automatically generated by Flutter?
![](https://qcloudimg.tencent-cloud.cn/raw/d95efdd4ae50f13f38f4c383ca755ae7.png)
1. Open `android\app\src\main\AndroidManifest.xml` and complete `xmlns:tools="http://schemas.android.com/tools" / android:label="@string/android_label" / tools:replace="android:label"` as follows.
```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="Replace it with your Android package name"
xmlns:tools="http://schemas.android.com/tools">
<application
android:label="@string/android_label"
tools:replace="android:label"
android:icon="@mipmap/ic_launcher" // Specify an icon path
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true">
```
2. Open `android\app\build.gradle` and complete `minSdkVersion` and `targetSdkVersion` in `defaultConfig`.
```gradle
defaultConfig {
applicationId "" // Replace it with your Android package name
minSdkVersion 21
targetSdkVersion 30
}
```
---
## 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.
- Telegram Group: <https://t.me/+1doS9AUBmndhNGNl>
- WhatsApp Group: <https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A>
- QQ Group: 788910197, chat in Chinese
Our Website: <https://www.tencentcloud.com/products/im>
---
## Appendix: Overview for each widgets
### TIMUIKitCore
`TIMUIKitCore` provides two static methods, including `getInstance` and `getSDKInstance`
- `getInstance`: Used for get the instance of `CoreServicesImpl`.
- `getSDKInstance`: Used for get the instance of IM SDK.
`CoreServicesImpl` is the main class of `TUIKit` , providing the methods includes initialization, logging in and out, getting user information, etc.
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
final V2TIMManager _sdkInstance = TIMUIKitCore.getSDKInstance();
// init
_coreInstance.init(
language: LanguageEnum?, // Specify the displaying language among English / Chinese, Traditional / Chinese, Simplified. If this field is not provided, the default is the system language
onTUIKitCallbackListener: ValueChanged<TIMCallback>, // The callback listener for information from TUIKit, includes errors from SDK API/ the info needs to reminds users/ errors from Flutter. You can reminds users up to your business needs, the description below for details.
sdkAppID: 0, // sdkAppID from Tencent IM console
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
listener: V2TimSDKListener());
// unInit
_coreInstance.unInit();
// login
_coreInstance.login(
userID: 0, // user ID
userSig: "" // [How do I calculate UserSig on the server?](https://www.tencentcloud.com/document/product/1047/34385)
)
// logout
_coreInstance.logout();
// getUsersInfo
_coreInstance.getUsersInfo(userIDList: ["123", "456"]);
// setOfflinePushConfig
_coreInstance.setOfflinePushConfig(
businessID: businessID, // The business from Tencent IM console, for each platform of devices
token: token, // The token from manufactors when registering the offline push
)
// setSelfInfo
_coreInstance.setSelfInfo(userFullInfo: userFullInfo) // set self userinfo
// setTheme
_coreInstance.setTheme(TUITheme theme: theme) // set theme color
/*
TUITheme(
// Primary color
final Color? primaryColor;
// Secondary color
final Color? secondaryColor;
// Info color, for secondary operations or prompts
final Color? infoColor;
// Weak background color, lighter than the main background color, used to fill gaps or shadows
final Color? weakBackgroundColor;
// Weak divider line color, for dividing lines or borders
final Color? weakDividerColor;
// Weak text color
final Color? weakTextColor;
// Dark text color
final Color? darkTextColor;
// Used for AppBar or Panels
final Color? lightPrimaryColor;
// Text color
final Color? textColor;
// Warning color for dangerous operation
final Color? cautionColor;
// Group owner identification color
final Color? ownerColor;
// Group administrator identification color
final Color? adminColor;)
*/
```
#### `onTUIKitCallbackListener`
This listener is used to get information including: errors form SDK API / errors form Flutter / some remind information that may need to pop up to prompt users.
Determine the type by `TIMCallbackType`.
> You may refer to our [DEMO](https://github.com/TencentCloud/TIMSDK/blob/master/Flutter/Demo/im-flutter-uikit/lib/src/pages/app.dart) for the codes in this part, and modifying up to your business needs.
##### Errors form SDK API(`TIMCallbackType.API_ERROR`)
In this scenario, SDK API original `errorMsg` and `errorCode` are provided.
[Error codes listed here](https://www.tencentcloud.com/document/product/1047/34348)
#### Errors form Flutter(`TIMCallbackType.FLUTTER_ERROR`)
This error is captured by listening Flutter natively throwing an exception, providing `stackTrace` (from `FlutterError.onError`) or `catchError` (from try-catch) when the error occurs.
#### Remind information(`TIMCallbackType.INFO`)
It is suggest to pup up to prompt users for this kind of messages.
Provide the `infoCode` info code to help you determine the current scene, and provide the default prompt recommendation `infoRecommendText`.
You can directly pop up our recommendations, or you can customize the recommendations according to the scene code. The language of recommendation text is adaptive according to the system language or the language you specified, do not judge the scene according to the recommendation language.
The rules for info code are as follows:
The info code consists of seven digits, the first five digits determine the components of the scene, and the last two digits determine the specific performance of the scene.
| The first five digits | Corresponding widget |
| ---------- | ---------------------- |
| 66601 | `TIMUIKitAddFriend` |
| 66602 | `TIMUIKitAddGroup` |
| 66603 | `TIMUIKitBlackList` |
| 66604 | `TIMUIKitChat` |
| 66605 | `TIMUIKitContact` |
| 66606 | `TIMUIKitConversation` |
| 66607 | `TIMUIKitGroup` |
| 66608 | `TIMUIKitGroupProfile` |
| 66609 | `TIMUIKitNewContact` |
| 66610 | `TIMUIKitGroupProfile` |
| 66611 | `TIMUIKitNewContact` |
| 66612 | `TIMUIKitProfile` |
| 66613 | `TIMUIKitSearch` |
| 66614 | General Widget |
All info codes are listed below:
| `infoCode` | Recommendation prompt `infoRecommendText` | Scene description |
| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 6660101 | Contact request sent | User requests to add another user as a contact. |
| 6660102 | This user is your contact. | When a user applies to add another user who is already a contact, the callback of `onTapAlreadyFriendsItem` is triggered. |
| 6660201 | Group request sent | Users apply to join a group chat that requires the approval of the administrator.|
| 6660202 | You are already in this group | When a user applies to join a group, it is determined that the user is already a member of the current group, triggering the callback of `onTapExistGroup`. |
| 6660401 | Failed to locate the original message | When the user needs to jump to the @ message or reference the message, the target message is not found in the message list. |
| 6660402 | Video saved successfully | After clicking on the video message in the message list, and chooses to save the video. |
| 6660403 | Failed to save the video | After clicking on the video message in the message list, and chooses to save the video. |
| 6660404 | Message too short | The user sent an overly short voice message. |
| 6660405 | Sending failed. The video cannot exceed 100 MB. | The user attempted to send a video larger than 100MB. |
| 6660406 | Image saved successfully | After clicking on the image in the message list, the user chooses to save the picture. |
| 6660407 | Failed to save the image | After clicking on the image in the message list, the user chooses to save the picture. |
| 6660408 | Copied | The user chooses to copy the text message in the pop-up window. |
| 6660409 | Not implemented | The user selects a non-implemented function in the pop-up. window |
| 6660410 | You are receiving other files | When the user clicks the download file message, the previous download task has not yet been completed. |
| 6660411 | Receiving | User clicks to download file message. |
| 6660412 | Video is available with .mp4 only | The user sent a video message in non-mp4 format |
| 6660413 | Added to download queue and waiting | Added to the queue to be downloaded, while other files are downloading |
| 6661001 | Modification failed due to network disconnection | When users try to modify group data in a non-network environment. |
| 6661002 | Failed to view the group members due to network disconnection | When users try to modify group data in a non-network environment. |
| 6661003 | Admin role canceled successfully | The user removes the other users from the administrator in the group. |
| 6661201 | Modification failed due to network disconnection | When a user tries to modify his or her contact information without a network environment. |
| 6661202 | Added successfully | Add other users as contact on the profile page and automatically add them successfully without verification. |
| 6661203 | Request sent successfully | Add other users as contact on the profile page, and the other user's settings need to be verified. |
| 6661204 | The user is blocked | Add other users as contacts on the profile page, who are on their own blocklist. |
| 6661205 | Added failed | Add other users as contact on the profile page, but failed, probably because the other party is forbidden to add contact. |
| 6661206 | Deleted successfully | Delete other users as contact on the profile page and succeed. |
| 6661207 | Deleted failed | Delete other users as contact on the profile page. Failed. |
| 6661401 | The input cannot be empty | When the user is entering information, an empty string is entered. |
| 6661402 | Please provide a life cycle hook navigating back to home or other pages. | When users quit the group or dissolve the group, they did not provide a way to return to the home page. |
| 6661403 | Insufficient disk storage space, it is recommended to clean up to obtain a better experience | After the login is successful, the device storage space will be automatically detected. If there is less than 1GB, it will be prompted. |
### TIMUIKitConversation
`TIMUIKitConversation` shows the conversation list.
The corresponding controller: `TIMUIKitConversationController` is also provided.
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final TIMUIKitConversationController _controller =
TIMUIKitConversationController();
void _handleOnConvItemTaped(V2TimConversation? selectedConv) {
// You can jump to the chat interface here.
}
List<ConversationItemSlidablePanel> _itemSlidableBuilder(
V2TimConversation conversationItem) {
return [
ConversationItemSlidablePanel(
onPressed: (context) {
_clearHistory(conversationItem);
},
backgroundColor: hexToColor("006EFF"),
foregroundColor: Colors.white,
label: 'Clear conversaation',
autoClose: true,
),
ConversationItemSlidablePanel(
onPressed: (context) {
_pinConversation(conversationItem);
},
backgroundColor: hexToColor("FF9C19"),
foregroundColor: Colors.white,
label: conversationItem.isPinned! ? 'unpined' : 'pinned',
)
];
}
TIMUIKitConversation(
lifeCycle: ConversationLifeCycle(), // The lifecycle hook
onTapItem: _handleOnConvItemTaped, // Callback of clicking conversation, can navigating to chat page
itemSlidableBuilder: _itemSlidableBuilder, // Operation items for conversation Item sliding to the left, conversation topping, etc.
controller: _controller, // Conversation component controller, through which you can get conversation data, set conversation data, pin conversation to top and other operations
itembuilder: (conversationItem) {} // Used to customize the conversation item. Can be combined with TIMUIKitConversationController to implement business logic.
conversationCollector: (conversation) {} // Conversation collector, which can customize whether the conversation is displayed
lastMessageBuilder: (V2TimMessage, List<V2TimGroupAtInfo?>) {} // Customize the second line of the conversation item, which is generally used to show the last message
)
```
---
### TIMUIKitChat
`TIMUIKitChat` is the main chat component that provides the display of message list and the ability to send messages.
It also supports custom display of various message types.
Additionally, it can be combined with `TIMUIKitChatController` to realize local storage and pre-rendering of messages, etc.
Currently supported message parsing:
- Text message.
- Image message.
- Video message.
- Voice message.
- Group message.
- Merge message.
- File message.
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
TIMUIKitChat(
lifeCycle: ChatLifeCycle(), // Lifecycle hook for TIMUIKitChat
conversationID: "", // User ID or Group ID
conversationType: ConversationType, // 1 is c2c chat, 2 is group chat
conversationShowName: "",
appBarActions: [], // appBar operation item, which can be used to jump to the page of group details and personal details, etc.
onTapAvatar: _onTapAvatar, // callback function for clicking the avatar, which can be used to jump to the user profile.
messageItemBuilder: (MessageItemBuilder) {
// Message item layout constructor, you can choose to customize part of the message type or message row layout.
},
extraTipsActionItemBuilder: (message) {
// The configuration for the menu, opend by long pressed messages
},
morePanelConfig: MorePanelConfig(), // The config for more panel area
appBarConfig: AppBar(), // The config for AppBar
mainHistoryListConfig: ListView(), // Additional config for ListView of historical message list
textFieldHintText: "", // The hint of inputTextField
draftText: "", // The draft of inputting message
initFindingMsg: 0, // The message been jumped
config: TIMUIKitChatConfig(), // The config for the whole TIMUIKitChat
onDealWithGroupApplication: (String groupID){
// Navigating to the pages for the page of [TIMUIKitGroupApplicationList] or other pages to handle joining group application for specific group
}
)
```
---
### TIMUIKitProfile
`TIMUIKitProfile` shows the detail information for a user, and manage the relationship.
```dart
TIMUIKitProfile(
userID: "",
controller: TIMUIKitProfileController(), // Profile Controller
profileWidgetBuilder: ProfileWidgetBuilder(), // Customized some kinds of item.
profileWidgetsOrder: List<ProfileWidgetEnum>, // Determine the vertical sequence for those profile widgets.
builder: (
BuildContext context,
V2TimFriendInfo friendInfo,
V2TimConversation conversation,
int friendType,
bool isMute) {
// Customized the whole page. `profileWidgetBuilder` and `profileWidgetsOrder` will no longer works if you define this.
},
lifeCycle: ProfileLifeCycle(),// Lifecycle hook for TIMUIKitProfile
)
```
---
### TIMUIKitGroupProfile
`TIMUIKitGroupProfile` shows the details of a group and can manage this group.
```dart
TIMUIKitGroupProfile(
groupID: "",
profileWidgetBuilder: GroupProfileWidgetBuilder(), // Customized some kinds of item.
profileWidgetsOrder: List<GroupProfileWidgetEnum>, // Determine the vertical sequence for those profile widgets.
builder: (BuildContext context, V2TimGroupInfo groupInfo, List<V2TimGroupMemberFullInfo?> groupMemberList){
// Customized the whole page. `profileWidgetBuilder` and `profileWidgetsOrder` will no longer works if you define this.
},
lifeCycle: GroupProfileLifeCycle, // Lifecycle hook for TIMUIKitGroupProfile
)
```
---
### TIMUIKitBlackList
`TIMUIKitBlackList` shows the list of blocked users.
```dart
TIMUIKitBlackList(
onTapItem: (_) {}, // Callback of clicking the item
emptyBuilder: () {} // The builder when no user listed
itemBuilder: () {} // Customized the user item
lifeCycle: BlockListLifeCycle(), // Lifecycle hook for TIMUIKitBlackList
)
```
---
### TIMUIKitGroup
`TIMUIKitGroup` shows the list of joined group.
```dart
TIMUIKitGroup(
onTapItem: (_) {}, // Callback of clicking the item
emptyBuilder: () {} // The builder when no group listed
itemBuilder: () {} // Customized the group item
)
```
---
### TIMUIKitContact
`TIMUIKitContact` shows the list of contacts.
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
TIMUIKitContact(
lifeCycle: FriendListLifeCycle(), // Lifecycle hook for TIMUIKitContact
topList: [
TopListItem(name: "New Contact", id: "newContact"),
TopListItem(name: "Group", id: "groupList"),
TopListItem(name: "Blocklist", id: "blackList")
], // Top list array
topListItemBuilder: _topListBuilder, // The builder for top list array
onTapItem: (item) { }, // Callback of clicking a contact
emptyBuilder: (context) => const Center(
child: Text("No cantact"),
), // The builder when no contact listed
);
```
### TIMUIKitSearch
`TIMUIKitSearch` is a global search widget. Global search supports search for "contacts" / "groups" / "chat records".
`TIMUIKitSearchMsgDetail` is an intra-conversation search component that can search for chat records for a specific conversation.
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
// Search globally
TIMUIKitSearch(
onTapConversation: _handleOnConvItemTapedWithPlace, // Function(V2TimConversation, V2TimMessage? message), navigating to specific message from specific conversation
onEnterConversation: (V2TimConversation conversation, String initKeyword){}, // Navigating to search in specific conversation. Please navigate to TIMUIKitSearchMsgDetail manually
);
// Search inside a specific conversation
TIMUIKitSearchMsgDetail(
currentConversation: conversation!,
onTapConversation: onTapConversation,
keyword: initKeyword ?? "",
);
```
## 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.
- Telegram Group: <https://t.me/+1doS9AUBmndhNGNl>
- WhatsApp Group: <https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A>
- QQ Group: 788910197, chat in Chinese
Our Website: <https://www.tencentcloud.com/products/im>

506
README_ZH.md Normal file
View File

@ -0,0 +1,506 @@
[English](https://github.com/TencentCloud/TIMSDK/blob/master/Flutter/IMSDK/tim_ui_kit/README.md) | 简体中文
# Flutter TUIKit
TUIKit 是基于 IM SDK 实现的一套 UI 组件,其包含会话、聊天、搜索、关系链、群组、音视频通话等功能,基于 UI 组件您可以像搭积木一样快速搭建起自己的业务逻辑。
## 建议阅读文档目录
**快速使用本TUIKit组件库建议阅读**
- **[图文介绍各组件总览](https://cloud.tencent.com/document/product/269/70747)**
- **[快速集成本TUIKit至您的Flutter项目](https://cloud.tencent.com/document/product/269/70746)**
集成更多高级功能建议阅读:
- [集成本地搜索](https://cloud.tencent.com/document/product/269/79121)
- [集成离线推送](https://cloud.tencent.com/document/product/269/74605)
- [集成音视频通话](https://cloud.tencent.com/document/product/269/72485)
## Widget
- TIMUIKitConversation 会话组件
- TIMUIKitChat 聊天组件
- TIMUIKitCore Core 组件
- TIMUIKitProfile 个人详情组件
- TIMUIKitGroupProfile 群组详情组件
- TIMUIKitGroup 群组列表组件
- TIMUIKitBlackList 黑名单列表组件
- TIMUIKitContact 联系人组件
- TIMUIKitNewContact 新的联系人
- TIMUIKitSearch 搜索
### 截图
![img](https://qcloudimg.tencent-cloud.cn/raw/f140dd76be01a65abfb7e6ba2bf50ed5.png)
## 国际化
我们默认提供 `简体中文` `繁体中文` `英语` 的语言支持;并允许开发者新增语言包,扩展多语言支持。
如果您需要使用国际化多语言能力,请参考 [腾讯云 IM Flutter TUIKit 国际化指南](https://docs.qq.com/doc/DSVN4aHVpZm1CSEhv?u=c927b5c7e9874f77b40b7549f3fffa57)。
## TIMUIKitCore
[本部分详细文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitCore/)
`TIMUIKitCore`提供两个静态方法`getInstance` 和 `getSDKInstance`
- `getInstance`: 返回 `CoreServicesImpl` 实例。
- `getSDKInstance`: 返回 IM SDK 实例。
`CoreServicesImpl` 为`TIMUIKit` 核心类,包含初始化、登录、登出、获取用户信息等方法。
基础用法如下先初始化IM再登录用户
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
final V2TIMManager _sdkInstance = TIMUIKitCore.getSDKInstance();
// init
_coreInstance.init(
language: LanguageEnum?, // 初始指定使用语言,`简体中文` `繁体中文` `英语`。不填默认跟随系统语言。
onTUIKitCallbackListener: ValueChanged<TIMCallback>, // TUIKit信息回调包含SDK API错误信息/TUIKit界面相关提示信息/Flutter层报错。您可根据需要选择性自定义展示给用户。详见下方说明
sdkAppID: 0, // 控制台申请的sdkAppID
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
listener: V2TimSDKListener());
// unInit
_coreInstance.unInit();
// login
_coreInstance.login(
userID: 0, // 用户ID
userSig: "" // 参考官方文档userSig生成
)
```
### `onTUIKitCallbackListener` 监听
该监听用于返回包括SDK API 错误 / Flutter 报错 / 一些可能需要弹窗提示用户的场景信息。
通过`TIMCallbackType`确定类型。
> 这部分的处理逻辑[可参考我们的 DEMO](https://github.com/TencentCloud/TIMSDK/blob/master/Flutter/Demo/im-flutter-uikit/lib/src/pages/app.dart),并根据您的需要,自行修改。
#### SDK API 错误(`TIMCallbackType.API_ERROR`
该场景下,提供 SDK API 原生`errorMsg`及`errorCode`。
[错误码请参考该文档](https://cloud.tencent.com/document/product/269/1671)
#### Flutter 报错(`TIMCallbackType.FLUTTER_ERROR`
该错误由监听 Flutter 原生抛出异常产生,提供错误发生时的`stackTrace`(来自`FlutterError.onError`)或`catchError`(来自 try-catch)。
#### 场景信息(`TIMCallbackType.INFO`
建议根据实际情况,将这些信息弹窗提示用户。具体弹窗规则和样式可由您决定。
提供`infoCode`场景码帮助您确定当前的场景,及默认的提示推荐语`infoRecommendText`。
您可直接弹窗我们的推荐语,也可根据场景码自定义推荐语。推荐语语言根据系统语言自适应或您指定的语言,请勿根据推荐语来判断场景。
场景码规则如下:
场景码由七位数组成,前五位数确定场景发生的组件,后两位确定具体的场景表现。
| 场景码开头 | 对应的组件 |
| ---------- | ---------------------- |
| 66601 | `TIMUIKitAddFriend` |
| 66602 | `TIMUIKitAddGroup` |
| 66603 | `TIMUIKitBlackList` |
| 66604 | `TIMUIKitChat` |
| 66605 | `TIMUIKitContact` |
| 66606 | `TIMUIKitConversation` |
| 66607 | `TIMUIKitGroup` |
| 66608 | `TIMUIKitGroupProfile` |
| 66609 | `TIMUIKitNewContact` |
| 66610 | `TIMUIKitGroupProfile` |
| 66611 | `TIMUIKitNewContact` |
| 66612 | `TIMUIKitProfile` |
| 66613 | `TIMUIKitSearch` |
| 66614 | 通用组件 |
全部场景码清单如下:
| 场景码 `infoCode` | 推荐提示语 `infoRecommendText` | 场景描述 |
| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 6660101 | 好友申请已发送 | 用户申请添加其他用户为联系人 |
| 6660102 | 该用户已是好友 | 用户申请添加其他已是好友的用户为好友时,触发 `onTapAlreadyFriendsItem` 回调 |
| 6660201 | 群申请已发送 | 用户申请加入需要管理员审批的群聊 |
| 6660202 | 您已是群成员 | 用户申请加群时,判断用户已经是当前群成员,触发 `onTapExistGroup` 回调 |
| 6660401 | 无法定位到原消息 | 当用户需要跳转至@消息或者是引用消息时,在消息列表中查不到目标消息 |
| 6660402 | 视频保存成功 | 用户在消息列表,点开视频消息后,选择保存视频 |
| 6660403 | 视频保存失败 | 用户在消息列表,点开视频消息后,选择保存视频 |
| 6660404 | 说话时间太短 | 用户发送了过短的语音消息 |
| 6660405 | 发送失败,视频不能大于 100MB | 用户试图发送大于 100MB 的视频 |
| 6660406 | 图片保存成功 | 用户在消息列表,点开图片大图后,选择保存图片 |
| 6660407 | 图片保存失败 | 用户在消息列表,点开图片大图后,选择保存图片 |
| 6660408 | 已复制 | 用户在弹窗内选择复制文字消息 |
| 6660409 | 暂未实现 | 用户在弹窗内选择非标功能 |
| 6660410 | 其他文件正在接收中 | 用户点击下载文件消息时,前序下载任务还未完成 |
| 6660411 | 正在接收中 | 用户点击下载文件消息 |
| 6660412 | 视频消息仅限 mp4 格式 | 用户发送了一条非 mp4 格式的视频消息 |
| 6660413 | 已加入待下载队列,其他文件下载中 | 已加入待下载队列,其他文件下载中 |
| 6661001 | 无网络连接,无法修改 | 当用户试图在无网络环境下,修改群资料 |
| 6661002 | 无网络连接,无法查看群成员 | 当用户试图在无网络环境下,修改群资料 |
| 6661003 | 成功取消管理员身份 | 用户将群内其他用户移除管理员 |
| 6661201 | 无网络连接,无法修改 | 当用户试图在无网络环境下,修改自己或联系人的资料 |
| 6661202 | 好友添加成功 | 在资料页添加其他用户为好友,并自动添加成功,无需验证 |
| 6661203 | 好友申请已发出 | 在资料页添加其他用户为好友,对方设置需要验证 |
| 6661204 | 当前用户在黑名单 | 在资料页添加其他用户为好友,对方在自己的黑名单内 |
| 6661205 | 好友添加失败 | 在资料页添加其他用户为好友,添加失败,可能是由于对方禁止加好友 |
| 6661206 | 好友删除成功 | 在资料页删除其他用户为好友,成功 |
| 6661207 | 好友删除失败 | 在资料页删除其他用户为好友,失败 |
| 6661401 | 输入不能为空 | 当用户在录入信息时,输入了空字符串 |
| 6661402 | 请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法 | 用户退出群或解散群时,为提供返回首页办法 |
| 6661403 | 设备存储空间不足,建议清理,以获得更好使用体验 | 在login成功后会自动检测设备存储空间如果不足1GB会提示存储空间不足 |
## TIMUIKitConversation
`TIMUIKitConversation` 为会话组件,拉取用户会话列表,默认提供一套 UI,用户也可自定义会话条目。同时提供对应的`TIMUIKitConversationController`。
[详细参数及用法可参考此文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitConversation/)
<img src="https://qcloudimg.tencent-cloud.cn/raw/a27b131d555b1158d150bd9b337c1d9d.png" style="width:60%;"/>
### TIMUIKitConversationController
#### 方法
- **loadData(int count)**:
加载会话列表, count 为单次加载数量
- **reloadData(int count)**:
重新加载会话列表, count 为单次加载数量
- **pinConversation({required String conversationID, required bool isPinned})**:
会话置顶
- **clearHistoryMessage({required V2TimConversation conversation})**:
清除指定会话消息
- **deleteConversation({required String conversationID})**:
删除指定会话
- **setConversationListener({V2TimConversationListener? listener})**:
添加会话监听器
- **dipose()**:
销毁
---
## TIMUIKitChat
`TIMUIKitChat` 为聊天组件,提供消息列表的展示及消息发送的能力,同时支持自定义各种消息类型的展示。同时可结合 TIMUIKitChatController 实现消息的本地存储及消息预渲染。
目前支持的消息解析:
- 文本消息
- 图片消息
- 视频消息
- 语音消息
- 群消息
- 合并消息
- 文件消息
[详细参数及用法可参考此文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitChat/)
![](https://qcloudimg.tencent-cloud.cn/raw/09b8b9b54fd0caa47069544343eba461.jpg)
### TIMUIKitChatController
#### 方法
- **clearHistory()**: 清除历史消息
- **dispose()**:销毁
- **sendMessage({required V2TimMessage messageInfo, String? receiverID, String? groupID, required ConvType convType})**:发送消息。根据 ConvTypereceiverID/groupID 二选一传入。
- **sendForwardMessage({required List<V2TimConversation> conversationList,})**:逐条转发
- **sendMergerMessage({ required List<V2TimConversation> conversationList, required String title, required List<String> abstractList, required BuildContext context, })**:合并转发
---
## TIMUIKitProfile
`TIMUIKitProfile` 为用户详情展示。同时支持自定义添加操作项.
[详细参数及用法可参考此文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitProfile/)
![](https://qcloudimg.tencent-cloud.cn/raw/03e88da6f1d63f688d2a8ee446da43ff.png)
### TIMUIKitProfileController
- **pinedConversation(bool isPined, String convID)**:
会话置顶, `isPined` 为是否置顶,`convID` 为需要置顶的会话 ID.
- **addUserToBlackList(bool shouldAdd, String userID)**:
添加用户至黑名单, `shouldAdd`为是否需要添加至黑名单, `userID`为需要被添加到黑名单的用户.
- **changeFriendVerificationMethod(int allowType)**:
更改好友验证方式, `0`为"同意任何用户添加好友"、`1`为"需要验证"、`2`为"拒绝任何人加好友".
- **updateRemarks(String userID, String remark)**:
更新好友备注, `userID`为被更新的用户 ID, `remark`为备注.
- **loadData**:
加载数据
- **dispose()**:
销毁
- **addFriend(String userID)**:
添加好友,`userID`为被添加好友的用户 ID.
---
## TIMUIKitGroupProfile
`TIMUIKitGroupProfile` 为群管理页面。同时支持自定义添加操作项.
[详细参数及用法可参考此文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitGroupProfile/)
`operationListBuilder``bottomOperationListBuilder` 主要给予用户可配置操作条目的能力,同时可结合子组件配合使用,可以自己选择搭配。
---
## TIMUIKitBlackList
`TIMUIKitBlackList` 为黑名单列表。
[详细参数及用法可参考此文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitBlackList/)
---
## TIMUIKitGroup
`TIMUIKitGroup` 为群列表。
[详细参数及用法可参考此文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitGroup/)
---
### TIMUIKitContact
`TIMUIKitContact` 为联系人列表组件。
[详细参数及用法可参考此文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitContact/)
---
### 本地搜索组件
`TIMUIKitSearch` 为全局搜索组件。全局搜索支持"联系人"/"群组"/"聊天记录"。
`TIMUIKitSearchMsgDetail` 为会话内搜索组件,可搜索会话内聊天记录。
[详细用法可参考此文档](https://cloud.tencent.com/document/product/269/79121)
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
// 全局搜索
TIMUIKitSearch(
onTapConversation: _handleOnConvItemTapedWithPlace, // Function(V2TimConversation, V2TimMessage? message), 跳转到特定conversation的特定message
onEnterSearchInConversation: (V2TimConversation conversation, String initKeyword){}, // 跳转至对应Conversation的会话内搜索请手动跳转至TIMUIKitSearchMsg组件。
);
// 会话内搜索
TIMUIKitSearchMsgDetail(
currentConversation: conversation!,
onTapConversation: onTapConversation,
keyword: initKeyword ?? "",
);
```
---
### 如何自定义 TIMUIKitChat 组件
为扩展`TIMUIKitChat`组件的自定义能力,我们将该组件包含的基础子组件对外暴露,用户可根据业务去选择和使用基础子组件实现满足自身的业务。基础子组件包含如下:
- `TIMUIKitAppBar`
- `TIMUIKitHistoryMessageList`
- `TIMUIKitHistoryMessageListItem`
- `TIMUIKitInputTextField`
下文将对以上组件介绍及使用用例。
#### TIMUIKitAppBar
该组件为`TIMUIKitChat`的 appbar 组件,用于自定义应用导航栏。相较于 flutter 默认的`appbar`, 该组件额外提供了`title`自适应`用户昵称, 群名称`改变而动态改变,主题色改变。具体参数如下:
| name | type | desc | optional |
| -------------------- | ------ | ------------------------------------ | -------- |
| config | AppBar | flutter appbar, 具体使用参考官方文档 | 可选 |
| showTotalUnReadCount | bool | 显示会话总未读数, 默认为 true | 可选 |
| conversationID | String | 会话 ID | 可选 |
| conversationShowName | String | 会话名称 | 可选 |
#### TIMUIKitHistoryMessageList
该组件为消息列表渲染组件,提供消息自动拉取,自动加载更多,跳转到指定消息。 具体参数如下:
| name | type | desc | optional |
| --------------------- | -------------------------------------------- | ---------------------------- | -------- |
| messageList | List<V2TimMessage?> | 消息列表,渲染数据源 | 必填 |
| tongueItemBuilder | TongueItemBuilder | 小舌头(回到底部)自定义构造器 | 可选 |
| groupAtInfoList | List<V2TimGroupAtInfo?> | 艾特信息 | 可选 |
| itemBuilder | Widget Function(BuildContext, V2TimMessage?) | 消息构造器 | 可选 |
| controller | TIMUIKitHistoryMessageListController | 控制列表跳转,滚动 | 可选 |
| onLoadMore | Function | 加载更多 | 必填 |
| mainHistoryListConfig | ListView | 自定义 ListView | 可选 |
#### TIMUIKitHistoryMessageListItem
该组件为消息实例组件,可根据提供的消息渲染不通的消息类型,包含`文本消息``图片消息`, `文件消息`,`通话消息`, `语音消息`等。同时支持消息自定义,主题定制能力。
| name | type | desc | optional |
| -------------------------------- | ------------------ | ------------------------------------------------------------ | -------- |
| message | V2TimMessage | 消息实例 | 必填 |
| onTapForOthersPortrait | Function | 远端用户头像 tap 回调 | 可选 |
| onScrollToIndex | Function | TIMUIKitHistoryMessageListController 的 scrollToIndex 方法,用于回复消息点击跳转到指定消息 | 可选 |
| onScrollToIndexBegin | Function | TIMUIKitHistoryMessageListController 的 scrollToIndexBegin 方法,长消息长按位置矫正 | 可选 |
| onLongPressForOthersHeadPortrait | Function | 远端用户头像长按 | 可选 |
| messageItemBuilder | MessageItemBuilder | 消息自定义构造器 | 可选 |
| topRowBuilder | Function | 昵称所在行自定义 builder | 可选 |
| bottomRowBuilder | Function | 消息显示之下 builder | 可选 |
| showAvatar | bool | 是否显示头像 | 可选 |
| showNickName | bool | 是否显示用户昵称 | 可选 |
| showMessageSending | bool | 是否显示消息发送中状态 | 可选 |
| showMessageReadRecipt | bool | 是否显示消息已读 | 可选 |
| showGroupMessageReadRecipt | bool | 是否显示群消息已读 | 可选 |
| allowLongPress | bool | 是否允许消息长按 | 可选 |
| allowAvatarTap | bool | 是否允许头像 tap | 可选 |
| allowAtUserWhenReply | bool | 是否在回复消息中提示对方 | 可选 |
| onLongPress | Function | 消息长按回掉 | 可选 |
| toolTipsConfig | ToolTipsConfig | 消息长按 tool tips 配置 | 可选 |
| padding | double | 消息间的间距 | 可选 |
| textPadding | EdgeInsetsGeometry | 文本消息内边距 | 可选 |
| userAvatarBuilder | Function | 用户头像构造器 | 可选 |
| themeData | MessageThemeData | 消息主题配置,可自定义字体颜色,大小等 | 可选 |
#### TIMUIKitInputTextField
该组件为输入框组件,提供`文本消息`,`图片消息`,`语音消息`等发送能力。参数如下
| name | type | desc | optional |
| ------------------ | -------------------------------- | ---------------------------------- | -------- |
| conversationID | String | 会话 ID | 必填 |
| conversationType | String | 会话类型 | 必填 |
| initText | String | 初始化文本 | 可选 |
| scrollController | AutoScrollController | 用于发送消息时将消息列表滚动到底部 | 可选 |
| hintText | String | 提示文本 | 可选 |
| morePanelConfig | MorePanelConfig | 更多面板配置 | 可选 |
| showSendAudio | bool | 是否显示发送语音 | 可选 |
| showSendEmoji | bool | 是否显示发送表情 | 可选 |
| showMorePannel | bool | 是否显示更多面板 | 可选 |
| backgroundColor | Color | 背景色 | 可选 |
| controller | TIMUIKitInputTextFieldController | 控制器,可控制输入框文本 | 可选 |
| onChanged | Function | 文本改变回调事件 | 可选 |
| customStickerPanel | Function | 自定义表情 | 可选 |
#### 如何使用基础组件
如下是一个完整的使用示例
```dart
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
import 'package:tim_ui_kit/ui/controller/tim_uikit_chat_controller.dart';
import 'package:tim_ui_kit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_history_message_list.dart';
import 'package:tim_ui_kit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart';
class Chat extends StatefulWidget {
final V2TimConversation selectedConversation;
final V2TimMessage? initFindingMsg;
const ChatV2(
{Key? key, required this.selectedConversation, this.initFindingMsg})
: super(key: key);
@override
State<StatefulWidget> createState() => _ChatV2State();
}
class _ChatV2State extends State<ChatV2> {
final TIMUIKitChatController _controller = TIMUIKitChatController();
final TIMUIKitHistoryMessageListController _historyMessageListController =
TIMUIKitHistoryMessageListController();
final TIMUIKitInputTextFieldController _textFieldController =
TIMUIKitInputTextFieldController();
bool _haveMoreData = true;
String? _getConvID() {
return widget.selectedConversation.type == 1
? widget.selectedConversation.userID
: widget.selectedConversation.groupID;
}
loadHistoryMessageList(String? lastMsgID, [int? count]) async {
if (_haveMoreData) {
_haveMoreData = await _controller.loadHistoryMessageList(
count: count ?? 20,
userID: widget.selectedConversation.userID,
groupID: widget.selectedConversation.groupID,
lastMsgID: lastMsgID);
}
}
@override
Widget build(BuildContext context) {
return TIMUIKitChatProviderScope(
conversationID: _getConvID() ?? "",
conversationType: widget.selectedConversation.type ?? 0,
builder: (context, w) {
return GestureDetector(
onTap: () {
_textFieldController.hideAllPanel();
},
child: Scaffold(
appBar: TIMUIKitAppBar(
config: AppBar(
title: Text(widget.selectedConversation.showName ?? ""),
),
),
body: Column(
children: [
Expanded(
child: TIMUIKitHistoryMessageListSelector(
builder: (context, messageList, w) {
return TIMUIKitHistoryMessageList(
controller: _historyMessageListController,
messageList: messageList,
onLoadMore: loadHistoryMessageList,
itemBuilder: (context, message) {
return TIMUIKitHistoryMessageListItem(
onScrollToIndex:
_historyMessageListController.scrollToIndex,
onScrollToIndexBegin:
_historyMessageListController.scrollToIndexBegin,
message: message!,
);
},
);
},
conversationID: _getConvID() ?? "",
)),
TIMUIKitInputTextField(
controller: _textFieldController,
conversationID: _getConvID() ?? "",
conversationType: widget.selectedConversation.type ?? 1,
scrollController:
_historyMessageListController.scrollController!,
)
],
),
),
);
},
);
}
}
```
在如上示例中需要注意的点:
- 在使用基础组件时必须通过`TIMUIKitChatProviderScope`组件包裹, 他会根据传入的`conversationID` 及`conversationType` 拉取对应的历史消息.该组件提供是基于通过`MultiProvider` 实现,同时可注入自定义的`provider`.其目的在于基础组件能够消费到业务层数据,同时可通过`TIMUIKitChatController` 控制业务层数据达到数据触发视图渲染的目的。
- 可以使用提供的`TIMUIKitAppBar`组件实现应用导航栏,同时也可根据业务的需要,自己实现 appBar.
- `TIMUIKitChatProviderScope`会加载历史消息到业务层, 通过`TIMUIKitHistoryMessageListSelector` 获取到业务层历史消息数据用于渲染,当历史消息数据发生改变时会触发渲染。
- 通过`TIMUIKitHistoryMessageList` 结合 `TIMUIKitHistoryMessageListItem` 实现消息页面的渲染
- `TIMUIKitInputTextField`实现发送消息
基础组件可根据业务需要自行更换以及组合。如若需要控制业务层数据,可通过`TIMUIKitChatController`提供的方法。
## 联系我们[](id:contact)
如果您在接入使用过程中有任何疑问,请加入 QQ 群788910197 咨询。
![](https://qcloudimg.tencent-cloud.cn/raw/eacb194c77a76b5361b2ae983ae63260.png)

4
analysis_options.yaml Normal file
View File

@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

325
doc/DETAIL.md Normal file
View File

@ -0,0 +1,325 @@
## TIMUIKitCore
`TIMUIKitCore`提供两个静态方法`getInstance` 和 `getSDKInstance`
- `getInstance`: 返回 `CoreServicesImpl` 实例。
- `getSDKInstance`: 返回SDK实例。
`CoreServicesImpl` 为`TIMUIKit` 核心类,包含初始化、登录、登出、获取用户信息等方法。
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
final V2TIMManager _sdkInstance = TIMUIKitCore.getSDKInstance();
/// init
_coreInstance.init(
sdkAppID: 0, // 控制台申请的sdkAppID
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
listener: V2TimSDKListener());
/// unInit
_coreInstance.unInit();
/// login
_coreInstance.login(
userID: 0, // 用户ID
userSig: "" // 参考官方文档userSig
)
/// logout
_coreInstance.logout();
/// getUsersInfo
_coreInstance.getUsersInfo(userIDList: ["123", "456"]);
/// setOfflinePushConfig
_coreInstance.setOfflinePushConfig(
businessID: businessID, // IM 控制台证书 ID接入 TPNS 不需要填写
token: token, // 注册应用到厂商平台或者 TPNS 时获取的 token
isTPNSToken: false // 是否接入配置 TPNStoken 是否是从TPNS 获取
)
/// setSelfInfo
_coreInstance.setSelfInfo(userFullInfo: userFullInfo) // 设置用户信息
/// setTheme
_coreInstance.setTheme(TUITheme theme: theme) // 设置主题色
/*
TUITheme(
// 应用主色
final Color? primaryColor;
// 应用次色
final Color? secondaryColor;
// 提示颜色,用于次级操作或提示
final Color? infoColor;
// 浅背景颜色,比主背景颜色浅,用于填充缝隙或阴影
final Color? weakBackgroundColor;
// 浅分割线颜色,用于分割线或边框
final Color? weakDividerColor;
// 浅字色
final Color? weakTextColor;
// 深字色
final Color? darkTextColor;
// 浅主色用于AppBar或Panels
final Color? lightPrimaryColor;
// 字色
final Color? textColor;
// 警示色,用于危险操作
final Color? cautionColor;
// 群主标识色
final Color? ownerColor;
// 群管理员标识色
final Color? adminColor;)
*/
```
### 静态方法
- **TIMUIKitCore.getInstance()**:
返回`CoreServicesImpl` 实例
- **TIMUIKitCore.getSDKInstance()**:
返回为 `V2TIMManager` 为`SDK 实例` 具体使用方式请参考[`Flutter IM SDK 文档`](https://pub.dev/documentation/tencent_im_sdk_plugin/latest/manager_v2_tim_manager/V2TIMManager/initSDK.html)
---
## TIMUIKitConversation
`TIMUIKitConversation` 为会话组件拉取用户会话列表默认提供一套UI,用户也可自定义会话条目。同时提供对应的`TIMUIKitConversationController`。
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final TIMUIKitConversationController _controller =
TIMUIKitConversationController();
void _handleOnConvItemTaped(V2TimConversation? selectedConv) {
/// 处理逻辑,在此可跳转至聊天界面
}
List<ConversationItemSlidablePanel> _itemSlidableBuilder(
V2TimConversation conversationItem) {
return [
ConversationItemSlidablePanel(
onPressed: (context) {
_clearHistory(conversationItem);
},
backgroundColor: hexToColor("006EFF"),
foregroundColor: Colors.white,
label: '清除聊天',
autoClose: true,
),
ConversationItemSlidablePanel(
onPressed: (context) {
_pinConversation(conversationItem);
},
backgroundColor: hexToColor("FF9C19"),
foregroundColor: Colors.white,
label: conversationItem.isPinned! ? '取消置顶' : '置顶',
)
];
}
TIMUIKitConversation(
onTapItem: _handleOnConvItemTaped, /// 会话Item tap回调 可用于跳转至聊天界面
itemSlidableBuilder: _itemSlidableBuilder, /// 会话Item 向左滑动 的操作项, 可自定义会话置顶等
controller: _controller, /// 会话组件控制器, 可通过其获取会话的数据,设置会话数据,会话置顶等操作
itembuilder: (conversationItem) {} /// 用于自定义会话Item 的UI。 可结合TIMUIKitConversationController 实现业务逻辑
conversationCollector: (conversation) {} /// 会话收集器,可自定义会话是否显示
)
```
### TIMUIKitConversationController
#### 方法:
- **loadData(int count)**:
加载会话列表, count 为单次加载数量
- **reloadData(int count)**:
重新加载会话列表, count 为单次加载数量
- **pinConversation({required String conversationID, required bool isPinned})**:
会话置顶
- **clearHistoryMessage({required V2TimConversation conversation})**:
清除指定会话消息
- **deleteConversation({required String conversationID})**:
删除指定会话
- **setConversationListener({V2TimConversationListener? listener})**:
添加会话监听器
- **dipose()**:
销毁
---
## TIMUIKitChat
`TIMUIKitChat` 为聊天组件提供消息列表的展示及消息发送的能力同时支持自定义各种消息类型的展示。同时可结合TIMUIKitChatController 实现消息的本地存储及消息预渲染。
目前支持的消息解析:
- 文本消息
- 图片消息
- 视频消息
- 语音消息
- 群消息
- 合并消息
- 文件消息
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
TIMUIKitChat(
conversationID: "", /// 会话ID
conversationType: 0, /// 会话类型
conversationShowName: "", /// 会话显示名称
onTapAvatar: _onTapAvatar, /// 头像tap 回调,可用于跳转至用户详情界面。
showNickName: false, /// 是否显示昵称
messageItemBuilder: (message) {
/// 自定义消息构造器、返回null 会使用默认构造器。
},
exteraTipsActionItemBuilder: (message, close) {
/// 消息长按Tips自定义配置项可根据业务额外配置
},
textFieldHintText: "", /// 输入框hintText
appBarConfig: AppBar(), /// 用于自定chat appBar
draftText: "", /// 会话草稿,用于草稿回显,
initFindingTimestamp: 0, /// 用于消息跳转,使用场景为,搜索历史消息后,可跳转至指定消息位置。
morePanelConfig: MorePanelConfig(), /// "+" 号面板配置可用于自定义面板Action.
)
```
### TIMUIKitChatController
#### 方法
- **setMessageListener({V2TimAdvancedMsgListener? listener})**: 设置高级消息监听器
- **removeMessageListener({V2TimAdvancedMsgListener? listener})**: 移除高级消息监听器
- **clearHistory()**: 清除历史消息
- **dispose()**:销毁
---
## TIMUIKitProfile
`TIMUIKitProfile` 为用户详情展示。同时支持自定义添加操作项.
```dart
TIMUIKitProfile(
userID: "",
controller: TIMUIKitProfileController(), // Profile Controller
operationListBuilder: (context, userInfo, conversation) {
///自定义操作项,例如消息免打扰、消息置顶等。 如若不传,会提供默认的操作项
},
bottomOperationBuilder: (context, friendInfo, conversation) {
/// 底部操作项,如删除好友等。
},
handleProfileDetailCardTap: (BuildContext context, V2TimUserFullInfo? userFullInfo) {
/// 个人详情tile tap 回调
},
canJumpToPersonalProfile: false, // 是否可以跳转至个人详情界面
)
```
### TIMUIKitProfileController
- **pinedConversation(bool isPined, String convID)**:
会话置顶, `isPined` 为是否置顶,`convID` 为需要置顶的会话ID.
- **addUserToBlackList(bool shouldAdd, String userID)**:
添加用户至黑名单, `shouldAdd`为是否需要添加至黑名单, `userID`为需要被添加到黑名单的用户.
- **changeFriendVerificationMethod(int allowType)**:
更改好友验证方式, `0`为"同意任何用户添加好友"、`1`为"需要验证"、`2`为"拒绝任何人加好友".
- **updateRemarks(String userID, String remark)**:
更新好友备注, `userID`为被更新的用户ID, `remark`为备注.
- **loadData**:
加载数据
- **dispose()**:
销毁
- **addFriend(String userID)**:
添加好友,`userID`为被添加好友的用户ID.
---
## TIMUIKitGroupProfile
`TIMUIKitGroupProfile` 为群管理页面。同时支持自定义添加操作项.
```dart
TIMUIKitGroupProfile(
groupID: "", //群ID 必填
operationListBuilder:(){}, // 操作项自定义构造器
bottomOperationListBuilder: () {}, // 底部操作项自定义构造器
)
```
`operationListBuilder``bottomOperationListBuilder` 主要给予用户可配置操作条目的能力,同时可结合子组件配合使用,可以自己选择搭配。
### 静态方法
- **TIMUIKitGroupProfile.memberTile()**:
群成员卡片、用于显示群成员概览、群成员列表、删除群成员等操作
- **TIMUIKitGroupProfile.groupNotification()**:
群公告显示及群公告更改
- **TIMUIKitGroupProfile.groupManage()**:
群管理、可设置管理员、禁言等
- **TIMUIKitGroupProfile.groupType()**:
显示群类型
- **TIMUIKitGroupProfile.groupAddOpt()**:
加群方式及修改
- **TIMUIKitGroupProfile.nameCard()**:
群昵称及修改
---
## TIMUIKitBlackList
`TIMUIKitBlackList` 为黑名单列表。
```dart
TIMUIKitBlackList(
onTapItem: (_) {}, /// tap item 回调
emptyBuilder: () {} /// 当列表为空时显示
itemBuilder: () {} /// 自定义 item
)
```
---
## TIMUIKitGroup
`TIMUIKitGroup` 为群列表。
```dart
TIMUIKitGroup(
onTapItem: (_) {}, /// tap item 回调
emptyBuilder: () {} /// 当列表为空时显示
itemBuilder: () {} /// 自定义 item
)
```
---
## TIMUIKitContact
`TIMUIKitContact` 为联系人组件,提供联系人列表。
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
TIMUIKitContact(
topList: [
TopListItem(name: "新的联系人", id: "newContact"),
TopListItem(name: "我的群聊", id: "groupList"),
TopListItem(name: "黑名单", id: "blackList")
], /// 顶部操作列表
topListItemBuilder: _topListBuilder, /// 顶部操作列表构造器
onTapItem: (item) { }, /// 点击联系人
emptyBuilder: (context) => const Center(
child: Text("无联系人"),
), /// 联系人列表为空时显示
);
```
---
## TIMUIKitNewContact
`TIMUIKitNewContact` 为新的联系人界面
```dart
TIMUIKitNewContact(
onAccept: (applicationInfo) {
/// 接受好友回调
},
onRefuse: (applicationInfo) {
/// 拒绝好友回调
},
emptyBuilder: () {
/// 未收到好友申请时回调
},
itemBuilder: () {
/// 自定义好友申请项构造器
}
)
```

236
doc/FAST_INTEGRATED.md Normal file
View File

@ -0,0 +1,236 @@
# 快速集成方案
## 什么是Flutter TIMUIKit?
Flutter TIMUIKit 是基于Flutter IM SDK 实现的一套UI组件其中包含会话、聊天、关系链、群组等功能基于UI组件您可以像搭积木一样快速搭建起自己的业务逻辑。
目前包含的组件如下:
- [TIMUIKitCore](DETAIL.md#timuikitcore) 核心
- [TIMUIKitConversation](DETAIL.md#timuikitconversation) 会话
- [TIMUIKitChat](DETAIL.md#timuikitchat) 聊天
- [TIMUIKitContact](DETAIL.md#timuikitcontact) 联系人
- [TIMUIKitProfile](DETAIL.md#timuikitprofile) 好友管理
- [TIMUIKitGroupProfile](DETAIL.md#timuikitgroupprofile) 群管理
- [TIMUIKitGroup](DETAIL.md#timuikitgroup) 我的群聊
- [TIMUIKitBlackList](DETAIL.md#timuikitblacklist) 黑名单
- [TIMUIKitNewContact](DETAIL.md#timuikitnewcontact) 新的联系人
![](https://imgcache.qq.com/operation/dianshi/other/uikit.e8f3557a9e34f99120644b7a4a5645ec30c2cbd2.jpg)
## 支持平台
- Android
- ios
## 如何集成?
如下会介绍如何使用`TIMUIKit`快速构建一个简单的即时通信应用.
### 步骤1: 创建Flutter应用
参考Flutter[文档](https://flutter.cn/docs/get-started/test-drive?tab=terminal)快速创建一个flutter应用。
### 步骤2: 安装依赖
在`pubspec.yaml`文件中的`dependencies`下添加`tim_ui_kit`。或者执行如下命令:
```
// step 1:
flutter pub add tim_ui_kit
// step 2:
flutter pub get
```
### 步骤3: 初始化TIMUIKit
在`initState`中初始化`TIMUIKit`,项目启动只需要初始化一次即可。
```dart
/// main.dart
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TIMUIKit Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'TIMUIKit Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
@override
void initState() {
_coreInstance.init(
sdkAppID: 0, // 控制台申请的sdkAppID
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
listener: V2TimSDKListener());
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(),
);
}
}
```
### 步骤4: 获取签名和登录
>- 正确的 `UserSig` 签发方式是将 `UserSig` 的计算代码集成到您的服务端,并提供面向 App 的接口,在需要 `UserSig` 时由您的 App 向业务服务器发起请求获取动态 `UserSig`。更多详情请参见 [服务端生成 UserSig](https://cloud.tencent.com/document/product/647/17275#Server)。
添加两个`TextField`用于输入`userID` 和 `userSig`。点击登录后掉用登录接口。
```dart
/// main.dart
/// 省略
class _MyHomePageState extends State<MyHomePage> {
/// 获取 TIMUIKitCore Instance
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
String userID = "";
String userSig = "";
/// 省略
void _login() {
// 登录
_coreInstance.login(userID: userID, userSig: userSig);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
onChanged: ((value) {
setState(() {
userID = value;
});
}),
decoration: InputDecoration(hintText: "userID"),
),
TextField(
onChanged: ((value) {
setState(() {
userSig = value;
});
}),
decoration: InputDecoration(hintText: "userSig"),
),
ElevatedButton(
onPressed: (() {
_login();
}),
child: const Text("登录"))
],
),
),
);
}
}
```
### 步骤4: 集成所需组件
- 创建`message.dart`文件集成`TIMUIKitConversation` 和 `TIMUIKitChat`包含不仅限于此。可根据您的需求集成更多的组件。
- 修改`main.dart`中代码,登录成功后跳转至该页面。
```dart
/// message.dart
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class Conversation extends StatelessWidget {
const Conversation({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
"消息",
style: TextStyle(color: Colors.black),
),
),
body: TIMUIKitConversation(
onTapItem: (selectedConv) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chat(
selectedConversation: selectedConv,
),
));
},
),
);
}
}
class Chat extends StatelessWidget {
final V2TimConversation selectedConversation;
const Chat({Key? key, required this.selectedConversation}) : super(key: key);
String? _getConvID() {
return selectedConversation.type == 1
? selectedConversation.userID
: selectedConversation.groupID;
}
@override
Widget build(BuildContext context) {
return TIMUIKitChat(
conversationID: _getConvID() ?? '', // groupID 或者 userID
conversationType: selectedConversation.type ?? 0, // 会话类型
conversationShowName: selectedConversation.showName ?? "", // 会话展示名称
onTapAvatar: (_) {}, // 点击消息发送者头像回调事件、可与TIMUIKitProfile关联使用
appBarActions: [
IconButton(
onPressed: () {}, icon: const Icon(Icons.more_horiz_outlined))
],
);
}
}
/// main.dart
/// 部分代码省略
void _login() async {
final res = await _coreInstance.login(userID: userID, userSig: userSig);
if (res.code == 0) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) => const Conversation()),
(route) => false,
);
}
}
```
### 常见问题
#### 1: 引入了 `TIMUIKit` 还需要引入 `IM SDK` 吗?
不需要再次引入`IM SDK`了。如果需要使用`IM SDK` 相关的接口,可通过 `TIMUIKitCore.getSDKInstance()`获取。为了保证`IM SDK` 的版本一致性我们推荐您使用该方式使用SDK。
#### 2: 发送语音、图片、文件等消息闪退?
请查看是否打开了`相机、麦克风、相册`等权限。

2
doc/I18N.md Normal file
View File

@ -0,0 +1,2 @@
【企微文档】TIM Flutter国际化方案
https://doc.weixin.qq.com/doc/w3_AWcADQY0AA8u9RhcnhqRSSW6RZc4w?scode=AJEAIQdfAAoaC0QLyWAWcADQY0AA8

8
doc/README.md Normal file
View File

@ -0,0 +1,8 @@
## TIMUIKit
TUIKit 是基于 IM SDK 实现的一套 UI 组件,其包含会话、聊天、搜索、关系链、群组、音视频通话等功能,基于 UI 组件您可以像搭积木一样快速搭建起自己的业务逻辑。
#### 架构
![](https://imgcache.qq.com/operation/dianshi/other/191645543019_.pic.06d8f22e726287c07cf38d362ec40d4deb4799c7.jpg)
- [快速集成文档](https://git.woa.com/29294-22989-29805-29810/im-flutter-uikit/blob/feature/add-doc/package_src/tim_ui_kit/doc/FAST_INTEGRATED.md)

606
doc/get_start.md Normal file
View File

@ -0,0 +1,606 @@
[toc]
通过阅读本文您可以了解集成Flutter SDK的方法。
## 环境要求
| | 版本 |
|---------|---------|
| Flutter | IM SDK最低要求Flutter 2.2.0版本TUIKit集成组件库最低要求Flutter 2.8.1版本。|
|Android|Android Studio 3.5及以上版本App 要求 Android 4.1及以上版本设备。|
|iOS|Xcode 11.0及以上版本,请确保您的项目已设置有效的开发者签名。|
## 前提条件
1. 您已 [注册腾讯云](https://cloud.tencent.com/document/product/378/17985) 帐号,并完成 [实名认证](https://cloud.tencent.com/document/product/378/3629)。
2. 参照 [创建并升级应用](https://cloud.tencent.com/document/product/269/32577) 创建应用,并记录好`SDKAppID`。
[](id:part1)
## Part1创建测试用户
在[IM控制台](https://console.cloud.tencent.com/im)选择您的应用,在左侧导航栏依次点击 **辅助工具**->**UserSig 生成&校验** ,创建两个 UserID 及其对应的 UserSig复制`UserID`、`签名Key`、`UserSig`这三个,后续登录时会用到。
>? 该账户仅限开发测试使用。应用上线前,正确的 `UserSig` 签发方式是由服务器端生成,并提供面向 App 的接口,在需要 `UserSig` 时由App向业务服务器发起请求获取动态 `UserSig`。更多详情请参见 [服务端生成 UserSig](https://cloud.tencent.com/document/product/269/32688#GeneratingdynamicUserSig)。
![](https://main.qcloudimg.com/raw/8315da2551bf35ec85ce10fd31fe2f52.png)
[](id:part2)
## Part2选择合适的方案集成Flutter SDK
IM提供了三种方式来集成您可以选择最合适的方案来集成
| | 适用场景 |
|---------|---------|
| [使用DEMO](#part3) | IM demo是一个完整的聊天app代码已开源如果您需要实现聊天类似场景可以使用demo进行二次开发。 [点此体验demo](https://cloud.tencent.com/document/product/269/36852) |
| [含UI集成](#part4) | IM的UI组件库`TUIKit`提供了通用的 UI 组件,例如会话列表、聊天界面和联系人列表等,开发者可根据实际业务需求通过该组件库快速地搭建自定义 IM 应用。**推荐优先使用该方案** |
| [自实现UI集成](#part5) | 如果TUIKit不能满足您应用的界面需求或者您需要比较多的定制可以使用该方案。 |
为帮助您更好的理解IM SDK的各API[我们还提供了API Example](https://github.com/TencentCloud/TIMSDK/tree/master/Flutter/IMSDK/im-flutter-plugin/tencent_im_sdk_plugin/example)演示各API的调用及监听的触发。
[](id:part3)
## Part3使用DEMO
### 跑通DEMO
1. 下载demo源码、安装依赖
```shell
#clone 代码
git clone https://github.com/TencentCloud/TIMSDK.git
#进入flutter的demo目录
cd TIMSDK/Flutter/Demo/im-flutter-uikit
#安装依赖
flutter pub get
```
2. 运行demo项目
```shell
#启动demo项目请替换SDK_APPID、KEY两个参数
flutter run --dart-define=SDK_APPID={YOUR_SDKAPPID} --dart-define=ISPRODUCT_ENV=false --dart-define=KEY={YOUR_KEY}
```
>?
>
>- `--dart-define=SDK_APPID={YOUR_SDKAPPID}` 其中`{YOUR_SDKAPPID}`需替换成您自己应用的 SDKAppID。
>- `--dart-define=ISPRODUCT_ENV=false` 对开发生产环境做判断,如您是开发环境请用 false。
>- `--dart-define=KEY={YOUR_KEY}` 其中`{YOUR_KEY}`需替换成 [Part1创建测试用户](#part1) 中的`密钥Key`信息。
>
#### 也可以使用 IDE 运行:(可选步骤)
<dx-tabs>
::: Android 平台[](id:android)
1. 在 Android Studio 打开 discuss/andorid 目录。
![](https://qcloudimg.tencent-cloud.cn/raw/6516f9b17c58915c4ebc93c5c8829831.png)
2. 启动一个 Android 的模拟器,单击 **Build And Run**Demo 可以运行起来。您可以随机输入一个 UserID数字字母组合
>?UI 可能会有部分调整更新,请以最新版为准。
:::
::: iOS 平台[](id:ios)
1. 打开 Xcode打开文件 discuss/ios/Runner.xcodeproj
![](https://qcloudimg.tencent-cloud.cn/raw/6d74814ba9bce54c7439e8b3cea53e73.png)
2. 连接 iPhone 真机,单击 **Build And Run**iOS 工程等待编译完成,会有新窗口弹出 Xcode 工程。
3. 打开 iOS 工程,设置主 Target 的 Signing & Capabilities需要苹果开发者帐号让项目可以在 iPhone 真机上运行。
4. 启动项目,在真机上进行 Demo 的调试。
![](https://qcloudimg.tencent-cloud.cn/raw/3fe6bbac88bb21ad7a7822bb297793b3.png)
:::
</dx-tabs>
#### DEMO代码结构概览
>? 我们DEMO的UI及业务逻辑部分使用Flutter TUIKit。DEMO层本身仅用于构建APP处理导航跳转及调用实例化TUIKit中各个组件。
| 文件夹 | 介绍 |
|---------|---------|
| lib | 程序核心目录 |
| lib/i18n | 国际化相关代码。这里的国际化不包含TUIKit本身的国际化能力和国际化词条您可按需引入 |
| lib/src | 项目主体目录 |
| lib/src/pages | 本DEMO几个重点导航页。项目初始化完成后`app.dart` 负责展示加载动画,并判断登陆态,将用户引导至 `login.dart``home_page.dart`。用户登录后,会将登陆信息通过 `shared_preference` 插件,存储至本地。以后每次启动应用,若在本地发现原来的登录信息,则自动使用该信息进行登录,若无或登录失败,则引导至登录页。自动登录过程中,用户还在 `app.dart` ,可看到加载动画。`home_page.dart`含一个底部Tab支撑本demo的四个主功能页的切换。 |
| lib/utils | 一些工具函数类 |
基本上,`lib/src` 内每个dart文件引入了一个TUIKit组件在文件内实例化组件后即可渲染页面。
主要文件如下:
| lib/src 主要文件 | 文件介绍 |
|---------|---------|
| add_friend.dart | 申请添加好友页面,使用 `TIMUIKitAddFriend` 组件|
| add_group.dart | 申请入群页面,使用 `TIMUIKitAddGroup` 组件|
| blacklist.dart| 黑名单列表页面,使用 `TIMUIKitBlackList` 组件 |
| chat.dart | 主聊天页面使用全套TUIKit聊天能力使用 `TIMUIKitChat` 组件 |
| chatv2.dart | 主聊天页面,使用原子化能力,使用 `TIMUIKitChat` 组件 |
| contact.dart | 联系人页面 ,使用 `TIMUIKitContact` 组件|
| conversation.dart | 会话列表界面,使用 `TIMUIKitConversation` 组件 |
| create_group.dart | 发起群聊页面纯Demo实现未使用组件 |
| group_application_list.dart | 入群申请列表页面,使用 `TIMUIKitGroupApplicationList` 组件 |
| group_list.dart | 群列表页面,使用 `TIMUIKitGroup` 组件 |
| group_profile.dart | 群资料及群管理页面,使用 `TIMUIKitGroupProfile` 组件 |
| newContact.dart | 联系人好友申请页面,使用 `TIMUIKitNewContact` 组件 |
| routes.dart | Demo的路由导航至登录页 `login.dart` 或主页面 `home_page.dart`。 |
| search.dart | 全局搜索及会话内搜索页面,使用 `TIMUIKitSearch`(全局搜索) 及 `TIMUIKitSearchMsgDetail`(会话内搜索) 组件 |
| user_profile.dart | 用户信息及关系链维护页面,使用 `TIMUIKitProfile` 组件|
大部分TUIKit组件需要传入导航跳转方法因此需要DEMO层处理 `Navigator`
以上介绍了我们的DEMO您可以直接直接修改它二次开发或参照它实现您的业务需求。
[](id:part4)
## Part4含UI集成使用TUIKit组件库半天完成IM能力植入
TUIKit 是基于腾讯云 IM SDK 的一款 UI 组件库,它提供了一些通用的 UI 组件,例如会话列表、聊天界面和联系人列表等,开发者可根据实际业务需求通过该组件库快速地搭建自定义 IM 应用。[点此查看TUIKit详细介绍](https://cloud.tencent.com/document/product/269/70746)
### 前提条件
您已经完成创建Flutter项目或有可以基于的Flutter项目。
### 接入步骤
#### 安装IM TUIkit
我们的TUIkit已经内含IM SDK因此仅需安装`tim_ui_kit`不需要再安装基础im sdk。
```shell
#在命令行执行
flutter pub add tim_ui_kit
```
#### 初始化
在您应用启动时初始化TUIKit。
请务必保证先执行 `TIMUIKitCore.getInstance()` ,再调用初始化函数 `init()` ,并将您的[sdkAppID]传入。
```dart
/// main.dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
@override
void initState() {
_coreInstance.init(
sdkAppID: 0, // Replace 0 with the SDKAppID of your IM application when integrating
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
listener: V2TimSDKListener());
super.initState();
}
}
```
#### 登录测试账户
此时,您可以使用最开始的时候,在控制台生成的测试账户,完成登录验证。
调用`_coreInstance.login`方法,登录一个测试账户。
```dart
import 'package:tim_ui_kit/tim_ui_kit.dart';
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
_coreInstance.login(userID: userID, userSig: userSig);
```
>? 该账户仅限开发测试使用。应用上线前,正确的 `UserSig` 签发方式是由服务器端生成,并提供面向 App 的接口,在需要 `UserSig` 时由App向业务服务器发起请求获取动态 `UserSig`。更多详情请参见 [服务端生成 UserSig](https://cloud.tencent.com/document/product/269/32688#GeneratingdynamicUserSig)。
#### 实现:会话列表页面
您可以以会话列表作为您的IM功能首页其涵盖了与所有有聊天记录的用户及群聊的会话。
![wecom-temp-320803-502538740c22124e9f3e0efc1d5a10ee](https://tuikit-1251787278.cos.ap-guangzhou.myqcloud.com/wecom-temp-320803-502538740c22124e9f3e0efc1d5a10ee.jpg)
请创建一个 `Conversation` 类,`body` 中使用 `TIMUIKitConversation` 组件,渲染会话列表。
您仅需传入一个 `onTapItem` 事件的处理函数,用于跳转至具体会话聊天页的导航。关于 `Chat` 类,会在下一步讲解。
```dart
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class Conversation extends StatelessWidget {
const Conversation({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
"Message",
style: TextStyle(color: Colors.black),
),
),
body: TIMUIKitConversation(
onTapItem: (selectedConv) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Chat(
selectedConversation: selectedConv,
),
));
},
),
);
}
}
```
#### 实现:会话聊天页面
该页面由顶部主体聊天历史记录及底部发送消息模块组成。
![20220701202206](https://tuikit-1251787278.cos.ap-guangzhou.myqcloud.com/20220701202206.png)
请创建一个 `Chat` 类,`body` 中使用 `TIMUIKitChat` 组件,渲染聊天页面。
您最好传入一个 `onTapAvatar` 事件的处理函数,用于跳转至联系人的详细信息页。关于 `UserProfile` 类,会在下一步讲解。
```dart
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class Chat extends StatelessWidget {
final V2TimConversation selectedConversation;
const Chat({Key? key, required this.selectedConversation}) : super(key: key);
String? _getConvID() {
return selectedConversation.type == 1
? selectedConversation.userID
: selectedConversation.groupID;
}
@override
Widget build(BuildContext context) {
return TIMUIKitChat(
conversationID: _getConvID() ?? '', // groupID or UserID
conversationType: selectedConversation.type ?? 1, // Conversation type
conversationShowName: selectedConversation.showName ?? "", // Conversation display name
onTapAvatar: (_) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfile(userID: userID),
));
}, // Callback for the clicking of the message sender profile photo. This callback can be used with `TIMUIKitProfile`.
);
}
```
#### 实现:用户详情页面
该页面默认,可在只传入一个 `userID` 的情况下,自动根据是否是好友,生成用户详情页。
请创建一个 `UserProfile` 类,`body` 中使用 `TIMUIKitProfile` 组件,渲染用户详情及关系链页面。
>? 如果您希望自定义该页面,请优先考虑使用 `profileWidgetBuilder` 传入需自定义的profile组件并配合 `profileWidgetsOrder` 确定纵向排列顺序;如果无法满足,才可使用 `builder`
![wecom-temp-215357-bdcdaa6f33a21573e0a2785a8cff72c0](https://tuikit-1251787278.cos.ap-guangzhou.myqcloud.com/wecom-temp-215357-bdcdaa6f33a21573e0a2785a8cff72c0.jpg)
```dart
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class UserProfile extends StatelessWidget {
final String userID;
const UserProfile({required this.userID, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
"Message",
style: TextStyle(color: Colors.black),
),
),
body: TIMUIKitProfile(
userID: widget.userID,
),
);
}
}
```
此时,您的应用已经可以完成消息收发,管理好友关系,展示用户详情及展示会话列表。
#### 更多能力
您还可以继续使用以下TUIKit插件快速实现完整IM功能。
[TIMUIKitContact](https://intl.cloud.tencent.com/document/product/1047/46297#timuikitcontact): 联系人列表页面。
[TIMUIKitGroupProfile](https://intl.cloud.tencent.com/document/product/1047/46297#timuikitgroupprofile): 群资料页面,使用方式与 `TIMUIKitProfile` 基本一致。
[TIMUIKitGroup](https://intl.cloud.tencent.com/document/product/1047/46297#timuikitgroup): 群列表界面。
[TIMUIKitBlackList](https://intl.cloud.tencent.com/document/product/1047/46297#timuikitblacklist): 黑名单列表界面。
[TIMUIKitNewContact](https://intl.cloud.tencent.com/document/product/1047/46297#timuikitnewcontact): 联系人(好友)申请列表。如需在外部显示小红点,可使用 `TIMUIKitUnreadCount` 小红点组件,其会自动挂载监听。
[TIMUIKitSearch](https://pub.dev/documentation/tim_ui_kit/latest/ui_views_TIMUIKitSearch_tim_uikit_search/TIMUIKitSearch-class.html): 搜索组件,支持全局搜索联系人/群组/聊天记录,也支持在特定会话中搜索聊天记录。两种模式取决于是否传入 `conversation`
详细UI插件指南[可参考本文档](https://cloud.tencent.com/document/product/269/70747#timuikitcontact)或[插件README](https://pub.dev/packages/tim_ui_kit)。
[](id:part5)
## Part5自实现UI集成
### 前提条件
您已经完成创建Flutter项目或有可以基于的Flutter项目。
### 接入步骤
#### 安装IM SDK
[本节详细文档](https://cloud.tencent.com/document/product/269/75286)
使用如下命令安装Flutter IM SDK最新版本。
在命令行执行:
```shell
flutter pub add tencent_im_sdk_plugin
```
#### 完成SDK初始化
[本节详细文档](https://cloud.tencent.com/document/product/269/75293)
调用`initSDK`完成SDK初始化。
将您的[sdkAppID]传入。
```Dart
import 'package:tencent_im_sdk_plugin/enum/V2TimSDKListener.dart';
import 'package:tencent_im_sdk_plugin/enum/log_level_enum.dart';
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
TencentImSDKPlugin.v2TIMManager.initSDK(
sdkAppID: 0, // Replace 0 with the SDKAppID of your IM application when integrating
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG, // Log
listener: V2TimSDKListener(),
);
```
在本步骤你可以针对IM SDK挂载一些监听主要包括网络状态及用户信息变更等[详情可参考该文档](https://pub.dev/documentation/tencent_im_sdk_plugin_platform_interface/latest/enum_V2TimSDKListener/V2TimSDKListener-class.html)。
#### 登录测试账户
[本节详细文档](https://cloud.tencent.com/document/product/269/75296)
此时,您可以使用最开始的时候,在控制台生成的测试账户,完成登录验证。
调用`TencentImSDKPlugin.v2TIMManager.login`方法,登录一个测试账户。
当返回值`res.code`为0时登录成功。
```dart
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
V2TimCallback res = await TencentImSDKPlugin.v2TIMManager.login(
userID: userID,
userSig: userSig,
);
```
>? 该账户仅限开发测试使用。应用上线前,正确的 `UserSig` 签发方式是将 `UserSig` 的计算代码集成到您的服务端,并提供面向 App 的接口,在需要 `UserSig` 时由您的 App 向业务服务器发起请求获取动态 `UserSig`。更多详情请参见 [服务端生成 UserSig](https://cloud.tencent.com/document/product/269/32688#GeneratingdynamicUserSig)。
#### 发送消息
[本节详细文档](https://cloud.tencent.com/document/product/269/75317)
此处以发送文本消息举例,其流程为:
1. 调用 `createTextMessage(String)`创建一个文本消息。
2. 根据其返回值拿到消息ID。
3. 调用 `sendMessage()` 发送该ID的消息。`receiver`可填入您此前创建的另一个测试账户ID。发送单聊消息无需填入`groupID`。
代码示例:
```dart
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
V2TimValueCallback<V2TimMsgCreateInfoResult> createMessage =
await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.createTextMessage(text: "The text to create");
String id = createMessage.data!.id!; // The message creation ID
V2TimValueCallback<V2TimMessage> res = await TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.sendMessage(
id: id, // Pass in the message creation ID to
receiver: "The userID of the destination user",
groupID: "The groupID of the destination group",
);
```
> 如果发送失败可能是由于您的sdkAppID不支持陌生人发送消息您可至控制台开启用于测试。
>
> [请点击此链接](https://console.cloud.tencent.com/im/login-message),关闭[好友关系链检查]。
#### 获取会话列表
[本节详细文档](https://cloud.tencent.com/document/product/269/75368)
在上一个步骤中,完成发送测试消息,现在可登录另一个测试账户,拉取会话列表。
![wecom-temp-320803-502538740c22124e9f3e0efc1d5a10ee](https://tuikit-1251787278.cos.ap-guangzhou.myqcloud.com/wecom-temp-320803-502538740c22124e9f3e0efc1d5a10ee.jpg)
获取会话列表的方式有两种:
1. 监听长连接回调,实时更新会话列表。
2. 请求API根据分页一次性获取会话列表。
常见应用场景为:
在启动应用程序后立即获取会话列表,然后监听长连接以实时更新会话列表的变化。
##### 一次性请求会话列表
为了获取会话列表,需要维护`nextSeq`,记录当前位置。
```dart
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
String nextSeq = "0";
getConversationList() async {
V2TimValueCallback<V2TimConversationResult> res = await TencentImSDKPlugin
.v2TIMManager
.getConversationManager()
.getConversationList(nextSeq: nextSeq, count: 10);
nextSeq = res.data?.nextSeq ?? "0";
}
```
此时,你可以看到您在上一步中,使用另一个测试账号,发来消息的会话。
##### 监听长链接实时获取会话列表
您在此步骤中需要先在SDK上挂载监听然后处理回调事件更新UI。
1. 挂载监听。
```dart
await TencentImSDKPlugin.v2TIMManager
.getConversationManager()
.setConversationListener(
listener: new V2TimConversationListener(
onConversationChanged: (List<V2TimConversation> list){
_onConversationListChanged(list);
},
onNewConversation:(List<V2TimConversation> list){
_onConversationListChanged(list);
},
```
2. 处理回调事件,将最新的会话列表展示在界面上。
```dart
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
List<V2TimConversation> _conversationList = [];
_onConversationListChanged(List<V2TimConversation> list) {
for (int element = 0; element < list.length; element++) {
int index = _conversationList.indexWhere(
(item) => item!.conversationID == list[element].conversationID);
if (index > -1) {
_conversationList.setAll(index, [list[element]]);
} else {
_conversationList.add(list[element]);
}
}
```
#### 接收消息
[本节详细文档](https://cloud.tencent.com/document/product/269/75320)
通过腾讯云IM Ffltter SDK接收消息有两种方式
1. 监听长连接回调,实时获取消息变化,更新渲染历史消息列表。
2. 请求API根据分页一次性获取历史消息。
常见应用场景为:
1. 界面进入新的会话后,首先一次性请求一定数量的历史消息,用于展示历史消息列表。
2. 监听长链接,实时接收新的消息,将其添加进历史消息列表中。
##### 一次性请求历史消息列表
每页拉取的消息数量不能太大否则会影响拉取速度。建议此处设置为20左右。
您应该动态记录当前页数,用于下一轮请求。
示例代码如下:
```dart
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
V2TimValueCallback<List<V2TimMessage>> res = await TencentImSDKPlugin
.v2TIMManager
.getMessageManager()
.getGroupHistoryMessageList(
groupID: "groupID",
count: 20,
lastMsgID: "",
);
List<V2TimMessage> msgList = res.data ?? [];
// here you can use msgList to render your message list
}
```
##### 监听长链接实时获取新消息
历史消息列表初始化后,新消息来自长链接 `V2TimAdvancedMsgListener.onRecvNewMessage`
`onRecvNewMessage`回调被触发后,您可以按需将新消息添加进历史消息列表中。
绑定监听器示例代码如下:
```dart
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
final adVancesMsgListener = V2TimAdvancedMsgListener(
onRecvNewMessage: (V2TimMessage newMsg) {
_onReceiveNewMsg(newMsg);
},
/// ... other listeners related to message
);
TencentImSDKPlugin.v2TIMManager
.getMessageManager()
.addAdvancedMsgListener(listener: adVancesMsgListener);
```
此时您已基本完成IM模块开发可以发送接收消息也可以进入不同的会话。
您可以继续完成[群组](https://cloud.tencent.com/document/product/269/75697)[用户资料](https://cloud.tencent.com/document/product/269/75418)[关系链](https://cloud.tencent.com/document/product/269/75421)[离线推送](https://cloud.tencent.com/document/product/269/75430)[本地搜索](https://cloud.tencent.com/document/product/269/75438)等相关功能开发。
详情可查看[自实现UI集成SDK文档](https://cloud.tencent.com/document/product/269/75260)。
## 常见问题
### 支持哪些平台?
目前支持 iOS 、Android 和 Web 三个平台,另外 Windows 和 Mac 版正在开发中,敬请期待。
### Android 单击 Build And Run 报错找不到可用设备?
确保设备没被其他资源占用,或单击 **Build** 生成 APK 包,再拖动进模拟器里运行。
### iOS 第一次运行报错?
配置运行后,如果报错,可以单击 **Product** > **Clean**,清除产物后重新 Build或者关闭 Xcode重新打开后再次 Build。
### Flutter 环境问题
如您需得知 Flutter 的环境是否存在问题,请运行 Flutter doctor 检测 Flutter 环境是否装好。
### 使用Flutter自动生成的项目引入TUIKit后报错
![20220706132722](https://tuikit-1251787278.cos.ap-guangzhou.myqcloud.com/20220706132722.png)
需要在\android\app\src\main\AndroidManifest.xml中进行修改。
打开 `\android\app\src\main\AndroidManifest.xml`,根据下图,补全。
![20220706133714](https://tuikit-1251787278.cos.ap-guangzhou.myqcloud.com/20220706133714.png)
打开 `\android\app\build.gradle`,根据下图,补全 `defaultConfig`
![20220706133740](https://tuikit-1251787278.cos.ap-guangzhou.myqcloud.com/20220706133740.png)
## 联系我们
如果您在接入使用过程中有任何疑问请加入QQ群788910197 咨询。

101
doc/theme.md Normal file
View File

@ -0,0 +1,101 @@
# 主题方案
## 1 介绍
TUIKit 自定义了 **TUITheme**用于规范TUIKit内的色彩使用。
请开发时务必注意,目前除 Colors.white 和 Colors.black 等底色/前景色外一律使用theme里提供的颜色。没有对应颜色可提出加到 TUITheme 里。
颜色概览:
// 应用主色
// Primary Color For The App
final Color? primaryColor;
// 应用次色
// Secondary Color For The App
final Color? secondaryColor;
// 提示颜色,用于次级操作或提示
// Info Color, Used For Secondary Action Or Info
final Color? infoColor;
// 浅背景颜色,比主背景颜色浅,用于填充缝隙或阴影
// Weak Background Color, Lighter Than Main Background, Used For Marginal Space Or Shadowy Space
final Color? weakBackgroundColor;
// 浅分割线颜色,用于分割线或边框
// Weak Divider Color, Used For Divider Or Border
final Color? weakDividerColor;
// 浅字色
// Weak Text Color
final Color? weakTextColor;
// 深字色
// Dark Text Color
final Color? darkTextColor;
// 浅主色用于AppBar或Panels
// Light Primary Color, Used For AppBar Or Several Panels
final Color? lightPrimaryColor;
// 字色
// TextColor
final Color? textColor;
// 警示色,用于危险操作
// Caution Color, Used For Warning Actions
final Color? cautionColor;
// 群主标识色
// Group Owner Identification Color
final Color? ownerColor;
// 群管理员标识色
// Group Admin Identification Color
final Color? adminColor;
//除各种固定颜色外提供2种MaterialColor
`primaryMaterialColor` && `lightPrimaryMaterialColor`
提供由 primaryColor 和 lightPrimaryColor 生成的十级色阶(50 ~ 900)eg: `primaryMaterialColor.shade50` `primaryMaterialColor.shade900`
## 2 使用方式
### 2.1 开发中
#### 2.1.1 Demo
通过 provider 里的 **DefaultThemeData.theme** 来获取theme。
通过 provider 里的 **DefaultThemeData.currentThemeType** 来获取/设置当前ThemeType。
设置 **DefaultThemeData.currentThemeType** 会将currentThemeType 写入localStorage 并同步 TUIKit 的 Theme。
当前支持四种ThemeType
`enum ThemeType { solemn, brisk, bright, fantasy }`
#### 2.1.2 UIKit
通过全局唯一的 `serviceLocator<TUIThemeViewModel>()` 获取当前 theme。
#### 2.1.3 AppBar
目前 **AppBar** 的统一处理如下:
字色目前统一为 Colors.white
`flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [
theme.lightPrimaryColor ?? CommonColor.lightPrimaryColor,
theme.primaryColor ?? CommonColor.primaryColor
]),
),
),`
`IconThemeData(
color: Colors.white,
),`
## 3 TODO
3.1 目前只支持暗色底白字,需要提供亮/暗两种主题间的切换。
3.2 自动识别亮/暗主题并更改字色。

46
example/.gitignore vendored Normal file
View File

@ -0,0 +1,46 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

10
example/.metadata Normal file
View File

@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 7e9793dee1b85a243edd0e06cb1658e98b077561
channel: stable
project_type: app

16
example/README.md Normal file
View File

@ -0,0 +1,16 @@
# example
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

13
example/android/.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks

View File

@ -0,0 +1,68 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example"
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,34 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<application
android:label="example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>

View File

@ -0,0 +1,6 @@
package com.example.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,31 @@
buildscript {
ext.kotlin_version = '1.6.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true

View File

@ -0,0 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip

View File

@ -0,0 +1,11 @@
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

34
example/ios/.gitignore vendored Normal file
View File

@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
</dict>
</plist>

View File

@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

41
example/ios/Podfile Normal file
View File

@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

206
example/ios/Podfile.lock Normal file
View File

@ -0,0 +1,206 @@
PODS:
- camera (0.0.1):
- Flutter
- connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
- DKImagePickerController/Core (4.3.3):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.3)
- DKImagePickerController/PhotoGallery (4.3.3):
- DKImagePickerController/Core
- DKPhotoGallery
- DKImagePickerController/Resource (4.3.3)
- DKPhotoGallery (0.0.17):
- DKPhotoGallery/Core (= 0.0.17)
- DKPhotoGallery/Model (= 0.0.17)
- DKPhotoGallery/Preview (= 0.0.17)
- DKPhotoGallery/Resource (= 0.0.17)
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Core (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Preview
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Model (0.0.17):
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Preview (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Resource
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Resource (0.0.17):
- SDWebImage
- SwiftyGif
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Flutter (1.0.0)
- flutter_plugin_record (0.0.1):
- Flutter
- fluttertoast (0.0.2):
- Flutter
- Toast
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- HydraAsync (2.0.6)
- image_gallery_saver (1.5.0):
- Flutter
- image_picker_ios (0.0.1):
- Flutter
- libwebp (1.2.1):
- libwebp/demux (= 1.2.1)
- libwebp/mux (= 1.2.1)
- libwebp/webp (= 1.2.1)
- libwebp/demux (1.2.1):
- libwebp/webp
- libwebp/mux (1.2.1):
- libwebp/demux
- libwebp/webp (1.2.1)
- open_file (0.0.1):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider_ios (0.0.1):
- Flutter
- "permission_handler (5.1.0+2)":
- Flutter
- photo_manager (2.0.0):
- Flutter
- FlutterMacOS
- ReachabilitySwift (5.0.0)
- SDWebImage (5.12.5):
- SDWebImage/Core (= 5.12.5)
- SDWebImage/Core (5.12.5)
- shared_preferences_ios (0.0.1):
- Flutter
- sqflite (0.0.2):
- Flutter
- FMDB (>= 2.7.5)
- SwiftyGif (5.4.3)
- tencent_im_sdk_plugin (1.0.5):
- Flutter
- HydraAsync
- TXIMSDK_Plus_iOS (= 6.1.2155.1)
- Toast (4.0.0)
- TXIMSDK_Plus_iOS (6.1.2155.1)
- video_player_avfoundation (0.0.1):
- Flutter
- video_thumbnail (0.0.1):
- Flutter
- libwebp
- wakelock (0.0.1):
- Flutter
DEPENDENCIES:
- camera (from `.symlinks/plugins/camera/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- flutter_plugin_record (from `.symlinks/plugins/flutter_plugin_record/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- open_file (from `.symlinks/plugins/open_file/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- permission_handler (from `.symlinks/plugins/permission_handler/ios`)
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- tencent_im_sdk_plugin (from `.symlinks/plugins/tencent_im_sdk_plugin/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
- video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
SPEC REPOS:
trunk:
- DKImagePickerController
- DKPhotoGallery
- FMDB
- HydraAsync
- libwebp
- ReachabilitySwift
- SDWebImage
- SwiftyGif
- Toast
- TXIMSDK_Plus_iOS
EXTERNAL SOURCES:
camera:
:path: ".symlinks/plugins/camera/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
flutter_plugin_record:
:path: ".symlinks/plugins/flutter_plugin_record/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
image_gallery_saver:
:path: ".symlinks/plugins/image_gallery_saver/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
open_file:
:path: ".symlinks/plugins/open_file/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios"
permission_handler:
:path: ".symlinks/plugins/permission_handler/ios"
photo_manager:
:path: ".symlinks/plugins/photo_manager/ios"
shared_preferences_ios:
:path: ".symlinks/plugins/shared_preferences_ios/ios"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
tencent_im_sdk_plugin:
:path: ".symlinks/plugins/tencent_im_sdk_plugin/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/ios"
video_thumbnail:
:path: ".symlinks/plugins/video_thumbnail/ios"
wakelock:
:path: ".symlinks/plugins/wakelock/ios"
SPEC CHECKSUMS:
camera: 9993f92f2c793e87b65e35f3a23c70582afb05b1
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
DKImagePickerController: 72fd378f244cef3d27288e0aebf217a4467e4012
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
flutter_plugin_record: 562ded56f3a109d769e72c3ef52ef20d835493d4
fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
HydraAsync: 8d589bd725b0224f899afafc9a396327405f8063
image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0
photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
SDWebImage: 0905f1b7760fc8ac4198cae0036600d67478751e
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
tencent_im_sdk_plugin: c68993c62fd0198cd47132a055a06c9cef33b8e3
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
TXIMSDK_Plus_iOS: 2b0e9440eacdb49f385c90a23ad6558013f0cac6
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
COCOAPODS: 1.11.3

View File

@ -0,0 +1,549 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
56220B3E3A69391FE44D9F6D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 842FC629A02A0DB5EA275F83 /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3843BAFB77932A36394807CF /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
80DA560C61DA3C7E0AFF698F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
842FC629A02A0DB5EA275F83 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D663F44D0AAE34D99189AE58 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
56220B3E3A69391FE44D9F6D /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
53913FEF742A463D71A8D172 /* Frameworks */ = {
isa = PBXGroup;
children = (
842FC629A02A0DB5EA275F83 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
74FBAFC8A9A3960F7CA99189 /* Pods */ = {
isa = PBXGroup;
children = (
80DA560C61DA3C7E0AFF698F /* Pods-Runner.debug.xcconfig */,
D663F44D0AAE34D99189AE58 /* Pods-Runner.release.xcconfig */,
3843BAFB77932A36394807CF /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
74FBAFC8A9A3960F7CA99189 /* Pods */,
53913FEF742A463D71A8D172 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
919EAFB910364FD42152D9D1 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
0023742D62D58E49F4FD6732 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
0023742D62D58E49F4FD6732 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
919EAFB910364FD42152D9D1 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,13 @@
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Example</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"

View File

@ -0,0 +1,73 @@
// ignore_for_file: file_names
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:archive/archive.dart';
import 'package:archive/archive_io.dart';
/// userSig
/// Generate userSig for Tencent Cloud instant messaging test
///
class GenerateTestUserSig {
GenerateTestUserSig({required this.sdkappid, required this.key});
int sdkappid;
String key;
/// UserSig
/// Generate UserSig
///
String genSig({
required String identifier,
required int expire,
}) {
int currTime = _getCurrentTime();
String sig = '';
Map<String, dynamic> sigDoc = <String, dynamic>{};
sigDoc.addAll({
"TLS.ver": "2.0",
"TLS.identifier": identifier,
// ignore: unnecessary_this
"TLS.sdkappid": this.sdkappid,
"TLS.expire": expire,
"TLS.time": currTime,
});
sig = _hmacsha256(
identifier: identifier,
currTime: currTime,
expire: expire,
);
sigDoc['TLS.sig'] = sig;
String jsonStr = json.encode(sigDoc);
List<int>? compress = const ZLibEncoder().encode(utf8.encode(jsonStr));
return _escape(content: base64.encode(compress));
}
int _getCurrentTime() {
return (DateTime.now().millisecondsSinceEpoch / 1000).floor();
}
String _hmacsha256({
required String identifier,
required int currTime,
int expire = 30 * 24 * 60 * 60,
}) {
int sdkappid = this.sdkappid;
String contentToBeSigned =
"TLS.identifier:$identifier\nTLS.sdkappid:$sdkappid\nTLS.time:$currTime\nTLS.expire:$expire\n";
Hmac hmacSha256 = Hmac(sha256, utf8.encode(key));
Digest hmacSha256Digest =
hmacSha256.convert(utf8.encode(contentToBeSigned));
return base64.encode(hmacSha256Digest.bytes);
}
String _escape({
required String content,
}) {
return content
.replaceAll('+', '*')
.replaceAll('/', '-')
.replaceAll('=', '_');
}
}

View File

@ -0,0 +1,14 @@
// ignore_for_file: file_names
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class TIMUIKitAddFriendExample extends StatelessWidget {
const TIMUIKitAddFriendExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return TIMUIKitAddFriend(onTapAlreadyFriendsItem: (String userID) {
});
}
}

View File

@ -0,0 +1,16 @@
// ignore_for_file: file_names
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class TIMUIKitAddGroupExample extends StatelessWidget {
const TIMUIKitAddGroupExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return TIMUIKitAddGroup(
onTapExistGroup: (groupID, conversation) {
},
);
}
}

View File

@ -0,0 +1,13 @@
// ignore_for_file: file_names
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class TIMUIKitBlackListExample extends StatelessWidget {
const TIMUIKitBlackListExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TIMUIKitBlackList();
}
}

View File

@ -0,0 +1,90 @@
// ignore_for_file: file_names
import 'package:example/TIMUIKitGroupProfileExample.dart';
import 'package:example/TIMUIKitProfileExample.dart';
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/business_logic/view_models/tui_chat_global_model.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class TIMUIKitChatExample extends StatelessWidget {
final V2TimConversation? selectedConversation;
const TIMUIKitChatExample({Key? key, this.selectedConversation})
: super(key: key);
String? _getConversationID() {
if(selectedConversation != null){
return selectedConversation!.type == 1
? selectedConversation!.userID
: selectedConversation!.groupID;
}
return null;
}
@override
Widget build(BuildContext context) {
return TIMUIKitChat(
config: const TIMUIKitChatConfig(
// 使
isAllowClickAvatar: true,
isAllowLongPressMessage: true,
isShowReadingStatus: true,
isShowGroupReadingStatus: true,
notificationTitle: "",
isUseMessageReaction: true,
groupReadReceiptPermissionList: [
GroupReceiptAllowType.work,
GroupReceiptAllowType.meeting,
GroupReceiptAllowType.public
],
),
conversationID: _getConversationID() ?? "10040818",
// Please fill in here according to the actual cleaning
conversationShowName: selectedConversation?.showName ??
selectedConversation?.userID ??
selectedConversation?.groupID ??
"Test Chat",
// Please fill in here according to the actual cleaning
conversationType: ConvType.values[selectedConversation?.type ?? 1],
appBarConfig: AppBar(
actions: [
IconButton(
padding: const EdgeInsets.only(left: 8, right: 16),
onPressed: () async {
final conversationType = selectedConversation?.type ?? 1;
if (conversationType == 1) {
final String? userID = selectedConversation?.userID;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(title: Text(userID ?? "User Profile")),
body: TIMUIKitProfileExample(userID: userID)),
));
} else {
final String? groupID = selectedConversation?.groupID;
if (groupID != null) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(title: Text(groupID)),
body: TIMUIKitGroupProfileExample(
groupID: groupID,
)),
));
}
}
},
icon: Image.asset(
'images/more.png',
package: 'tim_ui_kit',
height: 34,
width: 34,
))
],
),
);
}
}

View File

@ -0,0 +1,13 @@
// ignore_for_file: file_names
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class TIMUIKitContactExample extends StatelessWidget {
const TIMUIKitContactExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TIMUIKitContact();
}
}

View File

@ -0,0 +1,24 @@
// ignore_for_file: file_names, avoid_print
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/ui/views/TIMUIKitConversation/tim_uikit_conversation.dart';
import 'TIMUIKitChatExample.dart';
class TIMUIKitConversationExample extends StatelessWidget {
const TIMUIKitConversationExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return TIMUIKitConversation(
onTapItem: (value) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TIMUIKitChatExample(
selectedConversation: value,
),
));
},
);
}
}

View File

@ -0,0 +1,13 @@
// ignore_for_file: file_names
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class TIMUIKitGroupExample extends StatelessWidget {
const TIMUIKitGroupExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TIMUIKitGroup();
}
}

View File

@ -0,0 +1,19 @@
// ignore_for_file: file_names
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class TIMUIKitGroupProfileExample extends StatelessWidget {
final String? groupID;
const TIMUIKitGroupProfileExample({Key? key, this.groupID})
: super(key: key);
@override
Widget build(BuildContext context) {
return TIMUIKitGroupProfile(
groupID: groupID ??
'@TGS#1X2AML5H6', // Please fill in here according to the actual cleaning
);
}
}

View File

@ -0,0 +1,13 @@
// ignore_for_file: file_names
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class TIMUIKitNewContactExample extends StatelessWidget {
const TIMUIKitNewContactExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const TIMUIKitNewContact();
}
}

View File

@ -0,0 +1,16 @@
// ignore_for_file: file_names
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class TIMUIKitProfileExample extends StatelessWidget {
final String? userID;
const TIMUIKitProfileExample({Key? key, this.userID}) : super(key: key);
@override
Widget build(BuildContext context) {
return TIMUIKitProfile(
userID: userID ?? "10040818", // Please fill in here according to the actual cleaning
);
}
}

View File

@ -0,0 +1,19 @@
// ignore_for_file: avoid_print, file_names, deprecated_member_use
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
class TIMUIKitSearchExample extends StatelessWidget {
const TIMUIKitSearchExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return TIMUIKitSearch(
onTapConversation: (conv, message) {
print(conv.toJson());
print(message!.toJson());
},
onEnterConversation: (V2TimConversation conversation, String keyword) {},
);
}
}

206
example/lib/main.dart Normal file
View File

@ -0,0 +1,206 @@
// ignore_for_file: avoid_print
import 'package:example/GenerateUserSig.dart';
import 'package:example/TIMUIKitChatExample.dart';
import 'package:example/TIMUIKitConversationExample.dart';
import 'package:example/TIMUIKitProfileExample.dart';
import 'package:flutter/material.dart';
import 'package:tim_ui_kit/tim_ui_kit.dart';
import 'package:tim_ui_kit/ui/widgets/toast.dart';
import 'TIMUIKitAddFriendExample.dart';
import 'TIMUIKitAddGroupExample.dart';
import 'TIMUIKitBlackListExample.dart';
import 'TIMUIKitContactExample.dart';
import 'TIMUIKitGroupExample.dart';
import 'TIMUIKitGroupProfileExample.dart';
import 'TIMUIKitNewContactExample.dart';
import 'TIMUIKitSearchExample.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tencent IM UIKit',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Tencent IM UIKit'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
initTIMUIKIT();
}
CoreServicesImpl timCoreInstance = TIMUIKitCore.getInstance();
int getSDKAPPID() {
return const int.fromEnvironment('SDK_APPID', defaultValue: 0);
}
String getUserID() {
return const String.fromEnvironment('LOGINUSERID', defaultValue: "");
}
String getSecret() {
return const String.fromEnvironment('SECRET', defaultValue: "");
}
initTIMUIKIT() async {
int sdkappid = getSDKAPPID();
String userid = getUserID();
String secret = getSecret();
String usersig = GenerateTestUserSig(sdkappid: sdkappid, key: secret)
.genSig(identifier: userid, expire: 24 * 7 * 60 * 60 * 1000);
if (sdkappid == 0 || userid == '' || secret == '' || usersig == '') {
Toast("The running parameters are abnormal, please check");
return;
}
await timCoreInstance.init(
sdkAppID: sdkappid,
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
listener: V2TimSDKListener(
onConnectFailed: (code, error) {},
onConnectSuccess: () {},
onConnecting: () {},
onKickedOffline: () {},
onSelfInfoUpdated: (V2TimUserFullInfo info) {},
onUserSigExpired: () {},
),
);
V2TimCallback res =
await timCoreInstance.login(userID: userid, userSig: usersig);
print(
"Log in to Tencent Cloud Instant Messaging IM Return${res.toJson()}");
}
getAPIWidget(String apiName) {
switch (apiName) {
case 'TIMUIKitConversation':
return const TIMUIKitConversationExample();
case 'TIMUIKitChat':
return const TIMUIKitChatExample();
case 'TIMUIKitProfile':
return const TIMUIKitProfileExample();
case 'TIMUIKitAddFriend':
return const TIMUIKitAddFriendExample();
case 'TIMUIKitAddGroup':
return const TIMUIKitAddGroupExample();
case 'TIMUIKitBlackList':
return const TIMUIKitBlackListExample();
case 'TIMUIKitContact':
return const TIMUIKitContactExample();
case 'TIMUIKitGroup':
return const TIMUIKitGroupExample();
case 'TIMUIKitGroupProfile':
return const TIMUIKitGroupProfileExample();
case 'TIMUIKitNewContact':
return const TIMUIKitNewContactExample();
case 'TIMUIKitSearch':
return const TIMUIKitSearchExample();
}
}
openExamplePage(String apiName) {
if (apiConfig.contains(apiName)) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Scaffold(
appBar: AppBar(
title: Text(apiName),
),
body: getAPIWidget(apiName),
),
),
);
} else {
Toast("no such ket");
}
}
List<String> apiConfig = [
"TIMUIKitConversation",
"TIMUIKitChat",
"TIMUIKitProfile",
"TIMUIKitAddFriend",
"TIMUIKitAddGroup",
"TIMUIKitBlackList",
"TIMUIKitContact",
"TIMUIKitGroup",
"TIMUIKitGroupProfile",
"TIMUIKitNewContact",
"TIMUIKitSearch"
];
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Container(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
children: apiConfig
.map(
(e) => Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
openExamplePage(e);
},
child: Text(e),
),
)
],
),
)
.toList(),
),
),
),
);
}
}

1291
example/pubspec.lock Normal file

File diff suppressed because it is too large Load Diff

93
example/pubspec.yaml Normal file
View File

@ -0,0 +1,93 @@
name: example
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.15.0 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
tim_ui_kit:
path: ../../tim_ui_kit
tencent_im_sdk_plugin_web: ^0.3.2
archive: ^3.3.0
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^1.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages

View File

@ -0,0 +1,31 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
// ignore_for_file: avoid_relative_lib_imports
import 'package:example/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}

BIN
example/web/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

105
example/web/index.html Normal file
View File

@ -0,0 +1,105 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<script src='./tim-upload-plugin.js'></script>
<script src="./tim-js-friendship.js"></script>
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>example</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing || reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
</html>

35
example/web/manifest.json Normal file
View File

@ -0,0 +1,35 @@
{
"name": "example",
"short_name": "example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

File diff suppressed because one or more lines are too long

1
example/web/tim-js.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More