diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f5ef3e0 --- /dev/null +++ b/CHANGELOG.md @@ -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. \ No newline at end of file diff --git a/LICENSE b/LICENSE index 261eeb9..6d8d58f 100644 --- a/LICENSE +++ b/LICENSE @@ -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, diff --git a/README.md b/README.md new file mode 100644 index 0000000..03534b3 --- /dev/null +++ b/README.md @@ -0,0 +1,800 @@ + +
+ +

+ + Tencent Chat Logo + +

+ +

Tencent Cloud IM Chat UIKIt

+ +

+ Globally interconnected In-App Chat, user profile and relationship chains and offline push. +

+ +

+More languages: + 简体中文-快速入门 + 简体中文-README +

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

+ TUIKit has Chat SDK, UI components and basic business logic inside. You can choose our pure Chat SDK tencent_im_sdk_plugin if you tend to build the UI yourself. +

+ + +## 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 `` and ``. + +```xml + + + + + + + + + +``` + +#### 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 `` and `` to import them. + +```html + + +``` + +![](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: + +### 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. + + + +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 + + +``` + +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: +- WhatsApp Group: +- QQ Group: 788910197, chat in Chinese + +Our Website: + +--- + +## 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, // 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 _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) {} // 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, // 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, // Determine the vertical sequence for those profile widgets. + builder: (BuildContext context, V2TimGroupInfo groupInfo, List 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: +- WhatsApp Group: +- QQ Group: 788910197, chat in Chinese + +Our Website: \ No newline at end of file diff --git a/README_ZH.md b/README_ZH.md new file mode 100644 index 0000000..370508a --- /dev/null +++ b/README_ZH.md @@ -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, // 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/) + + + +### 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})**:发送消息。根据 ConvType,receiverID/groupID 二选一传入。 +- **sendForwardMessage({required List conversationList,})**:逐条转发 +- **sendMergerMessage({ required List conversationList, required String title, required List 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 | 消息列表,渲染数据源 | 必填 | +| tongueItemBuilder | TongueItemBuilder | 小舌头(回到底部)自定义构造器 | 可选 | +| groupAtInfoList | List | 艾特信息 | 可选 | +| 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 createState() => _ChatV2State(); +} + +class _ChatV2State extends State { + 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) diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/analysis_options.yaml @@ -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 diff --git a/doc/DETAIL.md b/doc/DETAIL.md new file mode 100644 index 0000000..768b793 --- /dev/null +++ b/doc/DETAIL.md @@ -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 // 是否接入配置 TPNS,token 是否是从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 _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: () { + /// 自定义好友申请项构造器 + } +) +``` + diff --git a/doc/FAST_INTEGRATED.md b/doc/FAST_INTEGRATED.md new file mode 100644 index 0000000..36592cc --- /dev/null +++ b/doc/FAST_INTEGRATED.md @@ -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 { + 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 { + /// 获取 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: [ + 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: 发送语音、图片、文件等消息闪退? +请查看是否打开了`相机、麦克风、相册`等权限。 \ No newline at end of file diff --git a/doc/I18N.md b/doc/I18N.md new file mode 100644 index 0000000..a4098c3 --- /dev/null +++ b/doc/I18N.md @@ -0,0 +1,2 @@ +【企微文档】TIM Flutter国际化方案 +https://doc.weixin.qq.com/doc/w3_AWcADQY0AA8u9RhcnhqRSSW6RZc4w?scode=AJEAIQdfAAoaC0QLyWAWcADQY0AA8 \ No newline at end of file diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..f69be76 --- /dev/null +++ b/doc/README.md @@ -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) diff --git a/doc/get_start.md b/doc/get_start.md new file mode 100644 index 0000000..0504401 --- /dev/null +++ b/doc/get_start.md @@ -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 运行:(可选步骤) + + +::: 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) +::: + + +#### 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 createMessage = + await TencentImSDKPlugin.v2TIMManager + .getMessageManager() + .createTextMessage(text: "The text to create"); + +String id = createMessage.data!.id!; // The message creation ID + +V2TimValueCallback 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 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 list){ + _onConversationListChanged(list); + }, + onNewConversation:(List list){ + _onConversationListChanged(list); + }, +``` + +2. 处理回调事件,将最新的会话列表展示在界面上。 + +```dart +import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart'; + +List _conversationList = []; + +_onConversationListChanged(List 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> res = await TencentImSDKPlugin + .v2TIMManager + .getMessageManager() + .getGroupHistoryMessageList( + groupID: "groupID", + count: 20, + lastMsgID: "", + ); + + List 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 咨询。 diff --git a/doc/theme.md b/doc/theme.md new file mode 100644 index 0000000..086547c --- /dev/null +++ b/doc/theme.md @@ -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()` 获取当前 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 自动识别亮/暗主题并更改字色。 \ No newline at end of file diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..0fa6b67 --- /dev/null +++ b/example/.gitignore @@ -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 diff --git a/example/.metadata b/example/.metadata new file mode 100644 index 0000000..5a02328 --- /dev/null +++ b/example/.metadata @@ -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 diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..a135626 --- /dev/null +++ b/example/README.md @@ -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. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/example/analysis_options.yaml @@ -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 diff --git a/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/example/android/.gitignore @@ -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 diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle new file mode 100644 index 0000000..5fe3c92 --- /dev/null +++ b/example/android/app/build.gradle @@ -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" +} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..c208884 --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3f41384 --- /dev/null +++ b/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt new file mode 100644 index 0000000..e793a00 --- /dev/null +++ b/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..3db14bb --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..d460d1e --- /dev/null +++ b/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..c208884 --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle new file mode 100644 index 0000000..4256f91 --- /dev/null +++ b/example/android/build.gradle @@ -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 +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..bc6a58a --- /dev/null +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/example/android/settings.gradle b/example/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/example/android/settings.gradle @@ -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" diff --git a/example/ios/.gitignore b/example/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/example/ios/.gitignore @@ -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 diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..8d4492f --- /dev/null +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Podfile b/example/ios/Podfile new file mode 100644 index 0000000..1e8c3c9 --- /dev/null +++ b/example/ios/Podfile @@ -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 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 0000000..68bacaa --- /dev/null +++ b/example/ios/Podfile.lock @@ -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 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5ad7353 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -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 = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 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 = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 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 = ""; }; + 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 = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 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 = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 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 = ""; }; +/* 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 = ""; + }; + 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 = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 74FBAFC8A9A3960F7CA99189 /* Pods */, + 53913FEF742A463D71A8D172 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 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 = ""; + }; +/* 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 = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* 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 */; +} diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..c87d15a --- /dev/null +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/example/ios/Runner/AppDelegate.swift @@ -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) + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -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" + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -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" + } +} diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -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. \ No newline at end of file diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist new file mode 100644 index 0000000..907f329 --- /dev/null +++ b/example/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + + diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/example/lib/GenerateUserSig.dart b/example/lib/GenerateUserSig.dart new file mode 100644 index 0000000..30e09ec --- /dev/null +++ b/example/lib/GenerateUserSig.dart @@ -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 sigDoc = {}; + 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? 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('=', '_'); + } +} diff --git a/example/lib/TIMUIKitAddFriendExample.dart b/example/lib/TIMUIKitAddFriendExample.dart new file mode 100644 index 0000000..bf9386a --- /dev/null +++ b/example/lib/TIMUIKitAddFriendExample.dart @@ -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) { + }); + } +} diff --git a/example/lib/TIMUIKitAddGroupExample.dart b/example/lib/TIMUIKitAddGroupExample.dart new file mode 100644 index 0000000..3795dee --- /dev/null +++ b/example/lib/TIMUIKitAddGroupExample.dart @@ -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) { + }, + ); + } +} diff --git a/example/lib/TIMUIKitBlackListExample.dart b/example/lib/TIMUIKitBlackListExample.dart new file mode 100644 index 0000000..14b8338 --- /dev/null +++ b/example/lib/TIMUIKitBlackListExample.dart @@ -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(); + } +} diff --git a/example/lib/TIMUIKitChatExample.dart b/example/lib/TIMUIKitChatExample.dart new file mode 100644 index 0000000..a152be3 --- /dev/null +++ b/example/lib/TIMUIKitChatExample.dart @@ -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, + )) + ], + ), + ); + } +} diff --git a/example/lib/TIMUIKitContactExample.dart b/example/lib/TIMUIKitContactExample.dart new file mode 100644 index 0000000..f72c6dd --- /dev/null +++ b/example/lib/TIMUIKitContactExample.dart @@ -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(); + } +} diff --git a/example/lib/TIMUIKitConversationExample.dart b/example/lib/TIMUIKitConversationExample.dart new file mode 100644 index 0000000..7fe030c --- /dev/null +++ b/example/lib/TIMUIKitConversationExample.dart @@ -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, + ), + )); + }, + ); + } +} diff --git a/example/lib/TIMUIKitGroupExample.dart b/example/lib/TIMUIKitGroupExample.dart new file mode 100644 index 0000000..7976cc8 --- /dev/null +++ b/example/lib/TIMUIKitGroupExample.dart @@ -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(); + } +} diff --git a/example/lib/TIMUIKitGroupProfileExample.dart b/example/lib/TIMUIKitGroupProfileExample.dart new file mode 100644 index 0000000..778d55e --- /dev/null +++ b/example/lib/TIMUIKitGroupProfileExample.dart @@ -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 + ); + } +} diff --git a/example/lib/TIMUIKitNewContactExample.dart b/example/lib/TIMUIKitNewContactExample.dart new file mode 100644 index 0000000..c827a50 --- /dev/null +++ b/example/lib/TIMUIKitNewContactExample.dart @@ -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(); + } +} diff --git a/example/lib/TIMUIKitProfileExample.dart b/example/lib/TIMUIKitProfileExample.dart new file mode 100644 index 0000000..b667679 --- /dev/null +++ b/example/lib/TIMUIKitProfileExample.dart @@ -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 + ); + } +} diff --git a/example/lib/TIMUIKitSearchExample.dart b/example/lib/TIMUIKitSearchExample.dart new file mode 100644 index 0000000..abe48cd --- /dev/null +++ b/example/lib/TIMUIKitSearchExample.dart @@ -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) {}, + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 0000000..93f4643 --- /dev/null +++ b/example/lib/main.dart @@ -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 createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + @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 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(), + ), + ), + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 0000000..64d6bb2 --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,1291 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "49.0.0" + adaptive_action_sheet: + dependency: transitive + description: + name: adaptive_action_sheet + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + archive: + dependency: "direct main" + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.1" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.9.0" + azlistview: + dependency: transitive + description: + name: azlistview + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + cached_network_image: + dependency: transitive + description: + name: cached_network_image + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + camera: + dependency: transitive + description: + name: camera + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.0+3" + camera_android: + dependency: transitive + description: + name: camera_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.0+3" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.8+6" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.2" + camera_web: + dependency: transitive + description: + name: camera_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0+1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + chewie: + dependency: transitive + description: + name: chewie + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.5" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + cross_file: + dependency: transitive + description: + name: cross_file + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.3+2" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.2" + csv: + dependency: transitive + description: + name: csv + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.1" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + dart_style: + dependency: transitive + description: + name: dart_style + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.4" + dio: + dependency: transitive + description: + name: dio + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.6" + disk_space: + dependency: transitive + description: + name: disk_space + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.1" + dotted_border: + dependency: transitive + description: + name: dotted_border + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0+2" + extended_image: + dependency: transitive + description: + name: extended_image + url: "https://pub.dartlang.org" + source: hosted + version: "6.2.1" + extended_image_library: + dependency: transitive + description: + name: extended_image_library + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + fast_i18n: + dependency: transitive + description: + name: fast_i18n + url: "https://pub.dartlang.org" + source: hosted + version: "5.12.6" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.4" + file_picker: + dependency: transitive + description: + name: file_picker + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.1" + file_utils: + dependency: transitive + description: + name: file_utils + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_blurhash: + dependency: transitive + description: + name: flutter_blurhash + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.0" + flutter_easyrefresh: + dependency: transitive + description: + name: flutter_easyrefresh + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.2" + flutter_image_compress: + dependency: transitive + description: + name: flutter_image_compress + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.3" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_markdown: + dependency: transitive + description: + name: flutter_markdown + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.12" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + flutter_plugin_record_plus: + dependency: transitive + description: + name: flutter_plugin_record_plus + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.11" + flutter_screenutil: + dependency: transitive + description: + name: flutter_screenutil + url: "https://pub.dartlang.org" + source: hosted + version: "5.5.4" + flutter_slidable_for_tencent_im: + dependency: transitive + description: + name: flutter_slidable_for_tencent_im + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.0" + flutter_spinkit: + dependency: transitive + description: + name: flutter_spinkit + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + flutter_svg: + dependency: transitive + description: + name: flutter_svg + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.5" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + url: "https://pub.dartlang.org" + source: hosted + version: "8.0.9" + get_it: + dependency: transitive + description: + name: get_it + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.0" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + globbing: + dependency: transitive + description: + name: globbing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.0" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.5" + http_client_helper: + dependency: transitive + description: + name: http_client_helper + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + i18n: + dependency: transitive + description: + name: i18n + url: "https://pub.dartlang.org" + source: hosted + version: "3.4.1" + image_gallery_saver: + dependency: transitive + description: + name: image_gallery_saver + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.1" + image_picker: + dependency: transitive + description: + name: image_picker + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.6" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.5+3" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.10" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.6+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.6.2" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.7.0" + link_preview_generator: + dependency: transitive + description: + name: link_preview_generator + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + loading_animation_widget: + dependency: transitive + description: + name: loading_animation_widget + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0+3" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + lpinyin: + dependency: transitive + description: + name: lpinyin + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + markdown: + dependency: transitive + description: + name: markdown + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.12" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + mime_type: + dependency: transitive + description: + name: mime_type + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + octo_image: + dependency: transitive + description: + name: octo_image + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + open_file: + dependency: transitive + description: + name: open_file + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + package_info_plus: + dependency: transitive + description: + name: package_info_plus + url: "https://pub.dartlang.org" + source: hosted + version: "1.4.2" + package_info_plus_linux: + dependency: transitive + description: + name: package_info_plus_linux + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + package_info_plus_macos: + dependency: transitive + description: + name: package_info_plus_macos + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + package_info_plus_web: + dependency: transitive + description: + name: package_info_plus_web + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.6" + package_info_plus_windows: + dependency: transitive + description: + name: package_info_plus_windows + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + path_drawing: + dependency: transitive + description: + name: path_drawing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.20" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.11.1" + permission_handler: + dependency: transitive + description: + name: permission_handler + url: "https://pub.dartlang.org" + source: hosted + version: "10.1.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + url: "https://pub.dartlang.org" + source: hosted + version: "10.1.0" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + url: "https://pub.dartlang.org" + source: hosted + version: "9.0.6" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "3.9.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.0" + photo_manager: + dependency: transitive + description: + name: photo_manager + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.1" + photo_view: + dependency: transitive + description: + name: photo_view + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + provider: + dependency: transitive + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.3" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + quick_log: + dependency: transitive + description: + name: quick_log + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.1" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.27.5" + scroll_to_index: + dependency: transitive + description: + name: scroll_to_index + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + scrollable_positioned_list: + dependency: transitive + description: + name: scrollable_positioned_list + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.3" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.15" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + shared_preferences_ios: + dependency: transitive + description: + name: shared_preferences_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + shared_preferences_macos: + dependency: transitive + description: + name: shared_preferences_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.0" + sqflite: + dependency: transitive + description: + name: sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3+1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + super_tooltip: + dependency: transitive + description: + name: super_tooltip + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0+3" + system_info2: + dependency: transitive + description: + name: system_info2 + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + tencent_im_base: + dependency: transitive + description: + name: tencent_im_base + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.52" + tencent_im_sdk_plugin: + dependency: transitive + description: + name: tencent_im_sdk_plugin + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.9" + tencent_im_sdk_plugin_platform_interface: + dependency: transitive + description: + name: tencent_im_sdk_plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.5" + tencent_im_sdk_plugin_web: + dependency: "direct main" + description: + name: tencent_im_sdk_plugin_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2" + tencent_wechat_camera_picker: + dependency: transitive + description: + name: tencent_wechat_camera_picker + url: "https://pub.dartlang.org" + source: hosted + version: "3.6.2+5" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.12" + tim_ui_kit: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.1.8" + transparent_image: + dependency: transitive + description: + name: transparent_image + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + tuple: + dependency: transitive + description: + name: tuple + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + universal_html: + dependency: transitive + description: + name: universal_html + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + universal_io: + dependency: transitive + description: + name: universal_io + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + url_launcher: + dependency: transitive + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.6" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.19" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.17" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.6" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + video_player: + dependency: transitive + description: + name: video_player + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.7" + video_player_android: + dependency: transitive + description: + name: video_player_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.9" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.7" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.4" + video_player_web: + dependency: transitive + description: + name: video_player_web + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.12" + video_thumbnail: + dependency: transitive + description: + name: video_thumbnail + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.3" + wakelock: + dependency: transitive + description: + name: wakelock + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.2" + wakelock_macos: + dependency: transitive + description: + name: wakelock_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0" + wakelock_platform_interface: + dependency: transitive + description: + name: wakelock_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + wakelock_web: + dependency: transitive + description: + name: wakelock_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0" + wakelock_windows: + dependency: transitive + description: + name: wakelock_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + wechat_assets_picker: + dependency: transitive + description: + name: wechat_assets_picker + url: "https://pub.dartlang.org" + source: hosted + version: "7.3.4" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.6.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+2" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" +sdks: + dart: ">=2.17.0 <3.0.0" + flutter: ">=3.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..e895899 --- /dev/null +++ b/example/pubspec.yaml @@ -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 diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart new file mode 100644 index 0000000..52f31f9 --- /dev/null +++ b/example/test/widget_test.dart @@ -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); + }); +} diff --git a/example/web/favicon.png b/example/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/example/web/favicon.png differ diff --git a/example/web/icons/Icon-192.png b/example/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/example/web/icons/Icon-192.png differ diff --git a/example/web/icons/Icon-512.png b/example/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/example/web/icons/Icon-512.png differ diff --git a/example/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/example/web/icons/Icon-maskable-192.png differ diff --git a/example/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/example/web/icons/Icon-maskable-512.png differ diff --git a/example/web/index.html b/example/web/index.html new file mode 100644 index 0000000..d139199 --- /dev/null +++ b/example/web/index.html @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + example + + + + + + + diff --git a/example/web/manifest.json b/example/web/manifest.json new file mode 100644 index 0000000..096edf8 --- /dev/null +++ b/example/web/manifest.json @@ -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" + } + ] +} diff --git a/example/web/tim-js-friendship.js b/example/web/tim-js-friendship.js new file mode 100644 index 0000000..feedca9 --- /dev/null +++ b/example/web/tim-js-friendship.js @@ -0,0 +1 @@ +'use strict';!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).TIM=t()}(this,(function(){function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,o)}return n}function t(t){for(var n=1;n=0||(r[n]=e[n]);return r}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(r[n]=e[n])}return r}function _(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function h(e,t){if(t&&("object"==typeof t||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return _(e)}function f(e){var t=l();return function(){var n,o=c(e);if(t){var r=c(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return h(this,n)}}function m(e,t){return v(e)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null==n)return;var o,r,a=[],s=!0,i=!1;try{for(n=n.call(e);!(s=(o=n.next()).done)&&(a.push(o.value),!t||a.length!==t);s=!0);}catch(c){i=!0,r=c}finally{try{s||null==n.return||n.return()}finally{if(i)throw r}}return a}(e,t)||I(e,t)||T()}function M(e){return function(e){if(Array.isArray(e))return E(e)}(e)||y(e)||I(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function v(e){if(Array.isArray(e))return e}function y(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}function I(e,t){if(e){if("string"==typeof e)return E(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?E(e,t):void 0}}function E(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[o++]}},e:function(e){throw e},f:r}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,i=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){i=!0,a=e},f:function(){try{s||null==n.return||n.return()}finally{if(i)throw a}}}}var A="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function D(e,t){return e(t={exports:{}},t.exports),t.exports}var C,N,R=function(e){return e&&e.Math==Math&&e},O=R("object"==typeof globalThis&&globalThis)||R("object"==typeof window&&window)||R("object"==typeof self&&self)||R("object"==typeof A&&A)||function(){return this}()||Function("return this")(),L=function(e){try{return!!e()}catch(t){return!0}},k=!L((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),G=!L((function(){var e=function(){}.bind();return"function"!=typeof e||e.hasOwnProperty("prototype")})),P=Function.prototype.call,b=G?P.bind(P):function(){return P.apply(P,arguments)},U={}.propertyIsEnumerable,w=Object.getOwnPropertyDescriptor,F={f:w&&!U.call({1:2},1)?function(e){var t=w(this,e);return!!t&&t.enumerable}:U},q=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}},x=Function.prototype,V=x.bind,B=x.call,H=G&&V.bind(B,B),K=G?function(e){return e&&H(e)}:function(e){return e&&function(){return B.apply(e,arguments)}},j=K({}.toString),W=K("".slice),Y=function(e){return W(j(e),8,-1)},$=O.Object,z=K("".split),J=L((function(){return!$("z").propertyIsEnumerable(0)}))?function(e){return"String"==Y(e)?z(e,""):$(e)}:$,X=O.TypeError,Q=function(e){if(null==e)throw X("Can't call method on "+e);return e},Z=function(e){return J(Q(e))},ee=function(e){return"function"==typeof e},te=function(e){return"object"==typeof e?null!==e:ee(e)},ne=function(e){return ee(e)?e:void 0},oe=function(e,t){return arguments.length<2?ne(O[e]):O[e]&&O[e][t]},re=K({}.isPrototypeOf),ae=oe("navigator","userAgent")||"",se=O.process,ie=O.Deno,ce=se&&se.versions||ie&&ie.version,ue=ce&&ce.v8;ue&&(N=(C=ue.split("."))[0]>0&&C[0]<4?1:+(C[0]+C[1])),!N&&ae&&(!(C=ae.match(/Edge\/(\d+)/))||C[1]>=74)&&(C=ae.match(/Chrome\/(\d+)/))&&(N=+C[1]);var le=N,de=!!Object.getOwnPropertySymbols&&!L((function(){var e=Symbol();return!String(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&le&&le<41})),pe=de&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,ge=O.Object,_e=pe?function(e){return"symbol"==typeof e}:function(e){var t=oe("Symbol");return ee(t)&&re(t.prototype,ge(e))},he=O.String,fe=function(e){try{return he(e)}catch(t){return"Object"}},me=O.TypeError,Me=function(e){if(ee(e))return e;throw me(fe(e)+" is not a function")},ve=function(e,t){var n=e[t];return null==n?void 0:Me(n)},ye=O.TypeError,Ie=Object.defineProperty,Ee=function(e,t){try{Ie(O,e,{value:t,configurable:!0,writable:!0})}catch(n){O[e]=t}return t},Te=O["__core-js_shared__"]||Ee("__core-js_shared__",{}),Se=D((function(e){(e.exports=function(e,t){return Te[e]||(Te[e]=void 0!==t?t:{})})("versions",[]).push({version:"3.21.0",mode:"global",copyright:"© 2014-2022 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.21.0/LICENSE",source:"https://github.com/zloirock/core-js"})})),Ae=O.Object,De=function(e){return Ae(Q(e))},Ce=K({}.hasOwnProperty),Ne=Object.hasOwn||function(e,t){return Ce(De(e),t)},Re=0,Oe=Math.random(),Le=K(1..toString),ke=function(e){return"Symbol("+(void 0===e?"":e)+")_"+Le(++Re+Oe,36)},Ge=Se("wks"),Pe=O.Symbol,be=Pe&&Pe.for,Ue=pe?Pe:Pe&&Pe.withoutSetter||ke,we=function(e){if(!Ne(Ge,e)||!de&&"string"!=typeof Ge[e]){var t="Symbol."+e;de&&Ne(Pe,e)?Ge[e]=Pe[e]:Ge[e]=pe&&be?be(t):Ue(t)}return Ge[e]},Fe=O.TypeError,qe=we("toPrimitive"),xe=function(e,t){if(!te(e)||_e(e))return e;var n,o=ve(e,qe);if(o){if(void 0===t&&(t="default"),n=b(o,e,t),!te(n)||_e(n))return n;throw Fe("Can't convert object to primitive value")}return void 0===t&&(t="number"),function(e,t){var n,o;if("string"===t&&ee(n=e.toString)&&!te(o=b(n,e)))return o;if(ee(n=e.valueOf)&&!te(o=b(n,e)))return o;if("string"!==t&&ee(n=e.toString)&&!te(o=b(n,e)))return o;throw ye("Can't convert object to primitive value")}(e,t)},Ve=function(e){var t=xe(e,"string");return _e(t)?t:t+""},Be=O.document,He=te(Be)&&te(Be.createElement),Ke=function(e){return He?Be.createElement(e):{}},je=!k&&!L((function(){return 7!=Object.defineProperty(Ke("div"),"a",{get:function(){return 7}}).a})),We=Object.getOwnPropertyDescriptor,Ye={f:k?We:function(e,t){if(e=Z(e),t=Ve(t),je)try{return We(e,t)}catch(n){}if(Ne(e,t))return q(!b(F.f,e,t),e[t])}},$e=k&&L((function(){return 42!=Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype})),ze=O.String,Je=O.TypeError,Xe=function(e){if(te(e))return e;throw Je(ze(e)+" is not an object")},Qe=O.TypeError,Ze=Object.defineProperty,et=Object.getOwnPropertyDescriptor,nt={f:k?$e?function(e,t,n){if(Xe(e),t=Ve(t),Xe(n),"function"==typeof e&&"prototype"===t&&"value"in n&&"writable"in n&&!n.writable){var o=et(e,t);o&&o.writable&&(e[t]=n.value,n={configurable:"configurable"in n?n.configurable:o.configurable,enumerable:"enumerable"in n?n.enumerable:o.enumerable,writable:!1})}return Ze(e,t,n)}:Ze:function(e,t,n){if(Xe(e),t=Ve(t),Xe(n),je)try{return Ze(e,t,n)}catch(o){}if("get"in n||"set"in n)throw Qe("Accessors not supported");return"value"in n&&(e[t]=n.value),e}},ot=k?function(e,t,n){return nt.f(e,t,q(1,n))}:function(e,t,n){return e[t]=n,e},rt=K(Function.toString);ee(Te.inspectSource)||(Te.inspectSource=function(e){return rt(e)});var at,st,it,ct=Te.inspectSource,ut=O.WeakMap,lt=ee(ut)&&/native code/.test(ct(ut)),dt=Se("keys"),pt=function(e){return dt[e]||(dt[e]=ke(e))},gt={},_t=O.TypeError,ht=O.WeakMap;if(lt||Te.state){var ft=Te.state||(Te.state=new ht),mt=K(ft.get),Mt=K(ft.has),vt=K(ft.set);at=function(e,t){if(Mt(ft,e))throw new _t("Object already initialized");return t.facade=e,vt(ft,e,t),t},st=function(e){return mt(ft,e)||{}},it=function(e){return Mt(ft,e)}}else{var yt=pt("state");gt[yt]=!0,at=function(e,t){if(Ne(e,yt))throw new _t("Object already initialized");return t.facade=e,ot(e,yt,t),t},st=function(e){return Ne(e,yt)?e[yt]:{}},it=function(e){return Ne(e,yt)}}var It={set:at,get:st,has:it,enforce:function(e){return it(e)?st(e):at(e,{})},getterFor:function(e){return function(t){var n;if(!te(t)||(n=st(t)).type!==e)throw _t("Incompatible receiver, "+e+" required");return n}}},Et=Function.prototype,Tt=k&&Object.getOwnPropertyDescriptor,St=Ne(Et,"name"),At={EXISTS:St,PROPER:St&&"something"===function(){}.name,CONFIGURABLE:St&&(!k||k&&Tt(Et,"name").configurable)},Dt=D((function(e){var t=At.CONFIGURABLE,n=It.get,o=It.enforce,r=String(String).split("String");(e.exports=function(e,n,a,s){var i,c=!!s&&!!s.unsafe,u=!!s&&!!s.enumerable,l=!!s&&!!s.noTargetGet,d=s&&void 0!==s.name?s.name:n;ee(a)&&("Symbol("===String(d).slice(0,7)&&(d="["+String(d).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),(!Ne(a,"name")||t&&a.name!==d)&&ot(a,"name",d),(i=o(a)).source||(i.source=r.join("string"==typeof d?d:""))),e!==O?(c?!l&&e[n]&&(u=!0):delete e[n],u?e[n]=a:ot(e,n,a)):u?e[n]=a:Ee(n,a)})(Function.prototype,"toString",(function(){return ee(this)&&n(this).source||ct(this)}))})),Ct=Math.ceil,Nt=Math.floor,Rt=function(e){var t=+e;return t!=t||0===t?0:(t>0?Nt:Ct)(t)},Ot=Math.max,Lt=Math.min,kt=function(e,t){var n=Rt(e);return n<0?Ot(n+t,0):Lt(n,t)},Gt=Math.min,Pt=function(e){return e>0?Gt(Rt(e),9007199254740991):0},bt=function(e){return Pt(e.length)},Ut=function(e){return function(t,n,o){var r,a=Z(t),s=bt(a),i=kt(o,s);if(e&&n!=n){for(;s>i;)if((r=a[i++])!=r)return!0}else for(;s>i;i++)if((e||i in a)&&a[i]===n)return e||i||0;return!e&&-1}},wt={includes:Ut(!0),indexOf:Ut(!1)},Ft=wt.indexOf,qt=K([].push),xt=function(e,t){var n,o=Z(e),r=0,a=[];for(n in o)!Ne(gt,n)&&Ne(o,n)&&qt(a,n);for(;t.length>r;)Ne(o,n=t[r++])&&(~Ft(a,n)||qt(a,n));return a},Vt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Bt=Vt.concat("length","prototype"),Ht={f:Object.getOwnPropertyNames||function(e){return xt(e,Bt)}},Kt={f:Object.getOwnPropertySymbols},jt=K([].concat),Wt=oe("Reflect","ownKeys")||function(e){var t=Ht.f(Xe(e)),n=Kt.f;return n?jt(t,n(e)):t},Yt=function(e,t,n){for(var o=Wt(t),r=nt.f,a=Ye.f,s=0;s>>0||(Sn(Tn,n)?16:10))}:yn;nn({global:!0,forced:parseInt!=An},{parseInt:An});var Dn,Cn=Object.keys||function(e){return xt(e,Vt)},Nn={f:k&&!$e?Object.defineProperties:function(e,t){Xe(e);for(var n,o=Z(t),r=Cn(t),a=r.length,s=0;a>s;)nt.f(e,n=r[s++],o[n]);return e}},Rn=oe("document","documentElement"),On=pt("IE_PROTO"),Ln=function(){},kn=function(e){return"