init
|
|
@ -0,0 +1,716 @@
|
|||
# 5.0.0
|
||||
* Migrate to Flutter 3.29.0.
|
||||
|
||||
# 4.0.8
|
||||
* Use the OfflinePushInfo constructor, not the fromJson function.
|
||||
|
||||
# 4.0.7
|
||||
* Fixed the issue that modifying your own adding friend permissions does not take effect.
|
||||
* Fixed the setGroupInfo exception problem.
|
||||
|
||||
# 4.0.6
|
||||
* Solve the updateSelfInfo exception problem.
|
||||
* The success of calling the initSDK interface is determined by the code of the return value. Avoid inaccurate judgment when calling the interface multiple times.
|
||||
|
||||
# 4.0.5
|
||||
* Upgrade tencent_cloud_chat_sdk to the minimum version 8.5.6864+6.
|
||||
* Changed the SDK interface call from getConversationListByConversaionIds to getConversationListByConversationIds.
|
||||
|
||||
# 4.0.4
|
||||
* Remove the import of tencent_im_base plugin.
|
||||
* Upgrade tencent_cloud_chat_sdk to the minimum version 8.5.6864+4.
|
||||
* Fix compilation issues in tim_uikit_group.dart, tui_group_listener_model.dart, tui_conversation_view_model.dart.
|
||||
|
||||
# 4.0.3
|
||||
* Fix compilation issues on Flutter 3.27.1
|
||||
|
||||
# 4.0.2
|
||||
* Optimize the display of group notification page.
|
||||
* Optimize the display of read receipt page.
|
||||
* TIMUIKitChatController sendMessage interface supports isExcludedFromContentModeration parameter.
|
||||
* Fixed abnormal playback and recording of videos on Huawei P30.
|
||||
* Optimize the display of tips messages when they are too long.
|
||||
|
||||
# 4.0.1
|
||||
* Upgraded the plugin tim_ui_kit_sticker_plugin to 4.0.1.
|
||||
* Add the 'useTencentCloudChatStickerPackageOldKeys' parameter in StickerPanelConfig to control whether the emoticon is compatible with version 3.x.
|
||||
|
||||
# 4.0.0
|
||||
## Breaking changes
|
||||
* Upgraded the plugin tim_ui_kit_sticker_plugin to 4.0.0.
|
||||
* Delete the isUseDefaultEmoji parameter in TIMUIKitChatConfig.
|
||||
* Delete the isUseDefaultEmoji parameter in each widget.
|
||||
|
||||
## Bug Fixes
|
||||
* Solve the problem that showReplyMessage and showForwardMessage in ToolTipsConfig do not take effect after being set to false.
|
||||
|
||||
# 3.1.0+2
|
||||
* Replace the flutter_slidable library with flutter_slidable_plus_plus to solve the compatibility issue of flutter 3.27.0 version.
|
||||
|
||||
# 3.1.0+1
|
||||
* Upgrade the third-party library version to adapt to Android AGP 8.0.
|
||||
|
||||
# 3.1.0
|
||||
## Bug Fixes
|
||||
* The interface for deleting messages is changed to the interface for deleting cloud messages.
|
||||
* C2C messages support read receipts
|
||||
* Fix and optimize some issues
|
||||
|
||||
# 3.0.0
|
||||
## Breaking Changes
|
||||
* Migrated to Flutter 3.24.0
|
||||
## Bug Fixes
|
||||
* Fix and optimize some issues
|
||||
|
||||
# 2.7.2
|
||||
* Fix the issue where failed messages cannot be resent.
|
||||
* Fix the issue where image messages that failed to send are not loaded using the local path.
|
||||
* Fix the issue where the screen turns white after dissolving or leaving a group.
|
||||
* Optimize the process of sending messages.
|
||||
* Optimize the alignment of buttons in the long-press message menu.
|
||||
* Limit the version range of the third-party library extended_image.
|
||||
|
||||
# 2.7.1
|
||||
* Fixed the 'keepAspectRatio' parameter error.
|
||||
|
||||
# 2.7.0
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
* Upgraded Low-Level Native Chat SDK to 8.0.
|
||||
|
||||
# 2.6.0
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
* Migrated to Flutter 3.22. Support for Flutter 3.19 and earlier versions has been discontinued.
|
||||
|
||||
|
||||
|
||||
# 2.5.1
|
||||
|
||||
## Improvements
|
||||
|
||||
* Improved memory usage, enhancing performance.
|
||||
* Improved the logger storage.
|
||||
|
||||
# 2.5.0
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
* Migrated to Flutter 3.19. Support for Flutter 3.16 and earlier versions has been discontinued.
|
||||
|
||||
## Notes
|
||||
|
||||
* Starting from Flutter 3.19, it is recommended to apply Flutter's Gradle plugins using Gradle's declarative plugins {} block (also known as the Plugin DSL) ([see details](https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply)).
|
||||
* In line with this, our sample app on the GitHub repo has also been migrated to this new approach. If you'd like to migrate to this new approach, please refer to our [sample app repo](https://github.com/TencentCloud/chat-demo-flutter).
|
||||
|
||||
# 2.4.3
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an keyboard issue on Web.
|
||||
|
||||
# 2.4.2
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an UI issue on Material3 mode.
|
||||
|
||||
# 2.4.1
|
||||
|
||||
## Improvements
|
||||
|
||||
* Enhanced stability for message reaction.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed some bugs.
|
||||
|
||||
# 2.4.0
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
* Migrated to Flutter 3.16. Support for Flutter 3.13 and earlier versions has been discontinued.
|
||||
* Upgraded the minimum supported Android Gradle Plugin to 7.3 to meet Flutter requirements.
|
||||
|
||||
# 2.3.3
|
||||
|
||||
## New Features
|
||||
|
||||
* Added a new lifecycle hook, `messageListShouldMount`.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue on time tag creator.
|
||||
|
||||
# 2.3.2
|
||||
|
||||
## Improvements
|
||||
|
||||
* Enhanced message list performance.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue that prevented the group member addition/removal modal from closing.
|
||||
* Addressed several other bugs.
|
||||
|
||||
# 2.3.1
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Resolved an issue that prevented the clearing of history messages after deleting a conversation.
|
||||
* Fixed an issue that prevented opening files sent by the user themselves on Android.
|
||||
|
||||
# 2.3.0
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
* Upgraded and migrated to support Flutter 3.13. Support for Flutter 3.10 and earlier versions has been discontinued.
|
||||
|
||||
## Recommendations
|
||||
|
||||
* Customers who do not wish to upgrade to Flutter 3.13 are advised to continue using version 2.2.1 of our Chat UIKit. However, we strongly recommend upgrading to Flutter 3.13.0 as it includes numerous performance improvements and introduces cutting-edge features.
|
||||
|
||||
# 2.2.1
|
||||
|
||||
## New Features
|
||||
|
||||
* Introduced a new `groupMemberList` configuration in `TUIKitChat`; when specified, TUIKit will not load it automatically, optimizing network traffic usage.
|
||||
* Added support for image copying on desktop platforms.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue preventing the removal of image loading status.
|
||||
* Resolved a problem that prevented images from being saved to the device gallery.
|
||||
* Addressed a potential issue causing the `mentionOtherMemberInGroup` function in `TIMUIKitChatController` to fail.
|
||||
* Corrected an issue that could lead to improper image rendering.
|
||||
|
||||
# 2.2.0
|
||||
|
||||
## New Features
|
||||
|
||||
* Introduced a newly-designed set of Emoji image stickers, available for seamless integration within textual content, providing an enhanced user experience.
|
||||
* Streamlined the implementation of stickers, removing the need for additional complex coding. Full functionality is enabled by default, with customization options available through the `stickerPanelConfig` configuration in `TIMUIKitChatConfig`.
|
||||
* Extended support for rendering embedded image stickers within text messages when the `Markdown` parsing mode is activated, combining a rich, user-friendly experience with the ability to display formatted Markdown text.
|
||||
|
||||
## Improvements
|
||||
|
||||
* Enhanced group chat functionality on the Desktop, enabling mentions (`@` tag) to be inserted at any position within a composed message, rather than only at the end. Additionally, deleting `@` tags has been optimized.
|
||||
* Maintained message sending permissions for the group owner and administrators during "mute all" scenarios.
|
||||
* Enabled the use of a return `null` value for the `customHoverBar` to utilize the default.
|
||||
* Refined the revoke button functionality for group administrators.
|
||||
* Removed full-screen support for video previews on the Web and introduced an alternative "Open in New Window" button for an enlarged view.
|
||||
* Implemented UIKit log recording to facilitate issue identification and troubleshooting.
|
||||
* Introduced a delete button for the small PNG sticker selection panel on mobile devices, which previously was only available in the Unicode emoji selection panel.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Resolved an issue preventing photo capturing on devices running Android 12 or lower.
|
||||
* Rectified display inaccuracies related to picture aspect ratios.
|
||||
* Addressed several issues concerning voice and video calls.
|
||||
|
||||
# 2.1.3+1
|
||||
|
||||
## New Features
|
||||
|
||||
* Introduced [a new custom internationalization language scheme](https://www.tencentcloud.com/document/product/1047/52154?from=pub) that supports adding language packs, adding or modifying entries, and makes customizing i18n more accessible. This feature helps your app achieve a more convenient globalization process and easier customer acquisition worldwide.
|
||||
* Provided a seamless experience for previewing large images and playing videos within desktop environments (applications and web) by avoiding frequent page transitions. Enhanced the user experience for image previews and video playback. Please note that video playback is currently supported only on the web and not in desktop applications.
|
||||
* Supported to integrate with the new online customer service plugin (tencent_cloud_chat_customer_service_plugin).
|
||||
* Added two new life cycle hooks, `messageDidSend` and `messageShouldMount` to `ChatLifeCycle`.
|
||||
|
||||
## Improvements
|
||||
|
||||
* Optimized the usage, interface, and interaction of the sticker panel.
|
||||
* Enhanced mobile video playback interaction and UI.
|
||||
* Refined the error prompt when sending a 0 KB file fails.
|
||||
* Enabled users to close modals on desktop by clicking the bottom gray overlay area.
|
||||
* Improved the UI and interaction of image and video messages in the message list.
|
||||
* Added the ability to open self-sent file messages without downloading.
|
||||
* Optimized the download status animation of file messages on the web.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue preventing mobile image previews from being dragged after zooming.
|
||||
* Resolved an issue that might cause the message selection status not to be removed after canceling a message forward action.
|
||||
* Addressed an issue that might cause the microphone usage not to end after sending a voice message, which means the microphone was not released.
|
||||
|
||||
# 2.1.2
|
||||
|
||||
## New Features
|
||||
|
||||
* Introduced a new message recall mode, which enables group administrators to recall any message from any group member. To enable this feature, set `isGroupAdminRecallEnabled` in `TIMUIKitChatConfig` to `true`.
|
||||
* Added support for draft text functionality on the Web. Activate this feature by setting `isUseDraftOnWeb` in `TIMUIKitChatConfig` to `true`. Since the Chat SDK doesn't support this functionality, draft data will be stored in TUIKit's memory. Be aware that draft text will be lost upon refreshing the website.
|
||||
* Enabled using the default message abstract text when `abstractMessageBuilder` returns `null`.
|
||||
|
||||
## Improvements
|
||||
|
||||
* The duration for video messages sent from the Web will no longer be displayed, as this type of video message does not contain an accurate video duration.
|
||||
* Removed the hover color on the message input area on Desktop.
|
||||
* Added auto-focus support for the message input area on Desktop.
|
||||
* Enhanced the rendering of text messages in markdown mode, particularly for clickable link extraction and HTML tag handling.
|
||||
* Limited the number of lines displayed for replied messages to a maximum of 2 lines to avoid occupying excessive space.
|
||||
* Optimized the message replying process, ensuring that a message referencing another message can still display the replied message, even when it is too old.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue that could cause the profile page to display no data.
|
||||
* Fixed an issue that could prevent the message sending button from being displayed after selecting an emoji on mobile Web.
|
||||
* Fixed an issue that could prevent the message long-press menu from showing on mobile Web.
|
||||
* Fixed an issue where editing a message would carry over to another conversation when switching between conversations.
|
||||
* Fixed an issue that could prevent displaying the `Modal` on Desktop.
|
||||
* Fixed an issue that caused the `iconImageAsset` from the `MessageToolTipItem` class to not work properly.
|
||||
|
||||
# 2.1.0+2
|
||||
|
||||
## Improvements
|
||||
|
||||
* Upgraded several dependencies to resolve conflicts with the Kotlin Gradle plugin.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue causing the message list to be displayed inaccurately when it contains a file without a suffix.
|
||||
|
||||
# 2.1.0+1
|
||||
|
||||
## Improvements
|
||||
|
||||
* Removed `disk_space` dependency as many customers reported difficulty in obtaining this dependency successfully.
|
||||
* Replaced `fc_native_video_thumbnail_for_us` with its original version `fc_native_video_thumbnail`.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue where `universal_html` could be blocking the compilation.
|
||||
|
||||
# 2.1.0
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
* Migrated to Flutter 3.10.0 and Dart 3.0.0, no longer supporting projects with Flutter < 3.10.0 and Dart < 3.0.0.
|
||||
* Updated the minimum requirement for Android AGP to 7.0, projects with AGP < 7.0 are no longer supported.
|
||||
|
||||
We highly recommend updating to these new versions for a better experience.
|
||||
|
||||
## New Features
|
||||
|
||||
* Added several methods to `TIMUIKitChatController`, including `hideAllBottomPanelOnMobile`, `mentionOtherMemberInGroup`, `setInputTextField`, and `getGroupMemberList`. Please refer to the corresponding annotations for usage.
|
||||
* Added more parameter fields to the `TIMUIKitChatController`'s `sendMessage` method. For details, please refer to the corresponding annotations.
|
||||
* Added `onSecondaryTapAvatar` to `TIMUIKitChat`, serving as callback trigger for secondary avatar clicks in the message list.
|
||||
* Introduced `isUseMessageHoverBarOnDesktop` and `desktopMessageInputFieldLines` to `TIMUIKitChatConfig`. For usage details, please refer to the corresponding annotations.
|
||||
|
||||
## Improvements
|
||||
|
||||
* Enhanced performance and user experience when switching conversations on Desktop, including features like text field auto-focus and draft text.
|
||||
* Enabled displaying correct new lines in markdown mode.
|
||||
* Changed the order of members in the mentioned member selection panel: Group Owner => Group Administrator => Member, sorted based on the code units' first differing position in the member show names.
|
||||
* Implemented auto-focus after clicking a member in the mentioned member selection panel.
|
||||
* Added text field auto-focus when replying to a message.
|
||||
* Updated other members' display names in at-tag messages to use `namecard`, followed by `nickname` and `userId`.
|
||||
* Widened Desktop message input area's control bar.
|
||||
* Replaced the default icon in Desktop's message input area from `png` to `svg` for better performance and clarity. `DesktopControlBarConfig` now supports defining `svgPath` for each item as well.
|
||||
* Improved Web platform detection.
|
||||
* Mentioning "all" or "at all" can now only be used by group owners and administrators.
|
||||
* Supported returning null for each message item builder in `MessageItemBuilder` to use the default message widget.
|
||||
* Enhanced group members filtering in the group member mentioned selection panel with case-insensitive fuzzy matching, leading to increased filtering accuracy.
|
||||
* For security purposes, downloading files by `fetch` and `blob` in the Web now replaces previewing files in a new browser tab, whereas previewing images and videos is displayed in a new tab on the Web.
|
||||
* Changed the default order in the message tooltip menu.
|
||||
* Previewing images and videos is set to open in a new tab on the Web.
|
||||
* Improved the ratio for sending video messages.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed issues when enabling the section function in markdown mode with `inEnableTextSelection` set to `true`.
|
||||
* Addressed an issue where the replied message was removed when selecting all text in the message and clicking backspace.
|
||||
* Fixed an issue where Chinese characters could not be entered while replying to a message.
|
||||
* Resolved some console errors during debugging.
|
||||
* Fixed an issue with links not opening in markdown mode.
|
||||
* Fixed an issue that caused two `Scrollbar`s to appear in the message input field on Desktop.
|
||||
* Solved an issue that might cause incorrect layout when the app is launched.
|
||||
* Addressed an issue where messages were directly sent when the Enter key was pressed while entering Chinese text.
|
||||
* Fixed related issues with the mentioned member selection panel on Desktop.
|
||||
* Resolved an issue where images couldn't be pasted directly into the message input area for sending on the Web.
|
||||
* Fixed an issue where files couldn't be sent on the Web.
|
||||
* Remedied an issue where media and files couldn't be opened when local downloaded resources were deleted; now, resources will automatically re-download.
|
||||
* Fixed an issue that caused the `iconImageAsset` of the `MessageToolTipItem` config to head internally to this chat UIKit.
|
||||
* Improved the downloading process of media and files by avoiding frequent calls to `setState`, thus preventing the entire project from re-rendering.
|
||||
|
||||
# 2.0.0
|
||||
|
||||
If you are upgrading from version 1.7.0, please refer to the changelog of all 2.0.0-preview versions, ranging from preview.1 to preview.7.
|
||||
|
||||
The main feature of this new 2.0.0 version is Desktop Support. Tencent Cloud Chat UIKit now supports all platforms, including iOS, Android, Web, Windows, and macOS, which has resulted in significant changes to the codebase. The UI has been improved to adapt to screens of various widths, with different layouts for both wide and narrow screens.
|
||||
|
||||
In addition, there are some significant changes compared to version 2.0.0-preview.7.
|
||||
|
||||
## New Features
|
||||
|
||||
* Added drag and drop support for multiple files in `TIMUIKitChat`, allowing direct sending.
|
||||
* Introduced functionality to open files or their containing folder (using `Finder` on `macOS` or `Explorer` on `Windows`) for file messages via the message operation tooltip menu on desktop.
|
||||
* Implemented text selection and copying in messages on desktop.
|
||||
* Added group joining application processing on Desktop.
|
||||
* Introduced `isAutoReportRead` to `TIMUIKitChatConfig` for controlling read status reporting.
|
||||
|
||||
## Improvements
|
||||
|
||||
* Enhanced group members selection panel for mentioning someone in a group chat.
|
||||
* Refined image display ratio on Desktop.
|
||||
* The Reply or Quote button is now labeled as `Reply` when `isAtWhenReply` is set to true, and `Quote` otherwise.
|
||||
* @ member tags can now be deleted at once.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed UI layout issue causing the `translate` button to display on two lines.
|
||||
* Addressed an issue causing the mute status not to change when switching to another conversation.
|
||||
* Fixed several issues causing bugs when opening files.
|
||||
* Resolved an issue causing secondary confirmation modal UI layout to be over-width on Desktop.
|
||||
* Fixed an issue causing UI layout errors on the profile page.
|
||||
* Addressed an issue where the `chatMessageItemFromSelfBgColor` configuration did not work.
|
||||
* Fixed an issue preventing files from being opened when the path contained Chinese characters on Windows.
|
||||
* Resolved an issue preventing images from being pasted and sent directly with Ctrl + V on Windows.
|
||||
* Fixed an issue causing errors in the muting members list.
|
||||
|
||||
# 2.0.0-preview.7
|
||||
|
||||
## New Features
|
||||
|
||||
* Added `additionalMessageToolTips` to `ToolTipsConfig`. This new property allows developers to add additional message operation tooltip items, apart from the default ones. The previous `additionalItemBuilder` has been replaced by this new property. With `additionalMessageToolTips`, developers only need to specify the data for the tooltip items, rather than providing a whole widget. This makes usage easier, as you no longer need to worry about the UI display.
|
||||
* Added `isPreloadMessagesAfterInit` to `TIMUIKitConfig`, allows determines whether TUIKit should preload some messages after initialization for faster message display.
|
||||
|
||||
## Improvements
|
||||
|
||||
* Message operation menu shows when long-pressing messages will not show if nothing operation item includes and do not use message sticker reaction module.
|
||||
* Renamed `desktopMessageHoverBar` to `additionalDesktopMessageHoverBarItem` in `TIMUIKitChatConfig` to control only the addition of extra operation items displayed on the hover bar of messages on desktop (macOS, Windows, and desktop version of Web), without affecting the default ones. Previously, it controlled the entire message hover bar, including covering the default items.
|
||||
* Renamed `showWideScreenModalFunc` to `showDesktopModalFunc` in `TIMUIKitConfig` for better clarity.
|
||||
* Upgraded several dependencies to their latest versions, including `ffi` to ^2.0.1, `file_picker` to ^5.2.9 and `device_info_plus` to ^8.2.0.
|
||||
* Added support for the new permission authorization schema on Android 13 and `targetSdkVersion` greater than 33.
|
||||
* Corrected the `textHight` to `textHeight` in `TIMUIKitChatConfig`, and modified the default value to 1.3.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue where the `showVideoCall` and `showVoiceCall` configuration options were not working.
|
||||
* Fixed potential `Windows` platform deployment prohibition issue.
|
||||
* Fixed an issue that may cause `setLocalCustomData` to be triggered repeatedly.
|
||||
|
||||
# 2.0.0-preview.6
|
||||
|
||||
## Improvements
|
||||
|
||||
* Permission requests now feature a gray translucent overlay for secondary confirmations on first-time requests, which was reintroduced after being removed in version 2.0.0-preview.4. Additionally, the overlay can now be successfully hidden once the permission authorization is complete.".
|
||||
* Time Divider on Message List: The default 12-hour display has been changed to a 24-hour display.
|
||||
* Message translation now targets the language of TUIKit instead of relying on the system language directly. The language of TUIKit can be set as the system language automatically or defined by the user. For more information, please refer to this documentation: https://www.tencentcloud.com/document/product/1047/52154.
|
||||
* Optimized the animation for message text input area.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue where the `Voice Call` and `Video Call` buttons were not working in group chat.
|
||||
* Fixed several null-safety issues.
|
||||
* Fixed a layout problem for the message operation menu when not using the message sticker reaction module.
|
||||
* Addressed a problem where the time ago display was not correct on the conversation item.
|
||||
* Fixed an issue where stickers could not be clicked in some cases.
|
||||
* Resolved an overflow error that occurred when opening the sticker panel.
|
||||
|
||||
# 2.0.0-preview.5
|
||||
|
||||
## New Features
|
||||
|
||||
* New Chat Configuration: `isAllowLongPressAvatarToAt`. This option controls whether users are allowed to mention another user in the group by long-pressing on their avatar.
|
||||
|
||||
## Improvements
|
||||
|
||||
* Improved tool bar configuration on desktop: The tool bar can now be customized using `desktopControlBarConfig` for embedded default items and `additionalDesktopControlBarItems` for additional tool items. These configurations come from TIMUIKitChatConfig.
|
||||
* Renamed the `wideMessageHoverBar` configuration option to `desktopMessageHoverBar` for better clarity.
|
||||
* Eliminated the dependency on `fluttertoast`. All necessary customer reminders are now triggered through the `onTUIKitCallbackListener` info callback in your project. For more information, please see: https://www.tencentcloud.com/document/product/1047/50054#how-do-i-get-an-api-call-error.2Fflutter-layer-error.2Fpop-up-prompt-message.3F.3Ca-id.3D.22callback.22.3E.3C.2Fa.3E.
|
||||
* Eliminated other six unnecessary dependency packages to reduce the size and improve performance.
|
||||
* Improved the clarity of the `sendMessage` function in `TIMUIKitChatController` by replacing the use of `convID` to represent both `userID` and `groupID` with separate parameters.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue where the message operation menu may show inaccurately when the message is too long.
|
||||
* Fixed a problem where the message operation menu had the potential to be too wide for certain types of messages, causing display issues.
|
||||
* Corrected an issue where the button to remove group members was not functioning correctly.
|
||||
* Addressed a problem where the message item could exceed the pixel limit and appear too wide.
|
||||
* Fixed a bug where certain JSON decoding operations could potentially fail.
|
||||
* Fixed an issue with sound messages on iOS devices playing only through earpiece instead of speaker by default.
|
||||
|
||||
# 2.0.0-preview.4
|
||||
|
||||
## New Features
|
||||
|
||||
* New Chat Configuration: `TIMUIKitChatConfig` now includes `offlinePushInfo`, which allows for customization of the entire `offlinePushInfo` for each message. This field has a higher priority than the previous separate configuration fields for this object.
|
||||
* New Color Configuration: Added `appbarTextColor` and `appbarBgColor` to configure the color for the Appbar. Also added `selectPanelBgColor` and `selectPanelTextIconColor` to configure the color of the messages multi-select panel.
|
||||
|
||||
## Improvements
|
||||
|
||||
* Improved Group Management: Muting members on Work Group is now not allowed.
|
||||
* Improved Avatar: Ensured that the avatar can be as small as possible while still covering the entire target box.
|
||||
* Permission Requests: Removed the gray translucent overlay for secondary confirmations on first-time permission requests.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue where the color defined by `chatBgColor` could not cover the entire chat screen when messages did not cover the whole page.
|
||||
* Fixed an issue where the history message list could not be scrolled in some cases.
|
||||
* Fixed an issue where the ratio of sending messages was incorrect, resulting in the wrong position of the read status label on the left.
|
||||
* Fixed an issue where loading messages could fail when the number of messages equaled the specified count.
|
||||
|
||||
# 2.0.0-preview.3
|
||||
|
||||
## New Features
|
||||
|
||||
* Integrated Callkit: The Calls button no longer needs to be added to `MorePanelConfig`. If `tencent_calls_uikit` is installed, the Video Call and Voice Call buttons will be displayed automatically.
|
||||
* Paste Images on Desktop: Users can now paste an image on the text field on Desktop to send it.
|
||||
* Screenshot Capture on Desktop: Users can now capture a screenshot on Desktop and send it.
|
||||
|
||||
## Improvements
|
||||
|
||||
* Improved Compatibility: The TUIKit is now compatible with Flutter versions 3.0.0 to 3.7.7.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* Fixed an issue where the `businessID` type may not be correct.
|
||||
* Fixed an issue where the `chatMessageItemFromSelfBgColor` configuration was not taking effect.
|
||||
|
||||
# 2.0.0-preview.2
|
||||
|
||||
## New Features
|
||||
|
||||
* Added support for opening files locally from file messages.
|
||||
|
||||
# 2.0.0-preview.1
|
||||
|
||||
## New Features
|
||||
|
||||
* Desktop Support: Tencent Cloud Chat UIKit now supports all platforms, including iOS, Android, Web, Windows, and macOS, resulting in significant changes to the codebase. The UI has been enhanced to adapt to screens of various widths, with different layouts for both wide and narrow screens.
|
||||
* Information Copy: The ability to copy information, such as Group ID, from the screen has been added.
|
||||
|
||||
## Improvements
|
||||
|
||||
* Improved group management logic, with non-administrators no longer able to access the management interface.
|
||||
* Optimized cursor positioning when sending messages.
|
||||
* Improved and optimized scrollbar functionality.
|
||||
* Enhanced clickable URL support in messages, with URLs now supporting both with and without the "https://" prefix.
|
||||
|
||||
# 1.7.0+1
|
||||
|
||||
* Fix: An issue that caused errors on mentioning all members.
|
||||
|
||||
# 1.7.0
|
||||
|
||||
* Addition: Support for quickly navigating to the first unread message in a group chat with more than 20 new unread messages, using the dynamic tongue located in the top right corner of the screen. This feature allows for swift movement through the messages, regardless of their quantity.
|
||||
* Addition: Customize the border radius for all avatars is now supported. You can set the default avatar border radius using `defaultAvatarBorderRadius` in `TIMUIKitConfig`.
|
||||
* Optimization: The delete button on the sticker sending panel has been improved for better usability.
|
||||
* Optimization: Some English labels on the screen have been updated to better reflect local expressions.
|
||||
* Fix: An issue causing errors when sending a large number of stickers has been resolved.
|
||||
* Fix: Some errors that were occurring in the sticker panel have been addressed.
|
||||
|
||||
# 1.6.2
|
||||
|
||||
* Optimization: Remove `fluttertoast`.
|
||||
* Fix: An issue that caused errors when sending files without extensions.
|
||||
|
||||
# 1.6.1
|
||||
|
||||
* Fix: A bug of muting someone in a group.
|
||||
* Fix: A bug on Flutter 3.7.0.
|
||||
|
||||
# 1.6.0
|
||||
|
||||
* Addition: `scrollToConversation` on `TIMUIKitConversationController`. You can now easily navigate to a specific conversation in the conversation list and move to the next unread conversation by double-clicking the tab bar, [refers to our sample app](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/conversation.dart).
|
||||
* Optimization: The performance of the history message list while scrolling over a large distance.
|
||||
|
||||
# 1.5.0+1
|
||||
|
||||
* Fix: An issue with video messages being oversize.
|
||||
|
||||
# 1.5.0
|
||||
|
||||
* Addition: New configuration `defaultAvatarAssetPath` on global `TIMUIKitConfig` to define the default avatar.
|
||||
* Addition: Supports Flutter 3.7.0.
|
||||
* Fix: `chatBgColor` configuration.
|
||||
|
||||
# 1.4.0
|
||||
|
||||
* Addition: Text translation. Long press the text messages to choose `Translate`, which can be turned off by `showTranslation` from `ToolTipsConfig`.
|
||||
* Optimization: The long press pop-up location and keyboard pop-up event.
|
||||
|
||||
# 1.3.2
|
||||
|
||||
* Fix: Text input field height, after choosing to mention someone.
|
||||
|
||||
# 1.3.1
|
||||
|
||||
* Optimization: Improve performance.
|
||||
|
||||
# 1.3.0
|
||||
|
||||
* Fix: A bug where group tips were not showing the nickname or remarks when transferring group owner.
|
||||
* Optimization: Remove the confirmation pop-up before opening the file.
|
||||
|
||||
# 1.2.0
|
||||
|
||||
* Fix: An issue where the input area was not showing when switching from recording to keyboard on `TIMUIKitChat`.
|
||||
* Fix: An issue where only the first receiver could receive the merged multiple forward messages.
|
||||
* Optimization: `MessageItemBuilder` can now be used for shows on the merger message screen.
|
||||
|
||||
# 1.1.0 And 1.1.0+1
|
||||
|
||||
* Addition: We have added support for two new languages - Japanese and Korean.
|
||||
* Addition: You can now add other languages apart from our default ones, such as English, Chinese (Simplified and Traditional), Japanese, and Korean. You can also modify the translations using the instructions provided in [this documentation](https://www.tencentcloud.com/document/product/1047/52154?from=pub).
|
||||
* Addition: The sticker plug-in is now embedded in TUIKit by default. We support three types of stickers - Unicode Emoji, small image emoji, and big image stickers. You can refer to [this documentation](https://www.tencentcloud.com/document/product/1047/52227?from=pub) for optimized usage.
|
||||
* Optimization: Themes are now more customizable.
|
||||
* Optimization: We have optimized the animation of the input area, keyboard, sticker panel, and more panel.
|
||||
* Optimization: You can now insert both Unicode and small image emojis at any position in text messages.
|
||||
* Optimization: You can now preview profile avatars with a large image by clicking it, and copy UserIDs in profile.
|
||||
* Optimization: We have improved several UI details, including `TIMUIKitAddFriend`, `TIMUIKitAddGroup`, `TIMUIKitGroupProfile`, and `TIMUIKitProfile`.
|
||||
* Optimization: `TIMUIKitGroupProfile` and `TIMUIKitProfile` can now update automatically after the `ID` is changed.
|
||||
* Optimization: We have added a new loading animation when downloading images or videos on `TIMUIKitGroupChat`.
|
||||
* Fix: We have fixed some bugs.
|
||||
|
||||
# 1.0.1
|
||||
|
||||
* Modification: Remove `groupTRTCTipsItemBuilder` from `MessageItemBuilder`, please use `customMessageItemBuilder` instead.
|
||||
* Modification: Remove default rendering for calling messages, you can choose to use the default widgets, `CallMessageItem` and `GroupCallMessageItem`, from our call plugin `tim_ui_kit_calling_plugin` directly. Refer to the [Demo](https://github.com/TencentCloud/chat-demo-flutter/tree/main/lib/utils/custom_message/custom_message_element.dart).
|
||||
|
||||
# 1.0.0
|
||||
|
||||
* Addition: We have added support for adding Flutter module to Native APP. For implementation details, please refer to [this documentation](https://www.tencentcloud.com/document/product/1047/51456?from=pub).
|
||||
* Addition: You can now customize stickers and emojis for text messages. For more information, please refer to [this documentation](https://cloud.tencent.com/document/product/269/80882).
|
||||
* Optimization: We have improved the loading duration for history message lists, especially those with lots of media and file messages.
|
||||
* Optimization: More panel area now supports scrolling.
|
||||
* Optimization: We have made loading latest messages when scrolling back to the bottom smoother.
|
||||
* Modification: It is now required to provide the call record widget to `messageItemBuilder` => `customMessageItemBuilder` of `TIMUIKitChat`. You can choose to use the default widget, `CallMessageItem`, from our call plugin `tim_ui_kit_calling_plugin` directly. Please refer to the [Demo](https://github.com/TencentCloud/chat-demo-flutter/tree/main/lib/utils/custom_message/custom_message_element.dart).
|
||||
* Fix: We have fixed the issue with the number of photos from the album on Android.
|
||||
* Fix: We have fixed the issue with long text going out of bounds in the group profile info card.
|
||||
* Fix: We have resolved some bugs.
|
||||
|
||||
> **Please note that modifications are required for the second and sixth lines**. Otherwise, the modules for stickers/emojis/call records will not work.
|
||||
|
||||
# 0.1.8
|
||||
|
||||
* Optimization: File batch downloading queue now allows clicking on multiple file messages at once.
|
||||
* Optimization: Group list widgets are now automatically updated.
|
||||
* Optimization: Camera capture now supports relatively lower performance devices and adjusts resolution automatically.
|
||||
* Optimization: Supports customization of the color and text style of the app bar, especially on TIMUIKitChat widget.
|
||||
* Fix: Friend remark or nickname no longer fails to show on group tips.
|
||||
* Fix: Resolved a crash when playing videos.
|
||||
* Fix: Several bugs.
|
||||
|
||||
# 0.1.7
|
||||
|
||||
* Addition: Big and RAW images are now supported, especially for those captured from the latest version of iOS and iPhone 14 Pro series, with automatic compression and formatting before sending.
|
||||
* Optimization: Improved performance and stability, especially for the history message list and launching.
|
||||
* Optimization: Initializing the `TIMUIKitChat` is now an idempotent operation.
|
||||
* Optimization: Loads the latest messages when scrolling back to the bottom.
|
||||
* Optimization: Supports Flutter both 2.x and 3.x series.
|
||||
* Fix: Resolved an issue with select photos permission.
|
||||
* Fix: Several bugs.
|
||||
|
||||
# 0.1.5
|
||||
|
||||
* Addition: Web support is now available, allowing TUIKit to be implemented on iOS/Android/Web platforms.
|
||||
* Addition: Disk storage checking is now performed after login, with controls available in `config` of `init`.
|
||||
* Addition: `timeDividerConfig`, `notificationAndroidSound`, `isSupportMarkdownForTextMessage`, and `onTapLink` are added to `TIMUIKitChatConfig`.
|
||||
* Remove: The default Emoji list has been removed due to copyright issues. You can provide your own sticker list to the panel using [tim_ui_kit_sticker_plugin](https://pub.dev/packages/tim_ui_kit_sticker_plugin).
|
||||
* Optimization: You can now choose to disable Markdown parsing for text messages.
|
||||
* Optimization: You can now choose to disable shows for @ messages in the conversation list.
|
||||
* Optimization: You can now return `null` for `notificationExt`/`notificationBody` in `TIMUIKitChatConfig` and `messageRowBuilder` in `MessageItemBuilder` to use default values based on your needs in a specific case. This means you can control whether to use customized settings based on the provided situation, without having to redefine the same logic as TUIKit in your code.
|
||||
* Optimization: Supports multiple lines for text messages.
|
||||
* Optimization: Rebuilt and improved the experience of `TIMUIKitChat`. Note that `TIMUIKitChatController` needs to be specified to `controller`, as shown in the [Demo](https://github.com/TencentCloud/tc-chat-demo-flutter/lib/src/chat.dart).
|
||||
* Fix: Several bugs.
|
||||
|
||||
# 0.1.3
|
||||
|
||||
* Addition: User inputting status is now available.
|
||||
* Addition: Message reactions with emoji/stickers are now available.
|
||||
* Addition: User online status is now available.
|
||||
|
||||
# 0.1.2
|
||||
|
||||
* Upgrade: flutter_record_plugin_plus to version 0.0.4.
|
||||
|
||||
# 0.1.1
|
||||
|
||||
* Addition: Lifecycle hooks are now available for the main widgets, referring to the parameter description for details.
|
||||
* Addition: Mute status display is now available for group chat on the chat page.
|
||||
* Addition: URL enrichment is now available for text messages.
|
||||
* Addition: Callback for global information (Flutter Error, Tips for Reminds, API Error), and you can display toast as needed.
|
||||
* Optimization: Image preview display has been improved.
|
||||
* Rebuilt: TUIKitGroupProfile and TUIKitProfile have been simplified for ease of use.
|
||||
|
||||
# 0.1.0-bugfix
|
||||
|
||||
* Upgrade: Tencent IM Native SDK.
|
||||
|
||||
# 0.1.0
|
||||
|
||||
* Addition: Atomization widgets for TIMUIKitChat.
|
||||
* Addition: Updating the UI when the message has been modified.
|
||||
* Addition: The application page for joining the group.
|
||||
* Addition: `updateMessage` API, users can refresh the view after modifying the local message.
|
||||
* Addition: Support for Traditional Chinese.
|
||||
* Addition: Customization for conversation list item.
|
||||
|
||||
# 0.0.9
|
||||
|
||||
* Addition: Offline push along with [tim_ui_kit_push_plugin](https://pub.dev/packages/tim_ui_kit_push_plugin).
|
||||
* Adapt: Flutter 3.0.0.
|
||||
* Optimization: Local preview of multimedia files.
|
||||
|
||||
# 0.0.8
|
||||
|
||||
* Addition: Group read receipt module.
|
||||
* Addition: Little tongue on the message list.
|
||||
* Addition: Examples.
|
||||
* Fix: Several bugs.
|
||||
|
||||
# 0.0.7
|
||||
|
||||
* Fix: Several bugs.
|
||||
|
||||
# 0.0.6
|
||||
|
||||
* Addition: New `sendMessage` method to the controller `TIMUIKitChatController` for TIMUIKitChat.
|
||||
* Addition: Configuration for TIMUIKitChat, which can control the functions for TIMUIKitChat components.
|
||||
* Support: Customized for more panel customized ability to TIMUIKitChat.
|
||||
* Optimization: User authorization standardized.
|
||||
|
||||
# 0.0.5
|
||||
|
||||
* Addition: Several new customized configs, includes, appBarConfig, morePanelConfig, and removed appBarActions config.
|
||||
* Optimization: Image preview displaying.
|
||||
* Upgrade: Tencent IM SDK.
|
||||
* Fix: The issue of conversation item duplication for TIMUIKitConversation.
|
||||
|
||||
# 0.0.4
|
||||
|
||||
* Optimization: TIMUIKitChat, especially for media files selector.
|
||||
* Optimization: Previewing of image messages, video messages.
|
||||
* Optimization: Theme color.
|
||||
* Optimization: UI for search components.
|
||||
* Upgrade: Tencent IM SDK.
|
||||
|
||||
# 0.0.3
|
||||
|
||||
* Addition: TIMUIKitSearch and TIMUIKitSearchMsgDetail, supports searching both in conversation and globally.
|
||||
* Addition: TIMUIKitAddFriend.
|
||||
* Addition: TIMUIKitAddGroup.
|
||||
* Addition: Theme style configuration.
|
||||
* Optimization: Internationalization.
|
||||
|
||||
# 0.0.2
|
||||
|
||||
* Optimization: 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.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
Copyright © 2013-2023 Tencent Cloud. All Rights Reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,387 @@
|
|||
# It is recommended to download the source code from [pub.dev](https://pub.dev/packages/tencent_cloud_chat_uikit/versions)
|
||||
|
||||
## Product Introduction
|
||||
You only need to integrate Chat SDK to easily gain chat, conversation, group capabilities, and you can also communicate with other products such as whiteboards through signaling messages. Chat can cover various business scenarios, support the access and use of various platforms, and fully meet the communication needs.
|
||||
|
||||
## Check Out Our Sample Apps
|
||||
This document introduces how to quickly run the Chat demo on the iOS platform.
|
||||
[<img src="https://im.sdk.qcloud.com/tools/resource/GitHubResource/build_flutter_chat_app.png" width="800"/>](https://www.youtube.com/watch?v=lawzmfW9vls)
|
||||
|
||||
For the other platforms, please refer to document:
|
||||
- [**chat-uikit-android**](https://github.com/TencentCloud/chat-uikit-android)
|
||||
- [**chat-uikit-ios**](https://github.com/TencentCloud/chat-uikit-ios)
|
||||
- [**chat-uikit-vue**](https://github.com/TencentCloud/chat-uikit-vue)
|
||||
- [**chat-uikit-react**](https://github.com/TencentCloud/chat-uikit-react)
|
||||
- [**chat-uikit-uniapp**](https://github.com/TencentCloud/chat-uikit-uniapp)
|
||||
- [**chat-uikit-wechat**](https://github.com/TencentCloud/chat-uikit-wechat)
|
||||
|
||||
## Introduction to TUIKit
|
||||
Chat SDK comes with TUIKit, which is an official set of UI components that have chat business logic built-in. TUIKit includes components like conversation, chat, relationship chain, and group.
|
||||
See [TUIKit Library Overview](https://trtc.io/document/50059?platform=flutter&product=chat&menulabel=uikit) for more details.
|
||||
|
||||
Developers can use these UI components to quickly and easily add In-APP chat modules to their mobile applications.
|
||||
<img src="https://qcloudimg.tencent-cloud.cn/raw/f140dd76be01a65abfb7e6ba2bf50ed5.png" width="1000"/>
|
||||
|
||||
Currently, Flutter TUIKit
|
||||
contains the following main components:
|
||||
|
||||
- TIMUIKitCore: Core entry
|
||||
- TIMUIKitConversation: Conversation list
|
||||
- TIMUIKitChat: Chat module, includes historical message list and message sending area, with some other features
|
||||
like message reaction and URL preview, etc.
|
||||
- TIMUIKitContact: Contacts list
|
||||
- TIMUIKitProfile: User profile and relationship management
|
||||
- TIMUIKitGroupProfile: Group profile and management
|
||||
- TIMUIKitGroup: The list of group self joined
|
||||
- TIMUIKitBlackList: The list of user been blocked
|
||||
- TIMUIKitNewContact: New contacts application list
|
||||
- TIMUIKitSearch: Search globally
|
||||
- TIMUIKitSearchMsgDetail: Search in specific conversation
|
||||
|
||||
In addition to these components, there are other useful components and widgets available to help
|
||||
developers meet their business needs, such as group entry application list and group member list.
|
||||
|
||||
## Compatible Platforms
|
||||
|
||||
The platforms are compatible with the deployment of our Chat UIKit.
|
||||
|
||||
- Android
|
||||
- iOS
|
||||
- Web (version 0.1.4 and later)
|
||||
- Windows (version 2.0.0 and later)
|
||||
- macOS (version 2.0.0 and later)
|
||||
|
||||
## Get Started
|
||||
|
||||
Please refer to [Run Demo](https://trtc.io/document/45907?platform=flutter&product=chat&menulabel=uikit) for a complete and detailed guide on getting started.
|
||||
|
||||
## Directions
|
||||
|
||||
The following guide describes how to quickly build a simple chat application using Flutter TUIKit.
|
||||
Refer to the appendix if you want to learn about the details and parameters of each widget.
|
||||
|
||||
### Step 0: Create two accounts for testing
|
||||
|
||||
Sign up and log in to the [Chat console](https://console.trtc.io/).
|
||||
|
||||
Create an application and enter it. Click Users and create two accounts.
|
||||
|
||||
> The correct way to distribute `UserSig` is to integrate the calculation code for `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://trtc.io/document/34385?product=chat&menulabel=serverapis).
|
||||
|
||||
### Step 1: Create a Flutter app and add permission configuration
|
||||
|
||||
Create a Flutter app quickly by following the [Flutter documentation](https://docs.flutter.dev/get-started/install).
|
||||
|
||||
TUIKit needs the permissions of shooting/album/recording/network for basic messaging functions. You need to declare these permissions manually to use the relevant capabilities normally.
|
||||
|
||||
#### Android
|
||||
|
||||
Open `android/app/src/main/AndroidManifest.xml` and add the following lines between `<manifest>` and `</manifest>`.
|
||||
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
|
||||
```
|
||||
|
||||
#### iOS
|
||||
|
||||
Open `ios/Podfile` and 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['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
|
||||
config.build_settings['ENABLE_BITCODE'] = 'NO'
|
||||
config.build_settings["ONLY_ACTIVE_ARCH"] = "NO"
|
||||
end
|
||||
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 `tencent_cloud_chat_uikit` under `dependencies` in the `pubspec.yaml` file, or run the following command:
|
||||
|
||||
```shell
|
||||
flutter pub add tencent_cloud_chat_uikit
|
||||
```
|
||||
|
||||
It supports Android and iOS by default. If you also want to use it on the web, refer to the following guide.
|
||||
|
||||
#### Web Support
|
||||
|
||||
Version 0.1.4 or later is required to support web.
|
||||
|
||||
If your existing Flutter project does not support web, run `flutter create .` in the project root directory to add web support.
|
||||
|
||||
Install JavaScript dependencies to `web/` using `npm` or `yarn`.
|
||||
|
||||
```shell
|
||||
cd web
|
||||
|
||||
npm init
|
||||
|
||||
npm i tim-js-sdk
|
||||
|
||||
npm i tim-upload-plugin
|
||||
```
|
||||
|
||||
Open `web/index.html` and add the following two lines between `<head>` and `</head>` to import them.
|
||||
|
||||
```html
|
||||
<script src="./node_modules/tim-upload-plugin/index.js"></script>
|
||||
<script src="./node_modules/tim-js-sdk/tim-js-friendship.js"></script>
|
||||
```
|
||||
|
||||
<img src="https://qcloudimg.tencent-cloud.cn/raw/a4d25e02c546e0878ba59fcda87f9c76.png" width="800"/>
|
||||
|
||||
|
||||
### Step 3: Initialize TUIKit
|
||||
|
||||
Initialize TUIKit when your app starts. You only need to perform the initialization once for the project to start.
|
||||
|
||||
Get the instance of TUIKit first using `TIMUIKitCore.getInstance()`, followed by initializing it with your `sdkAppID`.
|
||||
|
||||
```dart
|
||||
/// main.dart
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_coreInstance.init(
|
||||
sdkAppID: 0, // Replace 0 with the SDKAppID of your Tencent Cloud Chat application
|
||||
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
|
||||
listener: V2TimSDKListener());
|
||||
super.initState();
|
||||
}}
|
||||
```
|
||||
|
||||
> **You may also want to register a callback function for `onTUIKitCallbackListener` here. Refer to the appendix.**
|
||||
|
||||
### Step 4: Get the signature and log in
|
||||
|
||||
You can now log in one of the testing accounts generated in Step 0 to start the Tencent Cloud Chat module.
|
||||
|
||||
Log in using `_coreInstance.login`.
|
||||
|
||||
```dart
|
||||
/// main.dart
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
_coreInstance.login(userID: userID, userSig: userSig);
|
||||
```
|
||||
|
||||
Note: Importing UserSig to your application is only for debugging purposes and cannot be used for the release version. Before publishing your app, you should generate your UserSig from your server. Refer to: [Generate Signature](https://trtc.io/document/34385?product=chat&menulabel=serverapis).
|
||||
|
||||
## Step 5. Implementing the conversation list page
|
||||
|
||||
You can use the conversation (channel) list page as the homepage of your Chat module, which includes all conversations with users and groups that have chat records.
|
||||
|
||||
<img src="https://qcloudimg.tencent-cloud.cn/raw/a27b131d555b1158d150bd9b337c1d9d.png" width="300"/>
|
||||
|
||||
You can create a `Conversation` class, with `TIMUIKitConversation` as its body, to render the conversation list. You only need to provide the `onTapItem` callback, which allows users to navigate to the Chat page for each conversation. In the next step, we'll introduce the `Chat` class.
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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 consists of the main message list and a message sending bar at the bottom.
|
||||
|
||||
<img src="https://qcloudimg.tencent-cloud.cn/raw/09b8b9b54fd0caa47069544343eba461.jpg" width="600"/>
|
||||
|
||||
You can create a `Chat` class, with `TIMUIKitChat` as its body, to render the chat page. We recommend providing an `onTapAvatar` callback function to navigate to the profile page for the current contact, which we'll introduce in the next step.
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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() ?? '',
|
||||
conversationType: selectedConversation.type ?? 1,
|
||||
conversationShowName: selectedConversation.showName ?? "",
|
||||
onTapAvatar: (_) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => UserProfile(userID: userID),
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
## Step 7. Implementing the user profile page
|
||||
|
||||
This page shows the profile of a specific user and maintains the relationship between the current logged-in user and the other user.
|
||||
|
||||
<img src="https://qcloudimg.tencent-cloud.cn/raw/03e88da6f1d63f688d2a8ee446da43ff.png" width="600"/>
|
||||
|
||||
You can create a `UserProfile` class, with `TIMUIKitProfile` as its body, to render the user profile page.
|
||||
|
||||
The only parameter you have to provide is `userID`, while this component automatically generates the profile and relationship maintenance page based on the existence of friendship.
|
||||
|
||||
> **TIP**: Please use `profileWidgetBuilder` first to customize some profile widgets and determine their vertical sequence using `profileWidgetsOrder` if you want to customize this page. If this method cannot meet your business needs, you may consider using `builder` instead.
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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 and receive messages, display the conversation list, and manage contact friendships. You can use other components from TUIKit to quickly and easily implement the complete Chat module.
|
||||
|
||||
## FAQs
|
||||
|
||||
#### Do I need to integrate Chat SDK after integrating TUIKit?
|
||||
|
||||
No. You don't need to integrate Chat SDK again. If you want to use Chat SDK related APIs, you can
|
||||
get them via `TIMUIKitCore.getSDKInstance()`. This method is recommended to ensure Chat 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.
|
||||
<img src="https://qcloudimg.tencent-cloud.cn/raw/d495b2e8be86dac4b430e8f46a15cef4.png" width="800"/>
|
||||
|
||||
#### What should I do if an error occurs during debugging on a real iOS device when I am wearing an Apple Watch?
|
||||
<img src="https://qcloudimg.tencent-cloud.cn/raw/1ffcfe39a18329c86849d7d3b34b9a0e.png" width="800"/>
|
||||
|
||||
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.
|
||||
|
||||
#### What should I do when an error occurs on an Android device after TUIKit is imported into the application automatically generated by Flutter?
|
||||
<img src="https://qcloudimg.tencent-cloud.cn/raw/d95efdd4ae50f13f38f4c383ca755ae7.png" width="800"/>
|
||||
|
||||
1. Open `android\app\src\main\AndroidManifest.xml` and
|
||||
complete `xmlns:tools="http://schemas.android.com/tools" / android:label="@string/android_label" / tools:replace="android:label"`
|
||||
as follows.
|
||||
|
||||
```xml
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="Replace it with your Android package name"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<application android:label="@string/android_label" tools:replace="android:label"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
// Specify an icon path
|
||||
android:usesCleartextTraffic="true"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
```
|
||||
|
||||
2. Open `android\app\build.gradle` and complete `minSdkVersion` and `targetSdkVersion`
|
||||
in `defaultConfig`.
|
||||
|
||||
```gradle
|
||||
defaultConfig {
|
||||
applicationId "" // Replace it with your Android package name
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recommended Resources
|
||||
|
||||
For those who require real-time voice and video call capabilities alongside our Chat UIKit,
|
||||
we highly recommend our dedicated voice and video call UI component package, [tencent\_calls\_uikit](https://pub.dev/packages/tencent_calls_uikit).
|
||||
This robust and feature-rich package is specifically designed to complement our existing solution and seamlessly integrate with it,
|
||||
providing a comprehensive, unified communication experience for your users.
|
||||
|
|
@ -0,0 +1,496 @@
|
|||
[English](https://github.com/TencentCloud/tc-chat-uikit-flutter) | 简体中文
|
||||
|
||||
# Flutter TUIKit
|
||||
|
||||
TUIKit 是基于 Chat SDK 实现的一套 UI 组件,其包含会话、聊天、搜索、关系链、群组、音视频通话等功能,基于 UI 组件您可以像搭积木一样快速搭建起自己的业务逻辑。
|
||||
|
||||
TUIKit 效果图如下所示:
|
||||

|
||||
|
||||
快速集成 TUIKit 组件请参考视频:
|
||||
[<img src="https://im.sdk.qcloud.com/tools/resource/GitHubResource/build_flutter_chat_app.png" width="800"/>](https://www.youtube.com/watch?v=lawzmfW9vls)
|
||||
|
||||
TUIKit 主要 Widget 如下:
|
||||
- TIMUIKitConversation 会话组件
|
||||
- TIMUIKitChat 聊天组件
|
||||
- TIMUIKitCore Core 组件
|
||||
- TIMUIKitProfile 个人详情组件
|
||||
- TIMUIKitGroupProfile 群组详情组件
|
||||
- TIMUIKitGroup 群组列表组件
|
||||
- TIMUIKitBlackList 黑名单列表组件
|
||||
- TIMUIKitContact 联系人组件
|
||||
- TIMUIKitNewContact 新的联系人
|
||||
- TIMUIKitSearch 搜索
|
||||
|
||||
快速使用 TUIKit 组件库建议阅读:
|
||||
- [TUIKit 组件库](https://trtc.io/zh/document/50059?platform=flutter&product=chat)
|
||||
- [集成 TUIKit](https://trtc.io/zh/document/58585?platform=flutter&product=chat&menulabel=uikit)
|
||||
|
||||
## 国际化
|
||||
|
||||
我们默认提供 `简体中文` `繁体中文` `英语` 的语言支持;并允许开发者新增语言包,扩展多语言支持。
|
||||
|
||||
如果您需要使用国际化多语言能力,请参考 [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`: 返回 SDK 实例。
|
||||
|
||||
`CoreServicesImpl` 为`TIMUIKit` 核心类,包含初始化、登录、登出、获取用户信息等方法。
|
||||
|
||||
基础用法如下,先初始化 SDK,再登录用户:
|
||||
|
||||
```dart
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
final V2TIMManager _sdkInstance = TIMUIKitCore.getSDKInstance();
|
||||
|
||||
// init
|
||||
_coreInstance.init(
|
||||
language: LanguageEnum?, // 初始指定使用语言,`简体中文` `繁体中文` `英语`。不填默认跟随系统语言。
|
||||
onTUIKitCallbackListener: ValueChanged<TIMCallback>, // TUIKit信息回调,包含SDK API错误信息/TUIKit界面相关提示信息/Flutter层报错。您可根据需要,选择性自定义展示给用户。详见下方说明
|
||||
sdkAppID: 0, // 控制台申请的sdkAppID
|
||||
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
|
||||
listener: V2TimSDKListener());
|
||||
// unInit
|
||||
_coreInstance.unInit();
|
||||
|
||||
// login
|
||||
_coreInstance.login(
|
||||
userID: 0, // 用户ID
|
||||
userSig: "" // 参考官方文档userSig生成
|
||||
)
|
||||
```
|
||||
|
||||
### `onTUIKitCallbackListener` 监听
|
||||
|
||||
该监听用于返回包括:SDK API 错误 / Flutter 报错 / 一些可能需要弹窗提示用户的场景信息。
|
||||
|
||||
通过`TIMCallbackType`确定类型。
|
||||
|
||||
#### SDK API 错误(`TIMCallbackType.API_ERROR`)
|
||||
|
||||
该场景下,提供 SDK API 原生`errorMsg`及`errorCode`。
|
||||
|
||||
[错误码请参考该文档](https://trtc.io/zh/document/34348?platform=flutter&product=chat&menulabel=uikit)
|
||||
|
||||
#### 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(3.0.1废弃)| 好友申请已发送 | 用户申请添加其他用户为联系人 |
|
||||
| 6660102(3.0.1废弃)| 该用户已是好友 | 用户申请添加其他已是好友的用户为好友时,触发 `onTapAlreadyFriendsItem` 回调 |
|
||||
| 6660201 | 群申请已发送 | 用户申请加入需要管理员审批的群聊 |
|
||||
| 6660202 | 您已是群成员 | 用户申请加群时,判断用户已经是当前群成员,触发 `onTapExistGroup` 回调 |
|
||||
| 6660401(3.0.1废弃)| 无法定位到原消息 | 当用户需要跳转至@消息或者是引用消息时,在消息列表中查不到目标消息 |
|
||||
| 6660402 | 视频保存成功 | 用户在消息列表,点开视频消息后,选择保存视频 |
|
||||
| 6660403 | 视频保存失败 | 用户在消息列表,点开视频消息后,选择保存视频 |
|
||||
| 6660404 | 说话时间太短 | 用户发送了过短的语音消息 |
|
||||
| 6660405(3.0.1废弃)| 发送失败,视频不能大于 100MB | 用户试图发送大于 100MB 的视频 |
|
||||
| 6660406 | 图片保存成功 | 用户在消息列表,点开图片大图后,选择保存图片 |
|
||||
| 6660407 | 图片保存失败 | 用户在消息列表,点开图片大图后,选择保存图片 |
|
||||
| 6660408 | 已复制 | 用户在弹窗内选择复制文字消息 |
|
||||
| 6660409 | 暂未实现 | 用户在弹窗内选择非标功能 |
|
||||
| 6660410 | 其他文件正在接收中 | 用户点击下载文件消息时,前序下载任务还未完成 |
|
||||
| 6660411 | 正在接收中 | 用户点击下载文件消息 |
|
||||
| 6660412 | 视频消息仅限 mp4 格式 | 用户发送了一条非 mp4 格式的视频消息 |
|
||||
| 6660413 | 已加入待下载队列,其他文件下载中 | 已加入待下载队列,其他文件下载中 |
|
||||
| 6661001 | 无网络连接,无法修改 | 当用户试图在无网络环境下,修改群资料 |
|
||||
| 6661002 | 无网络连接,无法查看群成员 | 当用户试图在无网络环境下,修改群资料 |
|
||||
| 6661003 | 成功取消管理员身份 | 用户将群内其他用户移除管理员 |
|
||||
| 6661201 | 无网络连接,无法修改 | 当用户试图在无网络环境下,修改自己或联系人的资料 |
|
||||
| 6661202(3.0.1废弃)| 好友添加成功 | 在资料页添加其他用户为好友,并自动添加成功,无需验证 |
|
||||
| 6661203(3.0.1废弃)| 好友申请已发出 | 在资料页添加其他用户为好友,对方设置需要验证 |
|
||||
| 6661204(3.0.1废弃)| 当前用户在黑名单 | 在资料页添加其他用户为好友,对方在自己的黑名单内 |
|
||||
| 6661205(3.0.1废弃)| 好友添加失败 | 在资料页添加其他用户为好友,添加失败,可能是由于对方禁止加好友 |
|
||||
| 6661206(3.0.1废弃)| 好友删除成功 | 在资料页删除其他用户为好友,成功 |
|
||||
| 6661207(3.0.1废弃)| 好友删除失败 | 在资料页删除其他用户为好友,失败 |
|
||||
| 6661401 | 输入不能为空 | 当用户在录入信息时,输入了空字符串 |
|
||||
| 6661402(3.0.1废弃)| 请传入离开群组生命周期函数,提供返回首页或其他页面的导航方法 | 用户退出群或解散群时,为提供返回首页办法 |
|
||||
| 6661403 | 设备存储空间不足,建议清理,以获得更好使用体验 | 在login成功后,会自动检测设备存储空间,如果不足1GB,会提示存储空间不足 |
|
||||
|
||||
## TIMUIKitConversation
|
||||
|
||||
`TIMUIKitConversation` 为会话组件,拉取用户会话列表,默认提供一套 UI,用户也可自定义会话条目。同时提供对应的`TIMUIKitConversationController`。
|
||||
|
||||
[详细参数及用法可参考此文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitConversation/)
|
||||
|
||||
<img src="https://qcloudimg.tencent-cloud.cn/raw/a27b131d555b1158d150bd9b337c1d9d.png" style="width:60%;"/>
|
||||
|
||||
### TIMUIKitConversationController
|
||||
|
||||
#### 方法
|
||||
|
||||
- **loadData(int count)**:
|
||||
加载会话列表, count 为单次加载数量
|
||||
- **reloadData(int count)**:
|
||||
重新加载会话列表, count 为单次加载数量
|
||||
- **pinConversation({required String conversationID, required bool isPinned})**:
|
||||
会话置顶
|
||||
- **clearHistoryMessage({required V2TimConversation conversation})**:
|
||||
清除指定会话消息
|
||||
- **deleteConversation({required String conversationID})**:
|
||||
删除指定会话
|
||||
- **setConversationListener({V2TimConversationListener? listener})**:
|
||||
添加会话监听器
|
||||
- **dipose()**:
|
||||
销毁
|
||||
|
||||
---
|
||||
|
||||
## TIMUIKitChat
|
||||
|
||||
`TIMUIKitChat` 为聊天组件,提供消息列表的展示及消息发送的能力,同时支持自定义各种消息类型的展示。同时可结合 TIMUIKitChatController 实现消息的本地存储及消息预渲染。
|
||||
目前支持的消息解析:
|
||||
|
||||
- 文本消息
|
||||
- 图片消息
|
||||
- 视频消息
|
||||
- 语音消息
|
||||
- 群消息
|
||||
- 合并消息
|
||||
- 文件消息
|
||||
|
||||
[详细参数及用法可参考此文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitChat/)
|
||||
|
||||

|
||||
|
||||
### TIMUIKitChatController
|
||||
|
||||
#### 方法
|
||||
|
||||
- **clearHistory()**: 清除历史消息
|
||||
- **dispose()**:销毁
|
||||
- **sendMessage({required V2TimMessage messageInfo, String? receiverID, String? groupID, required ConvType convType})**:发送消息。根据 ConvType,receiverID/groupID 二选一传入。
|
||||
- **sendForwardMessage({required List<V2TimConversation> conversationList,})**:逐条转发
|
||||
- **sendMergerMessage({ required List<V2TimConversation> conversationList, required String title, required List<String> abstractList, required BuildContext context, })**:合并转发
|
||||
|
||||
---
|
||||
|
||||
## TIMUIKitProfile
|
||||
|
||||
`TIMUIKitProfile` 为用户详情展示。同时支持自定义添加操作项.
|
||||
|
||||
[详细参数及用法可参考此文档](https://comm.qq.com/im/doc/flutter/uikit-sdk-api/TIMUIKitProfile/)
|
||||
|
||||

|
||||
|
||||
### 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://trtc.io/zh/document/50036?platform=flutter&product=chat&menulabel=uikit)
|
||||
|
||||
```dart
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
// 全局搜索
|
||||
TIMUIKitSearch(
|
||||
onTapConversation: _handleOnConvItemTapedWithPlace, // Function(V2TimConversation, V2TimMessage? message), 跳转到特定conversation的特定message
|
||||
onEnterSearchInConversation: (V2TimConversation conversation, String initKeyword){}, // 跳转至对应Conversation的会话内搜索,请手动跳转至TIMUIKitSearchMsg组件。
|
||||
);
|
||||
|
||||
// 会话内搜索
|
||||
TIMUIKitSearchMsgDetail(
|
||||
currentConversation: conversation!,
|
||||
onTapConversation: onTapConversation,
|
||||
keyword: initKeyword ?? "",
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 如何自定义 TIMUIKitChat 组件
|
||||
|
||||
为扩展`TIMUIKitChat`组件的自定义能力,我们将该组件包含的基础子组件对外暴露,用户可根据业务去选择和使用基础子组件实现满足自身的业务。基础子组件包含如下:
|
||||
|
||||
- `TIMUIKitAppBar`
|
||||
- `TIMUIKitHistoryMessageList`
|
||||
- `TIMUIKitHistoryMessageListItem`
|
||||
- `TIMUIKitInputTextField`
|
||||
|
||||
下文将对以上组件介绍及使用用例。
|
||||
|
||||
#### TIMUIKitAppBar
|
||||
|
||||
该组件为`TIMUIKitChat`的 appbar 组件,用于自定义应用导航栏。相较于 flutter 默认的`appbar`, 该组件额外提供了`title`自适应`用户昵称, 群名称`改变而动态改变,主题色改变。具体参数如下:
|
||||
|
||||
| name | type | desc | optional |
|
||||
| -------------------- | ------ | ------------------------------------ | -------- |
|
||||
| config | AppBar | flutter appbar, 具体使用参考官方文档 | 可选 |
|
||||
| showTotalUnReadCount | bool | 显示会话总未读数, 默认为 true | 可选 |
|
||||
| conversationID | String | 会话 ID | 可选 |
|
||||
| conversationShowName | String | 会话名称 | 可选 |
|
||||
|
||||
#### TIMUIKitHistoryMessageList
|
||||
|
||||
该组件为消息列表渲染组件,提供消息自动拉取,自动加载更多,跳转到指定消息。 具体参数如下:
|
||||
|
||||
| name | type | desc | optional |
|
||||
| --------------------- | -------------------------------------------- | ---------------------------- | -------- |
|
||||
| messageList | List<V2TimMessage?> | 消息列表,渲染数据源 | 必填 |
|
||||
| tongueItemBuilder | TongueItemBuilder | 小舌头(回到底部)自定义构造器 | 可选 |
|
||||
| groupAtInfoList | List<V2TimGroupAtInfo?> | 艾特信息 | 可选 |
|
||||
| itemBuilder | Widget Function(BuildContext, V2TimMessage?) | 消息构造器 | 可选 |
|
||||
| controller | TIMUIKitHistoryMessageListController | 控制列表跳转,滚动 | 可选 |
|
||||
| onLoadMore | Function | 加载更多 | 必填 |
|
||||
| mainHistoryListConfig | ListView | 自定义 ListView | 可选 |
|
||||
|
||||
#### TIMUIKitHistoryMessageListItem
|
||||
|
||||
该组件为消息实例组件,可根据提供的消息渲染不通的消息类型,包含`文本消息`,`图片消息`, `文件消息`,`通话消息`, `语音消息`等。同时支持消息自定义,主题定制能力。
|
||||
|
||||
| name | type | desc | optional |
|
||||
| -------------------------------- | ------------------ | ------------------------------------------------------------ | -------- |
|
||||
| message | V2TimMessage | 消息实例 | 必填 |
|
||||
| onTapForOthersPortrait | Function | 远端用户头像 tap 回调 | 可选 |
|
||||
| onScrollToIndex | Function | TIMUIKitHistoryMessageListController 的 scrollToIndex 方法,用于回复消息点击跳转到指定消息 | 可选 |
|
||||
| onScrollToIndexBegin | Function | TIMUIKitHistoryMessageListController 的 scrollToIndexBegin 方法,长消息长按位置矫正 | 可选 |
|
||||
| onLongPressForOthersHeadPortrait | Function | 远端用户头像长按 | 可选 |
|
||||
| messageItemBuilder | MessageItemBuilder | 消息自定义构造器 | 可选 |
|
||||
| topRowBuilder | Function | 昵称所在行自定义 builder | 可选 |
|
||||
| bottomRowBuilder | Function | 消息显示之下 builder | 可选 |
|
||||
| showAvatar | bool | 是否显示头像 | 可选 |
|
||||
| showNickName | bool | 是否显示用户昵称 | 可选 |
|
||||
| showMessageSending | bool | 是否显示消息发送中状态 | 可选 |
|
||||
| showMessageReadRecipt | bool | 是否显示消息已读 | 可选 |
|
||||
| showGroupMessageReadRecipt | bool | 是否显示群消息已读 | 可选 |
|
||||
| allowLongPress | bool | 是否允许消息长按 | 可选 |
|
||||
| allowAvatarTap | bool | 是否允许头像 tap | 可选 |
|
||||
| allowAtUserWhenReply | bool | 是否在回复消息中提示对方 | 可选 |
|
||||
| onLongPress | Function | 消息长按回掉 | 可选 |
|
||||
| toolTipsConfig | ToolTipsConfig | 消息长按 tool tips 配置 | 可选 |
|
||||
| padding | double | 消息间的间距 | 可选 |
|
||||
| textPadding | EdgeInsetsGeometry | 文本消息内边距 | 可选 |
|
||||
| userAvatarBuilder | Function | 用户头像构造器 | 可选 |
|
||||
| themeData | MessageThemeData | 消息主题配置,可自定义字体颜色,大小等 | 可选 |
|
||||
|
||||
#### TIMUIKitInputTextField
|
||||
|
||||
该组件为输入框组件,提供`文本消息`,`图片消息`,`语音消息`等发送能力。参数如下
|
||||
|
||||
| name | type | desc | optional |
|
||||
| ------------------ | -------------------------------- | ---------------------------------- | -------- |
|
||||
| conversationID | String | 会话 ID | 必填 |
|
||||
| conversationType | String | 会话类型 | 必填 |
|
||||
| initText | String | 初始化文本 | 可选 |
|
||||
| scrollController | AutoScrollController | 用于发送消息时将消息列表滚动到底部 | 可选 |
|
||||
| hintText | String | 提示文本 | 可选 |
|
||||
| morePanelConfig | MorePanelConfig | 更多面板配置 | 可选 |
|
||||
| showSendAudio | bool | 是否显示发送语音 | 可选 |
|
||||
| showSendEmoji | bool | 是否显示发送表情 | 可选 |
|
||||
| showMorePannel | bool | 是否显示更多面板 | 可选 |
|
||||
| backgroundColor | Color | 背景色 | 可选 |
|
||||
| controller | TIMUIKitInputTextFieldController | 控制器,可控制输入框文本 | 可选 |
|
||||
| onChanged | Function | 文本改变回调事件 | 可选 |
|
||||
| customStickerPanel | Function | 自定义表情 | 可选 |
|
||||
|
||||
#### 如何使用基础组件
|
||||
|
||||
如下是一个完整的使用示例
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/controller/tim_uikit_chat_controller.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKItMessageList/tim_uikit_history_message_list.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/ui/views/TIMUIKitChat/TIMUIKitTextField/tim_uikit_text_field.dart';
|
||||
|
||||
class Chat extends StatefulWidget {
|
||||
final V2TimConversation selectedConversation;
|
||||
final V2TimMessage? initFindingMsg;
|
||||
|
||||
const ChatV2(
|
||||
{Key? key, required this.selectedConversation, this.initFindingMsg})
|
||||
: super(key: key);
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ChatV2State();
|
||||
}
|
||||
|
||||
class _ChatV2State extends State<ChatV2> {
|
||||
final TIMUIKitChatController _controller = TIMUIKitChatController();
|
||||
final TIMUIKitHistoryMessageListController _historyMessageListController =
|
||||
TIMUIKitHistoryMessageListController();
|
||||
final TIMUIKitInputTextFieldController _textFieldController =
|
||||
TIMUIKitInputTextFieldController();
|
||||
bool _haveMoreData = true;
|
||||
String? _getConvID() {
|
||||
return widget.selectedConversation.type == 1
|
||||
? widget.selectedConversation.userID
|
||||
: widget.selectedConversation.groupID;
|
||||
}
|
||||
|
||||
loadHistoryMessageList(String? lastMsgID, [int? count]) async {
|
||||
if (_haveMoreData) {
|
||||
_haveMoreData = await _controller.loadHistoryMessageList(
|
||||
count: count ?? 20,
|
||||
userID: widget.selectedConversation.userID,
|
||||
groupID: widget.selectedConversation.groupID,
|
||||
lastMsgID: lastMsgID);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TIMUIKitChatProviderScope(
|
||||
conversationID: _getConvID() ?? "",
|
||||
conversationType: widget.selectedConversation.type ?? 0,
|
||||
builder: (context, w) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_textFieldController.hideAllPanel();
|
||||
},
|
||||
child: Scaffold(
|
||||
appBar: TIMUIKitAppBar(
|
||||
config: AppBar(
|
||||
title: Text(widget.selectedConversation.showName ?? ""),
|
||||
),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TIMUIKitHistoryMessageListSelector(
|
||||
builder: (context, messageList, w) {
|
||||
return TIMUIKitHistoryMessageList(
|
||||
controller: _historyMessageListController,
|
||||
messageList: messageList,
|
||||
onLoadMore: loadHistoryMessageList,
|
||||
itemBuilder: (context, message) {
|
||||
return TIMUIKitHistoryMessageListItem(
|
||||
onScrollToIndex:
|
||||
_historyMessageListController.scrollToIndex,
|
||||
onScrollToIndexBegin:
|
||||
_historyMessageListController.scrollToIndexBegin,
|
||||
message: message!,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
conversationID: _getConvID() ?? "",
|
||||
)),
|
||||
TIMUIKitInputTextField(
|
||||
controller: _textFieldController,
|
||||
conversationID: _getConvID() ?? "",
|
||||
conversationType: widget.selectedConversation.type ?? 1,
|
||||
scrollController:
|
||||
_historyMessageListController.scrollController!,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
在如上示例中需要注意的点:
|
||||
|
||||
- 在使用基础组件时必须通过`TIMUIKitChatProviderScope`组件包裹, 他会根据传入的`conversationID` 及`conversationType` 拉取对应的历史消息.该组件提供是基于通过`MultiProvider` 实现,同时可注入自定义的`provider`.其目的在于基础组件能够消费到业务层数据,同时可通过`TIMUIKitChatController` 控制业务层数据达到数据触发视图渲染的目的。
|
||||
- 可以使用提供的`TIMUIKitAppBar`组件实现应用导航栏,同时也可根据业务的需要,自己实现 appBar.
|
||||
- `TIMUIKitChatProviderScope`会加载历史消息到业务层, 通过`TIMUIKitHistoryMessageListSelector` 获取到业务层历史消息数据用于渲染,当历史消息数据发生改变时会触发渲染。
|
||||
- 通过`TIMUIKitHistoryMessageList` 结合 `TIMUIKitHistoryMessageListItem` 实现消息页面的渲染
|
||||
- `TIMUIKitInputTextField`实现发送消息
|
||||
|
||||
基础组件可根据业务需要自行更换以及组合。如若需要控制业务层数据,可通过`TIMUIKitChatController`提供的方法。
|
||||
|
||||
## 更多阅读
|
||||
集成更多高级功能建议阅读:
|
||||
|
||||
- [本地搜索](https://trtc.io/zh/document/50036?platform=flutter&product=chat&menulabel=uikit)
|
||||
- [离线推送](https://trtc.io/zh/document/50032?platform=flutter&product=chat&menulabel=uikit)
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
## TIMUIKitCore
|
||||
`TIMUIKitCore`提供两个静态方法`getInstance` 和 `getSDKInstance`。
|
||||
- `getInstance`: 返回 `CoreServicesImpl` 实例。
|
||||
- `getSDKInstance`: 返回SDK实例。
|
||||
|
||||
`CoreServicesImpl` 为`TIMUIKit` 核心类,包含初始化、登录、登出、获取用户信息等方法。
|
||||
```dart
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
final TIMUIKitConversationController _controller =
|
||||
TIMUIKitConversationController();
|
||||
void _handleOnConvItemTaped(V2TimConversation? selectedConv) {
|
||||
/// 处理逻辑,在此可跳转至聊天界面
|
||||
}
|
||||
|
||||
List<ConversationItemSlidablePanel> _itemSlidableBuilder(
|
||||
V2TimConversation conversationItem) {
|
||||
return [
|
||||
ConversationItemSlidablePanel(
|
||||
onPressed: (context) {
|
||||
_clearHistory(conversationItem);
|
||||
},
|
||||
backgroundColor: hexToColor("006EFF"),
|
||||
foregroundColor: Colors.white,
|
||||
label: '清除聊天',
|
||||
autoClose: true,
|
||||
),
|
||||
ConversationItemSlidablePanel(
|
||||
onPressed: (context) {
|
||||
_pinConversation(conversationItem);
|
||||
},
|
||||
backgroundColor: hexToColor("FF9C19"),
|
||||
foregroundColor: Colors.white,
|
||||
label: conversationItem.isPinned! ? '取消置顶' : '置顶',
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
TIMUIKitConversation(
|
||||
onTapItem: _handleOnConvItemTaped, /// 会话Item tap回调 可用于跳转至聊天界面
|
||||
itemSlidableBuilder: _itemSlidableBuilder, /// 会话Item 向左滑动 的操作项, 可自定义会话置顶等
|
||||
controller: _controller, /// 会话组件控制器, 可通过其获取会话的数据,设置会话数据,会话置顶等操作
|
||||
itembuilder: (conversationItem) {} /// 用于自定义会话Item 的UI。 可结合TIMUIKitConversationController 实现业务逻辑
|
||||
conversationCollector: (conversation) {} /// 会话收集器,可自定义会话是否显示
|
||||
)
|
||||
```
|
||||
|
||||
### TIMUIKitConversationController
|
||||
|
||||
#### 方法:
|
||||
|
||||
- **loadData(int count)**:
|
||||
加载会话列表, count 为单次加载数量
|
||||
- **reloadData(int count)**:
|
||||
重新加载会话列表, count 为单次加载数量
|
||||
- **pinConversation({required String conversationID, required bool isPinned})**:
|
||||
会话置顶
|
||||
- **clearHistoryMessage({required V2TimConversation conversation})**:
|
||||
清除指定会话消息
|
||||
- **deleteConversation({required String conversationID})**:
|
||||
删除指定会话
|
||||
- **setConversationListener({V2TimConversationListener? listener})**:
|
||||
添加会话监听器
|
||||
- **dipose()**:
|
||||
销毁
|
||||
|
||||
---
|
||||
|
||||
## TIMUIKitChat
|
||||
`TIMUIKitChat` 为聊天组件,提供消息列表的展示及消息发送的能力,同时支持自定义各种消息类型的展示。同时可结合TIMUIKitChatController 实现消息的本地存储及消息预渲染。
|
||||
目前支持的消息解析:
|
||||
- 文本消息
|
||||
- 图片消息
|
||||
- 视频消息
|
||||
- 语音消息
|
||||
- 群消息
|
||||
- 合并消息
|
||||
- 文件消息
|
||||
|
||||
```dart
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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: () {
|
||||
/// 自定义好友申请项构造器
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -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) 新的联系人
|
||||
|
||||

|
||||
|
||||
## 支持平台
|
||||
- Android
|
||||
- ios
|
||||
|
||||
## 如何集成?
|
||||
如下会介绍如何使用`TIMUIKit`快速构建一个简单的即时通信应用.
|
||||
|
||||
### 步骤1: 创建Flutter应用
|
||||
参考Flutter[文档](https://flutter.cn/docs/get-started/test-drive?tab=terminal)快速创建一个flutter应用。
|
||||
|
||||
### 步骤2: 安装依赖
|
||||
在`pubspec.yaml`文件中的`dependencies`下添加`tencent_cloud_chat_uikit`。或者执行如下命令:
|
||||
```
|
||||
// step 1:
|
||||
flutter pub add tencent_cloud_chat_uikit
|
||||
|
||||
// step 2:
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
### 步骤3: 初始化TIMUIKit
|
||||
|
||||
在`initState`中初始化`TIMUIKit`,项目启动只需要初始化一次即可。
|
||||
```dart
|
||||
/// main.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
void main() {
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
// This widget is the root of your application.
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'TIMUIKit Demo',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
home: MyHomePage(title: 'TIMUIKit Demo'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
MyHomePage({Key? key, required this.title}) : super(key: key);
|
||||
final String title;
|
||||
|
||||
@override
|
||||
_MyHomePageState createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_coreInstance.init(
|
||||
sdkAppID: 0, // 控制台申请的sdkAppID
|
||||
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG,
|
||||
listener: V2TimSDKListener());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Container(),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
### 步骤4: 获取签名和登录
|
||||
>- 正确的 `UserSig` 签发方式是将 `UserSig` 的计算代码集成到您的服务端,并提供面向 App 的接口,在需要 `UserSig` 时由您的 App 向业务服务器发起请求获取动态 `UserSig`。更多详情请参见 [服务端生成 UserSig](https://cloud.tencent.com/document/product/647/17275#Server)。
|
||||
|
||||
添加两个`TextField`用于输入`userID` 和 `userSig`。点击登录后掉用登录接口。
|
||||
```dart
|
||||
/// main.dart
|
||||
/// 省略
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
/// 获取 TIMUIKitCore Instance
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
String userID = "";
|
||||
String userSig = "";
|
||||
|
||||
/// 省略
|
||||
|
||||
void _login() {
|
||||
// 登录
|
||||
_coreInstance.login(userID: userID, userSig: userSig);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
TextField(
|
||||
onChanged: ((value) {
|
||||
setState(() {
|
||||
userID = value;
|
||||
});
|
||||
}),
|
||||
decoration: InputDecoration(hintText: "userID"),
|
||||
),
|
||||
TextField(
|
||||
onChanged: ((value) {
|
||||
setState(() {
|
||||
userSig = value;
|
||||
});
|
||||
}),
|
||||
decoration: InputDecoration(hintText: "userSig"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: (() {
|
||||
_login();
|
||||
}),
|
||||
child: const Text("登录"))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 步骤4: 集成所需组件
|
||||
- 创建`message.dart`文件集成`TIMUIKitConversation` 和 `TIMUIKitChat`包含不仅限于此。可根据您的需求集成更多的组件。
|
||||
- 修改`main.dart`中代码,登录成功后跳转至该页面。
|
||||
```dart
|
||||
/// message.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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: 发送语音、图片、文件等消息闪退?
|
||||
请查看是否打开了`相机、麦克风、相册`等权限。
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
|
||||
腾讯云IM Flutter TUIKit默认自带 英文/简体中文/繁体中文/日语/韩语 语言包,作为界面展示语言。
|
||||
|
||||
根据此文档指引,您可以使用默认语言包,也可自定义语言翻译表述,并增添额外的非自带语言的支持。
|
||||
|
||||

|
||||
|
||||
## 使用自带语言
|
||||
|
||||
如果您的App,需要的语言仅包括英语/简体中文/繁体中文/日语/韩语,请参考本部分。
|
||||
|
||||
### 跟随系统语言
|
||||
|
||||
直接使用TUIKit即可,无需额外步骤。插件内部会跟随系统语言自适应。
|
||||
|
||||
### 预指定显示的语言
|
||||
|
||||
如果您需要在初始化是,手动设置TUIKit界面语言,请在`TIMUIKitCore.getInstance()`中调用`init`方法时,传入需要的语言。
|
||||
|
||||
```dart
|
||||
import 'package:tim_ui_kit/tim_ui_kit.dart';
|
||||
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
|
||||
final isInitSuccess = await _coreInstance.init(
|
||||
language: LanguageEnum.en, // 请在此处定义语言,枚举值见下方
|
||||
// ...其他配置
|
||||
);
|
||||
```
|
||||
|
||||
语言可选项,枚举值为:
|
||||
|
||||
```dart
|
||||
enum LanguageEnum {
|
||||
zhHant, //繁体中文
|
||||
zhHans, //简体中文
|
||||
en, // 英文
|
||||
ko, // 韩语
|
||||
ja // 日语
|
||||
}
|
||||
```
|
||||
|
||||
### 实时动态修改
|
||||
|
||||
调用 `I18nUtils(null, language);` 即可。此处的 `language` 为 [ISO 639-1 语言代码](#code)。示例如下:
|
||||
|
||||
```dart
|
||||
I18nUtils(null, "en");
|
||||
```
|
||||
|
||||
>? 语言代码清单见[附录](#code)。
|
||||
|
||||
## 使用更多语言/自定义翻译表述
|
||||
|
||||
如果您需要支持,除 英文/简体中文/繁体中文/日语/韩语 外的更多语言,或更改我们部分词条的翻译,请参考本部分。
|
||||
|
||||
>? 本方案仅适用于,目标语言为的阅读方向为从左至右的语言。对于阅读方向从右至左的小语种,如阿拉伯语,请自行在[GitHub fork](https://github.com/TencentCloud/chat-uikit-flutter)一份我们的源码,完成自定义左右镜像开发适配。
|
||||
|
||||
### 新增语言词条包
|
||||
|
||||
本章节的核心为本部分, 即,将您自定义的国际化多语言词条库文件,注入腾讯云IM项目内。
|
||||
|
||||
#### 获取语言模板
|
||||
|
||||
在您的项目中,允许如下代码。
|
||||
|
||||
```shell
|
||||
flutter pub run tencent_im_base
|
||||
```
|
||||
|
||||
根据提示,选择 `A` 选项。
|
||||
|
||||

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

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

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

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

|
||||
|
||||
>? 如果您是团队协同开发,或使用了远程流水线编译。需要在您同事电脑中或流水线编译命令脚本中,使用同样的方式,执行本章节所述方法。
|
||||
|
||||
### 跟随系统语言
|
||||
|
||||
直接使用TUIKit即可,无需额外步骤。
|
||||
|
||||
只要您新增的语言词条包命名[符合标准](#code),插件内部会跟随系统语言自适应。
|
||||
|
||||
### 预指定显示的语言
|
||||
|
||||
如果您需要在初始化是,手动设置TUIKit界面语言,请在`TIMUIKitCore.getInstance()`中调用`init`方法时,传入需要的语言。
|
||||
|
||||
```dart
|
||||
import 'package:tim_ui_kit/tim_ui_kit.dart';
|
||||
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
|
||||
final isInitSuccess = await _coreInstance.init(
|
||||
extraLanguage: "ja", // 请在此处定义语言,ISO 639-1 语言代码 见下方
|
||||
// ...其他配置
|
||||
);
|
||||
```
|
||||
|
||||
### 实时动态修改
|
||||
|
||||
调用 `I18nUtils(null, language);` 即可。此处的 `language` 为 [ISO 639-1 语言代码](#code)。示例如下:
|
||||
|
||||
```dart
|
||||
I18nUtils(null, "ja");
|
||||
```
|
||||
|
||||
>? 语言代码清单见[附录](#code)。
|
||||
|
||||
[](id:code)
|
||||
|
||||
## 附录:语言代码表
|
||||
|
||||
| 语言 | 代码 | 语言 | 代码 |
|
||||
|--------|--------|--------|--------|
|
||||
| 阿拉伯语 | ar | 保加利亚语 | bg |
|
||||
| 克罗地亚语 | hr | 捷克语 | cs |
|
||||
| 丹麦语 | da | 德语 | de |
|
||||
| 希腊语 | el | 英语 | en |
|
||||
| 爱沙尼亚语 | et | 西班牙语 | es |
|
||||
| 芬兰语 | fi | 法语 | fr |
|
||||
| 爱尔兰语 | ga | 印地语 | hi |
|
||||
| 匈牙利语 | hu | 希伯来语 | he |
|
||||
| 意大利语 | it | 日语 | ja |
|
||||
| 朝鲜语/韩语 | ko | 拉脱维亚语 | lv |
|
||||
| 立陶宛语 | lt | 荷兰语 | nl |
|
||||
| 挪威语 | no | 波兰语 | pl |
|
||||
| 葡萄牙语 | pt | 瑞典语 | sv |
|
||||
| 罗马尼亚语 | ro | 俄语 | ru |
|
||||
| 塞尔维亚语 | sr | 斯洛伐克语 | sk |
|
||||
| 斯洛文尼亚语 | sl | 泰语 | th |
|
||||
| 土耳其语 | tr | 乌克兰语 | uk-UA |
|
||||
| 中文(简体) | zh-Hans | 中文(繁体) | zh-Hant |
|
||||
|
||||
完整版[请见此处](https://quickref.me/iso-639-1)。
|
||||
|
||||
## 联系我们[](id:contact)
|
||||
|
||||
如果您在接入使用过程中有任何疑问,请通过如下方式联系我们。
|
||||
|
||||
- [Telegram Group](https://t.me/+1doS9AUBmndhNGNl)
|
||||
- [WhatsApp Group](https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A)
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
|
||||
English, Simplified Chinese, Traditional Chinese, Japanese and Korean have been embedded in Tencent Cloud Chat TUIKit as the default interface languages.
|
||||
|
||||
Adding other interface languages or modifying the current language items are available for you, according to the instructions of this tutorial.
|
||||
|
||||

|
||||
|
||||
## Using the default languages
|
||||
|
||||
If only Chinese(both traditional and simplified), English, Japanese and Korean are needed for your application, please refer to this section.
|
||||
|
||||
### Choosing device language
|
||||
|
||||
No further steps are needed, as meeting device language can be automatically.
|
||||
|
||||
### Pre-set the language manually
|
||||
|
||||
If you tend to specify the language manually, please provide the target language Enum to `init()` in `TIMUIKitCore.getInstance()`.
|
||||
|
||||
```dart
|
||||
import 'package:tim_ui_kit/tim_ui_kit.dart';
|
||||
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
|
||||
final isInitSuccess = await _coreInstance.init(
|
||||
language: LanguageEnum.en, // Enums as below
|
||||
// ...Other configurations
|
||||
);
|
||||
```
|
||||
|
||||
Enum options for language:
|
||||
|
||||
|
||||
```dart
|
||||
enum LanguageEnum {
|
||||
zhHant, // Chinese, traditional
|
||||
zhHans, // Chinese, simplified
|
||||
en, // English
|
||||
ko, // korean
|
||||
ja // Japanese
|
||||
}
|
||||
```
|
||||
|
||||
### Modify language dynamically
|
||||
|
||||
Please just invoking `I18nUtils(null, language);`, while the `language` here should be set as the [ISO 639 Language codes](#code).
|
||||
|
||||
Example code:
|
||||
|
||||
```dart
|
||||
I18nUtils(null, "en");
|
||||
```
|
||||
|
||||
## Need more languages / customize translation items
|
||||
|
||||
Adding languages, apart from English, Simplified Chinese, Traditional Chinese, Japanese and Korean, or modifying some translation items words, can be referred to this section.
|
||||
|
||||
>? This solution only works for languages with a left-to-right reading direction. For small languages that read from right to left, such as Arabic, please fork our source code from [our GitHub repository](https://github.com/TencentCloud/chat-uikit-flutter) to complete the custom left and right mirroring Development adaptation, and import to your project manually.
|
||||
|
||||
### Adding language translation files
|
||||
|
||||
The key of this section is this part, that is, inject your custom internationalized language file into the Tencent Cloud Chat.
|
||||
|
||||
#### Get the language template
|
||||
|
||||
Run the following command, and choose `A` as instruction.
|
||||
|
||||
```shell
|
||||
flutter pub run tencent_im_base
|
||||
```
|
||||
|
||||

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

|
||||
|
||||
Duplicate for language files, based on the template you are most familiar with.
|
||||
|
||||
The newly duplicated language files should be named as `strings_${language code}.i18n.json`. While, `${language code}` should be replaced by [ISO 639 Language Codes](#code). Such as, the file containing Danish language items should be named as `strings_da.i18n.json`.
|
||||
|
||||
Duplicate multiple language files, if you need to support multiple other languages.
|
||||
|
||||
#### Customize translations
|
||||
|
||||
Now, you can modify the language files generated in the previous step.
|
||||
|
||||
Open each language file, including the files you just duplicated, except `strings_zh-Hans.i18n.json`, translate or modify each `value` to target language, while keeping the md5 key unchanged.
|
||||
|
||||

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

|
||||
|
||||
#### Activate those language files
|
||||
|
||||
Run the following command, and choose `B` as instruction.
|
||||
|
||||
```dart
|
||||
flutter pub run tencent_im_base
|
||||
```
|
||||
|
||||
After the script has finished, those customization languages are activated on your local Flutter environment.
|
||||
|
||||

|
||||
|
||||
>? If you are developing with a team collaboratively, or using DevOps pipeline compilation. You also need to execute this solution on your colleague's computer or in the DevOps pipeline compilation command script.
|
||||
|
||||
### Choosing device language
|
||||
|
||||
No further steps are needed, as meeting device language can be automatically.
|
||||
|
||||
### Pre-set the language manually
|
||||
|
||||
If you tend to specify the language manually, please provide the [ISO 639 Language Codes](#code) of the language to `init()` in `TIMUIKitCore.getInstance()`.
|
||||
|
||||
```dart
|
||||
import 'package:tim_ui_kit/tim_ui_kit.dart';
|
||||
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
|
||||
final isInitSuccess = await _coreInstance.init(
|
||||
extraLanguage: "ja", // ISO 639 Language Codes
|
||||
// ...Other configurations
|
||||
);
|
||||
```
|
||||
|
||||
### Modify language dynamically
|
||||
|
||||
Please just invoking `I18nUtils(null, language);`, while the `language` here should be set as the [ISO 639 Language codes](#code).
|
||||
|
||||
Example code:
|
||||
|
||||
```dart
|
||||
I18nUtils(null, "en");
|
||||
```
|
||||
|
||||
[](id:code)
|
||||
|
||||
## Appendix: Language codes
|
||||
|
||||
| Language | Code | Language | Code |
|
||||
|--------|--------|--------|--------|
|
||||
| Arabic | ar | Bulgarian | bg |
|
||||
| Croatian | hr | Czech | cs |
|
||||
| Danish | da | German | de |
|
||||
| Greek | el | English | en |
|
||||
| Estonian | et | Spanish | es |
|
||||
| Finnish | fi | French | fr |
|
||||
| Irish | ga | Hindi | hi |
|
||||
| Hungarian | hu | Hebrew | he |
|
||||
| Italian | it | Japanese | ja |
|
||||
| Korean | ko | Latvian | lv |
|
||||
| Lithuanian | lt | Dutch | nl |
|
||||
| Norwegian | no | Polish | pl |
|
||||
| Portuguese | pt | Swedish | sv |
|
||||
| Romanian | ro | Russian | ru |
|
||||
| Serbian | sr | Slovak | sk |
|
||||
| Slovenian | sl | Thai | th |
|
||||
| Turkish | tr | Ukrainian | uk |
|
||||
| Chinese (Simplified)) | zh-Hans | Chinese (Traditional) | zh-Hant |
|
||||
|
||||
## Contact us[](id:contact)
|
||||
|
||||
If there's anything unclear or you have more ideas, feel free to contact us!
|
||||
|
||||
- [Telegram Group](https://t.me/+1doS9AUBmndhNGNl)
|
||||
- [WhatsApp Group](https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
## TIMUIKit
|
||||
TUIKit 是基于 IM SDK 实现的一套 UI 组件,其包含会话、聊天、搜索、关系链、群组、音视频通话等功能,基于 UI 组件您可以像搭积木一样快速搭建起自己的业务逻辑。
|
||||
|
||||
#### 架构
|
||||
|
||||

|
||||
|
||||
- [快速集成文档](https://git.woa.com/29294-22989-29805-29810/im-flutter-uikit/blob/feature/add-doc/package_src/tencent_cloud_chat_uikit/doc/FAST_INTEGRATED.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)。
|
||||
|
||||

|
||||
|
||||
[](id:part2)
|
||||
|
||||
## Part2:选择合适的方案集成Flutter SDK
|
||||
|
||||
|
||||
IM提供了三种方式来集成,您可以选择最合适的方案来集成:
|
||||
| | 适用场景 |
|
||||
|---------|---------|
|
||||
| [使用DEMO](#part3) | IM demo是一个完整的聊天app,代码已开源,如果您需要实现聊天类似场景,可以使用demo进行二次开发。 [点此体验demo](https://cloud.tencent.com/document/product/269/36852) |
|
||||
| [含UI集成](#part4) | IM的UI组件库`TUIKit`提供了通用的 UI 组件,例如会话列表、聊天界面和联系人列表等,开发者可根据实际业务需求通过该组件库快速地搭建自定义 IM 应用。**推荐优先使用该方案** |
|
||||
| [自实现UI集成](#part5) | 如果TUIKit不能满足您应用的界面需求,或者您需要比较多的定制,可以使用该方案。 |
|
||||
|
||||
|
||||
为帮助您更好的理解IM SDK的各API,[我们还提供了API Example](https://github.com/TencentCloud/TIMSDK/tree/master/Flutter/IMSDK/im-flutter-plugin/tencent_im_sdk_plugin/example),演示各API的调用及监听的触发。
|
||||
|
||||
|
||||
[](id:part3)
|
||||
|
||||
## Part3:使用DEMO
|
||||
|
||||
### 跑通DEMO
|
||||
|
||||
1. 下载demo源码、安装依赖
|
||||
|
||||
```shell
|
||||
#clone 代码
|
||||
git clone https://github.com/TencentCloud/TIMSDK.git
|
||||
|
||||
#进入flutter的demo目录
|
||||
cd TIMSDK/Flutter/Demo/im-flutter-uikit
|
||||
|
||||
#安装依赖
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
2. 运行demo项目:
|
||||
|
||||
```shell
|
||||
#启动demo项目,请替换SDK_APPID、KEY两个参数
|
||||
flutter run --dart-define=SDK_APPID={YOUR_SDKAPPID} --dart-define=ISPRODUCT_ENV=false --dart-define=KEY={YOUR_KEY}
|
||||
```
|
||||
|
||||
>?
|
||||
>
|
||||
>- `--dart-define=SDK_APPID={YOUR_SDKAPPID}` 其中`{YOUR_SDKAPPID}`需替换成您自己应用的 SDKAppID。
|
||||
>- `--dart-define=ISPRODUCT_ENV=false` 对开发生产环境做判断,如您是开发环境请用 false。
|
||||
>- `--dart-define=KEY={YOUR_KEY}` 其中`{YOUR_KEY}`需替换成 [Part1:创建测试用户](#part1) 中的`密钥(Key)`信息。
|
||||
>
|
||||
|
||||
#### 也可以使用 IDE 运行:(可选步骤)
|
||||
|
||||
<dx-tabs>
|
||||
::: Android 平台[](id:android)
|
||||
1. 在 Android Studio 打开 discuss/andorid 目录。
|
||||

|
||||
2. 启动一个 Android 的模拟器,单击 **Build And Run**,Demo 可以运行起来。您可以随机输入一个 UserID(数字字母组合)。
|
||||
>?UI 可能会有部分调整更新,请以最新版为准。
|
||||
:::
|
||||
::: iOS 平台[](id:ios)
|
||||
1. 打开 Xcode,打开文件 discuss/ios/Runner.xcodeproj:
|
||||

|
||||
2. 连接 iPhone 真机,单击 **Build And Run**,iOS 工程等待编译完成,会有新窗口弹出 Xcode 工程。
|
||||
3. 打开 iOS 工程,设置主 Target 的 Signing & Capabilities(需要苹果开发者帐号),让项目可以在 iPhone 真机上运行。
|
||||
4. 启动项目,在真机上进行 Demo 的调试。
|
||||

|
||||
:::
|
||||
</dx-tabs>
|
||||
|
||||
#### DEMO代码结构概览
|
||||
|
||||
>? 我们DEMO的UI及业务逻辑部分,使用Flutter TUIKit。DEMO层本身仅用于构建APP,处理导航跳转及调用实例化TUIKit中各个组件。
|
||||
|
||||
|
||||
| 文件夹 | 介绍 |
|
||||
|---------|---------|
|
||||
| lib | 程序核心目录 |
|
||||
| lib/i18n | 国际化相关代码。这里的国际化,不包含TUIKit本身的国际化能力和国际化词条,您可按需引入 |
|
||||
| lib/src | 项目主体目录 |
|
||||
| lib/src/pages | 本DEMO几个重点导航页。项目初始化完成后,由 `app.dart` 负责展示加载动画,并判断登陆态,将用户引导至 `login.dart` 或 `home_page.dart`。用户登录后,会将登陆信息通过 `shared_preference` 插件,存储至本地。以后每次启动应用,若在本地发现原来的登录信息,则自动使用该信息进行登录,若无或登录失败,则引导至登录页。自动登录过程中,用户还在 `app.dart` ,可看到加载动画。`home_page.dart`含一个底部Tab,支撑本demo的四个主功能页的切换。 |
|
||||
| lib/utils | 一些工具函数类 |
|
||||
|
||||
|
||||
基本上,`lib/src` 内每个dart文件引入了一个TUIKit组件,在文件内,实例化组件后,即可渲染页面。
|
||||
|
||||
主要文件如下:
|
||||
|
||||
|
||||
| lib/src 主要文件 | 文件介绍 |
|
||||
|---------|---------|
|
||||
| add_friend.dart | 申请添加好友页面,使用 `TIMUIKitAddFriend` 组件|
|
||||
| add_group.dart | 申请入群页面,使用 `TIMUIKitAddGroup` 组件|
|
||||
| blacklist.dart| 黑名单列表页面,使用 `TIMUIKitBlackList` 组件 |
|
||||
| chat.dart | 主聊天页面,使用全套TUIKit聊天能力,使用 `TIMUIKitChat` 组件 |
|
||||
| chatv2.dart | 主聊天页面,使用原子化能力,使用 `TIMUIKitChat` 组件 |
|
||||
| contact.dart | 联系人页面 ,使用 `TIMUIKitContact` 组件|
|
||||
| conversation.dart | 会话列表界面,使用 `TIMUIKitConversation` 组件 |
|
||||
| create_group.dart | 发起群聊页面,纯Demo实现,未使用组件 |
|
||||
| group_application_list.dart | 入群申请列表页面,使用 `TIMUIKitGroupApplicationList` 组件 |
|
||||
| group_list.dart | 群列表页面,使用 `TIMUIKitGroup` 组件 |
|
||||
| group_profile.dart | 群资料及群管理页面,使用 `TIMUIKitGroupProfile` 组件 |
|
||||
| newContact.dart | 联系人好友申请页面,使用 `TIMUIKitNewContact` 组件 |
|
||||
| routes.dart | Demo的路由,导航至登录页 `login.dart` 或主页面 `home_page.dart`。 |
|
||||
| search.dart | 全局搜索及会话内搜索页面,使用 `TIMUIKitSearch`(全局搜索) 及 `TIMUIKitSearchMsgDetail`(会话内搜索) 组件 |
|
||||
| user_profile.dart | 用户信息及关系链维护页面,使用 `TIMUIKitProfile` 组件|
|
||||
|
||||
大部分TUIKit组件需要传入导航跳转方法,因此需要DEMO层处理 `Navigator` 。
|
||||
|
||||
以上介绍了我们的DEMO,您可以直接直接修改它二次开发,或参照它实现您的业务需求。
|
||||
|
||||
[](id:part4)
|
||||
|
||||
## Part4:含UI集成,使用TUIKit组件库,半天完成IM能力植入
|
||||
|
||||
TUIKit 是基于腾讯云 IM SDK 的一款 UI 组件库,它提供了一些通用的 UI 组件,例如会话列表、聊天界面和联系人列表等,开发者可根据实际业务需求通过该组件库快速地搭建自定义 IM 应用。[点此查看TUIKit详细介绍](https://cloud.tencent.com/document/product/269/70746)
|
||||
|
||||
### 前提条件
|
||||
|
||||
您已经完成创建Flutter项目,或有可以基于的Flutter项目。
|
||||
|
||||
### 接入步骤
|
||||
|
||||
#### 安装IM TUIkit
|
||||
|
||||
我们的TUIkit已经内含IM SDK,因此仅需安装`tencent_cloud_chat_uikit`,不需要再安装基础im sdk。
|
||||
|
||||
```shell
|
||||
#在命令行执行:
|
||||
flutter pub add tencent_cloud_chat_uikit
|
||||
```
|
||||
|
||||
#### 初始化
|
||||
|
||||
在您应用启动时,初始化TUIKit。
|
||||
|
||||
请务必保证先执行 `TIMUIKitCore.getInstance()` ,再调用初始化函数 `init()` ,并将您的[sdkAppID]传入。
|
||||
|
||||
```dart
|
||||
/// main.dart
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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功能首页,其涵盖了与所有有聊天记录的用户及群聊的会话。
|
||||
|
||||

|
||||
|
||||
请创建一个 `Conversation` 类,`body` 中使用 `TIMUIKitConversation` 组件,渲染会话列表。
|
||||
|
||||
您仅需传入一个 `onTapItem` 事件的处理函数,用于跳转至具体会话聊天页的导航。关于 `Chat` 类,会在下一步讲解。
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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,
|
||||
),
|
||||
));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 实现:会话聊天页面
|
||||
|
||||
该页面由顶部主体聊天历史记录及底部发送消息模块组成。
|
||||
|
||||

|
||||
|
||||
请创建一个 `Chat` 类,`body` 中使用 `TIMUIKitChat` 组件,渲染聊天页面。
|
||||
|
||||
您最好传入一个 `onTapAvatar` 事件的处理函数,用于跳转至联系人的详细信息页。关于 `UserProfile` 类,会在下一步讲解。
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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` 。
|
||||
|
||||

|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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/tencent_cloud_chat_uikit/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/tencent_cloud_chat_uikit)。
|
||||
|
||||
[](id:part5)
|
||||
|
||||
## Part5:自实现UI集成
|
||||
|
||||
### 前提条件
|
||||
|
||||
您已经完成创建Flutter项目,或有可以基于的Flutter项目。
|
||||
|
||||
### 接入步骤
|
||||
|
||||
#### 安装IM SDK
|
||||
|
||||
[本节详细文档](https://cloud.tencent.com/document/product/269/75286)
|
||||
|
||||
使用如下命令,安装Flutter IM SDK最新版本。
|
||||
|
||||
在命令行执行:
|
||||
|
||||
```shell
|
||||
flutter pub add tencent_im_sdk_plugin
|
||||
```
|
||||
|
||||
#### 完成SDK初始化
|
||||
|
||||
[本节详细文档](https://cloud.tencent.com/document/product/269/75293)
|
||||
|
||||
调用`initSDK`,完成SDK初始化。
|
||||
|
||||
将您的[sdkAppID]传入。
|
||||
|
||||
```Dart
|
||||
import 'package:tencent_im_sdk_plugin/enum/V2TimSDKListener.dart';
|
||||
import 'package:tencent_im_sdk_plugin/enum/log_level_enum.dart';
|
||||
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
|
||||
TencentImSDKPlugin.v2TIMManager.initSDK(
|
||||
sdkAppID: 0, // Replace 0 with the SDKAppID of your IM application when integrating
|
||||
loglevel: LogLevelEnum.V2TIM_LOG_DEBUG, // Log
|
||||
listener: V2TimSDKListener(),
|
||||
);
|
||||
```
|
||||
|
||||
在本步骤,你可以针对IM SDK挂载一些监听,主要包括网络状态及用户信息变更等,[详情可参考该文档](https://pub.dev/documentation/tencent_im_sdk_plugin_platform_interface/latest/enum_V2TimSDKListener/V2TimSDKListener-class.html)。
|
||||
|
||||
#### 登录测试账户
|
||||
|
||||
[本节详细文档](https://cloud.tencent.com/document/product/269/75296)
|
||||
|
||||
此时,您可以使用最开始的时候,在控制台生成的测试账户,完成登录验证。
|
||||
|
||||
调用`TencentImSDKPlugin.v2TIMManager.login`方法,登录一个测试账户。
|
||||
|
||||
当返回值`res.code`为0时,登录成功。
|
||||
|
||||
```dart
|
||||
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
|
||||
V2TimCallback res = await TencentImSDKPlugin.v2TIMManager.login(
|
||||
userID: userID,
|
||||
userSig: userSig,
|
||||
);
|
||||
```
|
||||
|
||||
>? 该账户仅限开发测试使用。应用上线前,正确的 `UserSig` 签发方式是将 `UserSig` 的计算代码集成到您的服务端,并提供面向 App 的接口,在需要 `UserSig` 时由您的 App 向业务服务器发起请求获取动态 `UserSig`。更多详情请参见 [服务端生成 UserSig](https://cloud.tencent.com/document/product/269/32688#GeneratingdynamicUserSig)。
|
||||
|
||||
#### 发送消息
|
||||
|
||||
[本节详细文档](https://cloud.tencent.com/document/product/269/75317)
|
||||
|
||||
此处以发送文本消息举例,其流程为:
|
||||
|
||||
1. 调用 `createTextMessage(String)`创建一个文本消息。
|
||||
2. 根据其返回值,拿到消息ID。
|
||||
3. 调用 `sendMessage()` 发送该ID的消息。`receiver`可填入您此前创建的另一个测试账户ID。发送单聊消息无需填入`groupID`。
|
||||
|
||||
代码示例:
|
||||
|
||||
```dart
|
||||
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
|
||||
|
||||
V2TimValueCallback<V2TimMsgCreateInfoResult> createMessage =
|
||||
await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.createTextMessage(text: "The text to create");
|
||||
|
||||
String id = createMessage.data!.id!; // The message creation ID
|
||||
|
||||
V2TimValueCallback<V2TimMessage> res = await TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.sendMessage(
|
||||
id: id, // Pass in the message creation ID to
|
||||
receiver: "The userID of the destination user",
|
||||
groupID: "The groupID of the destination group",
|
||||
);
|
||||
```
|
||||
|
||||
> 如果发送失败,可能是由于您的sdkAppID不支持陌生人发送消息,您可至控制台开启,用于测试。
|
||||
>
|
||||
> [请点击此链接](https://console.cloud.tencent.com/im/login-message),关闭[好友关系链检查]。
|
||||
|
||||
#### 获取会话列表
|
||||
|
||||
[本节详细文档](https://cloud.tencent.com/document/product/269/75368)
|
||||
|
||||
在上一个步骤中,完成发送测试消息,现在可登录另一个测试账户,拉取会话列表。
|
||||
|
||||

|
||||
|
||||
获取会话列表的方式有两种:
|
||||
|
||||
1. 监听长连接回调,实时更新会话列表。
|
||||
2. 请求API,根据分页一次性获取会话列表。
|
||||
|
||||
常见应用场景为:
|
||||
|
||||
在启动应用程序后立即获取会话列表,然后监听长连接以实时更新会话列表的变化。
|
||||
|
||||
##### 一次性请求会话列表
|
||||
|
||||
为了获取会话列表,需要维护`nextSeq`,记录当前位置。
|
||||
|
||||
```dart
|
||||
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
|
||||
|
||||
String nextSeq = "0";
|
||||
|
||||
getConversationList() async {
|
||||
V2TimValueCallback<V2TimConversationResult> res = await TencentImSDKPlugin
|
||||
.v2TIMManager
|
||||
.getConversationManager()
|
||||
.getConversationList(nextSeq: nextSeq, count: 10);
|
||||
|
||||
nextSeq = res.data?.nextSeq ?? "0";
|
||||
}
|
||||
```
|
||||
|
||||
此时,你可以看到您在上一步中,使用另一个测试账号,发来消息的会话。
|
||||
|
||||
##### 监听长链接实时获取会话列表
|
||||
|
||||
您在此步骤中,需要先在SDK上挂载监听,然后处理回调事件,更新UI。
|
||||
|
||||
1. 挂载监听。
|
||||
|
||||
```dart
|
||||
await TencentImSDKPlugin.v2TIMManager
|
||||
.getConversationManager()
|
||||
.setConversationListener(
|
||||
listener: new V2TimConversationListener(
|
||||
onConversationChanged: (List<V2TimConversation> list){
|
||||
_onConversationListChanged(list);
|
||||
},
|
||||
onNewConversation:(List<V2TimConversation> list){
|
||||
_onConversationListChanged(list);
|
||||
},
|
||||
```
|
||||
|
||||
2. 处理回调事件,将最新的会话列表展示在界面上。
|
||||
|
||||
```dart
|
||||
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
|
||||
|
||||
List<V2TimConversation> _conversationList = [];
|
||||
|
||||
_onConversationListChanged(List<V2TimConversation> list) {
|
||||
for (int element = 0; element < list.length; element++) {
|
||||
int index = _conversationList.indexWhere(
|
||||
(item) => item!.conversationID == list[element].conversationID);
|
||||
if (index > -1) {
|
||||
_conversationList.setAll(index, [list[element]]);
|
||||
} else {
|
||||
_conversationList.add(list[element]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 接收消息
|
||||
|
||||
[本节详细文档](https://cloud.tencent.com/document/product/269/75320)
|
||||
|
||||
通过腾讯云IM Ffltter SDK接收消息有两种方式:
|
||||
|
||||
1. 监听长连接回调,实时获取消息变化,更新渲染历史消息列表。
|
||||
2. 请求API,根据分页一次性获取历史消息。
|
||||
|
||||
常见应用场景为:
|
||||
|
||||
1. 界面进入新的会话后,首先一次性请求一定数量的历史消息,用于展示历史消息列表。
|
||||
2. 监听长链接,实时接收新的消息,将其添加进历史消息列表中。
|
||||
|
||||
##### 一次性请求历史消息列表
|
||||
|
||||
每页拉取的消息数量不能太大,否则会影响拉取速度。建议此处设置为20左右。
|
||||
|
||||
您应该动态记录当前页数,用于下一轮请求。
|
||||
|
||||
示例代码如下:
|
||||
|
||||
```dart
|
||||
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
|
||||
|
||||
V2TimValueCallback<List<V2TimMessage>> res = await TencentImSDKPlugin
|
||||
.v2TIMManager
|
||||
.getMessageManager()
|
||||
.getGroupHistoryMessageList(
|
||||
groupID: "groupID",
|
||||
count: 20,
|
||||
lastMsgID: "",
|
||||
);
|
||||
|
||||
List<V2TimMessage> msgList = res.data ?? [];
|
||||
|
||||
// here you can use msgList to render your message list
|
||||
}
|
||||
```
|
||||
|
||||
##### 监听长链接实时获取新消息
|
||||
|
||||
历史消息列表初始化后,新消息来自长链接 `V2TimAdvancedMsgListener.onRecvNewMessage`。
|
||||
|
||||
`onRecvNewMessage`回调被触发后,您可以按需将新消息添加进历史消息列表中。
|
||||
|
||||
绑定监听器示例代码如下:
|
||||
|
||||
```dart
|
||||
import 'package:tencent_im_sdk_plugin/tencent_im_sdk_plugin.dart';
|
||||
|
||||
final adVancesMsgListener = V2TimAdvancedMsgListener(
|
||||
onRecvNewMessage: (V2TimMessage newMsg) {
|
||||
_onReceiveNewMsg(newMsg);
|
||||
},
|
||||
/// ... other listeners related to message
|
||||
);
|
||||
|
||||
TencentImSDKPlugin.v2TIMManager
|
||||
.getMessageManager()
|
||||
.addAdvancedMsgListener(listener: adVancesMsgListener);
|
||||
```
|
||||
|
||||
此时,您已基本完成IM模块开发,可以发送接收消息,也可以进入不同的会话。
|
||||
|
||||
您可以继续完成[群组](https://cloud.tencent.com/document/product/269/75697),[用户资料](https://cloud.tencent.com/document/product/269/75418),[关系链](https://cloud.tencent.com/document/product/269/75421),[离线推送](https://cloud.tencent.com/document/product/269/75430),[本地搜索](https://cloud.tencent.com/document/product/269/75438)等相关功能开发。
|
||||
|
||||
详情可查看[自实现UI集成SDK文档](https://cloud.tencent.com/document/product/269/75260)。
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 支持哪些平台?
|
||||
|
||||
目前支持 iOS 、Android 和 Web 三个平台,另外 Windows 和 Mac 版正在开发中,敬请期待。
|
||||
|
||||
### Android 单击 Build And Run 报错找不到可用设备?
|
||||
|
||||
确保设备没被其他资源占用,或单击 **Build** 生成 APK 包,再拖动进模拟器里运行。
|
||||
|
||||
### iOS 第一次运行报错?
|
||||
|
||||
配置运行后,如果报错,可以单击 **Product** > **Clean**,清除产物后重新 Build,或者关闭 Xcode,重新打开后再次 Build。
|
||||
|
||||
### Flutter 环境问题
|
||||
|
||||
如您需得知 Flutter 的环境是否存在问题,请运行 Flutter doctor 检测 Flutter 环境是否装好。
|
||||
|
||||
### 使用Flutter自动生成的项目,引入TUIKit后报错
|
||||
|
||||

|
||||
|
||||
需要在\android\app\src\main\AndroidManifest.xml中进行修改。
|
||||
|
||||
打开 `\android\app\src\main\AndroidManifest.xml`,根据下图,补全。
|
||||
|
||||

|
||||
|
||||
打开 `\android\app\build.gradle`,根据下图,补全 `defaultConfig`。
|
||||
|
||||

|
||||
|
||||
## 联系我们
|
||||
|
||||
如果您在接入使用过程中有任何疑问,请加入QQ群:788910197 咨询。
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
# 主题方案
|
||||
## 1 介绍
|
||||
TUIKit 自定义了 **TUITheme** 类,用于规范TUIKit内的色彩使用。
|
||||
|
||||
请开发时务必注意,目前除 Colors.white 和 Colors.black 等底色/前景色外一律使用theme里提供的颜色。没有对应颜色可提出加到 TUITheme 里。
|
||||
|
||||
颜色概览:
|
||||
|
||||
// 应用主色
|
||||
// Primary Color For The App
|
||||
final Color? primaryColor;
|
||||
|
||||
// 应用次色
|
||||
// Secondary Color For The App
|
||||
final Color? secondaryColor;
|
||||
|
||||
// 提示颜色,用于次级操作或提示
|
||||
// Info Color, Used For Secondary Action Or Info
|
||||
final Color? infoColor;
|
||||
|
||||
// 浅背景颜色,比主背景颜色浅,用于填充缝隙或阴影
|
||||
// Weak Background Color, Lighter Than Main Background, Used For Marginal Space Or Shadowy Space
|
||||
final Color? weakBackgroundColor;
|
||||
|
||||
// 浅分割线颜色,用于分割线或边框
|
||||
// Weak Divider Color, Used For Divider Or Border
|
||||
final Color? weakDividerColor;
|
||||
|
||||
// 浅字色
|
||||
// Weak Text Color
|
||||
final Color? weakTextColor;
|
||||
|
||||
// 深字色
|
||||
// Dark Text Color
|
||||
final Color? darkTextColor;
|
||||
|
||||
// 浅主色,用于AppBar或Panels
|
||||
// Light Primary Color, Used For AppBar Or Several Panels
|
||||
final Color? lightPrimaryColor;
|
||||
|
||||
// 字色
|
||||
// TextColor
|
||||
final Color? textColor;
|
||||
|
||||
// 警示色,用于危险操作
|
||||
// Caution Color, Used For Warning Actions
|
||||
final Color? cautionColor;
|
||||
|
||||
// 群主标识色
|
||||
// Group Owner Identification Color
|
||||
final Color? ownerColor;
|
||||
|
||||
// 群管理员标识色
|
||||
// Group Admin Identification Color
|
||||
final Color? adminColor;
|
||||
|
||||
//除各种固定颜色外提供2种MaterialColor
|
||||
`primaryMaterialColor` && `lightPrimaryMaterialColor`。
|
||||
提供由 primaryColor 和 lightPrimaryColor 生成的十级色阶(50 ~ 900),eg: `primaryMaterialColor.shade50` `primaryMaterialColor.shade900`
|
||||
|
||||
## 2 使用方式
|
||||
### 2.1 开发中
|
||||
#### 2.1.1 Demo
|
||||
|
||||
通过 provider 里的 **DefaultThemeData.theme** 来获取theme。
|
||||
|
||||
通过 provider 里的 **DefaultThemeData.currentThemeType** 来获取/设置当前ThemeType。
|
||||
|
||||
设置 **DefaultThemeData.currentThemeType** 会将currentThemeType 写入localStorage 并同步 TUIKit 的 Theme。
|
||||
|
||||
当前支持四种ThemeType:
|
||||
`enum ThemeType { solemn, brisk, bright, fantasy }`
|
||||
|
||||
#### 2.1.2 UIKit
|
||||
|
||||
通过全局唯一的 `serviceLocator<TUIThemeViewModel>()` 获取当前 theme。
|
||||
|
||||
#### 2.1.3 AppBar
|
||||
|
||||
目前 **AppBar** 的统一处理如下:
|
||||
|
||||
字色目前统一为 Colors.white
|
||||
|
||||
`flexibleSpace: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(colors: [
|
||||
theme.lightPrimaryColor ?? CommonColor.lightPrimaryColor,
|
||||
theme.primaryColor ?? CommonColor.primaryColor
|
||||
]),
|
||||
),
|
||||
),`
|
||||
|
||||
`IconThemeData(
|
||||
color: Colors.white,
|
||||
),`
|
||||
|
||||
|
||||
## 3 TODO
|
||||
3.1 目前只支持暗色底白字,需要提供亮/暗两种主题间的切换。
|
||||
|
||||
3.2 自动识别亮/暗主题并更改字色。
|
||||
|
|
@ -0,0 +1,938 @@
|
|||
[](id:toc)
|
||||
通过阅读本文,你可以了解在您现有的 Android / iOS 原生开发项目中,集成腾讯云IM Flutter 的方法。
|
||||
|
||||
有的时候,使用Flutter重写您现有的应用程序是不现实的。如果您想在现有APP中,使用腾讯云IM的能力,推荐采用混合开发方案,即将Flutter模块,嵌入您的原生开发APP项目中。
|
||||
|
||||
**可在很大程度上,降低您的工作量,快速在双端原生APP中,植入IM通信能力。**
|
||||
|
||||

|
||||
|
||||
## 环境要求
|
||||
|
||||
| 环境 | 版本 |
|
||||
|---------|---------|
|
||||
| Flutter | SDK 最低要求 Flutter 2.2.0版本,TUIKit 集成组件库最低要求 Flutter 2.10.0 版本。|
|
||||
|Android|Android Studio 3.5及以上版本,App 要求 Android 4.1及以上版本设备。|
|
||||
|iOS|Xcode 11.0及以上版本,请确保您的项目已设置有效的开发者签名。|
|
||||
|腾讯云IM SDK|[tencent_im_sdk_plugin](https://pub.dev/packages/tencent_im_sdk_plugin) 5.0 及以上版本, [tencent_cloud_chat_uikit](https://pub.dev/packages/tencent_cloud_chat_uikit) 0.2 及以上版本。|
|
||||
|
||||
## 快速了解
|
||||
|
||||
<div class="doc-video-mod"><iframe src="https://cloud.tencent.com/edu/learning/quick-play/3856-67266?source=gw.doc.media&withPoster=1¬ip=1"></iframe></div>
|
||||
|
||||
>?
|
||||
>
|
||||
> 对于以上的Demo项目,源代码可在我们的[GitHub仓库](https://github.com/TencentCloud/tencentchat-add-flutter-to-app)中找到,欢迎查阅。
|
||||
|
||||
## 前置知识点
|
||||
|
||||
开始之前,您需要了解腾讯云IM Flutter SDK及TUIKit的用法;及Flutter-原生混合开发原理。
|
||||
|
||||
### 腾讯云IM
|
||||
|
||||
#### 总体入门
|
||||
|
||||
在开始前,您首先需要了解腾讯云IM Flutter的SDK构成及使用方式。
|
||||
|
||||
主要包括两个SDK:[无UI版本](https://cloud.tencent.com/document/product/269/68823#.E7.AC.AC.E4.BA.94.E9.83.A8.E5.88.86.EF.BC.9A.E8.87.AA.E5.AE.9E.E7.8E.B0-ui-.E9.9B.86.E6.88.90)及[含UI组件库](https://cloud.tencent.com/document/product/269/70747)。本文将以 [含UI组件库(TUIKit)](https://cloud.tencent.com/document/product/269/70747) 为例,介绍混合开发方案。
|
||||
|
||||
**关于腾讯云IM Flutter详细用法,可从我们的 [快速入门文档](https://cloud.tencent.com/document/product/269/68823) 看起。**
|
||||
|
||||
[](id:modules)
|
||||
|
||||
#### 两个模块
|
||||
|
||||
腾讯云IM主要有两个部分,包括 Chat聊天模块 和 Call通话模块。
|
||||
|
||||
Chat聊天模块主要包括消息收发、会话管理、用户关系管理等。
|
||||
|
||||
Call通话模块主要包括音视频通话,包括一对一通话和群组多人通话。
|
||||
|
||||
### Flutter 混合开发
|
||||
|
||||
核心原理是,将 module 形式的Flutter项目,打包成Native端的可执行程序,嵌入Native项目中。因Flutter module可以通用,因此仅需编写一次Flutter module,即可嵌入 Android/iOS APP 中。
|
||||
|
||||
当您现有应用需要展示腾讯云IM相关页面时,可加载对应用于承载Flutter的Activity(Android)或ViewController(iOS)。
|
||||
|
||||
当需要两端通信时,如传递当前用户信息,传递音视频通话数据,触发离线推送数据,可采用[Method Channel](https://docs.flutter.dev/development/platform-integration/platform-channels#channels-and-platform-threading)方式进行。触发另一端的方法使用 `invokeMethod`,监听另一端发来的方法调用使用[预挂载的Method Channel监听器](https://docs.flutter.dev/development/platform-integration/platform-channels#executing-channel-handlers-on-background-threads)。
|
||||
|
||||
[](id:android)
|
||||
|
||||
#### 将 Flutter 模块添加至 Android 项目中
|
||||
|
||||
[详细学习](https://docs.flutter.dev/development/add-to-app/android/project-setup)
|
||||
|
||||
将Flutter module添加为Gradle中现有应用程序的依赖项。有两种方式可以实现这一点。
|
||||
|
||||
##### Android方式一:依赖 Android Archive (AAR)
|
||||
|
||||
AAR机制创建通用的Android AAR作为打包Flutter module的中介。如果您经常构建,它会增加一个构建步骤。
|
||||
|
||||
该选项将Flutter库打包为由AAR和POMS构件组成的通用本地Maven存储库。此选项允许您的团队在不安装Flutter SDK的情况下构建主机应用程序。然后,您可以从本地或远程存储库中分发构件。
|
||||
|
||||
因此,建议在线上生产环境,使用本方案。
|
||||
|
||||
**具体步骤:**
|
||||
|
||||
在您的Flutter module中,运行:
|
||||
|
||||
```shell
|
||||
flutter build aar
|
||||
```
|
||||
|
||||
然后,按照屏幕上的说明进行集成。
|
||||
|
||||

|
||||
|
||||
您的应用程序现在将Flutter模块作为依赖项包括在内。
|
||||
|
||||
##### Android方式二:依赖Flutter module源代码
|
||||
|
||||
源代码子项目机制是一个方便的一键构建过程,但需要Flutter SDK。这是Android Studio IDE插件使用的机制。
|
||||
|
||||
此方式可为您的Android项目和Flutter项目实现一步构建。当您同时处理两个部分并快速迭代时,此选项很方便,但您的团队必须安装Flutter SDK才能构建应用程序。
|
||||
|
||||
因此,建议在开发测试环境,使用本方案。
|
||||
|
||||
**具体步骤:**
|
||||
|
||||
将Flutter module作为一个子项目,添加至宿主APP的 `settings.gradle` 中:
|
||||
|
||||
```gradle
|
||||
// Include the host app project.
|
||||
include ':app' // assumed existing content
|
||||
setBinding(new Binding([gradle: this])) // new
|
||||
evaluate(new File( // new
|
||||
settingsDir.parentFile, // new
|
||||
'tencent_chat_module/.android/include_flutter.groovy' // new
|
||||
)) // new
|
||||
```
|
||||
|
||||
在您应用中的 `app/build.gradle => dependencies` 中引入对Flutter module的 `implementation`:
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation project(':flutter')
|
||||
}
|
||||
```
|
||||
|
||||
您的应用程序现在将Flutter模块作为依赖项包括在内。
|
||||
|
||||
[](id:ios)
|
||||
|
||||
#### 将 Flutter 模块添加至 iOS 项目中
|
||||
|
||||
[详细学习](https://docs.flutter.dev/development/add-to-app/ios/project-setup#embed-the-flutter-module-in-your-existing-application)
|
||||
|
||||
有两种方法可以在现有应用程序中嵌入Flutter。
|
||||
|
||||
##### iOS方式一:嵌入 CocoaPods 和 Flutter SDK 集成
|
||||
|
||||
使用CocoaPods依赖项管理器并安装Flutter SDK。这种方法要求每个从事项目工作的开发人员都有一个本地安装的Flutter SDK版本。
|
||||
|
||||
只需在Xcode中构建您的应用程序,即可自动运行脚本来嵌入您的DART和插件代码。这允许快速迭代最新版本的颤振模块,而无需在Xcode之外运行其他命令。
|
||||
|
||||
因此,建议在开发测试环境,使用本方案。
|
||||
|
||||
**具体步骤:**
|
||||
|
||||
将以下代码添加到Podfile中:
|
||||
|
||||
```
|
||||
// 上一步构建的Flutter Module的路径
|
||||
flutter_chat_application_path = '../tencent_chat_module'
|
||||
|
||||
load File.join(flutter_chat_application_path, '.ios', 'Flutter', 'podhelper.rb')
|
||||
```
|
||||
|
||||
对于每个需要嵌入Flutter的[Podfile target](https://guides.cocoapods.org/syntax/podfile.html#target),调用 `install_all_flutter_pods(flutter_chat_application_path)`.
|
||||
|
||||
```
|
||||
target 'MyApp' do
|
||||
install_all_flutter_pods(flutter_chat_application_path)
|
||||
end
|
||||
```
|
||||
|
||||
在Podfile的 `post_install` 块中,调用 `flutter_post_install(installer)`,并完成 [腾讯云IM TUIKit](https://cloud.tencent.com/document/product/269/70747) 所需的权限声明,包括麦克风权限/相机权限/相册权限。
|
||||
|
||||
```
|
||||
post_install do |installer|
|
||||
flutter_post_install(installer) if defined?(flutter_post_install)
|
||||
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
|
||||
```
|
||||
|
||||
执行 `pod install`。
|
||||
> ?
|
||||
>
|
||||
> - 在 `tencent_chat_module/pubspec.yaml` 中更改Flutter插件依赖时,请在Flutter Module目录中运行 `flutter pub get` 以刷新 `podhelper.rb` 脚本读取的插件列表。然后,从您iOS应用程序的根目录,再次执行 `pod install`。
|
||||
> - 对于 Apple Silicon 芯片 arm64 架构的 Mac电脑,可能需要执行 `arch -x86_64 pod install --repo-update`。
|
||||
|
||||
`podhelper.rb` 脚本将您的插件 / `Flutter.framework` / `App.framework` 植入您的项目中。
|
||||
|
||||
##### iOS方式二:在Xcode中嵌入frameworks
|
||||
|
||||
为Flutter引擎、已编译的DART代码和所有Flutter插件创建框架。手动嵌入框架,并在Xcode中更新现有应用程序的构建设置。
|
||||
|
||||
通过手动编辑现有的Xcode项目,您可以生成必要的framework并将它们嵌入到应用程序中。如果您的团队成员无法在本地安装Flutter SDK和CocoaPods,或者如果您不想在现有应用程序中使用CocoaPods作为依赖项管理器,则可以这样做。每次你在你的颤动模块中修改代码时,你都必须运行 `flutter build ios-framework`.
|
||||
|
||||
因此,建议在线上环境,使用本方案。
|
||||
|
||||
**具体步骤:**
|
||||
|
||||
在您的Flutter module中,运行如下代码。
|
||||
|
||||
下面的示例,假设您想要将framework生成到 `some/path/MyApp/Flutter/`.
|
||||
|
||||
```shell
|
||||
flutter build ios-framework --output=some/path/MyApp/Flutter/
|
||||
```
|
||||
|
||||
在 Xcode 中将生成的 frameworks 集成到你的既有应用中。例如,你可以在 `some/path/MyApp/Flutter/Release/` 目录拖拽 frameworks 到你的应用 target 编译设置的 General > Frameworks, Libraries, and Embedded Content 下,然后在 Embed 下拉列表中选择 “Embed & Sign”。
|
||||
|
||||
|
||||
## 混合开发选型
|
||||
|
||||
我们推荐您使用Flutter Module方式进行混合开发集成。
|
||||
|
||||
在Native原生项目中,构建Flutter引擎,来承载Flutter中的Chat及Call模块。有关两个模块的介绍,[请看此处](#modules)。
|
||||
|
||||
对于Flutter引擎的创建管理,目前两种方式:单Flutter引擎及多Flutter引擎。
|
||||
|
||||
| 引擎模式 | 介绍 | 优点 | 缺点 | Demo源码下载 |
|
||||
|---------|---------|---------|---------|---------|
|
||||
| [Flutter单引擎](#single) | Chat模块和Call模块在同一个Flutter引擎中承载。 | 方便,所有Flutter代码统一维护。 | 由于Call插件,在有电话呼入时,需要自动展示来电页面。如果在同一个引擎中,需要强制跳转至Flutter所在页面,体验较差。 | [点击下载](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Single%20Flutter%20Engines) |
|
||||
| [Flutter多引擎](#multiple) | Chat模块和Call模块分别承载于不同的Flutter引擎中,使用Flutter引擎组来统一管理这两个引擎。 | Call插件独立存在于一个Flutter引擎中,独立页面控制,来电时,直接将该页面弹窗即可,不影响用户当前所在页面,体验较好。 | 通话模块无法最小化成浮窗形式。 | [点击下载](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Multiple%20Flutter%20Engines) |
|
||||
|
||||
此外,我们还提供,将腾讯云 IM Native SDK 与 Flutter SDK 结合使用的方案,[适用场景和步骤介绍可查看这里](#native)。[Demo源码下载](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Initialize%20from%20Native)。
|
||||
|
||||
[](id:multiple)
|
||||
|
||||
## 方案一:Flutter 多引擎方案【推荐】
|
||||
|
||||
本方案中,Chat 和 Call 模块分别独立于不同的Flutter引擎。
|
||||
|
||||
使用多个Flutter引擎的优点是,每个实例都是独立的,并维护其自己的内部导航堆栈、UI和应用程序状态。这简化了整个应用程序代码的状态保持责任,并提高了模块化能力。
|
||||
|
||||

|
||||
|
||||
在Android和iOS上添加多个Flutter引擎,主要基于一个FlutterEngineGroup类(Android API、iOS API)来构造并管理多个FlutterEngine(Flutter引擎)。
|
||||
|
||||
在我们的项目中,我们基于一个统一的FlutterEngineGroup,来管理两个FlutterEngine(Flutter引擎),分别用于承载 Chat 和 Calling 模块。
|
||||
|
||||
[](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Multiple%20Flutter%20Engines)
|
||||
|
||||
### Flutter Module 开发
|
||||
|
||||
要将Flutter嵌入到现有应用程序中,请首先创建一个Flutter模块。
|
||||
|
||||
在您项目的根目录外层,运行
|
||||
|
||||
```
|
||||
cd some/path/
|
||||
flutter create --template module tencent_chat_module
|
||||
```
|
||||
|
||||
这会在 some/path/tencent_chat_module/ 创建一个 Flutter 模块项目。 在该目录中,您可以运行与在任何其他 Flutter 项目中相同的 Flutter 命令,例如 `flutter run --debug` 或 `flutter build ios`。 您还可以使用 Flutter 和 Dart 插件在 Android Studio, IntelliJ 或 VS Code 中运行该模块。 该项目在嵌入到现有应用程序之前包含模块的单视图示例版本,这对于测试代码的仅 Flutter 部分很有用。
|
||||
|
||||
`tencent_chat_module` 模块目录结构类似于普通的 Flutter 应用程序:
|
||||
|
||||
```
|
||||
tencent_chat_module/
|
||||
├── .ios/
|
||||
│ ├── Runner.xcworkspace
|
||||
│ └── Flutter/podhelper.rb
|
||||
├── lib/
|
||||
│ └── main.dart
|
||||
├── test/
|
||||
└── pubspec.yaml
|
||||
```
|
||||
|
||||
现在,我们可以在 `lib/` 中,编写代码了。
|
||||
|
||||
#### 梳理Flutter lib 目录
|
||||
|
||||
>?
|
||||
>
|
||||
> 以下代码结构,仅供参考,您可根据需要灵活组织,以引入腾讯云IM Flutter。
|
||||
|
||||
在 `lib/` 我们创建三个目录,`call`, `chat`, `common`。分别用于放置通话引擎,IM引擎,及通用model类。
|
||||
|
||||
```
|
||||
tencent_chat_module/
|
||||
├── lib/
|
||||
│ └── call/
|
||||
│ └── chat/
|
||||
│ └── common/
|
||||
```
|
||||
|
||||
#### 通用model类模块
|
||||
|
||||
新建 `common/common_model.dart` 文件,如下所示,新建两个class,用于定义Flutter与原生应用通信规范。
|
||||
|
||||
```dart
|
||||
class ChatInfo {
|
||||
String? sdkappid;
|
||||
String? userSig;
|
||||
String? userID;
|
||||
|
||||
ChatInfo.fromJSON(Map<String, dynamic> json) {
|
||||
sdkappid = json["sdkappid"].toString();
|
||||
userSig = json["userSig"].toString();
|
||||
userID = json["userID"].toString();
|
||||
}
|
||||
|
||||
Map<String, String> toMap(){
|
||||
final Map<String, String> map = {};
|
||||
if(sdkappid != null){
|
||||
map["sdkappid"] = sdkappid!;
|
||||
}
|
||||
if(userSig != null){
|
||||
map["userSig"] = userSig!;
|
||||
}
|
||||
if(userID != null){
|
||||
map["userID"] = userID!;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class CallInfo{
|
||||
String? userID;
|
||||
String? groupID;
|
||||
|
||||
CallInfo();
|
||||
|
||||
CallInfo.fromJSON(Map<String, dynamic> json) {
|
||||
groupID = json["groupID"].toString();
|
||||
userID = json["userID"].toString();
|
||||
}
|
||||
|
||||
Map<String, String> toMap(){
|
||||
final Map<String, String> map = {};
|
||||
if(userID != null){
|
||||
map["userID"] = userID!;
|
||||
}
|
||||
if(groupID != null){
|
||||
map["groupID"] = groupID!;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Chat 模块
|
||||
|
||||
**首先编写IM引擎。本模块所有代码及文件,均在 `lib/chat` 目录下。**
|
||||
|
||||
1. 新建全局状态管理Model,名为 `model.dart`。
|
||||
该Model用于挂载初始化并管理腾讯云IM Flutter模块,离线推送能力,全局状态管理,维护与Native间通信。
|
||||
是整个Chat模块的核心。
|
||||
详细代码可查看Demo源码。重点关注三个部分:
|
||||
- Future<dynamic> _handleMessage(MethodCall call): 动态监听 Native 透传来的事件,包括登录信息及点击推送事件。
|
||||
- Future<void> handleClickNotification(Map<String, dynamic> msg): 点击通知处理事件,来自Native透传,从 Map 中取出数据,跳转至对应的子模块,如某个具体会话。
|
||||
- Future<void> initChat(): 初始化腾讯云IM/登录腾讯云IM/并完成离线推送的初始化及Token上报。该方法使用线程锁机制,保证同时只能执行一个,并在初始化成功后,不重复执行。
|
||||
|
||||
> ?
|
||||
>
|
||||
> 请根据 [离线推送接入指引](https://cloud.tencent.com/document/product/269/74605),完成厂商离线推送功能接入,才可正常上报推送Token,使用推送功能。
|
||||
|
||||
2. 新建 `chat_main.dart` 文件,用于Chat模块主入口。
|
||||
- 该页面也是Flutter Chat模块的首页。
|
||||
- 在Demo中,该页面在未登录前为加载状态,登录后展示会话列表。
|
||||
- 此外,还需要在这里,完成 `didChangeAppLifecycleState` 监听与前后台切换事件上报,详情请查看[离线推送插件文档步骤5](https://cloud.tencent.com/document/product/269/74605#.E6.AD.A5.E9.AA.A45.EF.BC.9A.E5.89.8D.E5.90.8E.E5.8F.B0.E5.88.87.E6.8D.A2.E7.9B.91.E5.90.AC.3Ca-id.3D.22step_5.22.3E.3C.2Fa.3E)。
|
||||
- 详细代码可查看Demo源码。
|
||||
|
||||
3. 新建 `push.dart` 文件,用于单例管理 [离线推送插件](https://cloud.tencent.com/document/product/269/74605) 能力。用于获取并上报Token/获取推送权限等操作。详细代码可查看Demo源码。
|
||||
|
||||
4. 新建 `conversation.dart` 文件,用于承载TUIKit的会话模块组件 `TIMUIKitConversation`。详细代码可查看Demo源码。
|
||||
|
||||
5. 新建 `chat.dart` 文件,用于承载TUIKit的历史消息列表和发送消息模块组件 `TIMUIKitChat`。
|
||||
该页面还有跳转至 Profile 及 Group Profile 页面的能力。
|
||||
详细代码可查看Demo源码。
|
||||
|
||||
6. 新建 `user_profile.dart` 文件,用于承载TUIKit的用户信息及关系链管理模块组件 `TIMUIKitProfile`。详细代码可查看Demo源码。
|
||||
|
||||
7. 新建 `group_profile.dart` 文件,用于承载TUIKit的群信息及群管理模块组件 `TIMUIKitGroupProfile`。详细代码可查看Demo源码。
|
||||
|
||||
此时,Chat模块已开发完成。最终结构如下:
|
||||
|
||||
```
|
||||
tencent_chat_module/
|
||||
├── lib/
|
||||
│ └── call/
|
||||
│ └── chat.dart
|
||||
│ └── model.dart
|
||||
│ └── chat_main.dart
|
||||
│ └── push.dart
|
||||
│ └── conversation.dart
|
||||
│ └── user_profile.dart
|
||||
│ └── group_profile.dart
|
||||
│ └── chat/
|
||||
│ └── common/
|
||||
```
|
||||
|
||||
#### Call 模块
|
||||
|
||||
该模块用于承载音视频通话能力,该能力由 [音视频通话插件](https://cloud.tencent.com/document/product/269/72485) 提供。
|
||||
|
||||
该模块的核心是,监听收到新的通话邀请时,通过调用Native方法,自动弹出通话页面;并接受 Chat 模块经由Native转发来的通话请求,主动发起通话。
|
||||
|
||||
**首先编写IM引擎。本模块所有代码及文件,均在 `lib/call` 目录下。**
|
||||
|
||||
1. 新建全局状态管理Model,名为 `model.dart`。
|
||||
该Model用于挂载初始化并管理 [音视频通话插件](https://cloud.tencent.com/document/product/269/72485),全局状态管理,维护与Native间通信。
|
||||
是整个Call模块的核心。
|
||||
详细代码可查看Demo源码。重点关注两个部分:
|
||||
- _onRtcListener = TUICallingListener(...): 定义了通话事件的监听器,通过 Method Channel 通知Native层,动态控制 Call 模块所属的 ViewController(iOS)/Activity(Android) 的前端展示与否。
|
||||
- Future<dynamic> _handleMessage(MethodCall call): 动态监听 Native 透传来的主动发起通话请求,来自 Call 模块的调用,主动发起通话。
|
||||
|
||||
2. 新建 `call_main.dart` 文件,用于Call模块主入口。
|
||||
该组件用于注入[音视频通话插件所需绑定的navigatorKey](https://cloud.tencent.com/document/product/269/72485#.E6.AD.A5.E9.AA.A41.EF.BC.9A.E5.BC.95.E5.85.A5-navigatorkey)。
|
||||
详细代码可查看Demo源码。
|
||||
|
||||
|
||||
#### 配置各个Flutter引擎的入口
|
||||
|
||||
开发完上述三个模块后,现在可完成最终对外暴露的main方法,作为Flutter引擎的入口。
|
||||
|
||||
1. 默认入口
|
||||
|
||||
打开 `lib/main.dart` 文件,将 `main()` 方法改成一个空 MaterialApp 即可。
|
||||
|
||||
该方法作为 Flutter Module 的默认入口,在Flutter多引擎,使用FlutterEngineGroup管理的背景下,如果没有子Flutter Engine不设置任何entry point,这个方法就不会被用到。
|
||||
|
||||
例如,在我们的场景中,这个默认 `main()` 方法就没有被用上。
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
runApp(MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
home: Container(),
|
||||
));
|
||||
}
|
||||
```
|
||||
|
||||
2. 配置 Chat 模块的入口
|
||||
|
||||
使用 `@pragma('vm:entry-point')` 注解,将该方法标记为一个 `entry-point` 入口。方法名 `chatMain` 即该入口的名称,在Native中,也使用该名称,创建对应Flutter引擎。
|
||||
|
||||
使用全局 `ChangeNotifierProvider` 状态管理,维护 `ChatInfoModel` 数据及业务逻辑。
|
||||
|
||||
```dart
|
||||
@pragma('vm:entry-point')
|
||||
void chatMain() {
|
||||
// This call ensures the Flutter binding has been set up before creating the
|
||||
// MethodChannel-based model.
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final model = ChatInfoModel();
|
||||
|
||||
runApp(
|
||||
ChangeNotifierProvider.value(
|
||||
value: model,
|
||||
child: const ChatAPP(),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
3. 配置 Call 模块的入口
|
||||
|
||||
同理,该入口命名为 `callMain`。
|
||||
|
||||
使用全局 `ChangeNotifierProvider` 状态管理,维护 `CallInfoModel` 数据及业务逻辑。
|
||||
|
||||
```dart
|
||||
@pragma('vm:entry-point')
|
||||
void callMain() {
|
||||
// This call ensures the Flutter binding has been set up before creating the
|
||||
// MethodChannel-based model.
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final model = CallInfoModel();
|
||||
|
||||
runApp(
|
||||
ChangeNotifierProvider.value(
|
||||
value: model,
|
||||
child: const CallAPP(),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
至此,Flutter Module部分,Dart代码编写完成。
|
||||
|
||||
接下来,开始编写 Native 代码。
|
||||
|
||||
### iOS Native 开发
|
||||
|
||||
本文以 Swift 语言为例。
|
||||
|
||||
>?
|
||||
>
|
||||
> 以下代码结构,仅供参考,您可根据需要灵活组织。
|
||||
|
||||
进入您的iOS项目目录。
|
||||
|
||||
如果您现有的应用程序,假设叫做 `MyApp`, 还没有Podfile,请按照[CocoaPods入门指南](https://guides.cocoapods.org/using/using-cocoapods.html)将 `Podfile` 添加到项目中。
|
||||
|
||||
#### 引入 Flutter Module
|
||||
|
||||
请参考[此部分](#ios),将Flutter module引入您的原生应用程序中。建议采用方式一。
|
||||
|
||||
#### 在 iOS 项目中,管理Flutter引擎
|
||||
|
||||

|
||||
|
||||
**创建一个 `FlutterEngineGroup` (Flutter 引擎组),统一管理多个引擎实例。**
|
||||
|
||||
在 `AppDelegate.swift` 文件中,添加如下代码:
|
||||
|
||||
```swift
|
||||
@UIApplicationMain
|
||||
class AppDelegate: FlutterAppDelegate {
|
||||
lazy var flutterEngines = FlutterEngineGroup(name: "chat.flutter.tencent", project: nil)
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**创建一个用于管理Flutter引擎的单例对象。**
|
||||
|
||||
这个 Swift 单例对象,用于集中管理 Flutter 实例,并方便在项目中各处,直接调用。
|
||||
|
||||
Demo代码的逻辑是,使用新的路由,承载Chat的ViewController;Call的ViewController,通过present和dismiss动态弹窗维护。
|
||||
|
||||
新建 `FlutterUtils.swift` 文件,编写代码。本部分详细代码,可查看Demo源码。
|
||||
|
||||
重点关注:
|
||||
- private override init(): 初始化各 Flutter 引擎实例,注册Method Channel,监听事件。
|
||||
- func reportChatInfo(): 将用户登录信息和SDKAPPID透传至Flutter Module,使Flutter层得以初始化并登录腾讯云IM。
|
||||
- func launchCallFunc(): 用于拉起Call的Flutter页面,可被Call模块收到通话邀请触发,也可被Chat模块主动发起通话触发。
|
||||
- func triggerNotification(msg: String): 将 iOS Native 层收到的离线推送消息点击事件,及其包含的ext信息,以 JSON String形式,透传至 Flutter 层绑定的监听处理事件。用于处理离线推送点击跳转,例如至对应会话。
|
||||
|
||||
**监听及转发离线推送点击事件**
|
||||
|
||||
离线推送的初始化/Token上报/点击事件对应的会话跳转处理,已在Flutter Chat模块中进行,因此,Native区域,仅需透传点击通知事件的ext即可。
|
||||
|
||||
之所以这么做,是因为点击通知事件已在Native被拦截消费,Flutter层无法直接拿到,必须经由Native转发。
|
||||
|
||||
在 `AppDelegate.swift` 文件中,新增如下代码。具体代码,可以参考Demo源码。
|
||||
|
||||

|
||||
|
||||
此时,iOS Native层编写完成。
|
||||
|
||||
### Android Native 开发
|
||||
|
||||
本文以 Kotlin 语言为例。
|
||||
|
||||
>?
|
||||
>
|
||||
> 以下代码结构,仅供参考,您可根据需要灵活组织。
|
||||
|
||||
#### 引入 Flutter Module
|
||||
|
||||
请参考[此部分](#android),将Flutter module引入您的原生应用程序中。建议采用方式二。
|
||||
|
||||
#### 在 Android 项目中,管理Flutter引擎
|
||||
|
||||
**创建一个用于管理Flutter引擎的单例对象。**
|
||||
|
||||
这个 Kotlin 单例对象,用于集中管理 Flutter 实例,并方便在项目中各处,直接调用。
|
||||
|
||||
新建 `FlutterUtils.kt` 文件,并定义 `FlutterUtils` 静态类。
|
||||
|
||||
```kotlin
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object FlutterUtils {}
|
||||
```
|
||||
|
||||
**创建 `FlutterEngineGroup` (Flutter 引擎组),统一管理多个引擎实例。**
|
||||
|
||||
在 `FlutterUtils.kt` 文件中,定义一个 `FlutterEngineGroup`,及配套各个Flutter Engine实例和Method Channel,并在初始化时,将其初始化。
|
||||
|
||||
```kotlin
|
||||
lateinit var context : Context
|
||||
lateinit var flutterEngines: FlutterEngineGroup
|
||||
private lateinit var chatFlutterEngine:FlutterEngine
|
||||
private lateinit var callFlutterEngine:FlutterEngine
|
||||
|
||||
lateinit var chatMethodChannel: MethodChannel
|
||||
lateinit var callMethodChannel: MethodChannel
|
||||
|
||||
// 初始化
|
||||
flutterEngines = FlutterEngineGroup(context)
|
||||
...
|
||||
```
|
||||
|
||||
**继续完成该用于管理Flutter引擎的单例对象。**
|
||||
|
||||
Demo代码的逻辑是,使用新的路由,承载Chat和Call的Activity。
|
||||
|
||||
Chat的Activity,由用户主动进入及退出;Call的Activity,由监听器或主动外呼,自动导航进及返回出。
|
||||
|
||||
重点关注:
|
||||
- fun init(): 初始化各 Flutter 引擎实例,注册Method Channel,监听事件。
|
||||
- fun reportChatInfo(): 将用户登录信息和SDKAPPID透传至Flutter Module,使Flutter层得以初始化并登录腾讯云IM。
|
||||
- fun launchCallFunc(): 用于拉起Call的Flutter页面,可被Call模块收到通话邀请触发,也可被Chat模块主动发起通话触发。
|
||||
- fun triggerNotification(msg: String): 将 iOS Native 层收到的离线推送消息点击事件,及其包含的ext信息,以 JSON String形式,透传至 Flutter 层绑定的监听处理事件。用于处理离线推送点击跳转,例如至对应会话。
|
||||
|
||||
本单例 object 的详细代码,可以参考Demo源码。
|
||||
|
||||
**在 总入口 `MyApplication` 中,初始化上述对象**
|
||||
|
||||
在 `MyApplication.kt` 文件中,将全局context传入单例对象,并执行初始化。
|
||||
|
||||
```kotlin
|
||||
class MyApplication : MultiDexApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
FlutterUtils.context = this // new
|
||||
FlutterUtils.init() // new
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**监听及转发离线推送点击事件**
|
||||
|
||||
离线推送的初始化/Token上报/点击事件对应的会话跳转处理,已在Flutter Chat模块中进行,因此,Native区域,仅需透传点击通知事件的ext即可。
|
||||
|
||||
之所以这么做,是因为点击通知事件已在Native被拦截消费,Flutter层无法直接拿到,必须经由Native转发。
|
||||
|
||||
> 由于不同厂商的离线推送接入步骤不一致,本文以OPPO为例,全部厂商接入方案,可查看[本文档](https://cloud.tencent.com/document/product/269/75428).
|
||||
|
||||
在腾讯云IM控制台中,新增OPPO的推送证书,`点击后续动作` 选择 `打开应用内指定页面`,`应用内页面` 以 `Activity` 方式,配置一个用于处理离线推送信息的页面,建议为应用首页。如,我们的Demo配置为:`com.tencent.chat.android.MainActivity`.
|
||||
|
||||

|
||||
|
||||
在上方控制台配置的用于离线推送的Activity文件中,新增如下代码。
|
||||
|
||||
该代码的作用是,当厂商拉起相应Activity时,从Bundle中取出HashMap形式ext信息,触发单例对象中的方法,将这个信息,手动转发至Flutter中。具体代码,可以参考Demo源码。
|
||||
|
||||

|
||||
|
||||
此时,Android Native层编写完成。
|
||||
|
||||
|
||||
[](id:single)
|
||||
|
||||
## 方案二:Flutter 单引擎方案
|
||||
|
||||
本方案,将Chat模块和Call模块,写在同一个Flutter引擎实例中。
|
||||
|
||||

|
||||
|
||||
这两个模块只能同时出现同时隐藏,仅需维护一个Flutter引擎即可。
|
||||
|
||||
[](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Single%20Flutter%20Engines)
|
||||
|
||||
### Flutter Module 开发
|
||||
|
||||
要将Flutter嵌入到现有应用程序中,请首先创建一个Flutter模块。
|
||||
|
||||
在您项目的根目录外层,运行
|
||||
|
||||
```
|
||||
cd some/path/
|
||||
flutter create --template module tencent_chat_module
|
||||
```
|
||||
|
||||
这会在 some/path/tencent_chat_module/ 创建一个 Flutter 模块项目。 在该目录中,您可以运行与在任何其他 Flutter 项目中相同的 Flutter 命令,例如 `flutter run --debug` 或 `flutter build ios`。 您还可以使用 Flutter 和 Dart 插件在 Android Studio, IntelliJ 或 VS Code 中运行该模块。 该项目在嵌入到现有应用程序之前包含模块的单视图示例版本,这对于测试代码的仅 Flutter 部分很有用。
|
||||
|
||||
`tencent_chat_module` 模块目录结构类似于普通的 Flutter 应用程序:
|
||||
|
||||
```
|
||||
tencent_chat_module/
|
||||
├── .ios/
|
||||
│ ├── Runner.xcworkspace
|
||||
│ └── Flutter/podhelper.rb
|
||||
├── lib/
|
||||
│ └── main.dart
|
||||
├── test/
|
||||
└── pubspec.yaml
|
||||
```
|
||||
|
||||
现在,我们可以在 `lib/` 中,编写代码了。
|
||||
|
||||
#### main.dart
|
||||
|
||||
修改 `main.dart` 文件,引入[TUIKit](https://cloud.tencent.com/document/product/269/70747), [离线推送插件](https://cloud.tencent.com/document/product/269/74605)及[音视频通话插件](https://cloud.tencent.com/document/product/269/72485)。
|
||||
|
||||
全局状态,我们的IM SDK及Method Channel与Native通信状态,管理于 `ChatInfoModel` 中。
|
||||
|
||||
接收到Native传来的用户信息及SDKAPPID后,调用 `_coreInstance.init()` 及 `_coreInstance.login() ` 初始化并登录腾讯云IM。并初始化音视频推送插件及离线推送插件,完成推送Token上报。
|
||||
|
||||
> ?
|
||||
>
|
||||
> 请根据 [离线推送接入指引](https://cloud.tencent.com/document/product/269/74605),完成厂商离线推送功能接入,才可正常上报推送Token,使用推送功能。
|
||||
|
||||
对于音视频通话插件,需要关注:
|
||||
- 监听收到新的通话邀请时,通过调用Native方法,让Native检测用户当前是否在本Flutter模块页面,如果不在,需要强制将前端页面调整至本模块,以展示来电页面。
|
||||
|
||||
对于离线推送插件,需要关注:
|
||||
- 点击通知处理事件,来自Native透传,从 Map 中取出数据,跳转至对应的子模块,如某个具体会话。
|
||||
|
||||
完成首页的制作,在未登录时展示加载动画;登录成功后,展示会话列表页面。
|
||||
|
||||
此外,还需要在这里,完成 `didChangeAppLifecycleState` 监听与前后台切换事件上报,详情请查看[离线推送插件文档步骤5](https://cloud.tencent.com/document/product/269/74605#.E6.AD.A5.E9.AA.A45.EF.BC.9A.E5.89.8D.E5.90.8E.E5.8F.B0.E5.88.87.E6.8D.A2.E7.9B.91.E5.90.AC.3Ca-id.3D.22step_5.22.3E.3C.2Fa.3E)。
|
||||
|
||||
详细代码可查看Demo源码。
|
||||
|
||||
#### 其他TUIKit模块引入
|
||||
|
||||
1. 新建 `push.dart` 文件,用于单例管理 [离线推送插件](https://cloud.tencent.com/document/product/269/74605) 能力。用于获取并上报Token/获取推送权限等操作。详细代码可查看Demo源码。
|
||||
|
||||
2. 新建 `conversation.dart` 文件,用于承载TUIKit的会话模块组件 `TIMUIKitConversation`。详细代码可查看Demo源码。
|
||||
|
||||
3. 新建 `chat.dart` 文件,用于承载TUIKit的历史消息列表和发送消息模块组件 `TIMUIKitChat`。
|
||||
该页面还有跳转至 Profile 及 Group Profile 页面的能力。
|
||||
详细代码可查看Demo源码。
|
||||
|
||||
4. 新建 `user_profile.dart` 文件,用于承载TUIKit的用户信息及关系链管理模块组件 `TIMUIKitProfile`。详细代码可查看Demo源码。
|
||||
|
||||
5. 新建 `group_profile.dart` 文件,用于承载TUIKit的群信息及群管理模块组件 `TIMUIKitGroupProfile`。详细代码可查看Demo源码。
|
||||
|
||||
至此,统一的Flutter Module开发完成。
|
||||
|
||||
### iOS Native 开发
|
||||
|
||||
本文以 Swift 语言为例。
|
||||
|
||||
>?
|
||||
>
|
||||
> 以下代码结构,仅供参考,您可根据需要灵活组织。
|
||||
|
||||
进入您的iOS项目目录。
|
||||
|
||||
如果您现有的应用程序,假设叫做 `MyApp`, 还没有Podfile,请按照[CocoaPods入门指南](https://guides.cocoapods.org/using/using-cocoapods.html)将 `Podfile` 添加到项目中。
|
||||
|
||||
#### 引入 Flutter Module
|
||||
|
||||
请参考[此部分](#ios),将Flutter module引入您的原生应用程序中。建议采用方式一。
|
||||
|
||||
#### 在 iOS 项目中,管理Flutter引擎
|
||||
|
||||
**创建一个FlutterEngine。**
|
||||
|
||||
创建FlutterEngine的适当位置特定于您的主应用程序入口。作为一个例子,我们演示了如何在 `AppDelegate` 中的app启动时创建一个FlutterEngine,并公开为一个属性。
|
||||
|
||||
```swift
|
||||
import UIKit
|
||||
import Flutter
|
||||
import FlutterPluginRegistrant
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: FlutterAppDelegate { // More on the FlutterAppDelegate.
|
||||
lazy var flutterEngine = FlutterEngine(name: "tencent cloud chat")
|
||||
|
||||
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Runs the default Dart entrypoint with a default Flutter route.
|
||||
flutterEngine.run();
|
||||
GeneratedPluginRegistrant.register(with: self.flutterEngine);
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**创建一个用于管理Flutter引擎的单例对象。**
|
||||
|
||||
这个 Swift 单例对象,用于集中管理 Flutter Method Channel,并提供一系列与 Flutter Module 通信的方法,方便在项目中各处,直接调用。
|
||||
|
||||
这些方法包括:
|
||||
- private override init(): 初始化 Method Channel,并为其绑定事件监听方法。
|
||||
- func reportChatInfo(): 将用户登录信息和SDKAPPID透传至Flutter Module,使Flutter层得以初始化并登录腾讯云IM。
|
||||
- func launchChatFunc(): 拉起或导航至 Flutter Module 所在 ViewController。
|
||||
- func triggerNotification(msg: String): 将 iOS Native 层收到的离线推送消息点击事件,及其包含的ext信息,以 JSON String形式,透传至 Flutter 层绑定的监听处理事件。用于处理离线推送点击跳转,例如至对应会话。
|
||||
|
||||
详细代码可查看Demo源码。
|
||||
|
||||
**监听及转发离线推送点击事件**
|
||||
|
||||
离线推送的初始化/Token上报/点击事件对应的会话跳转处理,已在Flutter Chat模块中进行,因此,Native区域,仅需透传点击通知事件的ext即可。
|
||||
|
||||
之所以这么做,是因为点击通知事件已在Native被拦截消费,Flutter层无法直接拿到,必须经由Native转发。
|
||||
|
||||
在 `AppDelegate.swift` 文件中,新增如下代码。具体代码,可以参考Demo源码。
|
||||
|
||||

|
||||
|
||||
此时,iOS Native层编写完成。
|
||||
|
||||
### Android Native 开发
|
||||
|
||||
本文以 Kotlin 语言为例。
|
||||
|
||||
>?
|
||||
>
|
||||
> 以下代码结构,仅供参考,您可根据需要灵活组织。
|
||||
|
||||
#### 引入 Flutter Module
|
||||
|
||||
请参考[此部分](#android),将Flutter module引入您的原生应用程序中。建议采用方式二。
|
||||
|
||||
#### 在 Android 项目中,管理Flutter引擎
|
||||
|
||||
**创建一个用于管理Flutter引擎的单例对象。**
|
||||
|
||||
这个 Kotlin 单例对象,用于集中管理 Flutter Method Channel,并提供一系列与 Flutter Module 通信的方法,方便在项目中各处,直接调用。
|
||||
|
||||
新建 `FlutterUtils.kt` 文件,并定义 `FlutterUtils` 静态类。
|
||||
|
||||
```kotlin
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object FlutterUtils {}
|
||||
```
|
||||
|
||||
**创建一个 `FlutterEngine` (Flutter 引擎)。**
|
||||
|
||||
在 `FlutterUtils.kt` 文件中,定义一个 `FlutterEngine`,并在初始化时,将其初始化。
|
||||
|
||||
```kotlin
|
||||
lateinit var context : Context
|
||||
private lateinit var flutterEngine:FlutterEngine
|
||||
|
||||
// 初始化
|
||||
flutterEngine = FlutterEngine(context)
|
||||
```
|
||||
|
||||
**继续完成该用于管理Flutter引擎的单例对象。**
|
||||
|
||||
Demo代码的逻辑是,使用新的路由,承载Chat和Call的Activity。
|
||||
|
||||
Chat的Activity,由用户主动进入及退出;Call的Activity,由监听器或主动外呼,自动导航进及返回出。
|
||||
|
||||
重点关注:
|
||||
- fun init(): 初始化 Method Channel,并为其绑定事件监听方法。
|
||||
- fun reportChatInfo(): 将用户登录信息和SDKAPPID透传至Flutter Module,使Flutter层得以初始化并登录腾讯云IM。
|
||||
- fun launchChatFunc(): 拉起或导航至 Flutter Module 所在 ViewController。
|
||||
- fun triggerNotification(msg: String): 将 iOS Native 层收到的离线推送消息点击事件,及其包含的ext信息,以 JSON String形式,透传至 Flutter 层绑定的监听处理事件。用于处理离线推送点击跳转,例如至对应会话。
|
||||
|
||||
本单例 object 的详细代码,可以参考Demo源码。
|
||||
|
||||
**在 总入口 `MyApplication` 中,初始化上述对象**
|
||||
|
||||
在 `MyApplication.kt` 文件中,将全局context传入单例对象,并执行初始化。
|
||||
|
||||
```kotlin
|
||||
class MyApplication : MultiDexApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
FlutterUtils.context = this // new
|
||||
FlutterUtils.init() // new
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**监听及转发离线推送点击事件**
|
||||
|
||||
离线推送的初始化/Token上报/点击事件对应的会话跳转处理,已在Flutter Chat模块中进行,因此,Native区域,仅需透传点击通知事件的ext即可。
|
||||
|
||||
之所以这么做,是因为点击通知事件已在Native被拦截消费,Flutter层无法直接拿到,必须经由Native转发。
|
||||
|
||||
> 由于不同厂商的离线推送接入步骤不一致,本文以OPPO为例,全部厂商接入方案,可查看[本文档](https://cloud.tencent.com/document/product/269/75428).
|
||||
|
||||
在腾讯云IM控制台中,新增OPPO的推送证书,`点击后续动作` 选择 `打开应用内指定页面`,`应用内页面` 以 `Activity` 方式,配置一个用于处理离线推送信息的页面,建议为应用首页。如,我们的Demo配置为:`com.tencent.chat.android.MainActivity`.
|
||||
|
||||

|
||||
|
||||
在上方控制台配置的用于离线推送的Activity文件中,新增如下代码。
|
||||
|
||||
该代码的作用是,当厂商拉起相应Activity时,从Bundle中取出HashMap形式ext信息,触发单例对象中的方法,将这个信息,手动转发至Flutter中。具体代码,可以参考Demo源码。
|
||||
|
||||

|
||||
|
||||
此时,Android Native层编写完成。
|
||||
|
||||
[](id:native)
|
||||
|
||||
## 附加方案:在 Native 层,初始化并登录腾讯云IM
|
||||
|
||||
有的时候,对于Chat和Call模块能力,您希望对于高频的简单应用场景,能深入嵌入您现有的业务逻辑中。
|
||||
|
||||
例如对于游戏场景,在对局内,希望能直接发起会话。
|
||||
|
||||
而您的完整功能Chat模块,使用Flutter实现,仅是您APP中一个重要性较低的子模块,因此不希望一上来就启动一个完整的Flutter Module。
|
||||
|
||||
这个时候,您可以在Native层调用腾讯云IM Native SDK的初始化及登录方法,此后,便可在您需要的高频简单场景,直接使用腾讯云IM Native SDK,构建 In-App Chat 能力。
|
||||
|
||||
>?
|
||||
> 当然,在此种情况下,您也可以选择提前先在 Flutter 初始化并登录腾讯云IM,此时,您将不再需要在 Native 层再次初始化并登录。两端仅需初始化并登录一次,即可在双端都能使用。
|
||||
|
||||
由于Flutter SDK已自带Native SDK,您不需要在Native层,再次引入,即可直接使用。
|
||||
|
||||
### Native初始化并登录
|
||||
|
||||
以 iOS Swift 代码为例,演示如何在 Native 层,初始化并登录。
|
||||
|
||||
```swift
|
||||
import ImSDK_Plus
|
||||
|
||||
|
||||
func initTencentChat(){
|
||||
if(isLoginSuccess == true){
|
||||
return
|
||||
}
|
||||
let data = V2TIMManager.sharedInstance().initSDK( 您的SDKAPPID , config: nil);
|
||||
if (data == true){
|
||||
V2TIMManager.sharedInstance().login(
|
||||
chatInfo.userID,
|
||||
userSig: chatInfo.userSig,
|
||||
succ: {
|
||||
self.isLoginSuccess = true
|
||||
self.reportChatInfo()
|
||||
},
|
||||
fail: onLoginFailed()
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
此后,在 Native 层面,便可直接使用Native SDK,搭建您的业务功能模块。详情可查阅 [iOS 快速入门]() 或 [Android 快速入门](https://cloud.tencent.com/document/product/269/36838)。
|
||||
|
||||
### 初始化 Flutter TUIKit
|
||||
|
||||
如果您已在 Native 层完成初始化并登录,您不需要再次在 Flutter 层再次执行,但需要调用 TUIKit的 `_coreInstance.setDataFromNative()`,将当前用户信息传入。
|
||||
|
||||
```dart
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
_coreInstance.setDataFromNative(userId: chatInfo?.userID ?? "");
|
||||
```
|
||||
**更详细代码,请查阅我们的Demo 源码。**
|
||||
|
||||
[](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Initialize%20from%20Native)
|
||||
|
||||
-----
|
||||
|
||||
至此,腾讯云IM Flutter - Native 混合开发方式已全部介绍完成。
|
||||
|
||||
您可以基于本文档给出的方案,快速在您现有的原生开发 Android/iOS APP 中,使用 Flutter SDK,使用同一套Flutter代码,快速植入 Chat 和 Call 模块能力。
|
||||
|
||||
如果您还有任何疑问,欢迎随时联系我们。
|
||||
|
||||

|
||||
|
||||
|
||||
## Reference
|
||||
|
||||
1. [Integrate a Flutter module into your Android project](https://docs.flutter.dev/development/add-to-app/android/project-setup).
|
||||
2. [Integrate a Flutter module into your iOS project](https://docs.flutter.dev/development/add-to-app/ios/project-setup).
|
||||
3. [Adding a Flutter screen to an iOS app](https://docs.flutter.dev/development/add-to-app/ios/add-flutter-screen?tab=no-engine-vc-swift-tab).
|
||||
4. [Multiple Flutter screens or views](https://docs.flutter.dev/development/add-to-app/multiple-flutters).
|
||||
|
|
@ -0,0 +1,974 @@
|
|||
|
||||
If you already have an Android/iOS APP being public online, In-APP chat and call modules may need to be added as your business develops.
|
||||
|
||||
For instance, adding chat modules to video apps to facilitate interaction between viewers; adding chat modules to eshop apps to facilitate communication between customers and merchants; or adding chat modules to music and entertainment apps so that people with the same interests Taste groups, find organizations and communicate.
|
||||
|
||||
Adding the chat module requires the Android/iOS team to develop and access it separately, for your existing application. It not only consumes a lot, but also may cause inconsistent experience and out-of-sync message sending and receiving.
|
||||
|
||||
So, it is recommended to integrate Tencent Cloud Chat with Flutter, coding once and deploying to all platforms, which is really convenient.
|
||||
|
||||
But, it’s sometimes not practical to rewrite your entire application in Flutter all at once. For those situations, Flutter can be integrated into your existing application piecemeal, as a library or module. That module can then be imported into your Android or iOS (currently supported platforms) app to render a part of your app’s UI in Flutter.
|
||||
|
||||
With this solution, you are able to integrate Tencent Cloud Chat Flutter SDKs to your existing Android/iOS application.
|
||||
|
||||
**It could reduce your workload, to adding chat and call modules to your existing, to a large extent.**
|
||||
|
||||

|
||||
|
||||
## Environment requirements
|
||||
|
||||
| | Version |
|
||||
|---------|---------|
|
||||
| Flutter | Flutter 2.2.0 or later for the IM SDK; Flutter 2.10.0 or later for the TUIKit integration component library.|
|
||||
|Android|Android Studio 3.5 or later; devices with Android 4.1 or later for apps. |
|
||||
|iOS| Xcode 11.0 or later. Ensure that your project has a valid developer signature. |
|
||||
|Tencent Cloud Chat Flutter SDK|[tencent_im_sdk_plugin](https://pub.dev/packages/tencent_im_sdk_plugin) 5.0 or later, [tencent_cloud_chat_uikit](https://pub.dev/packages/tencent_cloud_chat_uikit) 0.2 or later. |
|
||||
|
||||
## What you need to know first
|
||||
|
||||
Before starting, you are recommended to know about Tencent Cloud Chat and adding the Flutter module to existing apps.
|
||||
|
||||
### Tencent Cloud Chat
|
||||
|
||||
#### Overall
|
||||
|
||||
Before starting, you are supposed to be familiar with our SDKs, and the basic usage.
|
||||
|
||||
Two main SDKs are included, [non-UI SDK](https://www.tencentcloud.com/document/product/1047/45907#part-5.-self-implementing-integration), and [TUIKit](https://www.tencentcloud.com/document/product/1047/50059).
|
||||
|
||||
In this tutorial, we will mainly develop with TUIKit, with UI library and basic business logic.
|
||||
|
||||
**You could get to know about our SDKs, with [Get Started](https://www.tencentcloud.com/document/product/1047/45907).**
|
||||
|
||||
[](id:modules)
|
||||
|
||||
#### Two modules
|
||||
|
||||
Two main modules are included, Chat and Call.
|
||||
|
||||
Chat module includes, sending and receiving messages, relationship management, etc.
|
||||
|
||||
Call module includes voice call and video call, for both one-to-one call and group call.
|
||||
|
||||
### Adding Flutter to Native APP
|
||||
|
||||
Be simplified, the key of this solution is, embedding the Flutter module to your native application in a subproject. As the cross-platform feature of Flutter, one single Flutter module can be added to both Android and iOS projects.
|
||||
|
||||
To launch a Flutter screen from an existing iOS/Android, you start a [FlutterEngine](https://api.flutter.dev/objcdoc/Classes/FlutterEngine.html) and a FlutterViewController/FlutterActiviy.
|
||||
|
||||
The `FlutterEngine` serves as a host to the Dart VM and your Flutter runtime, and the `FlutterViewController`/`FlutterActivity` attaches to a FlutterEngine to pass input events into Flutter and to display frames rendered by the `FlutterEngine`.
|
||||
|
||||
The `FlutterEngine` may have the same lifespan as your `FlutterViewController`/`FlutterActivity` or outlive your `FlutterViewController`/`FlutterActivity`.
|
||||
|
||||
[Method Channel](https://docs.flutter.dev/development/platform-integration/platform-channels#channels-and-platform-threading) can be used to communicate between Native APP and Flutter module if necessary, like transmitting the current user info, the EXT of offline push and call data. Invoking a method on the method channel, and listening for the invoking with a `MethodCallHandler` being preset.
|
||||
|
||||
[](id:android)
|
||||
|
||||
#### Adding to an Android app
|
||||
|
||||
[Details documentation](https://docs.flutter.dev/development/add-to-app/android/project-setup)
|
||||
|
||||
Adding the Flutter module as a dependency of your existing app in Gradle. There are two ways to achieve this. The AAR mechanism creates generic Android AARs as intermediaries that package your Flutter module. This is good when your downstream app builders don’t want to have the Flutter SDK installed. But, it adds one more build step if you build frequently.
|
||||
|
||||
The source code subproject mechanism is a convenient one-click build process, but requires the Flutter SDK. This is the mechanism used by the Android Studio IDE plugin.
|
||||
|
||||
##### Option A - Depend on the Android Archive (AAR)
|
||||
|
||||
This option packages your Flutter library as a generic local Maven repository composed of AARs and POMs artifacts.
|
||||
|
||||
This option allows your team to build the host app without installing the Flutter SDK. You can then distribute the artifacts from a local or remote repository.
|
||||
|
||||
It's recommended to use this option for the released version.
|
||||
|
||||
**Steps:**
|
||||
|
||||
Run the following command on your Flutter module.
|
||||
|
||||
```shell
|
||||
flutter build aar
|
||||
```
|
||||
|
||||
Then, follow the on-screen instructions to integrate.
|
||||
|
||||

|
||||
|
||||
Your app now includes the Flutter module as a dependency.
|
||||
|
||||
##### Option B - Depend on the module’s source code
|
||||
|
||||
This option enables a one-step build for both your Android project and Flutter project.
|
||||
|
||||
This option is convenient when you work on both parts simultaneously and rapidly iterate, but your team must install the Flutter SDK to build the host app.
|
||||
|
||||
It's recommended to use this option when development and debugging.
|
||||
|
||||
**Steps:**
|
||||
|
||||
Include the Flutter module as a subproject in the host app’s `settings.gradle`:
|
||||
|
||||
```gradle
|
||||
// Include the host app project.
|
||||
include ':app' // assumed existing content
|
||||
setBinding(new Binding([gradle: this])) // new
|
||||
evaluate(new File( // new
|
||||
settingsDir.parentFile, // new
|
||||
'tencent_chat_module/.android/include_flutter.groovy' // new
|
||||
)) // new
|
||||
```
|
||||
|
||||
Introduce an `implementation` dependency on the Flutter module from your app:
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation project(':flutter')
|
||||
}
|
||||
```
|
||||
|
||||
Your app now includes the Flutter module as a dependency.
|
||||
|
||||
[](id:ios)
|
||||
|
||||
#### Adding to an iOS app
|
||||
|
||||
[Details documentation](https://docs.flutter.dev/development/add-to-app/ios/project-setup#embed-the-flutter-module-in-your-existing-application)
|
||||
|
||||
There are two ways to embed Flutter in your existing application.
|
||||
|
||||
- Use the CocoaPods dependency manager and install Flutter SDK. (Recommended)
|
||||
- Create frameworks for the Flutter engine, your compiled Dart code, and all Flutter plugins. Manually embed the frameworks, and update your existing application’s build settings in Xcode.
|
||||
|
||||
>?
|
||||
> Your app does not run on a simulator in Release mode because Flutter does not yet support outputting x86/x86_64 ahead-of-time (AOT) binaries for your Dart code. You can run in Debug mode on a simulator or a real device, and Release on a real device.
|
||||
>
|
||||
> To run your app on a simulator follow the instructions in the bottom of section [embed the frameworks](https://docs.flutter.dev/development/add-to-app/ios/project-setup#embed-the-frameworks).
|
||||
|
||||
##### Option A - Embed with CocoaPods and the Flutter SDK
|
||||
|
||||
This method requires every developer working on your project to have a locally installed version of the Flutter SDK. Simply build your application in Xcode to automatically run the script to embed your Dart and plugin code.
|
||||
|
||||
This allows rapid iteration with the most up-to-date version of your Flutter module without running additional commands outside of Xcode.
|
||||
|
||||
It's recommended to use this option when development and debugging.
|
||||
|
||||
**Steps:**
|
||||
|
||||
Add the following lines to your `Podfile`:
|
||||
|
||||
```
|
||||
// The path of your Flutter module
|
||||
flutter_chat_application_path = '../tencent_chat_module'
|
||||
|
||||
load File.join(flutter_chat_application_path, '.ios', 'Flutter', 'podhelper.rb')
|
||||
```
|
||||
|
||||
For each [Podfile target](https://guides.cocoapods.org/syntax/podfile.html#target) that needs to embed Flutter, call `install_all_flutter_pods(flutter_application_path)`.
|
||||
|
||||
```
|
||||
target 'MyApp' do
|
||||
install_all_flutter_pods(flutter_chat_application_path)
|
||||
end
|
||||
```
|
||||
|
||||
In the `Podfile`’s `post_install` block, call `flutter_post_install(installer)`, and with the statement of necessary permissions.
|
||||
|
||||
```
|
||||
post_install do |installer|
|
||||
flutter_post_install(installer) if defined?(flutter_post_install)
|
||||
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
|
||||
```
|
||||
|
||||
Run `pod install`.
|
||||
|
||||
> ?
|
||||
>
|
||||
> - When you change the Flutter plugin dependencies in `tencent_chat_module/pubspec.yaml`, run `flutter pub get` in your Flutter module directory to refresh the list of plugins read by the `podhelper.rb` script. Then, run pod install again from the root directory of your application.
|
||||
> - You may need to run `arch -x86_64 pod install --repo-update` on the Mac with Apple Silicon, like M1 or M2.
|
||||
|
||||
The `podhelper.rb` script embeds your plugins, `Flutter.framework`, and `App.framework` into your project.
|
||||
|
||||
##### Option B - Embed frameworks in Xcode
|
||||
|
||||
Alternatively, you can generate the necessary frameworks and embed them in your application by manually editing your existing Xcode project.
|
||||
|
||||
You may do this if members of your team can’t locally install Flutter SDK and CocoaPods, or if you don’t want to use CocoaPods as a dependency manager in your existing applications.
|
||||
|
||||
You must run `flutter build ios-framework` every time you make code changes in your Flutter module.
|
||||
|
||||
It's recommended to use this option for the released version.
|
||||
|
||||
**Steps:**
|
||||
|
||||
Run the following command on your Flutter module.
|
||||
|
||||
The following example assumes that you want to generate the frameworks to `some/path/MyApp/Flutter/`.
|
||||
|
||||
```shell
|
||||
flutter build ios-framework --output=some/path/MyApp/Flutter/
|
||||
```
|
||||
|
||||
Embed and link the generated frameworks into your existing application in Xcode.
|
||||
|
||||
## The mode of adding
|
||||
|
||||
It's recommended to add the Flutter module to your existing application.
|
||||
|
||||
Two modules needed to be added by `FlutterEngine`, Chat and Call. For details, [see here](#modules).
|
||||
|
||||
Two modes of `FlutterEngine` are provided, single `FlutterEngine` or two `FlutterEngine`s with a `FlutterEngineGroup`.
|
||||
|
||||
| Mode | Introduction | Props | Cons | Demo |
|
||||
|---------|---------|---------|---------|---------|
|
||||
| [Single FlutterEngine](#single) | Both Chat and Call integrate into one `FlutterEngine` | Convenient | It's required to navigate to the Flutter page, when new call income, to show the call status, which may interrupt the current status, with a relatively bad experience. | [GitHub](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Single%20Flutter%20Engines) |
|
||||
| [Multiple FlutterEngines](#multiple) | Call and Chat modules located in two separate FlutterEngine | Only the presence of the calling page separately when a new call is incoming is necessary, and dismiss it automatically when call ends, without the navigating of current status, for a better experience. | Minimize for the calling page is not allowed. | [GitHub](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Multiple%20Flutter%20Engines) |
|
||||
|
||||
Additionally, the solution of integrating both Native Chat SDK and Flutter Chat SDK is also provided. For details, [see here](#native), and demo can be found from [GitHub](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Initialize%20from%20Native).
|
||||
|
||||
[](id:multiple)
|
||||
|
||||
## Solution A: Multiple FlutterEngines (Recommended)
|
||||
|
||||
The advantage of using multiple Flutter instances is that each instance is independent and maintains its own internal navigation stack, UI, and application states. This simplifies the overall application code’s responsibility for state keeping and improves modularity. More details on the scenarios motivating the usage of multiple Flutters can be found at docs.flutter.dev/go/multiple-flutters.
|
||||
|
||||

|
||||
|
||||
The primary API for adding multiple Flutter instances on both Android and iOS is based on a new `FlutterEngineGroup` class to construct `FlutterEngine`s, rather than the `FlutterEngine` constructors used in the [Solution B: Single FlutterEngine](#single).
|
||||
|
||||
Whereas the `FlutterEngine` API was direct and easier to consume, the `FlutterEngine` spawned from the same `FlutterEngineGroup` have the performance advantage of sharing many of the common, reusable resources such as the GPU context, font metrics, and isolate group snapshot, leading to a faster initial rendering latency and lower memory footprint.
|
||||
|
||||
In our project, one single `FlutterEngineGroup` is used to manage the two `FlutterEngine`s, including Chat and Call modules.
|
||||
|
||||
[](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Multiple%20Flutter%20Engines)
|
||||
|
||||
### The development of Flutter module
|
||||
|
||||
To embed Flutter into your existing application, first create a Flutter module.
|
||||
|
||||
From the command line, run:
|
||||
|
||||
```
|
||||
cd some/path/
|
||||
flutter create --template module tencent_chat_module
|
||||
```
|
||||
|
||||
A Flutter module project is created at `some/path/tencent_chat_module/`. From that directory, you can run the same flutter commands you would in any other Flutter project, like `flutter run --debug` or `flutter build ios`. You can also run the module in Android Studio/IntelliJ or VS Code with the Flutter and Dart plugins. This project contains a single-view example version of your module before it’s embedded in your existing application, which is useful for incrementally testing the Flutter-only parts of your code.
|
||||
|
||||
The `tencent_chat_module` module directory structure is similar to a normal Flutter application:
|
||||
|
||||
```
|
||||
tencent_chat_module/
|
||||
├── .ios/
|
||||
│ ├── Runner.xcworkspace
|
||||
│ └── Flutter/podhelper.rb
|
||||
├── lib/
|
||||
│ └── main.dart
|
||||
├── test/
|
||||
└── pubspec.yaml
|
||||
```
|
||||
|
||||
Now, we can code within `lib/`.
|
||||
|
||||
#### The structure of `lib/`
|
||||
|
||||
>?
|
||||
>
|
||||
> The following structure and code is for demonstration purposes only, you could modify it to meet your actual needs dynamically.
|
||||
|
||||
Now, let's create three directory within `lib/`, including `call`, `chat` and `common`. Which is used for Call module, Chat module and some common classes separately.
|
||||
|
||||
```
|
||||
tencent_chat_module/
|
||||
├── lib/
|
||||
│ └── call/
|
||||
│ └── chat/
|
||||
│ └── common/
|
||||
```
|
||||
|
||||
#### Common model classes
|
||||
|
||||
Add the two following classes to a new file, `common/common_model.dart`. Used for definition the communication proxy between native and Flutter.
|
||||
|
||||
```dart
|
||||
class ChatInfo {
|
||||
String? sdkappid;
|
||||
String? userSig;
|
||||
String? userID;
|
||||
|
||||
ChatInfo.fromJSON(Map<String, dynamic> json) {
|
||||
sdkappid = json["sdkappid"].toString();
|
||||
userSig = json["userSig"].toString();
|
||||
userID = json["userID"].toString();
|
||||
}
|
||||
|
||||
Map<String, String> toMap(){
|
||||
final Map<String, String> map = {};
|
||||
if(sdkappid != null){
|
||||
map["sdkappid"] = sdkappid!;
|
||||
}
|
||||
if(userSig != null){
|
||||
map["userSig"] = userSig!;
|
||||
}
|
||||
if(userID != null){
|
||||
map["userID"] = userID!;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class CallInfo{
|
||||
String? userID;
|
||||
String? groupID;
|
||||
|
||||
CallInfo();
|
||||
|
||||
CallInfo.fromJSON(Map<String, dynamic> json) {
|
||||
groupID = json["groupID"].toString();
|
||||
userID = json["userID"].toString();
|
||||
}
|
||||
|
||||
Map<String, String> toMap(){
|
||||
final Map<String, String> map = {};
|
||||
if(userID != null){
|
||||
map["userID"] = userID!;
|
||||
}
|
||||
if(groupID != null){
|
||||
map["groupID"] = groupID!;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Chat Module
|
||||
|
||||
**The following files and codes are located in the `lib/chat` directory.**
|
||||
|
||||
1. Create a file, `model.dart`, used as a state container.
|
||||
This model is used to initialize and maintain the instance of Tencent Cloud Chat, offline line push module, global state, and the communication with native apps.
|
||||
Is the core of the Chat module.
|
||||
Detailed implementation can refer to the source code of the demo, while it's recommended to focus on these three functions:
|
||||
- Future<dynamic> _handleMessage(MethodCall call): Listening for the message call from native app.
|
||||
- Future<void> handleClickNotification(Map<String, dynamic> msg): The function invoked by the callback after clicking the notification.
|
||||
- Future<void> initChat(): Initialize and log in Tencent Cloud Chat SDK and offline push plugin, upload token. This method uses the sync lock mechanism to ensure that only one can be executed at the same time, and after the initialization is successful, it will not be executed repeatedly.
|
||||
|
||||
> ?
|
||||
>
|
||||
> Please configure the offline push before uploading the token and use this capability, referring to this [documentation](https://www.tencentcloud.com/document/product/1047/50032).
|
||||
|
||||
2. Create a file, `chat_main.dart`, used as the main entrance of the chat module.
|
||||
- Also, used as the home page of the chat module.
|
||||
- It shows the loading status before logged in, followed by the conversation list.
|
||||
- Besides, the current status of the application needs to be reported to the Tencent Cloud Chat backend upon each foreground/background switch from here. Referring to this [documentation](https://www.tencentcloud.com/document/product/1047/50032#step-5.-listen-for-the-foreground.2Fbackground-switch.3Ca-id.3D.22step_5.22.3E.3C.2Fa.3E).
|
||||
- Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
3. Create a file, `push.dart`, used for maintaining the [offline push plugin](https://www.tencentcloud.com/document/product/1047/50032). Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
4. Create a file, `conversation.dart`, used for implementing conversation list widget `TIMUIKitConversation`. Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
5. Create a file, `user_profile.dart`, used to implement the user profile widget `TIMUIKitProfile`. Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
6. Create a file, `group_profile.dart`, used to implement group profile widget `TIMUIKitGroupProfile`. Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
7. Create a file, `chat.dart`, used for implementing the history message list and sending messages widget `TIMUIKitChat`. This page can also navigate to `user_profile.dart` and `conversation.dart`.Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
Now, Chat module has been developed, with the following structure:
|
||||
|
||||
```
|
||||
tencent_chat_module/
|
||||
├── lib/
|
||||
│ └── call/
|
||||
│ └── chat.dart
|
||||
│ └── model.dart
|
||||
│ └── chat_main.dart
|
||||
│ └── push.dart
|
||||
│ └── conversation.dart
|
||||
│ └── user_profile.dart
|
||||
│ └── group_profile.dart
|
||||
│ └── chat/
|
||||
│ └── common/
|
||||
```
|
||||
|
||||
#### Call Module
|
||||
|
||||
This module is used for voice call and video call, provided by our [calling plugin](https://pub.dev/packages/tim_ui_kit_calling_plugin).
|
||||
|
||||
The key feature of this module is, when receiving the income calling, invoke the method to native requesting show this page; or, initiate a call to others, when receiving the request from Chat module, via native.
|
||||
|
||||
**The following files and codes are located in the `lib/calls` directory.**
|
||||
|
||||
1. Create a file, `model.dart`, used as a state container.
|
||||
This model is used for initializing and maintaining the instance of [Calling plug-in](https://pub.dev/packages/tim_ui_kit_calling_plugin), global state, and the communication with native apps.
|
||||
Is the core of the Call module.
|
||||
Detailed implementation can refer to the source code of the demo, while it's recommended to focus on these three functions:
|
||||
- _onRtcListener = TUICallingListener(...): The listener of the calling events, notify native to show this page, when receiving a new call.
|
||||
- Future<dynamic> _handleMessage(MethodCall call): The listener of the method channel call events, mainly used for initiating a call to others, when receiving the request from the Chat module, via native.
|
||||
|
||||
2. Create a file, `call_main.dart`, used as the main entry point of the Call module.
|
||||
The `navigatorKey` used for the launch calling page is supposed to be added here.
|
||||
Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
#### Configure the entry point for each modules
|
||||
|
||||
After developing the three parts above, now we can configure the entry point for each module, used as the entrance for FlutterEngine.
|
||||
|
||||
1. Default entry
|
||||
|
||||
Open `lib/main.dart`, modify the default main functions to return an empty `MaterialApp`.
|
||||
|
||||
This function is the entry point, while not being used in this solution.
|
||||
|
||||
As, the name of the entry point is necessary while the creation of a `FlutterEngine`, and we won't create a `FlutterEngine` without a name.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
runApp(MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
home: Container(),
|
||||
));
|
||||
}
|
||||
```
|
||||
|
||||
2. The entry for Chat module
|
||||
|
||||
Use `@pragma('vm:entry-point')` annotation to mark a method as an entry point. The method name `chatMain` is the name of the entry.
|
||||
|
||||
In Native, this name is also used to create the corresponding `FlutterEngine`.
|
||||
|
||||
Use global `ChangeNotifierProvider` status management to maintain `ChatInfoModel` data and business logic.
|
||||
|
||||
```dart
|
||||
@pragma('vm:entry-point')
|
||||
void chatMain() {
|
||||
// This call ensures the Flutter binding has been set up before creating the
|
||||
// MethodChannel-based model.
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final model = ChatInfoModel();
|
||||
|
||||
runApp(
|
||||
ChangeNotifierProvider.value(
|
||||
value: model,
|
||||
child: const ChatAPP(),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
3. The entry for Call module
|
||||
|
||||
This entry point is named as `callMain`.
|
||||
|
||||
Use global `ChangeNotifierProvider` status management to maintain `CallInfoModel` data and business logic.
|
||||
|
||||
```dart
|
||||
@pragma('vm:entry-point')
|
||||
void callMain() {
|
||||
// This call ensures the Flutter binding has been set up before creating the
|
||||
// MethodChannel-based model.
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final model = CallInfoModel();
|
||||
|
||||
runApp(
|
||||
ChangeNotifierProvider.value(
|
||||
value: model,
|
||||
child: const CallAPP(),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
So far, the Dart code for the Flutter Module has been written.
|
||||
|
||||
Now, let's take a look at the native integration for your existing app.
|
||||
|
||||
### iOS Native development
|
||||
|
||||
Here, we take `Swift` as an example, while `Objective-C` is also available.
|
||||
|
||||
>?
|
||||
>
|
||||
> The following structure and code is for demonstration purposes only, you could modify it to meet your actual needs dynamically.
|
||||
|
||||
Open your iOS project within XCode.
|
||||
|
||||
If your existing application (MyApp) doesn’t already have a `Podfile`, follow the [CocoaPods getting started guide](https://guides.cocoapods.org/using/using-cocoapods.html) to add a Podfile to your project.
|
||||
|
||||
#### Import Flutter Module
|
||||
|
||||
Please refer to [this part](#ios), adding the Flutter module to your existing iOS app.
|
||||
|
||||
#### FlutterEngineGroup
|
||||
|
||||

|
||||
|
||||
**Create a `FlutterEngineGroup` to maintain and manage the `FlutterEngine`s.**
|
||||
|
||||
The proper place to create a `FlutterEngineGroup` is specific to your host app. As an example, we demonstrate creating a `FlutterEngineGroup`, exposed as a property, on app startup in the app delegate.
|
||||
|
||||
Add the following to `AppDelegate.swift`.
|
||||
|
||||
```swift
|
||||
@UIApplicationMain
|
||||
class AppDelegate: FlutterAppDelegate {
|
||||
lazy var flutterEngines = FlutterEngineGroup(name: "chat.flutter.tencent", project: nil)
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**Create a singleton static object to hold `FlutterEngine`s.**
|
||||
|
||||
This singleton is used for managing those `FlutterEngine`s in one place, and provides methods to the whole project to invoke methods related to the Flutter module.
|
||||
|
||||
The basic implementation logic of the demo is that, using a new navigator for the ViewController of Chat, while `present` and `dismiss` the ViewController of Call automatically.
|
||||
|
||||
Create a new file, `FlutterUtils.swift`, and coding, refer to our demo source code.
|
||||
|
||||
Mainly focus on:
|
||||
|
||||
- private override init(): Initialize each Flutter instance, register method channel events.
|
||||
- func reportChatInfo(): Report the current user info to the Flutter module, for initialization and login Tencent Cloud Chat SDK.
|
||||
- func launchCallFunc(): Present the ViewController for Call module, invoked when new call income or user active it manually from Chat module.
|
||||
- func triggerNotification(msg: String): Transit the data of notification, after the user clicks it, and Chat module may navigate to the corresponding chat page.
|
||||
|
||||
**Listening and transit the notification click event**
|
||||
|
||||
Only transit of the data of notification after clicking is necessary as, the initialization of Push plug-in, uploading token and the navigating for notification clicking events have been done in Flutter Chat module.
|
||||
|
||||
The reason why we need to do this is because the clicking event has been consumed by Native, so it is impossible for the Flutter Push plug-in to receive this event.
|
||||
|
||||
Add the following codes to `AppDelegate.swift`.
|
||||
|
||||

|
||||
|
||||
Now, we finished the implementation for iOS.
|
||||
|
||||
### Android Native Development
|
||||
|
||||
Here, we take `Kotlin` as an example, while `Java` is also available.
|
||||
|
||||
>?
|
||||
>
|
||||
> The following structure and code is for demonstration purposes only, you could modify it to meet your actual needs dynamically.
|
||||
|
||||
Open your Android project within Android Studio.
|
||||
|
||||
#### Import Flutter Module
|
||||
|
||||
Please refer to [this part](#android), adding the Flutter module to your existing Android app.
|
||||
|
||||
#### FlutterEngineGroup
|
||||
|
||||
**Create a `FlutterEngineGroup` to maintain and manage the `FlutterEngine`s.**
|
||||
|
||||
The proper place to create a `FlutterEngineGroup` is specific to your host app. As an example, we demonstrate creating a `FlutterEngineGroup`, exposed as a property, on app startup in the app delegate.
|
||||
|
||||
Create a new file, `FlutterUtils.kt`, and define a singleton static object `FlutterUtils`.
|
||||
|
||||
```kotlin
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object FlutterUtils {}
|
||||
```
|
||||
|
||||
**Create a `FlutterEngineGroup` to maintain and manage the `FlutterEngine`s.**
|
||||
|
||||
Define a `FlutterEngineGroup`, `FlutterEngine`s and corresponding `MethodChannel`s in `FlutterUtils.kt`.
|
||||
|
||||
```kotlin
|
||||
lateinit var context : Context
|
||||
lateinit var flutterEngines: FlutterEngineGroup
|
||||
private lateinit var chatFlutterEngine:FlutterEngine
|
||||
private lateinit var callFlutterEngine:FlutterEngine
|
||||
|
||||
lateinit var chatMethodChannel: MethodChannel
|
||||
lateinit var callMethodChannel: MethodChannel
|
||||
|
||||
// Initialize them
|
||||
flutterEngines = FlutterEngineGroup(context)
|
||||
...
|
||||
```
|
||||
|
||||
**Further developed for this singleton static object**
|
||||
|
||||
The basic implementation logic of the demo is that, using a new navigator for the `Activity` for both Chat and Chat.
|
||||
|
||||
The `Activity` for Chat is entered and exited by the user, while the `Activity` for Call has been entered and exited automatically, triggered by the listener or making a call manually.
|
||||
|
||||
Mainly focus on:
|
||||
- fun init(): Initialize each Flutter instance, register method channel events.
|
||||
- fun reportChatInfo(): Report the current user info to the Flutter module, for initialization and login Tencent Cloud Chat SDK.
|
||||
- fun launchCallFunc(): Present the `Activity` for Call module, invoked when new call income or user active it manually from Chat module.
|
||||
- fun triggerNotification(msg: String): Transit the data of notification, after the user clicks it, and Chat module may navigate to the corresponding chat page.
|
||||
|
||||
You can refer to the demo source code for this object.
|
||||
|
||||
**Initialize the singleton static object above from the main entry `MyApplication`.**
|
||||
|
||||
Transit the global context to the singleton static object, and initialize it from `MyApplication.kt`.
|
||||
|
||||
```kotlin
|
||||
class MyApplication : MultiDexApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
FlutterUtils.context = this // new
|
||||
FlutterUtils.init() // new
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Listening and transit the notification click event**
|
||||
|
||||
Only transit of the data of notification after clicking is necessary as, the initialization of Push plug-in, uploading token and the navigating for notification clicking events have been done in Flutter Chat module.
|
||||
|
||||
The reason why we need to do this is the clicking event has been consumed by Android Kotlin, so it is impossible for the Flutter Push plug-in to receive this event.
|
||||
|
||||
> Due to the diversity and inconsistency among different manufacturers, we only take OPPO as an example. For the whole manufacturer's support, please refer to this [documentation](https://www.tencentcloud.com/document/product/1047/50032).
|
||||
|
||||
Add a new push certificate to the Tencent Cloud Chat console, Select **Open specified in-app page > activity** for the opening method and enter an activity to receive the notification clicking event with EXT data, it's suggested to set it as the home page or the main entrance. Like, we set `MainActivity` for our demo, `com.tencent.chat.android.MainActivity`.
|
||||
|
||||

|
||||
|
||||
Adding the following codes to the `Activity`, set for the console above.
|
||||
|
||||
The EXT data of the notification can be found from `Bundle` when the `Activity` has been launched by the device, when the user clicks the notification.
|
||||
|
||||
You can receive the EXT from `Activity`, and transit them to Flutter.
|
||||
|
||||
You can refer to the demo source code for this capability.
|
||||
|
||||

|
||||
|
||||
Now, we finished the implementation for Android.
|
||||
|
||||
[](id:single)
|
||||
|
||||
## Solution B: Single FlutterEngine
|
||||
|
||||
In this solution, the Chat module and Call module embed in one single Flutter instance.
|
||||
|
||||

|
||||
|
||||
As a result, those modules can only be shown or hidden at the same time.
|
||||
|
||||
[](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Single%20Flutter%20Engines)
|
||||
|
||||
### Flutter Module development
|
||||
|
||||
To embed Flutter into your existing application, first create a Flutter module.
|
||||
|
||||
From the command line, run:
|
||||
|
||||
```
|
||||
cd some/path/
|
||||
flutter create --template module tencent_chat_module
|
||||
```
|
||||
|
||||
A Flutter module project is created at `some/path/tencent_chat_module/`. From that directory, you can run the same flutter commands you would in any other Flutter project, like `flutter run --debug` or `flutter build ios`. You can also run the module in Android Studio/IntelliJ or VS Code with the Flutter and Dart plugins. This project contains a single-view example version of your module before it’s embedded in your existing application, which is useful for incrementally testing the Flutter-only parts of your code.
|
||||
|
||||
The `tencent_chat_module` module directory structure is similar to a normal Flutter application:
|
||||
|
||||
```
|
||||
tencent_chat_module/
|
||||
├── .ios/
|
||||
│ ├── Runner.xcworkspace
|
||||
│ └── Flutter/podhelper.rb
|
||||
├── lib/
|
||||
│ └── main.dart
|
||||
├── test/
|
||||
└── pubspec.yaml
|
||||
```
|
||||
|
||||
Now, we can code within `lib/`.
|
||||
|
||||
#### main.dart
|
||||
|
||||
Modify `main.dart`, integrating [TUIKit](https://www.tencentcloud.com/document/product/1047/50054), [Offline Push plug-in](https://www.tencentcloud.com/document/product/1047/50032) and [Call Plug-in](https://pub.dev/packages/tim_ui_kit_calling_plugin).
|
||||
|
||||
The global state, method channel and our Tencent Cloud Chat SDKs, maintained by `ChatInfoModel`.
|
||||
|
||||
After receiving the login user info from Native, invoke `_coreInstance.init()` and `_coreInstance.login()` to initialize and login the SDK. Also, Call plug-in and Push plug-in need to be initialized.
|
||||
|
||||
> ?
|
||||
>
|
||||
> Please configure the offline push before uploading the token and use this capability, referring to this [documentation](https://www.tencentcloud.com/document/product/1047/50032).
|
||||
|
||||
Tips for Call plug-in:
|
||||
- Listening for the call invitation, when a new call comes, notify Native to launch the Flutter page if not currently located in.
|
||||
|
||||
Tips for Push plug-in:
|
||||
- The callback event of notification clicking, transmitted from Native, and used for navigating to corresponding chat from EXT data.
|
||||
|
||||
Also, this is used as the home page of the chat module. It shows the loading status before logged in, followed by the conversation list.
|
||||
|
||||
Besides, the current status of the application needs to be reported to the Tencent Cloud Chat backend upon each foreground/background switch from here. Referring to this [documentation](https://www.tencentcloud.com/document/product/1047/50032#step-5.-listen-for-the-foreground.2Fbackground-switch.3Ca-id.3D.22step_5.22.3E.3C.2Fa.3E).
|
||||
|
||||
Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
#### Other widgets from TUIKit
|
||||
|
||||
1. Create a file, `push.dart`, used for maintaining the [offline push plugin](https://www.tencentcloud.com/document/product/1047/50032). Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
2. Create a file, `conversation.dart`, used to implement group profile widget `TIMUIKitGroupProfile`. Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
3. Create a file, `user_profile.dart`, used to implement the user profile widget `TIMUIKitProfile`. Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
4. Create a file, `group_profile.dart`, used to implement group profile widget `TIMUIKitGroupProfile`. Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
|
||||
5. Create a file, `chat.dart`, used for implementing the history message list and sending messages widget `TIMUIKitChat`. This page can also navigate to `user_profile.dart` and `conversation.dart`.Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
Now, the Flutter module has been developed.
|
||||
|
||||
### iOS Native development
|
||||
|
||||
Here, we take `Swift` as an example, while `Objective-C` is also available.
|
||||
|
||||
>?
|
||||
>
|
||||
> The following structure and code is for demonstration purposes only, you could modify it to meet your actual needs dynamically.
|
||||
|
||||
Open your iOS project within XCode.
|
||||
|
||||
If your existing application (MyApp) doesn’t already have a `Podfile`, follow the [CocoaPods getting started guide](https://guides.cocoapods.org/using/using-cocoapods.html) to add a Podfile to your project.
|
||||
|
||||
#### Import Flutter Module
|
||||
|
||||
Please refer to [this part](#ios), adding the Flutter module to your existing iOS app.
|
||||
|
||||
#### FlutterEngine
|
||||
|
||||
**Create a FlutterEngine.**
|
||||
|
||||
The proper place to create a `FlutterEngine` is specific to your host app. As an example, we demonstrate creating a `FlutterEngine`, exposed as a property, on app startup in the app delegate.
|
||||
|
||||
```swift
|
||||
import UIKit
|
||||
import Flutter
|
||||
import FlutterPluginRegistrant
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: FlutterAppDelegate { // More on the FlutterAppDelegate.
|
||||
lazy var flutterEngine = FlutterEngine(name: "tencent cloud chat")
|
||||
|
||||
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Runs the default Dart entrypoint with a default Flutter route.
|
||||
flutterEngine.run();
|
||||
GeneratedPluginRegistrant.register(with: self.flutterEngine);
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Create a singleton static object to manage the FlutterEngine.**
|
||||
|
||||
This singleton is used for managing `FlutterEngine` in one place, and provides methods to the whole project to invoke methods related to the Flutter module.
|
||||
|
||||
The basic implementation logic of the demo is that, using a new navigator for the ViewController of Flutter module, and show or hidden can be handled automatically according to call.
|
||||
|
||||
Create a new file, `FlutterUtils.swift`, and coding, refer to our demo source code.
|
||||
|
||||
Mainly focus on:
|
||||
|
||||
- private override init(): Initialize each Flutter instance, register method channel events.
|
||||
- func reportChatInfo(): Report the current user info to the Flutter module, for initialization and login Tencent Cloud Chat SDK.
|
||||
- func launchChatFunc(): Present the ViewController for Flutter module.
|
||||
- func triggerNotification(msg: String): Transit the data of notification, after the user clicks it, and Chat module may navigate to the corresponding chat page.
|
||||
|
||||
**Listening and transit the notification click event**
|
||||
|
||||
Only transit of the data of notification after clicking is necessary as, the initialization of Push plug-in, uploading token and the navigating for notification clicking events have been done in Flutter Chat module.
|
||||
|
||||
The reason why we need to do this is the clicking event has been consumed by iOS Swift, so it is impossible for the Flutter Push plug-in to receive this event.
|
||||
|
||||
Add the following codes to `AppDelegate.swift`.
|
||||
|
||||

|
||||
|
||||
Now, we finished the implementation for iOS.
|
||||
|
||||
### Android Native Development
|
||||
|
||||
Here, we take `Kotlin` as an example, while `Java` is also available.
|
||||
|
||||
>?
|
||||
>
|
||||
> The following structure and code is for demonstration purposes only, you could modify it to meet your actual needs dynamically.
|
||||
|
||||
Open your Android project within Android Studio.
|
||||
|
||||
#### Import Flutter Module
|
||||
|
||||
Please refer to [this part](#android), adding the Flutter module to your existing Android app.
|
||||
|
||||
#### FlutterEngine
|
||||
|
||||
**Create a singleton static object to manage the FlutterEngine.**
|
||||
|
||||
This singleton is used for managing `FlutterEngine` in one place, and provides methods to the whole project to invoke methods related to the Flutter module.
|
||||
|
||||
Create a new file, `FlutterUtils.kt`, and define a singleton static object `FlutterUtils`.
|
||||
|
||||
```kotlin
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object FlutterUtils {}
|
||||
```
|
||||
|
||||
**Create a `FlutterEngine`.**
|
||||
|
||||
Define a `FlutterEngine` and corresponding `MethodChannel` in `FlutterUtils.kt`.
|
||||
|
||||
```kotlin
|
||||
lateinit var context : Context
|
||||
private lateinit var flutterEngine:FlutterEngine
|
||||
|
||||
// 初始化
|
||||
flutterEngine = FlutterEngine(context)
|
||||
```
|
||||
|
||||
**Further developed for this singleton static object**
|
||||
|
||||
The basic implementation logic of the demo is that, using a new navigator for the `Activity` of Flutter module, and show or hidden can be handled automatically according to call.
|
||||
|
||||
Mainly focus on:
|
||||
|
||||
- fun init(): Initialize each Flutter instance, register method channel events.
|
||||
- fun reportChatInfo(): Report the current user info to the Flutter module, for initialization and login Tencent Cloud Chat SDK.
|
||||
- fun launchChatFunc(): Present the `Activity` for Flutter module.
|
||||
- fun triggerNotification(msg: String): Transit the data of notification, after the user clicks it, and Chat module may navigate to the corresponding chat page.
|
||||
|
||||
Detailed implementation can refer to the source code of the demo.
|
||||
|
||||
**Initialize the singleton static object above from the main entry `MyApplication`.**
|
||||
|
||||
Transit the global context to the singleton static object, and initialize it from `MyApplication.kt`.
|
||||
|
||||
```kotlin
|
||||
class MyApplication : MultiDexApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
FlutterUtils.context = this // new
|
||||
FlutterUtils.init() // new
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Listening and transit the notification click event**
|
||||
|
||||
Only transit of the data of notification after clicking is necessary as, the initialization of Push plug-in, uploading token and the navigating for notification clicking events have been done in Flutter Chat module.
|
||||
|
||||
The reason why we need to do this is the clicking event has been consumed by Android Kotlin, so it is impossible for the Flutter Push plug-in to receive this event.
|
||||
|
||||
> Due to the diversity and inconsistency among different manufacturers, we only take OPPO as an example. For the whole manufacturer's support, please refer to this [documentation](https://www.tencentcloud.com/document/product/1047/50032).
|
||||
|
||||
Add a new push certificate to the Tencent Cloud Chat console, Select **Open specified in-app page > activity** for the opening method and enter an activity to receive the notification clicking event with EXT data, it's suggested to set it as the home page or the main entrance. Like, we set `MainActivity` for our demo, `com.tencent.chat.android.MainActivity`.
|
||||
|
||||

|
||||
|
||||
Adding the following codes to the `Activity`, set for the console above.
|
||||
|
||||
The EXT data of the notification can be found from `Bundle` when the `Activity` has been launched by the device, when the user clicks the notification.
|
||||
|
||||
You can receive the EXT from `Activity`, and transit them to Flutter.
|
||||
|
||||
You can refer to the demo source code for this capability.
|
||||
|
||||

|
||||
|
||||
Now, we finished the implementation for Android.
|
||||
|
||||
[](id:native)
|
||||
|
||||
## Additional solution: Initialize Tencent Cloud Chat from Native
|
||||
|
||||
Sometimes, you may prefer to integrate a chat module to your existing UI without a complex chat page.
|
||||
|
||||
Like, assuming that you have a game, and hope players can chat with each other during the match, without navigating to the full screen chat page.
|
||||
|
||||
Means, you may not wish to launch a complex Flutter engine, before the user switches to the chat page, but hope they can still chat in a small module directly.
|
||||
|
||||
In this case, you are supposed to initialize and login Tencent Cloud Chat with Native SDK.
|
||||
|
||||
>?
|
||||
> However, you can also choose to initialize and login within Flutter up to your needs. This process should only be executed once, no matter where you execute it.
|
||||
|
||||
It's unnecessary to import Native SDK manually, as our Flutter SDK can help you integrate it.
|
||||
|
||||
### Initialize and login
|
||||
|
||||
Take the iOS Swift code as an example to demonstrate how to initialize and log in at Native.
|
||||
|
||||
```swift
|
||||
import ImSDK_Plus
|
||||
|
||||
|
||||
func initTencentChat(){
|
||||
if(isLoginSuccess == true){
|
||||
return
|
||||
}
|
||||
let data = V2TIMManager.sharedInstance().initSDK( Yours SDKAPPID , config: nil);
|
||||
if (data == true){
|
||||
V2TIMManager.sharedInstance().login(
|
||||
chatInfo.userID,
|
||||
userSig: chatInfo.userSig,
|
||||
succ: {
|
||||
self.isLoginSuccess = true
|
||||
self.reportChatInfo()
|
||||
},
|
||||
fail: onLoginFailed()
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After that, you could use the API provided by Native SDK to implement your chat modules to your existing UI page manually.
|
||||
|
||||
For more information about the Native SDK, please refer to [this documentation](https://www.tencentcloud.com/document/product/1047/47968).
|
||||
|
||||
### Initialize Flutter TUIKit
|
||||
|
||||
The current user info should be provided to Flutter TUIKit, after initialized and logged in from Native, by invoking `_coreInstance.setDataFromNative()`.
|
||||
|
||||
```dart
|
||||
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();
|
||||
_coreInstance.setDataFromNative(userId: chatInfo?.userID ?? "");
|
||||
```
|
||||
|
||||
You can refer to the demo source code for this module.
|
||||
|
||||
[](https://github.com/TencentCloud/tencentchat-add-flutter-to-app/tree/main/Initialize%20from%20Native)
|
||||
|
||||
-----
|
||||
|
||||
That's all you need to add Tencent Cloud Chat to your existing application.
|
||||
|
||||
You can easily add In-App Chat and Voice/Video Call to your application with Flutter.
|
||||
|
||||
If there's anything unclear or you have more ideas, feel free to contact us!
|
||||
|
||||
- Telegram Group: https://t.me/+1doS9AUBmndhNGNl
|
||||
- WhatsApp Group: https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A
|
||||
- QQ Group: 788910197, chat in Chinese
|
||||
|
||||
## Reference
|
||||
|
||||
1. [Integrate a Flutter module into your Android project](https://docs.flutter.dev/development/add-to-app/android/project-setup).
|
||||
2. [Integrate a Flutter module into your iOS project](https://docs.flutter.dev/development/add-to-app/ios/project-setup).
|
||||
3. [Adding a Flutter screen to an iOS app](https://docs.flutter.dev/development/add-to-app/ios/add-flutter-screen?tab=no-engine-vc-swift-tab).
|
||||
4. [Multiple Flutter screens or views](https://docs.flutter.dev/development/add-to-app/multiple-flutters).
|
||||
|
|
@ -0,0 +1,453 @@
|
|||
|
||||
以下为您介绍,如何为腾讯云IM Flutter TUIKit引入表情能力。
|
||||
|
||||
我们的 `TIMUIKitChat` 组件中,支持发送及接收三种类型的表情:
|
||||
|
||||
| 表情类型 | 发送形式 | 是否文字混排 | 发送内容 | 解析方式 | 引入方式 | TUIKit默认自带 |
|
||||
|---------|---------|---------|---------|---------|---------|---------|
|
||||
| [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) Emoji表情 | 文本消息 | 是 | [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html)编码 | 设备自动将[Unicode](https://unicode.org/emoji/charts/full-emoji-list.html)编码解析成小表情。不同的设备,对[Unicode](https://unicode.org/emoji/charts/full-emoji-list.html)解析后的图形,略有不同 | [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) List | 文档中提供一套[默认Unicode列表示例](#unicode) |
|
||||
| 小图片表情 | 文本消息 | 是 | 表情图片名称 | 根据名称,自动匹配本地Asset图片资源 | 图片资源预存于Asset,并定义 `List` | 一套 QQ 同款小表情图库,可直接使用 |
|
||||
| 大图片表情 | 表情消息 | 否 | `baseURL` 拼接图片文件名,表情图片Asset路径 | 通过路径,解析Asset资源 | 图片资源预存于Asset,并定义 `List` | - |
|
||||
|
||||

|
||||
|
||||
现在,我们就来动手接入TUIKit的表情能力。
|
||||
|
||||
>? [TUIKit](https://pub.dev/packages/tencent_cloud_chat_uikit)在升级至1.1.0版本后,对表情能力用法有较大改动。请您在升级最新版后,根据本文档指引,重新适配接入表情能力。谢谢~
|
||||
|
||||
## STEP1: 自定义表情图片资源
|
||||
|
||||
>? **本步骤选做:**
|
||||
> 如果您需要用到非默认提供的QQ小表情外的其他图片表情,如自定义图片小表情及图片大表情,才需完成本步骤。
|
||||
> QQ小表情包,TUIKit已自带提供,无需在本步骤引入,请关注后续步骤。
|
||||
|
||||
### 将文件导入项目
|
||||
|
||||
请将您的表情资源文件,导入项目的 `assets/custom_face_resource/` 目录内。无论是图片大表情,亦或是图片小表情,都需按此步骤引入。
|
||||
|
||||
该目录内,请使用不同的文件夹,区分表情面板中的不同Tab。每个Tab内表情,仅支持一种类型,图片大表情或图片小表情。
|
||||
|
||||
文件夹的名称,请使用该Tab的 `name` 命名。该命名不会对客户展现,请根据开发需要,自定义即可。
|
||||
|
||||
请保证,所有表情资源文件,不要重名。
|
||||
|
||||
此路径的表情图片放置引入,可参考我们的[Demo](https://github.com/TencentCloud/chat-demo-flutter/tree/main/assets/custom_face_resource)。
|
||||
|
||||

|
||||
|
||||
### 声明表情文件
|
||||
|
||||
打开 `pubspec.yaml` 文件,在 `flutter` => `assets` 中,声明刚刚引入的表情资源文件。
|
||||
|
||||
```yaml
|
||||
flutter:
|
||||
assets:
|
||||
- assets/custom_face_resource/
|
||||
```
|
||||
|
||||
### 在代码中配置图片资源List
|
||||
|
||||
>? 本步骤代码示例请[参考此处](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/utils/constant.dart),直接看 `emojiList` 即可。
|
||||
|
||||
在您代码定义静态参数或配置的地方,定义一个 `static List<CustomEmojiFaceData>`,用于将本地图片资源,转换成TUIKit可以接受的格式,后续以 `List` 方式传入。
|
||||
|
||||
此 `List` 中,每个item都是 `CustomEmojiFaceData`,每个 `CustomEmojiFaceData` 构成一个表情面板中的Tab。具体参数说明如下:
|
||||
|
||||
```dart
|
||||
CustomEmojiFaceData(
|
||||
{
|
||||
String name, // 文件夹目录名称
|
||||
String icon, // Tab中的icon资源文件名
|
||||
List<String> list, // 每个图片的文件名,以List
|
||||
bool isEmoji // 是否为图片小表情,默认为false,即图片大表情
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
示例代码如下:
|
||||
|
||||
```dart
|
||||
static final List<CustomEmojiFaceData> emojiList = [
|
||||
// 使用图片小表情,支持图文混排,以文本消息形式发送
|
||||
CustomEmojiFaceData(
|
||||
name: '4349',
|
||||
icon: "aircraft.png",
|
||||
isEmoji: true,
|
||||
list: [
|
||||
"aircraft.png",
|
||||
"alarmClock.png",
|
||||
"anger.png",
|
||||
// ...
|
||||
]),
|
||||
|
||||
// 使用图片大表情,不支持图文混排,以表情消息形式发送
|
||||
CustomEmojiFaceData(
|
||||
name: '4350',
|
||||
icon: "menu@2x.png",
|
||||
list: [
|
||||
"yz00@2x.png",
|
||||
// ...
|
||||
]),
|
||||
]
|
||||
```
|
||||
|
||||
## STEP2: 自定义Emoji Unicode 字符串List
|
||||
|
||||
>? **本步骤选做:**
|
||||
> 如果您需要用到[Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) Emoji表情,才需完成本步骤。
|
||||
|
||||
在您代码定义静态参数或配置的地方,定义一个 `List<Map<String, Object>>` Unicode 列表,供传入。
|
||||
|
||||
该列表,我们提供一套示例,在[附录](#unicode)中。
|
||||
|
||||
您看直接复制引入这套示例列表,或基于此修改。
|
||||
|
||||
## STEP3: 表情资源预读入内存
|
||||
|
||||
>?
|
||||
> 本步骤代码示例请[参考此处](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/pages/app.dart),直接看 `setCustomSticker` 方法即可。
|
||||
> QQ小表情包,TUIKit已自带提供,无需在本步骤引入,请关注后续步骤。
|
||||
|
||||
在您的项目启动后,首个 `TIMUIKitChat` 组件渲染前,将上一步定义的图片表情资源List,转换成TUIKit表情的实例,放入全局Provider中,存储于内存里。
|
||||
|
||||
**本步骤方法仅需执行一次,一次性全部读入内存中。** 因展示渲染表情资源为高频操作,如每次展示前才动态读入内存,对资源与性能占用比较大。
|
||||
|
||||
单个表情内存实例,使用 `CustomSticker` 类生成。如果传入了 `unicode` 则为 Unicode Emoji表情,否则为图片类型表情。
|
||||
|
||||
```dart
|
||||
class CustomSticker {
|
||||
int? unicode; // Unicode int值。如果传入了 `unicode` 则为 Unicode Emoji表情,否则为图片类型表情
|
||||
String name; // 表情名称
|
||||
int index; // 表情序号
|
||||
bool isEmoji; // 是否为图片小表情,默认为图片大表情
|
||||
}
|
||||
```
|
||||
|
||||
每个Tab的内存实例,使用 `CustomStickerPackage` 类生成。
|
||||
|
||||
```dart
|
||||
class CustomStickerPackage { // 一个系列的表情包定义为一个package,占据一个表情面板Tab
|
||||
String name; // 表情包package name,该Tab文件夹名称。
|
||||
String? baseUrl; // 表情包package baseUrl,建议配置成:"assets/custom_face_resource/${表情包文件夹名称 即 表情包package name}"
|
||||
List<CustomSticker> stickerList; // 表情资源列表
|
||||
CustomSticker menuItem; // 表情面包Tab按钮icon
|
||||
bool isEmoji; // 是否为图片小表情,默认为图片大表情
|
||||
}
|
||||
```
|
||||
|
||||
综上所述,需要写的代码,我们给出示例版本。
|
||||
|
||||
表情项一演示如何使用Emoji Unicode表情包,表情项二演示如何使用图片类型(包含大或小)表情包。您可以根据需要,使用全部或部分代码。
|
||||
|
||||
```dart
|
||||
setCustomSticker() async {
|
||||
// 定义一个大List来承载各个表情包 package Tab
|
||||
List<CustomStickerPackage> customStickerPackageList = [];
|
||||
|
||||
// 表情项一:使用Emoji Unicode表情列表。可以嵌入文字内容中。
|
||||
// `emojiData` 来自于STEP2。
|
||||
final defEmojiList = emojiData.asMap().keys.map((emojiIndex) {
|
||||
final emoji = Emoji.fromJson(emojiData[emojiIndex]);
|
||||
return CustomSticker(
|
||||
index: emojiIndex, name: emoji.name, unicode: emoji.unicode);
|
||||
}).toList();
|
||||
customStickerPackageList.add(CustomStickerPackage(
|
||||
name: "defaultEmoji",
|
||||
stickerList: defEmojiList,
|
||||
menuItem: defEmojiList[0]));
|
||||
|
||||
// 表情项二:使用您提供的图片表情包。
|
||||
// 务必保证 `customEmojiPackage.name` 为该Tab文件夹名称。
|
||||
// `Const.emojiList` 来自于STEP1。
|
||||
customStickerPackageList.addAll(Const.emojiList.map((customEmojiPackage) {
|
||||
return CustomStickerPackage(
|
||||
name: customEmojiPackage.name,
|
||||
baseUrl: "assets/custom_face_resource/${customEmojiPackage.name}",
|
||||
stickerList: customEmojiPackage.list
|
||||
.asMap()
|
||||
.keys
|
||||
.map((idx) =>
|
||||
CustomSticker(index: idx, name: customEmojiPackage.list[idx]))
|
||||
.toList(),
|
||||
menuItem: CustomSticker(
|
||||
index: 0,
|
||||
name: customEmojiPackage.icon,
|
||||
));
|
||||
}).toList());
|
||||
|
||||
Provider.of<CustomStickerPackageData>(context, listen: false)
|
||||
.customStickerPackageList = customStickerPackageList;
|
||||
}
|
||||
```
|
||||
|
||||
## STEP4: 为 TIMUIKitChat 组件添加表情解析能力
|
||||
|
||||
>?
|
||||
> 本步骤代码示例请[参考此处](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/chat.dart),重点浏览 `renderCustomStickerPanel`, `customStickerPanel` 及 `customEmojiList` 即可。
|
||||
|
||||
将以下代码,直接拷贝进入您用于承载 `TIMUIKitChat` 组件的类中。
|
||||
|
||||
```dart
|
||||
Widget renderCustomStickerPanel({
|
||||
sendTextMessage,
|
||||
sendFaceMessage,
|
||||
deleteText,
|
||||
addCustomEmojiText,
|
||||
addText,
|
||||
List<CustomEmojiFaceData> defaultCustomEmojiStickerList = const [],
|
||||
}) {
|
||||
final theme = Provider.of<DefaultThemeData>(context).theme;
|
||||
final customStickerPackageList =
|
||||
Provider.of<CustomStickerPackageData>(context).customStickerPackageList;
|
||||
final defaultEmojiList =
|
||||
defaultCustomEmojiStickerList.map((customEmojiPackage) {
|
||||
return CustomStickerPackage(
|
||||
name: customEmojiPackage.name,
|
||||
baseUrl: "assets/custom_face_resource/${customEmojiPackage.name}",
|
||||
isEmoji: customEmojiPackage.isEmoji,
|
||||
isDefaultEmoji: true,
|
||||
stickerList: customEmojiPackage.list
|
||||
.asMap()
|
||||
.keys
|
||||
.map((idx) =>
|
||||
CustomSticker(index: idx, name: customEmojiPackage.list[idx]))
|
||||
.toList(),
|
||||
menuItem: CustomSticker(
|
||||
index: 0,
|
||||
name: customEmojiPackage.icon,
|
||||
));
|
||||
}).toList();
|
||||
return StickerPanel(
|
||||
sendTextMsg: sendTextMessage,
|
||||
sendFaceMsg: (index, data) =>
|
||||
sendFaceMessage(index + 1, (data.split("/")[3]).split("@")[0]),
|
||||
deleteText: deleteText,
|
||||
addText: addText,
|
||||
addCustomEmojiText: addCustomEmojiText,
|
||||
customStickerPackageList: [
|
||||
...defaultEmojiList,
|
||||
...customStickerPackageList
|
||||
],
|
||||
backgroundColor: theme.weakBackgroundColor,
|
||||
lightPrimaryColor: theme.lightPrimaryColor);
|
||||
}
|
||||
```
|
||||
|
||||
### STEP4.1: 渲染图片小表情
|
||||
|
||||
>? **本步骤选做:**
|
||||
> - 如果您的项目需要用到图片小表情,包括自定义图片小表情,或直接使用默认自带 QQ 同款图片小表情,才需完成本步骤。
|
||||
> - 图片小表情展现形式和Unicode Emoji类似,建议Unicode Emoji和图片小表情选用一个即可。即,如果您选用了Unicode Emoji,可直接跳过本步骤。
|
||||
|
||||
- STEP4.1(a) 为使用自定义图片小表情;
|
||||
- STEP4.1(b) 为使用默认自带 QQ 同款图片小表情。
|
||||
|
||||
以上方案,建议直接选用一个方案即可。
|
||||
|
||||
如果需要同时使用,请保证您的自定义图片小表情名称,不要和我们默认提供的 QQ 同款图片小表情重复。
|
||||
|
||||
#### STEP4.1(a): 添加渲染解析自定义图片小表情的支持
|
||||
|
||||
在您用于承载 `TIMUIKitChat` 组件的 `build` 方法中,定义一个 `List customEmojiList` 变量,用于存放图片小表情列表。
|
||||
|
||||
```dart
|
||||
List customEmojiList =
|
||||
Const.emojiList.where((element) => element.isEmoji == true).toList();
|
||||
```
|
||||
|
||||
并将此列表,传入 `TIMUIKitChat` 组件的 `customEmojiStickerList` 参数内。
|
||||
|
||||
```dart
|
||||
return TIMUIKitChat(
|
||||
customEmojiStickerList: customEmojiList,
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||
>? 如果您用于承载 `TIMUIKitChat` 组件的类为 `StatefulWidget`,您可将 `customEmojiList` 变量,放如State中,仅在首次build时,才去执行 `where` 命令,优化性能。
|
||||
|
||||
#### STEP4.1(b): 启用 QQ 小表情包
|
||||
|
||||
将 `TIMUIKitChat` 的 `TIMUIKitChatConfig` 的 `isUseDefaultEmoji` 参数,设置为 `true` 即可。此时,会向表情包面板最左侧,自动生成一个承载 QQ 小表情包的 Tab。
|
||||
|
||||
```dart
|
||||
return TIMUIKitChat(
|
||||
config: TIMUIKitChatConfig(
|
||||
isUseDefaultEmoji: true,
|
||||
// ......
|
||||
),
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||

|
||||
|
||||
### STEP4.2: 将表情包能力,注入 TIMUIKitChat
|
||||
|
||||
将本步骤最开始让您复制的代码方法,传入 `TIMUIKitChat` 组件的 `customStickerPanel` 参数内。
|
||||
|
||||
```dart
|
||||
return TIMUIKitChat(
|
||||
customStickerPanel: renderCustomStickerPanel,
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||
此时,TUIKit表情能力接入完成。您可正常收发测试。如在接入过程中,有任何问题,欢迎随时[联系我们](#contact)。
|
||||
|
||||
[](id:unicode)
|
||||
|
||||
## 附录: Emoji Unicode 列表示例
|
||||
|
||||
本列表仅用于示例演示,您可根据需要,增加或修改。
|
||||
|
||||
```dart
|
||||
List<Map<String, Object>> emojiData = [
|
||||
{"name": "GRINNING FACE WITH SMILING EYES", "unicode": 128513},
|
||||
{"name": "FACE WITH TEARS OF JOY", "unicode": 128514},
|
||||
{"name": "SMILING FACE WITH OPEN MOUTH", "unicode": 128515},
|
||||
{"name": "SMILING FACE WITH OPEN MOUTH AND SMILING EYES", "unicode": 128516},
|
||||
{"name": "SMILING FACE WITH OPEN MOUTH AND COLD SWEAT", "unicode": 128517},
|
||||
{
|
||||
"name": "SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES",
|
||||
"unicode": 128518
|
||||
},
|
||||
{"name": "WINKING FACE", "unicode": 128521},
|
||||
{"name": "SMILING FACE WITH SMILING EYES", "unicode": 128522},
|
||||
{"name": "FACE SAVOURING DELICIOUS FOOD", "unicode": 128523},
|
||||
{"name": "RELIEVED FACE", "unicode": 128524},
|
||||
{"name": "SMILING FACE WITH HEART-SHAPED EYES", "unicode": 128525},
|
||||
{"name": "SMIRKING FACE", "unicode": 128527},
|
||||
{"name": "UNAMUSED FACE", "unicode": 128530},
|
||||
{"name": "FACE WITH COLD SWEAT", "unicode": 128531},
|
||||
{"name": "PENSIVE FACE", "unicode": 128532},
|
||||
{"name": "CONFOUNDED FACE", "unicode": 128534},
|
||||
{"name": "FACE THROWING A KISS", "unicode": 128536},
|
||||
{"name": "KISSING FACE WITH CLOSED EYES", "unicode": 128538},
|
||||
{"name": "FACE WITH STUCK-OUT TONGUE AND WINKING EYE", "unicode": 128540},
|
||||
{
|
||||
"name": "FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES",
|
||||
"unicode": 128541
|
||||
},
|
||||
{"name": "DISAPPOINTED FACE", "unicode": 128542},
|
||||
{"name": "ANGRY FACE", "unicode": 128544},
|
||||
{"name": "POUTING FACE", "unicode": 128545},
|
||||
{"name": "CRYING FACE", "unicode": 128546},
|
||||
{"name": "PERSEVERING FACE", "unicode": 128547},
|
||||
{"name": "FACE WITH LOOK OF TRIUMPH", "unicode": 128548},
|
||||
{"name": "DISAPPOINTED BUT RELIEVED FACE", "unicode": 128549},
|
||||
{"name": "FEARFUL FACE", "unicode": 128552},
|
||||
{"name": "WEARY FACE", "unicode": 128553},
|
||||
{"name": "SLEEPY FACE", "unicode": 128554},
|
||||
{"name": "TIRED FACE", "unicode": 128555},
|
||||
{"name": "LOUDLY CRYING FACE", "unicode": 128557},
|
||||
{"name": "FACE WITH OPEN MOUTH AND COLD SWEAT", "unicode": 128560},
|
||||
{"name": "FACE SCREAMING IN FEAR", "unicode": 128561},
|
||||
{"name": "ASTONISHED FACE", "unicode": 128562},
|
||||
{"name": "FLUSHED FACE", "unicode": 128563},
|
||||
{"name": "DIZZY FACE", "unicode": 128565},
|
||||
{"name": "FACE WITH MEDICAL MASK", "unicode": 128567},
|
||||
{"name": "GRINNING CAT FACE WITH SMILING EYES", "unicode": 128568},
|
||||
{"name": "CAT FACE WITH TEARS OF JOY", "unicode": 128569},
|
||||
{"name": "SMILING CAT FACE WITH OPEN MOUTH", "unicode": 128570},
|
||||
{"name": "SMILING CAT FACE WITH HEART-SHAPED EYES", "unicode": 128571},
|
||||
{"name": "CAT FACE WITH WRY SMILE", "unicode": 128572},
|
||||
{"name": "KISSING CAT FACE WITH CLOSED EYES", "unicode": 128573},
|
||||
{"name": "POUTING CAT FACE", "unicode": 128574},
|
||||
{"name": "CRYING CAT FACE", "unicode": 128575},
|
||||
{"name": "WEARY CAT FACE", "unicode": 128576},
|
||||
{"name": "FACE WITH NO GOOD GESTURE", "unicode": 128581},
|
||||
{"name": "FACE WITH OK GESTURE", "unicode": 128582},
|
||||
{"name": "PERSON BOWING DEEPLY", "unicode": 128583},
|
||||
{"name": "SEE-NO-EVIL MONKEY", "unicode": 128584},
|
||||
{"name": "HEAR-NO-EVIL MONKEY", "unicode": 128585},
|
||||
{"name": "SPEAK-NO-EVIL MONKEY", "unicode": 128586},
|
||||
{"name": "HAPPY PERSON RAISING ONE HAND", "unicode": 128587},
|
||||
{"name": "PERSON RAISING BOTH HANDS IN CELEBRATION", "unicode": 128588},
|
||||
{"name": "PERSON FROWNING", "unicode": 128589},
|
||||
{"name": "PERSON WITH POUTING FACE", "unicode": 128590},
|
||||
{"name": "PERSON WITH FOLDED HANDS", "unicode": 128591},
|
||||
{"name": "BLACK SCISSORS", "unicode": 9986},
|
||||
{"name": "WHITE HEAVY CHECK MARK", "unicode": 9989},
|
||||
{"name": "AIRPLANE", "unicode": 9992},
|
||||
{"name": "ENVELOPE", "unicode": 9993},
|
||||
{"name": "RAISED FIST", "unicode": 9994},
|
||||
{"name": "RAISED HAND", "unicode": 9995},
|
||||
{"name": "VICTORY HAND", "unicode": 9996},
|
||||
{"name": "PENCIL", "unicode": 9999},
|
||||
{"name": "BLACK NIB", "unicode": 10002},
|
||||
{"name": "HEAVY CHECK MARK", "unicode": 10004},
|
||||
{"name": "HEAVY MULTIPLICATION X", "unicode": 10006},
|
||||
{"name": "SPARKLES", "unicode": 10024},
|
||||
{"name": "EIGHT SPOKED ASTERISK", "unicode": 10035},
|
||||
{"name": "EIGHT POINTED BLACK STAR", "unicode": 10036},
|
||||
{"name": "SNOWFLAKE", "unicode": 10052},
|
||||
{"name": "SPARKLE", "unicode": 10055},
|
||||
{"name": "CROSS MARK", "unicode": 10060},
|
||||
{"name": "NEGATIVE SQUARED CROSS MARK", "unicode": 10062},
|
||||
{"name": "BLACK QUESTION MARK ORNAMENT", "unicode": 10067},
|
||||
{"name": "WHITE QUESTION MARK ORNAMENT", "unicode": 10068},
|
||||
{"name": "WHITE EXCLAMATION MARK ORNAMENT", "unicode": 10069},
|
||||
{"name": "HEAVY EXCLAMATION MARK SYMBOL", "unicode": 10071},
|
||||
{"name": "HEAVY BLACK HEART", "unicode": 10084},
|
||||
{"name": "HEAVY PLUS SIGN", "unicode": 10133},
|
||||
{"name": "HEAVY MINUS SIGN", "unicode": 10134},
|
||||
{"name": "HEAVY DIVISION SIGN", "unicode": 10135},
|
||||
{"name": "BLACK RIGHTWARDS ARROW", "unicode": 10145},
|
||||
{"name": "CURLY LOOP", "unicode": 10160},
|
||||
{"name": "ROCKET", "unicode": 128640},
|
||||
{"name": "RAILWAY CAR", "unicode": 128643},
|
||||
{"name": "HIGH-SPEED TRAIN", "unicode": 128644},
|
||||
{"name": "HIGH-SPEED TRAIN WITH BULLET NOSE", "unicode": 128645},
|
||||
{"name": "METRO", "unicode": 128647},
|
||||
{"name": "STATION", "unicode": 128649},
|
||||
{"name": "BUS", "unicode": 128652},
|
||||
{"name": "BUS STOP", "unicode": 128655},
|
||||
{"name": "AMBULANCE", "unicode": 128657},
|
||||
{"name": "FIRE ENGINE", "unicode": 128658},
|
||||
{"name": "POLICE CAR", "unicode": 128659},
|
||||
{"name": "TAXI", "unicode": 128661},
|
||||
{"name": "AUTOMOBILE", "unicode": 128663},
|
||||
{"name": "RECREATIONAL VEHICLE", "unicode": 128665},
|
||||
{"name": "DELIVERY TRUCK", "unicode": 128666},
|
||||
{"name": "SHIP", "unicode": 128674},
|
||||
{"name": "SPEEDBOAT", "unicode": 128676},
|
||||
{"name": "HORIZONTAL TRAFFIC LIGHT", "unicode": 128677},
|
||||
{"name": "CONSTRUCTION SIGN", "unicode": 128679},
|
||||
{"name": "POLICE CARS REVOLVING LIGHT", "unicode": 128680},
|
||||
{"name": "TRIANGULAR FLAG ON POST", "unicode": 128681},
|
||||
{"name": "DOOR", "unicode": 128682},
|
||||
{"name": "NO ENTRY SIGN", "unicode": 128683},
|
||||
{"name": "SMOKING SYMBOL", "unicode": 128684},
|
||||
{"name": "NO SMOKING SYMBOL", "unicode": 128685},
|
||||
{"name": "BICYCLE", "unicode": 128690},
|
||||
{"name": "PEDESTRIAN", "unicode": 128694},
|
||||
{"name": "MENS SYMBOL", "unicode": 128697},
|
||||
{"name": "WOMENS SYMBOL", "unicode": 128698},
|
||||
{"name": "RESTROOM", "unicode": 128699},
|
||||
{"name": "BABY SYMBOL", "unicode": 128700},
|
||||
{"name": "TOILET", "unicode": 128701},
|
||||
{"name": "WATER CLOSET", "unicode": 128702},
|
||||
{"name": "BATH", "unicode": 128704},
|
||||
{"name": "CIRCLED LATIN CAPITAL LETTER M", "unicode": 9410},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER A", "unicode": 127344},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER B", "unicode": 127345},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER O", "unicode": 127358},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER P", "unicode": 127359},
|
||||
{"name": "NEGATIVE SQUARED AB", "unicode": 127374},
|
||||
{"name": "SQUARED CL", "unicode": 127377},
|
||||
{"name": "SQUARED COOL", "unicode": 127378},
|
||||
{"name": "SQUARED FREE", "unicode": 127379},
|
||||
{"name": "SQUARED ID", "unicode": 127380},
|
||||
{"name": "SQUARED NEW", "unicode": 127381},
|
||||
];
|
||||
```
|
||||
|
||||
[](id:contact)
|
||||
|
||||
## 联系我们
|
||||
|
||||
如果您在接入使用过程中有任何疑问,请扫码加入微信群,或加入QQ群:788910197 咨询。
|
||||
|
||||

|
||||
|
||||
|
|
@ -0,0 +1,457 @@
|
|||
|
||||
## Overview
|
||||
|
||||
Tencent Cloud Chat Flutter TUIKit provides powerful Emoji and Sticker modules to help you customize the Emoji and Sticker sharing of your app.
|
||||
|
||||
Through simple configurations, you can easily choose and integrate those three types of stickers to your app.
|
||||
|
||||
| Sticker Type | Message Type | Embed to text | Sending content | Render | Import | Provide by default |
|
||||
|---------|---------|---------|---------|---------|---------|---------|
|
||||
| [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) Emoji | Text Message | Yes | [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) |[Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) to Emoji can be compiled automatically by devices, while different devices or platforms have different Emoji rendering | [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) List | Sample [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) List is provided [here](#unicode) |
|
||||
| Small Image Emoji | Text Message | Yes | Image name | Match local Asset image resources according to name automatically | Image stored in asset, and define `List` | A set of QQ Emoji is provided by default |
|
||||
| Big Image Sticker | Sticker Message | No | `baseURL` join with image name, as asset path | Render image from asset | Image stored in asset, and define `List` | - |
|
||||
|
||||

|
||||
|
||||
Now, let's start integrating Emoji and Sticker to your app with TUIKit.
|
||||
|
||||
>? The usage of this module has been modified since the 1.1.0 version of [TUIKit](https://pub.dev/packages/tencent_cloud_chat_uikit). Please check all the parts in this tutorial if you upgrade the version.
|
||||
|
||||
## STEP 1: Customize Image Emoji and Sticker
|
||||
|
||||
>? **This step is optional:**
|
||||
> This step is necessary only if image stickers, except default QQ emoji one, includes both small and big, are needed.
|
||||
> QQ Emoji set is provided by default, and is unnecessary to import in this step.
|
||||
|
||||
### Import Image File to Project
|
||||
|
||||
Please add your image resources file to `assets/custom_face_resource/` of your project, including both small and big images.
|
||||
|
||||
In this directory, separate subdirectory with different sticker packages, means each Tabs on Sticker panel. Only one type of sticker is allowed in each Tab(package or subdirectory).
|
||||
|
||||
Name those subdirectories differently, and this name will be used as the `name` field of `CustomEmojiFaceData` and `CustomStickerPackage` in the following steps. Please allocate the name as you need.
|
||||
|
||||
Also, please make sure that all image resource files do not have the same name.
|
||||
|
||||
You can refer to our [sample project](https://github.com/TencentCloud/chat-demo-flutter/tree/main/assets/custom_face_resource), if not clear.
|
||||
|
||||

|
||||
|
||||
### Add assets to app
|
||||
|
||||
Open `pubspec.yaml`, add those following lines to `flutter` => `assets`.
|
||||
|
||||
```yaml
|
||||
flutter:
|
||||
assets:
|
||||
- assets/custom_face_resource/
|
||||
```
|
||||
|
||||
### Configure assets list
|
||||
|
||||
>? The sample code for this part can be found [here](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/utils/constant.dart), mainly focused on `emojiList`.
|
||||
|
||||
Define a static `List<CustomEmojiFaceData>` in your project, aiming for transferring the local image assets to TUIKit, as List.
|
||||
|
||||
In this `List`, each item is `CustomEmojiFaceData`, while it constitutes each Tab in the sticker panel.
|
||||
|
||||
```dart
|
||||
CustomEmojiFaceData(
|
||||
{
|
||||
String name, // The name of the package and subdirectory.
|
||||
String icon, // The file name of the icon on the Tab.
|
||||
List<String> list, // The list of the files name.
|
||||
bool isEmoji //Whether it contains small image emojis, default is big image stickers.
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
Sample Code:
|
||||
|
||||
```dart
|
||||
static final List<CustomEmojiFaceData> emojiList = [
|
||||
// Small Image Emoji, embedded in text messages.
|
||||
CustomEmojiFaceData(
|
||||
name: '4349',
|
||||
icon: "aircraft.png",
|
||||
isEmoji: true,
|
||||
list: [
|
||||
"aircraft.png",
|
||||
"alarmClock.png",
|
||||
"anger.png",
|
||||
// ...
|
||||
]),
|
||||
|
||||
// Big image stickers, sent as sticker messages independently.
|
||||
CustomEmojiFaceData(
|
||||
name: '4350',
|
||||
icon: "menu@2x.png",
|
||||
list: [
|
||||
"yz00@2x.png",
|
||||
// ...
|
||||
]),
|
||||
]
|
||||
```
|
||||
|
||||
## Step 2: Customize the Unicode Emoji List
|
||||
|
||||
>? **This step is optional:**
|
||||
> This step is necessary only if Unicode Emoji is needed.
|
||||
|
||||
Define a static `List<Map<String, Object>>` of Unicode in your project, you can build it based on the [sample list we provided](#unicode).
|
||||
|
||||
You can add, delete and modify some items in this List, based on the official [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html).
|
||||
|
||||
|
||||
## Step 3: Cache the Emoji and Sticker to memory
|
||||
|
||||
>?
|
||||
> - Sample code for this step [can be found here](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/pages/app.dart), mainly focus on `setCustomSticker` function.
|
||||
> - QQ Emoji has been embedded by default, and unnecessary to do this step.
|
||||
|
||||
Cache those Emoji and Stickers to global `Provider`, memory, **just after your app launched, and before the first `TIMUIKitChat` shows**.
|
||||
|
||||
**This steps should only be done once.** Aiming for reducing the load of memory IO, as rendering each sticker is a high frequency event, and will cost a lot.
|
||||
|
||||
The instance of each sticker is generated by the following `CustomSticker` class. It will show Unicode Emoji if `unicode` is not null, otherwise it shows an image.
|
||||
|
||||
```dart
|
||||
class CustomSticker {
|
||||
int? unicode; // Unicode int value。It will show Unicode Emoji if this field is not null, otherwise it shows an image.
|
||||
String name; // The name of the sticker
|
||||
int index; // The index of the sticker
|
||||
bool isEmoji; // Whether it is a small image emoji, while a big image sticker is as default.
|
||||
}
|
||||
```
|
||||
|
||||
The instance of each Tab on sticker panel, each sticker package, is generated by the `CustomStickerPackage` class.
|
||||
|
||||
```dart
|
||||
class CustomStickerPackage { // Each Tab on sticker panel, each sticker package
|
||||
String name; // The name of this sticker package, subdirectory, and the Tab.
|
||||
String? baseUrl; // Sticker package baseUrl,recommend specify as "assets/custom_face_resource/${package name}"
|
||||
List<CustomSticker> stickerList; // The list of the image files name
|
||||
CustomSticker menuItem; // The file name of the icon of Tab
|
||||
bool isEmoji; // Whether it contains small image emojis, while big image stickers are as default.
|
||||
}
|
||||
```
|
||||
|
||||
For the classes shown above, we provide sample codes as follows, for the code you may need to write.
|
||||
|
||||
`Solution A` shows the usage of Unicode Emoji while `Solution B` shows the usage of image stickers. You can choose all or part of them as needed.
|
||||
|
||||
|
||||
```dart
|
||||
setCustomSticker() async {
|
||||
// Define a list to store sticker packages.
|
||||
List<CustomStickerPackage> customStickerPackageList = [];
|
||||
|
||||
// Solution A: Use Emoji Unicode list. Can be added to text messages.
|
||||
// `emojiData` comes from step 2.
|
||||
final defEmojiList = emojiData.asMap().keys.map((emojiIndex) {
|
||||
final emoji = Emoji.fromJson(emojiData[emojiIndex]);
|
||||
return CustomSticker(
|
||||
index: emojiIndex, name: emoji.name, unicode: emoji.unicode);
|
||||
}).toList();
|
||||
customStickerPackageList.add(CustomStickerPackage(
|
||||
name: "defaultEmoji",
|
||||
stickerList: defEmojiList,
|
||||
menuItem: defEmojiList[0]));
|
||||
|
||||
// Solution B: Use the image sticker.
|
||||
// Please make sure `customEmojiPackage.name` is the name of the subdirectory.
|
||||
customStickerPackageList.addAll(Const.emojiList.map((customEmojiPackage) {
|
||||
return CustomStickerPackage(
|
||||
name: customEmojiPackage.name,
|
||||
baseUrl: "assets/custom_face_resource/${customEmojiPackage.name}",
|
||||
stickerList: customEmojiPackage.list
|
||||
.asMap()
|
||||
.keys
|
||||
.map((idx) =>
|
||||
CustomSticker(index: idx, name: customEmojiPackage.list[idx]))
|
||||
.toList(),
|
||||
menuItem: CustomSticker(
|
||||
index: 0,
|
||||
name: customEmojiPackage.icon,
|
||||
));
|
||||
}).toList());
|
||||
|
||||
Provider.of<CustomStickerPackageData>(context, listen: false)
|
||||
.customStickerPackageList = customStickerPackageList;
|
||||
}
|
||||
```
|
||||
|
||||
## STEP 4: Adding those stickers to TIMUIKitChat
|
||||
|
||||
>?
|
||||
> - Sample code for this step [can be found here](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/chat.dart), mainly focus on `renderCustomStickerPanel`, `customStickerPanel` and `customEmojiList`.
|
||||
|
||||
Copy the following codes to the class that contains the `TIMUIKitChat` widget directly.
|
||||
|
||||
```dart
|
||||
Widget renderCustomStickerPanel({
|
||||
sendTextMessage,
|
||||
sendFaceMessage,
|
||||
deleteText,
|
||||
addCustomEmojiText,
|
||||
addText,
|
||||
List<CustomEmojiFaceData> defaultCustomEmojiStickerList = const [],
|
||||
}) {
|
||||
final theme = Provider.of<DefaultThemeData>(context).theme;
|
||||
final customStickerPackageList =
|
||||
Provider.of<CustomStickerPackageData>(context).customStickerPackageList;
|
||||
final defaultEmojiList =
|
||||
defaultCustomEmojiStickerList.map((customEmojiPackage) {
|
||||
return CustomStickerPackage(
|
||||
name: customEmojiPackage.name,
|
||||
baseUrl: "assets/custom_face_resource/${customEmojiPackage.name}",
|
||||
isEmoji: customEmojiPackage.isEmoji,
|
||||
isDefaultEmoji: true,
|
||||
stickerList: customEmojiPackage.list
|
||||
.asMap()
|
||||
.keys
|
||||
.map((idx) =>
|
||||
CustomSticker(index: idx, name: customEmojiPackage.list[idx]))
|
||||
.toList(),
|
||||
menuItem: CustomSticker(
|
||||
index: 0,
|
||||
name: customEmojiPackage.icon,
|
||||
));
|
||||
}).toList();
|
||||
return StickerPanel(
|
||||
sendTextMsg: sendTextMessage,
|
||||
sendFaceMsg: (index, data) =>
|
||||
sendFaceMessage(index + 1, (data.split("/")[3]).split("@")[0]),
|
||||
deleteText: deleteText,
|
||||
addText: addText,
|
||||
addCustomEmojiText: addCustomEmojiText,
|
||||
customStickerPackageList: [
|
||||
...defaultEmojiList,
|
||||
...customStickerPackageList
|
||||
],
|
||||
backgroundColor: theme.weakBackgroundColor,
|
||||
lightPrimaryColor: theme.lightPrimaryColor);
|
||||
}
|
||||
```
|
||||
|
||||
### STEP 4.1: Render Small Image Emoji
|
||||
|
||||
>? **This step is optional:**
|
||||
> - This step is necessary only if small images emoji are needed for your app, except the QQ Emoji we provided by default.
|
||||
> - Unicode Emoji and small image emoji are similar, it is not recommended to integrate these two types of emoji at the same time.
|
||||
|
||||
- STEP 4.1(a) shows the usage of using custom small image emoji.
|
||||
- STEP 4.1(b) shows the usage of using default QQ emojis.
|
||||
|
||||
It is recommended to choose one of them.
|
||||
|
||||
If you tend to use both of them, please make sure those image resource files do not have the same name.
|
||||
|
||||
#### STEP 4.1(a): Render custom small image emoji
|
||||
|
||||
Add a `List customEmojiList` field to the `build` function of the `Widget` that contains `TIMUIKitChat`, storing the list of small image emoji.
|
||||
|
||||
```dart
|
||||
List customEmojiList =
|
||||
Const.emojiList.where((element) => element.isEmoji == true).toList();
|
||||
```
|
||||
|
||||
And transferring this list to `customEmojiStickerList` of `TIMUIKitChat`.
|
||||
|
||||
```dart
|
||||
return TIMUIKitChat(
|
||||
customEmojiStickerList: customEmojiList,
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||
>?
|
||||
> If this widget is a `StatefulWidget`, choosing to place this list to state, and execute the `where` method once, to improve the performance are recommended.
|
||||
|
||||
#### STEP 4.1(b): Enable QQ Emoji
|
||||
|
||||
Enable the `isUseDefaultEmoji` of `TIMUIKitChatConfig` from `TIMUIKitChat` to `true`. Meanwhile, a Tab shows the default QQ Emoji will occur on the left of the sticker panel.
|
||||
|
||||
```dart
|
||||
return TIMUIKitChat(
|
||||
config: TIMUIKitChatConfig(
|
||||
isUseDefaultEmoji: true,
|
||||
// ......
|
||||
),
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||

|
||||
|
||||
### STEP 4.2: Add the sticker panel to TIMUIKitChat
|
||||
|
||||
Transfer the function, you copied in this step, to the `customStickerPanel` field of `TIMUIKitChat`.
|
||||
|
||||
```dart
|
||||
return TIMUIKitChat(
|
||||
customStickerPanel: renderCustomStickerPanel,
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||
That's all you need to integrate Emoji and Sticker modules to your app, with Tencent Cloud Chat Flutter TUIKit.
|
||||
|
||||
[](id:unicode)
|
||||
|
||||
## Appendix: Sample list of Emoji Unicodes
|
||||
|
||||
The list is for sample and presentation purposes only, you can modify it as you need.
|
||||
|
||||
```dart
|
||||
List<Map<String, Object>> emojiData = [
|
||||
{"name": "GRINNING FACE WITH SMILING EYES", "unicode": 128513},
|
||||
{"name": "FACE WITH TEARS OF JOY", "unicode": 128514},
|
||||
{"name": "SMILING FACE WITH OPEN MOUTH", "unicode": 128515},
|
||||
{"name": "SMILING FACE WITH OPEN MOUTH AND SMILING EYES", "unicode": 128516},
|
||||
{"name": "SMILING FACE WITH OPEN MOUTH AND COLD SWEAT", "unicode": 128517},
|
||||
{
|
||||
"name": "SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES",
|
||||
"unicode": 128518
|
||||
},
|
||||
{"name": "WINKING FACE", "unicode": 128521},
|
||||
{"name": "SMILING FACE WITH SMILING EYES", "unicode": 128522},
|
||||
{"name": "FACE SAVOURING DELICIOUS FOOD", "unicode": 128523},
|
||||
{"name": "RELIEVED FACE", "unicode": 128524},
|
||||
{"name": "SMILING FACE WITH HEART-SHAPED EYES", "unicode": 128525},
|
||||
{"name": "SMIRKING FACE", "unicode": 128527},
|
||||
{"name": "UNAMUSED FACE", "unicode": 128530},
|
||||
{"name": "FACE WITH COLD SWEAT", "unicode": 128531},
|
||||
{"name": "PENSIVE FACE", "unicode": 128532},
|
||||
{"name": "CONFOUNDED FACE", "unicode": 128534},
|
||||
{"name": "FACE THROWING A KISS", "unicode": 128536},
|
||||
{"name": "KISSING FACE WITH CLOSED EYES", "unicode": 128538},
|
||||
{"name": "FACE WITH STUCK-OUT TONGUE AND WINKING EYE", "unicode": 128540},
|
||||
{
|
||||
"name": "FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES",
|
||||
"unicode": 128541
|
||||
},
|
||||
{"name": "DISAPPOINTED FACE", "unicode": 128542},
|
||||
{"name": "ANGRY FACE", "unicode": 128544},
|
||||
{"name": "POUTING FACE", "unicode": 128545},
|
||||
{"name": "CRYING FACE", "unicode": 128546},
|
||||
{"name": "PERSEVERING FACE", "unicode": 128547},
|
||||
{"name": "FACE WITH LOOK OF TRIUMPH", "unicode": 128548},
|
||||
{"name": "DISAPPOINTED BUT RELIEVED FACE", "unicode": 128549},
|
||||
{"name": "FEARFUL FACE", "unicode": 128552},
|
||||
{"name": "WEARY FACE", "unicode": 128553},
|
||||
{"name": "SLEEPY FACE", "unicode": 128554},
|
||||
{"name": "TIRED FACE", "unicode": 128555},
|
||||
{"name": "LOUDLY CRYING FACE", "unicode": 128557},
|
||||
{"name": "FACE WITH OPEN MOUTH AND COLD SWEAT", "unicode": 128560},
|
||||
{"name": "FACE SCREAMING IN FEAR", "unicode": 128561},
|
||||
{"name": "ASTONISHED FACE", "unicode": 128562},
|
||||
{"name": "FLUSHED FACE", "unicode": 128563},
|
||||
{"name": "DIZZY FACE", "unicode": 128565},
|
||||
{"name": "FACE WITH MEDICAL MASK", "unicode": 128567},
|
||||
{"name": "GRINNING CAT FACE WITH SMILING EYES", "unicode": 128568},
|
||||
{"name": "CAT FACE WITH TEARS OF JOY", "unicode": 128569},
|
||||
{"name": "SMILING CAT FACE WITH OPEN MOUTH", "unicode": 128570},
|
||||
{"name": "SMILING CAT FACE WITH HEART-SHAPED EYES", "unicode": 128571},
|
||||
{"name": "CAT FACE WITH WRY SMILE", "unicode": 128572},
|
||||
{"name": "KISSING CAT FACE WITH CLOSED EYES", "unicode": 128573},
|
||||
{"name": "POUTING CAT FACE", "unicode": 128574},
|
||||
{"name": "CRYING CAT FACE", "unicode": 128575},
|
||||
{"name": "WEARY CAT FACE", "unicode": 128576},
|
||||
{"name": "FACE WITH NO GOOD GESTURE", "unicode": 128581},
|
||||
{"name": "FACE WITH OK GESTURE", "unicode": 128582},
|
||||
{"name": "PERSON BOWING DEEPLY", "unicode": 128583},
|
||||
{"name": "SEE-NO-EVIL MONKEY", "unicode": 128584},
|
||||
{"name": "HEAR-NO-EVIL MONKEY", "unicode": 128585},
|
||||
{"name": "SPEAK-NO-EVIL MONKEY", "unicode": 128586},
|
||||
{"name": "HAPPY PERSON RAISING ONE HAND", "unicode": 128587},
|
||||
{"name": "PERSON RAISING BOTH HANDS IN CELEBRATION", "unicode": 128588},
|
||||
{"name": "PERSON FROWNING", "unicode": 128589},
|
||||
{"name": "PERSON WITH POUTING FACE", "unicode": 128590},
|
||||
{"name": "PERSON WITH FOLDED HANDS", "unicode": 128591},
|
||||
{"name": "BLACK SCISSORS", "unicode": 9986},
|
||||
{"name": "WHITE HEAVY CHECK MARK", "unicode": 9989},
|
||||
{"name": "AIRPLANE", "unicode": 9992},
|
||||
{"name": "ENVELOPE", "unicode": 9993},
|
||||
{"name": "RAISED FIST", "unicode": 9994},
|
||||
{"name": "RAISED HAND", "unicode": 9995},
|
||||
{"name": "VICTORY HAND", "unicode": 9996},
|
||||
{"name": "PENCIL", "unicode": 9999},
|
||||
{"name": "BLACK NIB", "unicode": 10002},
|
||||
{"name": "HEAVY CHECK MARK", "unicode": 10004},
|
||||
{"name": "HEAVY MULTIPLICATION X", "unicode": 10006},
|
||||
{"name": "SPARKLES", "unicode": 10024},
|
||||
{"name": "EIGHT SPOKED ASTERISK", "unicode": 10035},
|
||||
{"name": "EIGHT POINTED BLACK STAR", "unicode": 10036},
|
||||
{"name": "SNOWFLAKE", "unicode": 10052},
|
||||
{"name": "SPARKLE", "unicode": 10055},
|
||||
{"name": "CROSS MARK", "unicode": 10060},
|
||||
{"name": "NEGATIVE SQUARED CROSS MARK", "unicode": 10062},
|
||||
{"name": "BLACK QUESTION MARK ORNAMENT", "unicode": 10067},
|
||||
{"name": "WHITE QUESTION MARK ORNAMENT", "unicode": 10068},
|
||||
{"name": "WHITE EXCLAMATION MARK ORNAMENT", "unicode": 10069},
|
||||
{"name": "HEAVY EXCLAMATION MARK SYMBOL", "unicode": 10071},
|
||||
{"name": "HEAVY BLACK HEART", "unicode": 10084},
|
||||
{"name": "HEAVY PLUS SIGN", "unicode": 10133},
|
||||
{"name": "HEAVY MINUS SIGN", "unicode": 10134},
|
||||
{"name": "HEAVY DIVISION SIGN", "unicode": 10135},
|
||||
{"name": "BLACK RIGHTWARDS ARROW", "unicode": 10145},
|
||||
{"name": "CURLY LOOP", "unicode": 10160},
|
||||
{"name": "ROCKET", "unicode": 128640},
|
||||
{"name": "RAILWAY CAR", "unicode": 128643},
|
||||
{"name": "HIGH-SPEED TRAIN", "unicode": 128644},
|
||||
{"name": "HIGH-SPEED TRAIN WITH BULLET NOSE", "unicode": 128645},
|
||||
{"name": "METRO", "unicode": 128647},
|
||||
{"name": "STATION", "unicode": 128649},
|
||||
{"name": "BUS", "unicode": 128652},
|
||||
{"name": "BUS STOP", "unicode": 128655},
|
||||
{"name": "AMBULANCE", "unicode": 128657},
|
||||
{"name": "FIRE ENGINE", "unicode": 128658},
|
||||
{"name": "POLICE CAR", "unicode": 128659},
|
||||
{"name": "TAXI", "unicode": 128661},
|
||||
{"name": "AUTOMOBILE", "unicode": 128663},
|
||||
{"name": "RECREATIONAL VEHICLE", "unicode": 128665},
|
||||
{"name": "DELIVERY TRUCK", "unicode": 128666},
|
||||
{"name": "SHIP", "unicode": 128674},
|
||||
{"name": "SPEEDBOAT", "unicode": 128676},
|
||||
{"name": "HORIZONTAL TRAFFIC LIGHT", "unicode": 128677},
|
||||
{"name": "CONSTRUCTION SIGN", "unicode": 128679},
|
||||
{"name": "POLICE CARS REVOLVING LIGHT", "unicode": 128680},
|
||||
{"name": "TRIANGULAR FLAG ON POST", "unicode": 128681},
|
||||
{"name": "DOOR", "unicode": 128682},
|
||||
{"name": "NO ENTRY SIGN", "unicode": 128683},
|
||||
{"name": "SMOKING SYMBOL", "unicode": 128684},
|
||||
{"name": "NO SMOKING SYMBOL", "unicode": 128685},
|
||||
{"name": "BICYCLE", "unicode": 128690},
|
||||
{"name": "PEDESTRIAN", "unicode": 128694},
|
||||
{"name": "MENS SYMBOL", "unicode": 128697},
|
||||
{"name": "WOMENS SYMBOL", "unicode": 128698},
|
||||
{"name": "RESTROOM", "unicode": 128699},
|
||||
{"name": "BABY SYMBOL", "unicode": 128700},
|
||||
{"name": "TOILET", "unicode": 128701},
|
||||
{"name": "WATER CLOSET", "unicode": 128702},
|
||||
{"name": "BATH", "unicode": 128704},
|
||||
{"name": "CIRCLED LATIN CAPITAL LETTER M", "unicode": 9410},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER A", "unicode": 127344},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER B", "unicode": 127345},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER O", "unicode": 127358},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER P", "unicode": 127359},
|
||||
{"name": "NEGATIVE SQUARED AB", "unicode": 127374},
|
||||
{"name": "SQUARED CL", "unicode": 127377},
|
||||
{"name": "SQUARED COOL", "unicode": 127378},
|
||||
{"name": "SQUARED FREE", "unicode": 127379},
|
||||
{"name": "SQUARED ID", "unicode": 127380},
|
||||
{"name": "SQUARED NEW", "unicode": 127381},
|
||||
];
|
||||
```
|
||||
|
||||
[](id:contact)
|
||||
|
||||
## Contact Us
|
||||
|
||||
If there's anything unclear or you have more ideas, feel free to contact us!
|
||||
|
||||
- Telegram Group: https://t.me/+1doS9AUBmndhNGNl
|
||||
- WhatsApp Group: https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,454 @@
|
|||
|
||||
以下为您介绍,如何为腾讯云IM Flutter TUIKit引入表情能力。
|
||||
|
||||
我们的 `TIMUIKitChat` 组件中,支持发送及接收三种类型的表情:
|
||||
|
||||
| 表情类型 | 发送形式 | 是否文字混排 | 发送内容 | 解析方式 | 引入方式 | TUIKit默认自带 |
|
||||
|---------|---------|---------|---------|---------|---------|---------|
|
||||
| [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) Emoji表情 | 文本消息 | 是 | [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html)编码 | 设备自动将[Unicode](https://unicode.org/emoji/charts/full-emoji-list.html)编码解析成小表情。不同的设备,对[Unicode](https://unicode.org/emoji/charts/full-emoji-list.html)解析后的图形,略有不同 | [Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) List | 文档中提供一套[默认Unicode列表示例](#unicode) |
|
||||
| 小图片表情 | 文本消息 | 是 | 表情图片名称 | 根据名称,自动匹配本地Asset图片资源 | 图片资源预存于Asset,并定义 `List` | 一套 QQ 同款小表情图库,可直接使用 |
|
||||
| 大图片表情 | 表情消息 | 否 | `baseURL` 拼接图片文件名,表情图片Asset路径 | 通过路径,解析Asset资源 | 图片资源预存于Asset,并定义 `List` | - |
|
||||
|
||||

|
||||
|
||||
现在,我们就来动手接入TUIKit的表情能力。
|
||||
|
||||
>? [TUIKit](https://pub.dev/packages/tencent_cloud_chat_uikit)在升级至1.1.0版本后,对表情能力用法有较大改动。请您在升级最新版后,根据本文档指引,重新适配接入表情能力。谢谢~
|
||||
|
||||
## STEP1: 自定义表情图片资源
|
||||
|
||||
>? **本步骤选做:**
|
||||
> 如果您需要用到非默认提供的QQ小表情外的其他图片表情,如自定义图片小表情及图片大表情,才需完成本步骤。
|
||||
> QQ小表情包,TUIKit已自带提供,无需在本步骤引入,请关注后续步骤。
|
||||
|
||||
### 将文件导入项目
|
||||
|
||||
请将您的表情资源文件,导入项目的 `assets/custom_face_resource/` 目录内。无论是图片大表情,亦或是图片小表情,都需按此步骤引入。
|
||||
|
||||
该目录内,请使用不同的文件夹,区分表情面板中的不同Tab。每个Tab内表情,仅支持一种类型,图片大表情或图片小表情。
|
||||
|
||||
文件夹的名称,请使用该Tab的 `name` 命名。该命名不会对客户展现,请根据开发需要,自定义即可。
|
||||
|
||||
请保证,所有表情资源文件,不要重名。
|
||||
|
||||
此路径的表情图片放置引入,可参考我们的[Demo](https://github.com/TencentCloud/chat-demo-flutter/tree/main/assets/custom_face_resource)。
|
||||
|
||||

|
||||
|
||||
### 声明表情文件
|
||||
|
||||
打开 `pubspec.yaml` 文件,在 `flutter` => `assets` 中,声明刚刚引入的表情资源文件。
|
||||
|
||||
```yaml
|
||||
flutter:
|
||||
assets:
|
||||
- assets/custom_face_resource/
|
||||
```
|
||||
|
||||
### 在代码中配置图片资源List
|
||||
|
||||
>? 本步骤代码示例请[参考此处](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/utils/constant.dart),直接看 `emojiList` 即可。
|
||||
|
||||
在您代码定义静态参数或配置的地方,定义一个 `static List<CustomEmojiFaceData>`,用于将本地图片资源,转换成TUIKit可以接受的格式,后续以 `List` 方式传入。
|
||||
|
||||
此 `List` 中,每个item都是 `CustomEmojiFaceData`,每个 `CustomEmojiFaceData` 构成一个表情面板中的Tab。具体参数说明如下:
|
||||
|
||||
```dart
|
||||
CustomEmojiFaceData(
|
||||
{
|
||||
String name, // 文件夹目录名称
|
||||
String icon, // Tab中的icon资源文件名
|
||||
List<String> list, // 每个图片的文件名,以List
|
||||
bool isEmoji // 是否为图片小表情,默认为false,即图片大表情
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
示例代码如下:
|
||||
|
||||
```dart
|
||||
static final List<CustomEmojiFaceData> emojiList = [
|
||||
// 使用图片小表情,支持图文混排,以文本消息形式发送
|
||||
CustomEmojiFaceData(
|
||||
name: '4349',
|
||||
icon: "aircraft.png",
|
||||
isEmoji: true,
|
||||
list: [
|
||||
"aircraft.png",
|
||||
"alarmClock.png",
|
||||
"anger.png",
|
||||
// ...
|
||||
]),
|
||||
|
||||
// 使用图片大表情,不支持图文混排,以表情消息形式发送
|
||||
CustomEmojiFaceData(
|
||||
name: '4350',
|
||||
icon: "menu@2x.png",
|
||||
list: [
|
||||
"yz00@2x.png",
|
||||
// ...
|
||||
]),
|
||||
]
|
||||
```
|
||||
|
||||
## STEP2: 自定义Emoji Unicode 字符串List
|
||||
|
||||
>? **本步骤选做:**
|
||||
> 如果您需要用到[Unicode](https://unicode.org/emoji/charts/full-emoji-list.html) Emoji表情,才需完成本步骤。
|
||||
|
||||
在您代码定义静态参数或配置的地方,定义一个 `List<Map<String, Object>>` Unicode 列表,供传入。
|
||||
|
||||
该列表,我们提供一套示例,在[附录](#unicode)中。
|
||||
|
||||
您看直接复制引入这套示例列表,或基于此修改。
|
||||
|
||||
## STEP3: 表情资源预读入内存
|
||||
|
||||
>?
|
||||
> 本步骤代码示例请[参考此处](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/pages/app.dart),直接看 `setCustomSticker` 方法即可。
|
||||
> QQ小表情包,TUIKit已自带提供,无需在本步骤引入,请关注后续步骤。
|
||||
|
||||
在您的项目启动后,首个 `TIMUIKitChat` 组件渲染前,将上一步定义的图片表情资源List,转换成TUIKit表情的实例,放入全局Provider中,存储于内存里。
|
||||
|
||||
**本步骤方法仅需执行一次,一次性全部读入内存中。** 因展示渲染表情资源为高频操作,如每次展示前才动态读入内存,对资源与性能占用比较大。
|
||||
|
||||
单个表情内存实例,使用 `CustomSticker` 类生成。如果传入了 `unicode` 则为 Unicode Emoji表情,否则为图片类型表情。
|
||||
|
||||
```dart
|
||||
class CustomSticker {
|
||||
int? unicode; // Unicode int值。如果传入了 `unicode` 则为 Unicode Emoji表情,否则为图片类型表情
|
||||
String name; // 表情名称
|
||||
int index; // 表情序号
|
||||
bool isEmoji; // 是否为图片小表情,默认为图片大表情
|
||||
}
|
||||
```
|
||||
|
||||
每个Tab的内存实例,使用 `CustomStickerPackage` 类生成。
|
||||
|
||||
```dart
|
||||
class CustomStickerPackage { // 一个系列的表情包定义为一个package,占据一个表情面板Tab
|
||||
String name; // 表情包package name,该Tab文件夹名称。
|
||||
String? baseUrl; // 表情包package baseUrl,建议配置成:"assets/custom_face_resource/${表情包文件夹名称 即 表情包package name}"
|
||||
List<CustomSticker> stickerList; // 表情资源列表
|
||||
CustomSticker menuItem; // 表情面包Tab按钮icon
|
||||
bool isEmoji; // 是否为图片小表情,默认为图片大表情
|
||||
}
|
||||
```
|
||||
|
||||
综上所述,需要写的代码,我们给出示例版本。
|
||||
|
||||
表情项一演示如何使用Emoji Unicode表情包,表情项二演示如何使用图片类型(包含大或小)表情包。您可以根据需要,使用全部或部分代码。
|
||||
|
||||
```dart
|
||||
setCustomSticker() async {
|
||||
// 定义一个大List来承载各个表情包 package Tab
|
||||
List<CustomStickerPackage> customStickerPackageList = [];
|
||||
|
||||
// 表情项一:使用Emoji Unicode表情列表。可以嵌入文字内容中。
|
||||
// `emojiData` 来自于STEP2。
|
||||
final defEmojiList = emojiData.asMap().keys.map((emojiIndex) {
|
||||
final emoji = Emoji.fromJson(emojiData[emojiIndex]);
|
||||
return CustomSticker(
|
||||
index: emojiIndex, name: emoji.name, unicode: emoji.unicode);
|
||||
}).toList();
|
||||
customStickerPackageList.add(CustomStickerPackage(
|
||||
name: "defaultEmoji",
|
||||
stickerList: defEmojiList,
|
||||
menuItem: defEmojiList[0]));
|
||||
|
||||
// 表情项二:使用您提供的图片表情包。
|
||||
// 务必保证 `customEmojiPackage.name` 为该Tab文件夹名称。
|
||||
// `Const.emojiList` 来自于STEP1。
|
||||
customStickerPackageList.addAll(Const.emojiList.map((customEmojiPackage) {
|
||||
return CustomStickerPackage(
|
||||
name: customEmojiPackage.name,
|
||||
baseUrl: "assets/custom_face_resource/${customEmojiPackage.name}",
|
||||
stickerList: customEmojiPackage.list
|
||||
.asMap()
|
||||
.keys
|
||||
.map((idx) =>
|
||||
CustomSticker(index: idx, name: customEmojiPackage.list[idx]))
|
||||
.toList(),
|
||||
menuItem: CustomSticker(
|
||||
index: 0,
|
||||
name: customEmojiPackage.icon,
|
||||
));
|
||||
}).toList());
|
||||
|
||||
Provider.of<CustomStickerPackageData>(context, listen: false)
|
||||
.customStickerPackageList = customStickerPackageList;
|
||||
}
|
||||
```
|
||||
|
||||
## STEP4: 为 TIMUIKitChat 组件添加表情解析能力
|
||||
|
||||
>?
|
||||
> 本步骤代码示例请[参考此处](https://github.com/TencentCloud/chat-demo-flutter/blob/main/lib/src/chat.dart),重点浏览 `renderCustomStickerPanel`, `customStickerPanel` 及 `customEmojiList` 即可。
|
||||
|
||||
将以下代码,直接拷贝进入您用于承载 `TIMUIKitChat` 组件的类中。
|
||||
|
||||
```dart
|
||||
Widget renderCustomStickerPanel({
|
||||
sendTextMessage,
|
||||
sendFaceMessage,
|
||||
deleteText,
|
||||
addCustomEmojiText,
|
||||
addText,
|
||||
List<CustomEmojiFaceData> defaultCustomEmojiStickerList = const [],
|
||||
}) {
|
||||
final theme = Provider.of<DefaultThemeData>(context).theme;
|
||||
final customStickerPackageList =
|
||||
Provider.of<CustomStickerPackageData>(context).customStickerPackageList;
|
||||
final defaultEmojiList =
|
||||
defaultCustomEmojiStickerList.map((customEmojiPackage) {
|
||||
return CustomStickerPackage(
|
||||
name: customEmojiPackage.name,
|
||||
baseUrl: "assets/custom_face_resource/${customEmojiPackage.name}",
|
||||
isEmoji: customEmojiPackage.isEmoji,
|
||||
isDefaultEmoji: true,
|
||||
stickerList: customEmojiPackage.list
|
||||
.asMap()
|
||||
.keys
|
||||
.map((idx) =>
|
||||
CustomSticker(index: idx, name: customEmojiPackage.list[idx]))
|
||||
.toList(),
|
||||
menuItem: CustomSticker(
|
||||
index: 0,
|
||||
name: customEmojiPackage.icon,
|
||||
));
|
||||
}).toList();
|
||||
return StickerPanel(
|
||||
sendTextMsg: sendTextMessage,
|
||||
sendFaceMsg: (index, data) =>
|
||||
sendFaceMessage(index + 1, (data.split("/")[3]).split("@")[0]),
|
||||
deleteText: deleteText,
|
||||
addText: addText,
|
||||
addCustomEmojiText: addCustomEmojiText,
|
||||
customStickerPackageList: [
|
||||
...defaultEmojiList,
|
||||
...customStickerPackageList
|
||||
],
|
||||
backgroundColor: theme.weakBackgroundColor,
|
||||
lightPrimaryColor: theme.lightPrimaryColor);
|
||||
}
|
||||
```
|
||||
|
||||
### STEP4.1: 渲染图片小表情
|
||||
|
||||
>? **本步骤选做:**
|
||||
> - 如果您的项目需要用到图片小表情,包括自定义图片小表情,或直接使用默认自带 QQ 同款图片小表情,才需完成本步骤。
|
||||
> - 图片小表情展现形式和Unicode Emoji类似,建议Unicode Emoji和图片小表情选用一个即可。即,如果您选用了Unicode Emoji,可直接跳过本步骤。
|
||||
|
||||
- STEP4.1(a) 为使用自定义图片小表情;
|
||||
- STEP4.1(b) 为使用默认自带 QQ 同款图片小表情。
|
||||
|
||||
以上方案,建议直接选用一个方案即可。
|
||||
|
||||
如果需要同时使用,请保证您的自定义图片小表情名称,不要和我们默认提供的 QQ 同款图片小表情重复。
|
||||
|
||||
#### STEP4.1(a): 添加渲染解析自定义图片小表情的支持
|
||||
|
||||
在您用于承载 `TIMUIKitChat` 组件的 `build` 方法中,定义一个 `List customEmojiList` 变量,用于存放图片小表情列表。
|
||||
|
||||
```dart
|
||||
List customEmojiList =
|
||||
Const.emojiList.where((element) => element.isEmoji == true).toList();
|
||||
```
|
||||
|
||||
并将此列表,传入 `TIMUIKitChat` 组件的 `customEmojiStickerList` 参数内。
|
||||
|
||||
```dart
|
||||
return TIMUIKitChat(
|
||||
customEmojiStickerList: customEmojiList,
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||
>? 如果您用于承载 `TIMUIKitChat` 组件的类为 `StatefulWidget`,您可将 `customEmojiList` 变量,放如State中,仅在首次build时,才去执行 `where` 命令,优化性能。
|
||||
|
||||
#### STEP4.1(b): 启用 QQ 小表情包
|
||||
|
||||
将 `TIMUIKitChat` 的 `TIMUIKitChatConfig` 的 `isUseDefaultEmoji` 参数,设置为 `true` 即可。此时,会向表情包面板最左侧,自动生成一个承载 QQ 小表情包的 Tab。
|
||||
|
||||
```dart
|
||||
return TIMUIKitChat(
|
||||
config: TIMUIKitChatConfig(
|
||||
isUseDefaultEmoji: true,
|
||||
// ......
|
||||
),
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||

|
||||
|
||||
### STEP4.2: 将表情包能力,注入 TIMUIKitChat
|
||||
|
||||
将本步骤最开始让您复制的代码方法,传入 `TIMUIKitChat` 组件的 `customStickerPanel` 参数内。
|
||||
|
||||
```dart
|
||||
return TIMUIKitChat(
|
||||
customStickerPanel: renderCustomStickerPanel,
|
||||
// ......
|
||||
);
|
||||
```
|
||||
|
||||
此时,TUIKit表情能力接入完成。您可正常收发测试。如在接入过程中,有任何问题,欢迎随时[联系我们](#contact)。
|
||||
|
||||
[](id:unicode)
|
||||
|
||||
## 附录: Emoji Unicode 列表示例
|
||||
|
||||
本列表仅用于示例演示,您可根据需要,增加或修改。
|
||||
|
||||
```dart
|
||||
List<Map<String, Object>> emojiData = [
|
||||
{"name": "GRINNING FACE WITH SMILING EYES", "unicode": 128513},
|
||||
{"name": "FACE WITH TEARS OF JOY", "unicode": 128514},
|
||||
{"name": "SMILING FACE WITH OPEN MOUTH", "unicode": 128515},
|
||||
{"name": "SMILING FACE WITH OPEN MOUTH AND SMILING EYES", "unicode": 128516},
|
||||
{"name": "SMILING FACE WITH OPEN MOUTH AND COLD SWEAT", "unicode": 128517},
|
||||
{
|
||||
"name": "SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES",
|
||||
"unicode": 128518
|
||||
},
|
||||
{"name": "WINKING FACE", "unicode": 128521},
|
||||
{"name": "SMILING FACE WITH SMILING EYES", "unicode": 128522},
|
||||
{"name": "FACE SAVOURING DELICIOUS FOOD", "unicode": 128523},
|
||||
{"name": "RELIEVED FACE", "unicode": 128524},
|
||||
{"name": "SMILING FACE WITH HEART-SHAPED EYES", "unicode": 128525},
|
||||
{"name": "SMIRKING FACE", "unicode": 128527},
|
||||
{"name": "UNAMUSED FACE", "unicode": 128530},
|
||||
{"name": "FACE WITH COLD SWEAT", "unicode": 128531},
|
||||
{"name": "PENSIVE FACE", "unicode": 128532},
|
||||
{"name": "CONFOUNDED FACE", "unicode": 128534},
|
||||
{"name": "FACE THROWING A KISS", "unicode": 128536},
|
||||
{"name": "KISSING FACE WITH CLOSED EYES", "unicode": 128538},
|
||||
{"name": "FACE WITH STUCK-OUT TONGUE AND WINKING EYE", "unicode": 128540},
|
||||
{
|
||||
"name": "FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES",
|
||||
"unicode": 128541
|
||||
},
|
||||
{"name": "DISAPPOINTED FACE", "unicode": 128542},
|
||||
{"name": "ANGRY FACE", "unicode": 128544},
|
||||
{"name": "POUTING FACE", "unicode": 128545},
|
||||
{"name": "CRYING FACE", "unicode": 128546},
|
||||
{"name": "PERSEVERING FACE", "unicode": 128547},
|
||||
{"name": "FACE WITH LOOK OF TRIUMPH", "unicode": 128548},
|
||||
{"name": "DISAPPOINTED BUT RELIEVED FACE", "unicode": 128549},
|
||||
{"name": "FEARFUL FACE", "unicode": 128552},
|
||||
{"name": "WEARY FACE", "unicode": 128553},
|
||||
{"name": "SLEEPY FACE", "unicode": 128554},
|
||||
{"name": "TIRED FACE", "unicode": 128555},
|
||||
{"name": "LOUDLY CRYING FACE", "unicode": 128557},
|
||||
{"name": "FACE WITH OPEN MOUTH AND COLD SWEAT", "unicode": 128560},
|
||||
{"name": "FACE SCREAMING IN FEAR", "unicode": 128561},
|
||||
{"name": "ASTONISHED FACE", "unicode": 128562},
|
||||
{"name": "FLUSHED FACE", "unicode": 128563},
|
||||
{"name": "DIZZY FACE", "unicode": 128565},
|
||||
{"name": "FACE WITH MEDICAL MASK", "unicode": 128567},
|
||||
{"name": "GRINNING CAT FACE WITH SMILING EYES", "unicode": 128568},
|
||||
{"name": "CAT FACE WITH TEARS OF JOY", "unicode": 128569},
|
||||
{"name": "SMILING CAT FACE WITH OPEN MOUTH", "unicode": 128570},
|
||||
{"name": "SMILING CAT FACE WITH HEART-SHAPED EYES", "unicode": 128571},
|
||||
{"name": "CAT FACE WITH WRY SMILE", "unicode": 128572},
|
||||
{"name": "KISSING CAT FACE WITH CLOSED EYES", "unicode": 128573},
|
||||
{"name": "POUTING CAT FACE", "unicode": 128574},
|
||||
{"name": "CRYING CAT FACE", "unicode": 128575},
|
||||
{"name": "WEARY CAT FACE", "unicode": 128576},
|
||||
{"name": "FACE WITH NO GOOD GESTURE", "unicode": 128581},
|
||||
{"name": "FACE WITH OK GESTURE", "unicode": 128582},
|
||||
{"name": "PERSON BOWING DEEPLY", "unicode": 128583},
|
||||
{"name": "SEE-NO-EVIL MONKEY", "unicode": 128584},
|
||||
{"name": "HEAR-NO-EVIL MONKEY", "unicode": 128585},
|
||||
{"name": "SPEAK-NO-EVIL MONKEY", "unicode": 128586},
|
||||
{"name": "HAPPY PERSON RAISING ONE HAND", "unicode": 128587},
|
||||
{"name": "PERSON RAISING BOTH HANDS IN CELEBRATION", "unicode": 128588},
|
||||
{"name": "PERSON FROWNING", "unicode": 128589},
|
||||
{"name": "PERSON WITH POUTING FACE", "unicode": 128590},
|
||||
{"name": "PERSON WITH FOLDED HANDS", "unicode": 128591},
|
||||
{"name": "BLACK SCISSORS", "unicode": 9986},
|
||||
{"name": "WHITE HEAVY CHECK MARK", "unicode": 9989},
|
||||
{"name": "AIRPLANE", "unicode": 9992},
|
||||
{"name": "ENVELOPE", "unicode": 9993},
|
||||
{"name": "RAISED FIST", "unicode": 9994},
|
||||
{"name": "RAISED HAND", "unicode": 9995},
|
||||
{"name": "VICTORY HAND", "unicode": 9996},
|
||||
{"name": "PENCIL", "unicode": 9999},
|
||||
{"name": "BLACK NIB", "unicode": 10002},
|
||||
{"name": "HEAVY CHECK MARK", "unicode": 10004},
|
||||
{"name": "HEAVY MULTIPLICATION X", "unicode": 10006},
|
||||
{"name": "SPARKLES", "unicode": 10024},
|
||||
{"name": "EIGHT SPOKED ASTERISK", "unicode": 10035},
|
||||
{"name": "EIGHT POINTED BLACK STAR", "unicode": 10036},
|
||||
{"name": "SNOWFLAKE", "unicode": 10052},
|
||||
{"name": "SPARKLE", "unicode": 10055},
|
||||
{"name": "CROSS MARK", "unicode": 10060},
|
||||
{"name": "NEGATIVE SQUARED CROSS MARK", "unicode": 10062},
|
||||
{"name": "BLACK QUESTION MARK ORNAMENT", "unicode": 10067},
|
||||
{"name": "WHITE QUESTION MARK ORNAMENT", "unicode": 10068},
|
||||
{"name": "WHITE EXCLAMATION MARK ORNAMENT", "unicode": 10069},
|
||||
{"name": "HEAVY EXCLAMATION MARK SYMBOL", "unicode": 10071},
|
||||
{"name": "HEAVY BLACK HEART", "unicode": 10084},
|
||||
{"name": "HEAVY PLUS SIGN", "unicode": 10133},
|
||||
{"name": "HEAVY MINUS SIGN", "unicode": 10134},
|
||||
{"name": "HEAVY DIVISION SIGN", "unicode": 10135},
|
||||
{"name": "BLACK RIGHTWARDS ARROW", "unicode": 10145},
|
||||
{"name": "CURLY LOOP", "unicode": 10160},
|
||||
{"name": "ROCKET", "unicode": 128640},
|
||||
{"name": "RAILWAY CAR", "unicode": 128643},
|
||||
{"name": "HIGH-SPEED TRAIN", "unicode": 128644},
|
||||
{"name": "HIGH-SPEED TRAIN WITH BULLET NOSE", "unicode": 128645},
|
||||
{"name": "METRO", "unicode": 128647},
|
||||
{"name": "STATION", "unicode": 128649},
|
||||
{"name": "BUS", "unicode": 128652},
|
||||
{"name": "BUS STOP", "unicode": 128655},
|
||||
{"name": "AMBULANCE", "unicode": 128657},
|
||||
{"name": "FIRE ENGINE", "unicode": 128658},
|
||||
{"name": "POLICE CAR", "unicode": 128659},
|
||||
{"name": "TAXI", "unicode": 128661},
|
||||
{"name": "AUTOMOBILE", "unicode": 128663},
|
||||
{"name": "RECREATIONAL VEHICLE", "unicode": 128665},
|
||||
{"name": "DELIVERY TRUCK", "unicode": 128666},
|
||||
{"name": "SHIP", "unicode": 128674},
|
||||
{"name": "SPEEDBOAT", "unicode": 128676},
|
||||
{"name": "HORIZONTAL TRAFFIC LIGHT", "unicode": 128677},
|
||||
{"name": "CONSTRUCTION SIGN", "unicode": 128679},
|
||||
{"name": "POLICE CARS REVOLVING LIGHT", "unicode": 128680},
|
||||
{"name": "TRIANGULAR FLAG ON POST", "unicode": 128681},
|
||||
{"name": "DOOR", "unicode": 128682},
|
||||
{"name": "NO ENTRY SIGN", "unicode": 128683},
|
||||
{"name": "SMOKING SYMBOL", "unicode": 128684},
|
||||
{"name": "NO SMOKING SYMBOL", "unicode": 128685},
|
||||
{"name": "BICYCLE", "unicode": 128690},
|
||||
{"name": "PEDESTRIAN", "unicode": 128694},
|
||||
{"name": "MENS SYMBOL", "unicode": 128697},
|
||||
{"name": "WOMENS SYMBOL", "unicode": 128698},
|
||||
{"name": "RESTROOM", "unicode": 128699},
|
||||
{"name": "BABY SYMBOL", "unicode": 128700},
|
||||
{"name": "TOILET", "unicode": 128701},
|
||||
{"name": "WATER CLOSET", "unicode": 128702},
|
||||
{"name": "BATH", "unicode": 128704},
|
||||
{"name": "CIRCLED LATIN CAPITAL LETTER M", "unicode": 9410},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER A", "unicode": 127344},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER B", "unicode": 127345},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER O", "unicode": 127358},
|
||||
{"name": "NEGATIVE SQUARED LATIN CAPITAL LETTER P", "unicode": 127359},
|
||||
{"name": "NEGATIVE SQUARED AB", "unicode": 127374},
|
||||
{"name": "SQUARED CL", "unicode": 127377},
|
||||
{"name": "SQUARED COOL", "unicode": 127378},
|
||||
{"name": "SQUARED FREE", "unicode": 127379},
|
||||
{"name": "SQUARED ID", "unicode": 127380},
|
||||
{"name": "SQUARED NEW", "unicode": 127381},
|
||||
];
|
||||
```
|
||||
|
||||
[](id:contact)
|
||||
|
||||
## 联系我们
|
||||
|
||||
If there's anything unclear or you have more ideas, feel free to contact us!
|
||||
|
||||
- Telegram Group: https://t.me/+1doS9AUBmndhNGNl
|
||||
- WhatsApp Group: https://chat.whatsapp.com/Gfbxk7rQBqc8Rz4pzzP27A
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# 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.
|
||||
|
||||
version:
|
||||
revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
channel: stable
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
- platform: linux
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
- platform: macos
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
- platform: windows
|
||||
create_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
base_revision: b8f7f1f9869bb2d116aa6a70dbeac61000b52849
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.example">
|
||||
<application
|
||||
android:label="example"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.example.example
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
|
After Width: | Height: | Size: 544 B |
|
After Width: | Height: | Size: 442 B |
|
After Width: | Height: | Size: 721 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>App</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.flutter.flutter.app</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>App</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>9.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# Uncomment this line to define a global platform for your project
|
||||
platform :ios, '11.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
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
PODS:
|
||||
- audioplayers_darwin (0.0.1):
|
||||
- Flutter
|
||||
- camera_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
- disk_space (0.0.1):
|
||||
- Flutter
|
||||
- DKImagePickerController/Core (4.3.4):
|
||||
- DKImagePickerController/ImageDataManager
|
||||
- DKImagePickerController/Resource
|
||||
- DKImagePickerController/ImageDataManager (4.3.4)
|
||||
- DKImagePickerController/PhotoGallery (4.3.4):
|
||||
- DKImagePickerController/Core
|
||||
- DKPhotoGallery
|
||||
- DKImagePickerController/Resource (4.3.4)
|
||||
- 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
|
||||
- fc_native_video_thumbnail_for_us (0.0.1):
|
||||
- Flutter
|
||||
- file_picker (0.0.1):
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_image_compress (1.0.0):
|
||||
- Flutter
|
||||
- Mantle
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- flutter_plugin_record_plus (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.4):
|
||||
- libwebp/demux (= 1.2.4)
|
||||
- libwebp/mux (= 1.2.4)
|
||||
- libwebp/webp (= 1.2.4)
|
||||
- libwebp/demux (1.2.4):
|
||||
- libwebp/webp
|
||||
- libwebp/mux (1.2.4):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.2.4)
|
||||
- Mantle (2.2.0):
|
||||
- Mantle/extobjc (= 2.2.0)
|
||||
- Mantle/extobjc (2.2.0)
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
- pasteboard (0.0.1):
|
||||
- Flutter
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- permission_handler_apple (9.0.4):
|
||||
- Flutter
|
||||
- photo_manager (2.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- ReactiveObjC (3.1.1)
|
||||
- SDWebImage (5.15.5):
|
||||
- SDWebImage/Core (= 5.15.5)
|
||||
- SDWebImage/Core (5.15.5)
|
||||
- SDWebImageWebPCoder (0.11.0):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.15)
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- sqflite (0.0.2):
|
||||
- Flutter
|
||||
- FMDB (>= 2.7.5)
|
||||
- SwiftyGif (5.4.4)
|
||||
- tencent_cloud_chat_sdk (5.1.2):
|
||||
- Flutter
|
||||
- HydraAsync
|
||||
- TXIMSDK_Plus_iOS (= 7.1.3925)
|
||||
- tencent_cloud_uikit_core (0.0.1):
|
||||
- Flutter
|
||||
- TUICore (= 7.1.3925)
|
||||
- tencent_open_file (0.0.1):
|
||||
- Flutter
|
||||
- Toast (4.0.0)
|
||||
- TUICore (7.1.3925):
|
||||
- ReactiveObjC
|
||||
- SDWebImage
|
||||
- TUICore/ImSDK_Plus (= 7.1.3925)
|
||||
- TUICore/Base (7.1.3925):
|
||||
- ReactiveObjC
|
||||
- SDWebImage
|
||||
- TUICore/ImSDK_Plus (7.1.3925):
|
||||
- ReactiveObjC
|
||||
- SDWebImage
|
||||
- TUICore/Base
|
||||
- TXIMSDK_Plus_iOS (= 7.1.3925)
|
||||
- TXIMSDK_Plus_iOS (7.1.3925)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- video_player_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
- wakelock (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`)
|
||||
- camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
|
||||
- disk_space (from `.symlinks/plugins/disk_space/ios`)
|
||||
- fc_native_video_thumbnail_for_us (from `.symlinks/plugins/fc_native_video_thumbnail_for_us/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`)
|
||||
- flutter_plugin_record_plus (from `.symlinks/plugins/flutter_plugin_record_plus/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`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- pasteboard (from `.symlinks/plugins/pasteboard/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
- tencent_cloud_chat_sdk (from `.symlinks/plugins/tencent_cloud_chat_sdk/ios`)
|
||||
- tencent_cloud_uikit_core (from `.symlinks/plugins/tencent_cloud_uikit_core/ios`)
|
||||
- tencent_open_file (from `.symlinks/plugins/tencent_open_file/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
|
||||
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- FMDB
|
||||
- HydraAsync
|
||||
- libwebp
|
||||
- Mantle
|
||||
- ReactiveObjC
|
||||
- SDWebImage
|
||||
- SDWebImageWebPCoder
|
||||
- SwiftyGif
|
||||
- Toast
|
||||
- TUICore
|
||||
- TXIMSDK_Plus_iOS
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
audioplayers_darwin:
|
||||
:path: ".symlinks/plugins/audioplayers_darwin/ios"
|
||||
camera_avfoundation:
|
||||
:path: ".symlinks/plugins/camera_avfoundation/ios"
|
||||
disk_space:
|
||||
:path: ".symlinks/plugins/disk_space/ios"
|
||||
fc_native_video_thumbnail_for_us:
|
||||
:path: ".symlinks/plugins/fc_native_video_thumbnail_for_us/ios"
|
||||
file_picker:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_image_compress:
|
||||
:path: ".symlinks/plugins/flutter_image_compress/ios"
|
||||
flutter_plugin_record_plus:
|
||||
:path: ".symlinks/plugins/flutter_plugin_record_plus/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"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
pasteboard:
|
||||
:path: ".symlinks/plugins/pasteboard/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
photo_manager:
|
||||
:path: ".symlinks/plugins/photo_manager/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/ios"
|
||||
sqflite:
|
||||
:path: ".symlinks/plugins/sqflite/ios"
|
||||
tencent_cloud_chat_sdk:
|
||||
:path: ".symlinks/plugins/tencent_cloud_chat_sdk/ios"
|
||||
tencent_cloud_uikit_core:
|
||||
:path: ".symlinks/plugins/tencent_cloud_uikit_core/ios"
|
||||
tencent_open_file:
|
||||
:path: ".symlinks/plugins/tencent_open_file/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
video_player_avfoundation:
|
||||
:path: ".symlinks/plugins/video_player_avfoundation/ios"
|
||||
wakelock:
|
||||
:path: ".symlinks/plugins/wakelock/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40
|
||||
camera_avfoundation: 07c77549ea54ad95d8581be86617c094a46280d9
|
||||
disk_space: e94d34bbdf77954adfb39e60bde9cc5c7233eda6
|
||||
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
||||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||
fc_native_video_thumbnail_for_us: 69559e6500bff0f6340f044ec0847366fa6f6233
|
||||
file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
flutter_image_compress: 5a5e9aee05b6553048b8df1c3bc456d0afaac433
|
||||
flutter_plugin_record_plus: 79b8e13ee7ed9a94f6c067018653599528cee1fc
|
||||
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
HydraAsync: 8d589bd725b0224f899afafc9a396327405f8063
|
||||
image_gallery_saver: 259eab68fb271cfd57d599904f7acdc7832e7ef2
|
||||
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
|
||||
libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
|
||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
||||
pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0
|
||||
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
|
||||
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
|
||||
photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
|
||||
ReactiveObjC: 011caa393aa0383245f2dcf9bf02e86b80b36040
|
||||
SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe
|
||||
SDWebImageWebPCoder: 295a6573c512f54ad2dd58098e64e17dcf008499
|
||||
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
|
||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
|
||||
tencent_cloud_chat_sdk: 17f2ddd7de43495312603e7c9dac04d76352e246
|
||||
tencent_cloud_uikit_core: 2c4ccb41c33b45b5c69750b9774fa389fc20cdb2
|
||||
tencent_open_file: 1261db508715b8f43ef3b7e31c90824838038165
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
TUICore: edc9e0911f6a04224620d7098ff9d7f4b1f0291a
|
||||
TXIMSDK_Plus_iOS: 3edf95acc3dff794287ea858b5205ed6f4dd339f
|
||||
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
|
||||
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
|
||||
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||
|
||||
PODFILE CHECKSUM: 7368163408c647b7eb699d0d788ba6718e18fb8d
|
||||
|
||||
COCOAPODS: 1.12.0
|
||||
|
|
@ -0,0 +1,549 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
56220B3E3A69391FE44D9F6D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 842FC629A02A0DB5EA275F83 /* Pods_Runner.framework */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
3843BAFB77932A36394807CF /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
80DA560C61DA3C7E0AFF698F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
842FC629A02A0DB5EA275F83 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
D663F44D0AAE34D99189AE58 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
56220B3E3A69391FE44D9F6D /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
53913FEF742A463D71A8D172 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
842FC629A02A0DB5EA275F83 /* Pods_Runner.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
74FBAFC8A9A3960F7CA99189 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
80DA560C61DA3C7E0AFF698F /* Pods-Runner.debug.xcconfig */,
|
||||
D663F44D0AAE34D99189AE58 /* Pods-Runner.release.xcconfig */,
|
||||
3843BAFB77932A36394807CF /* Pods-Runner.profile.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||
);
|
||||
name = Flutter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
74FBAFC8A9A3960F7CA99189 /* Pods */,
|
||||
53913FEF742A463D71A8D172 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146EF1CF9000F007C117D /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146F01CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
97C147021CF9000F007C117D /* Info.plist */,
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
919EAFB910364FD42152D9D1 /* [CP] Check Pods Manifest.lock */,
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
0023742D62D58E49F4FD6732 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1300;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
97C146ED1CF9000F007C117D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 1100;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
97C146ED1CF9000F007C117D /* Runner */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
0023742D62D58E49F4FD6732 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Thin Binary";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||
};
|
||||
919EAFB910364FD42152D9D1 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C146FB1CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C147001CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
97C147031CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147041CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
97C147061CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147071CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147031CF9000F007C117D /* Debug */,
|
||||
97C147041CF9000F007C117D /* Release */,
|
||||
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147061CF9000F007C117D /* Debug */,
|
||||
97C147071CF9000F007C117D /* Release */,
|
||||
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1300"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 564 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 68 B |
|
After Width: | Height: | Size: 68 B |
|
After Width: | Height: | Size: 68 B |
|
|
@ -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.
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="LaunchImage" width="168" height="185"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Flutter View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Example</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>example</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1 @@
|
|||
#import "GeneratedPluginRegistrant.h"
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// ignore_for_file: file_names
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class TIMUIKitAddFriendExample extends StatelessWidget {
|
||||
const TIMUIKitAddFriendExample({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TIMUIKitAddFriend(onTapAlreadyFriendsItem: (String userID) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// ignore_for_file: file_names
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class TIMUIKitAddGroupExample extends StatelessWidget {
|
||||
const TIMUIKitAddGroupExample({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TIMUIKitAddGroup(
|
||||
onTapExistGroup: (groupID, conversation) {
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// ignore_for_file: file_names
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class TIMUIKitBlackListExample extends StatelessWidget {
|
||||
const TIMUIKitBlackListExample({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const TIMUIKitBlackList();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
// ignore_for_file: file_names
|
||||
|
||||
import 'package:example/TIMUIKitGroupProfileExample.dart';
|
||||
import 'package:example/TIMUIKitProfileExample.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class TIMUIKitChatExample extends StatelessWidget {
|
||||
final V2TimConversation? selectedConversation;
|
||||
|
||||
const TIMUIKitChatExample({Key? key, this.selectedConversation})
|
||||
: super(key: key);
|
||||
|
||||
Widget renderCustomStickerPanel({
|
||||
sendTextMessage,
|
||||
sendFaceMessage,
|
||||
deleteText,
|
||||
addCustomEmojiText,
|
||||
addText,
|
||||
List<CustomEmojiFaceData> defaultCustomEmojiStickerList = const [],
|
||||
double? height,
|
||||
double? width
|
||||
}) {
|
||||
final defaultEmojiList =
|
||||
defaultCustomEmojiStickerList.map((customEmojiPackage) {
|
||||
return CustomStickerPackage(
|
||||
name: customEmojiPackage.name,
|
||||
baseUrl: "assets/custom_face_resource/${customEmojiPackage.name}",
|
||||
isEmoji: customEmojiPackage.isEmoji,
|
||||
isDefaultEmoji: true,
|
||||
stickerList: customEmojiPackage.list
|
||||
.asMap()
|
||||
.keys
|
||||
.map((idx) =>
|
||||
CustomSticker(index: idx, name: customEmojiPackage.list[idx]))
|
||||
.toList(),
|
||||
menuItem: CustomSticker(
|
||||
index: 0,
|
||||
name: customEmojiPackage.icon,
|
||||
));
|
||||
}).toList();
|
||||
return StickerPanel(
|
||||
sendTextMsg: sendTextMessage,
|
||||
sendFaceMsg: (index, data) =>
|
||||
sendFaceMessage(index + 1, (data.split("/")[3]).split("@")[0]),
|
||||
deleteText: deleteText,
|
||||
addText: addText,
|
||||
addCustomEmojiText: addCustomEmojiText,
|
||||
customStickerPackageList: [
|
||||
...defaultEmojiList,
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TIMUIKitChat(
|
||||
conversation: selectedConversation ??
|
||||
V2TimConversation(
|
||||
conversationID: "c2c_10040818",
|
||||
userID: "10040818",
|
||||
showName: "Test Chat",
|
||||
type: 1),
|
||||
customStickerPanel: renderCustomStickerPanel,
|
||||
config: const TIMUIKitChatConfig(
|
||||
// 仅供演示,非全部配置项,实际使用中,可只传和默认项不同的参数,无需传入所有开关
|
||||
isAllowClickAvatar: true,
|
||||
isAllowLongPressMessage: true,
|
||||
isShowReadingStatus: true,
|
||||
isShowGroupReadingStatus: true,
|
||||
notificationTitle: "",
|
||||
isUseMessageReaction: true,
|
||||
groupReadReceiptPermissionList: [
|
||||
GroupReceiptAllowType.work,
|
||||
GroupReceiptAllowType.meeting,
|
||||
GroupReceiptAllowType.public
|
||||
],
|
||||
),
|
||||
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: 'tencent_cloud_chat_uikit',
|
||||
height: 34,
|
||||
width: 34,
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// ignore_for_file: file_names
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class TIMUIKitContactExample extends StatelessWidget {
|
||||
const TIMUIKitContactExample({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const TIMUIKitContact();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// ignore_for_file: file_names, avoid_print
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/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,
|
||||
),
|
||||
));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// ignore_for_file: file_names
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class TIMUIKitGroupExample extends StatelessWidget {
|
||||
const TIMUIKitGroupExample({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const TIMUIKitGroup();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// ignore_for_file: file_names
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// ignore_for_file: file_names
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class TIMUIKitNewContactExample extends StatelessWidget {
|
||||
const TIMUIKitNewContactExample({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const TIMUIKitNewContact();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// ignore_for_file: file_names
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// ignore_for_file: avoid_print, file_names, deprecated_member_use
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
|
||||
class TIMUIKitSearchExample extends StatelessWidget {
|
||||
const TIMUIKitSearchExample({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TIMUIKitSearch(
|
||||
onTapConversation: (conv, message) {
|
||||
},
|
||||
onEnterConversation: (V2TimConversation conversation, String keyword) {},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/V2TimSDKListener.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/enum/log_level_enum.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_callback.dart';
|
||||
import 'package:tencent_cloud_chat_sdk/models/v2_tim_user_full_info.dart';
|
||||
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';
|
||||
import 'TIMUIKitAddFriendExample.dart';
|
||||
import 'TIMUIKitAddGroupExample.dart';
|
||||
import 'TIMUIKitBlackListExample.dart';
|
||||
import 'TIMUIKitChatExample.dart';
|
||||
import 'TIMUIKitContactExample.dart';
|
||||
import 'TIMUIKitConversationExample.dart';
|
||||
import 'TIMUIKitGroupExample.dart';
|
||||
import 'TIMUIKitGroupProfileExample.dart';
|
||||
import 'TIMUIKitNewContactExample.dart';
|
||||
import 'TIMUIKitProfileExample.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 Cloud Chat UIKit'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
const MyHomePage({Key? key, required this.title}) : super(key: key);
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initTIMUIKIT();
|
||||
}
|
||||
|
||||
CoreServicesImpl timCoreInstance = TIMUIKitCore.getInstance();
|
||||
|
||||
int getSDKAPPID() {
|
||||
return const int.fromEnvironment('SDK_APPID', defaultValue: 0);
|
||||
}
|
||||
|
||||
String getUserID() {
|
||||
return const String.fromEnvironment('LOGINUSERID', defaultValue: "10045363");
|
||||
}
|
||||
|
||||
String getSecret() {
|
||||
return const String.fromEnvironment('SECRET', defaultValue: "");
|
||||
}
|
||||
|
||||
String getUsersig() {
|
||||
return const String.fromEnvironment('USERSIG', defaultValue: "");
|
||||
}
|
||||
|
||||
initTIMUIKIT() async {
|
||||
int sdkappid = getSDKAPPID();
|
||||
String userid = getUserID();
|
||||
String secret = getSecret();
|
||||
String usersig = getUsersig();
|
||||
if (sdkappid == 0 || userid == '' || secret == '' || usersig == '') {
|
||||
print("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 {
|
||||
print("no such ket");
|
||||
}
|
||||
}
|
||||
|
||||
List<String> apiConfig = [
|
||||
"TIMUIKitConversation",
|
||||
"TIMUIKitChat",
|
||||
"TIMUIKitProfile",
|
||||
"TIMUIKitAddFriend",
|
||||
"TIMUIKitAddGroup",
|
||||
"TIMUIKitBlackList",
|
||||
"TIMUIKitContact",
|
||||
"TIMUIKitGroup",
|
||||
"TIMUIKitGroupProfile",
|
||||
"TIMUIKitNewContact",
|
||||
"TIMUIKitSearch"
|
||||
];
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// This method is rerun every time setState is called, for instance as done
|
||||
// by the _incrementCounter method above.
|
||||
//
|
||||
// The Flutter framework has been optimized to make rerunning build methods
|
||||
// fast, so that you can just rebuild anything that needs updating rather
|
||||
// than having to individually change instances of widgets.
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
// Here we take the value from the MyHomePage object that was created by
|
||||
// the App.build method, and use it to set our appbar title.
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
child: Center(
|
||||
// Center is a layout widget. It takes a single child and positions it
|
||||
// in the middle of the parent.
|
||||
child: Column(
|
||||
children: apiConfig
|
||||
.map(
|
||||
(e) => Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
openExamplePage(e);
|
||||
},
|
||||
child: Text(e),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
flutter/ephemeral
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
# Project-level configuration.
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(runner LANGUAGES CXX)
|
||||
|
||||
# The name of the executable created for the application. Change this to change
|
||||
# the on-disk name of your application.
|
||||
set(BINARY_NAME "example")
|
||||
# The unique GTK application identifier for this application. See:
|
||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||
set(APPLICATION_ID "com.example.example")
|
||||
|
||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||
# versions of CMake.
|
||||
cmake_policy(SET CMP0063 NEW)
|
||||
|
||||
# Load bundled libraries from the lib/ directory relative to the binary.
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
|
||||
|
||||
# Root filesystem for cross-building.
|
||||
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
|
||||
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
endif()
|
||||
|
||||
# Define build configuration options.
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE "Debug" CACHE
|
||||
STRING "Flutter build mode" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||
"Debug" "Profile" "Release")
|
||||
endif()
|
||||
|
||||
# Compilation settings that should be applied to most targets.
|
||||
#
|
||||
# Be cautious about adding new options here, as plugins use this function by
|
||||
# default. In most cases, you should add new options to specific targets instead
|
||||
# of modifying this function.
|
||||
function(APPLY_STANDARD_SETTINGS TARGET)
|
||||
target_compile_features(${TARGET} PUBLIC cxx_std_14)
|
||||
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
|
||||
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
|
||||
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
|
||||
endfunction()
|
||||
|
||||
# Flutter library and tool build rules.
|
||||
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
|
||||
add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||
|
||||
# System-level dependencies.
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||
|
||||
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
|
||||
|
||||
# Define the application target. To change its name, change BINARY_NAME above,
|
||||
# not the value here, or `flutter run` will no longer work.
|
||||
#
|
||||
# Any new source files that you add to the application should be added here.
|
||||
add_executable(${BINARY_NAME}
|
||||
"main.cc"
|
||||
"my_application.cc"
|
||||
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
||||
)
|
||||
|
||||
# Apply the standard set of build settings. This can be removed for applications
|
||||
# that need different build settings.
|
||||
apply_standard_settings(${BINARY_NAME})
|
||||
|
||||
# Add dependency libraries. Add any application-specific dependencies here.
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
|
||||
|
||||
# Run the Flutter tool portions of the build. This must not be removed.
|
||||
add_dependencies(${BINARY_NAME} flutter_assemble)
|
||||
|
||||
# Only the install-generated bundle's copy of the executable will launch
|
||||
# correctly, since the resources must in the right relative locations. To avoid
|
||||
# people trying to run the unbundled copy, put it in a subdirectory instead of
|
||||
# the default top-level location.
|
||||
set_target_properties(${BINARY_NAME}
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
|
||||
)
|
||||
|
||||
# Generated plugin build rules, which manage building the plugins and adding
|
||||
# them to the application.
|
||||
include(flutter/generated_plugins.cmake)
|
||||
|
||||
|
||||
# === Installation ===
|
||||
# By default, "installing" just makes a relocatable bundle in the build
|
||||
# directory.
|
||||
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
||||
endif()
|
||||
|
||||
# Start with a clean build bundle directory every time.
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
|
||||
" COMPONENT Runtime)
|
||||
|
||||
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
||||
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
|
||||
|
||||
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
|
||||
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
|
||||
install(FILES "${bundled_library}"
|
||||
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endforeach(bundled_library)
|
||||
|
||||
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||
# from a previous install.
|
||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||
install(CODE "
|
||||
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||
" COMPONENT Runtime)
|
||||
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||
|
||||
# Install the AOT library on non-Debug builds only.
|
||||
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||
COMPONENT Runtime)
|
||||
endif()
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# This file controls Flutter-level build steps. It should not be edited.
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
|
||||
|
||||
# Configuration provided via flutter tool.
|
||||
include(${EPHEMERAL_DIR}/generated_config.cmake)
|
||||
|
||||
# TODO: Move the rest of this into files in ephemeral. See
|
||||
# https://github.com/flutter/flutter/issues/57146.
|
||||
|
||||
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
|
||||
# which isn't available in 3.10.
|
||||
function(list_prepend LIST_NAME PREFIX)
|
||||
set(NEW_LIST "")
|
||||
foreach(element ${${LIST_NAME}})
|
||||
list(APPEND NEW_LIST "${PREFIX}${element}")
|
||||
endforeach(element)
|
||||
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# === Flutter Library ===
|
||||
# System-level dependencies.
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
|
||||
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
|
||||
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
|
||||
|
||||
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
|
||||
|
||||
# Published to parent scope for install step.
|
||||
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
|
||||
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
|
||||
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
|
||||
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
|
||||
|
||||
list(APPEND FLUTTER_LIBRARY_HEADERS
|
||||
"fl_basic_message_channel.h"
|
||||
"fl_binary_codec.h"
|
||||
"fl_binary_messenger.h"
|
||||
"fl_dart_project.h"
|
||||
"fl_engine.h"
|
||||
"fl_json_message_codec.h"
|
||||
"fl_json_method_codec.h"
|
||||
"fl_message_codec.h"
|
||||
"fl_method_call.h"
|
||||
"fl_method_channel.h"
|
||||
"fl_method_codec.h"
|
||||
"fl_method_response.h"
|
||||
"fl_plugin_registrar.h"
|
||||
"fl_plugin_registry.h"
|
||||
"fl_standard_message_codec.h"
|
||||
"fl_standard_method_codec.h"
|
||||
"fl_string_codec.h"
|
||||
"fl_value.h"
|
||||
"fl_view.h"
|
||||
"flutter_linux.h"
|
||||
)
|
||||
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
|
||||
add_library(flutter INTERFACE)
|
||||
target_include_directories(flutter INTERFACE
|
||||
"${EPHEMERAL_DIR}"
|
||||
)
|
||||
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
|
||||
target_link_libraries(flutter INTERFACE
|
||||
PkgConfig::GTK
|
||||
PkgConfig::GLIB
|
||||
PkgConfig::GIO
|
||||
)
|
||||
add_dependencies(flutter flutter_assemble)
|
||||
|
||||
# === Flutter tool backend ===
|
||||
# _phony_ is a non-existent file to force this command to run every time,
|
||||
# since currently there's no way to get a full input/output list from the
|
||||
# flutter tool.
|
||||
add_custom_command(
|
||||
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/_phony_
|
||||
COMMAND ${CMAKE_COMMAND} -E env
|
||||
${FLUTTER_TOOL_ENVIRONMENT}
|
||||
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
|
||||
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(flutter_assemble DEPENDS
|
||||
"${FLUTTER_LIBRARY}"
|
||||
${FLUTTER_LIBRARY_HEADERS}
|
||||
)
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <desktop_drop/desktop_drop_plugin.h>
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <pasteboard/pasteboard_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) desktop_drop_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin");
|
||||
desktop_drop_plugin_register_with_registrar(desktop_drop_registrar);
|
||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) pasteboard_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin");
|
||||
pasteboard_plugin_register_with_registrar(pasteboard_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// Generated file. Do not edit.
|
||||
//
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||
#define GENERATED_PLUGIN_REGISTRANT_
|
||||
|
||||
#include <flutter_linux/flutter_linux.h>
|
||||
|
||||
// Registers Flutter plugins.
|
||||
void fl_register_plugins(FlPluginRegistry* registry);
|
||||
|
||||
#endif // GENERATED_PLUGIN_REGISTRANT_
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
desktop_drop
|
||||
file_selector_linux
|
||||
pasteboard
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
|
||||
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
||||
|
||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||
endforeach(ffi_plugin)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#include "my_application.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
g_autoptr(MyApplication) app = my_application_new();
|
||||
return g_application_run(G_APPLICATION(app), argc, argv);
|
||||
}
|
||||