Add Flutter to your existing app
Adding in-app chat and call modules to an Android/iOS app can be necessary as a business grows. For example, chat modules can be added to video, e-commerce, or entertainment apps to facilitate communication between users.
However, adding these modules can be time-consuming and lead to an inconsistent user experience. One solution is to integrate Tencent Cloud Chat with Flutter, which allows for coding once and deploying to all platforms.
If it's not practical to rewrite the entire application in Flutter, Flutter can be integrated as a library or module. This module can then be imported into the existing Android or iOS app to render a part of the app's UI in Flutter.
This can greatly reduce your workload and allow you to quickly integrate chat and call features of the Chat SDK into your native apps.
Note:
To respect the copyright of emoji designs, the Chat Demo/TUIKit project does not include cutouts of large emoji elements. Please replace them with your own designs or other emoji packs for which you hold the copyright before officially launching for commercial use. The default smiley face emoji pack shown below is copyrighted by Tencent RTC and is available for licensed use for a fee. If you need to obtain a license, please contact us.
Environment requirements
Platform | Version |
Flutter | Flutter 2.2.0 or later for the Chat 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 SDK |
Sample Code:
What you need to know first
Before starting, we recommend getting to know the Tencent Cloud Chat SDK for Flutter, TUIKit, and the principles of hybrid app development with Flutter.
Tencent Cloud Chat
Overall
Before starting, you should be familiar with Tencent Cloud Chat SDK for Flutter and how it is used.
Chat offers two SDKs. One is an SDK with no UI components included. The other is UIKit, which includes UI components.
To demonstrate hybrid app development, this tutorial is written for development using TUIKit.
Modules of Tencent Cloud Chat
Tencent Cloud Chat includes two main modules: Chat and Call.
The Chat module enables you to send and receive messages, management user relationships, etc.
The Call module enables you to send and receive audio and video calls, including one-to-one call and group calls.
Adding Flutter to Native Apps
The basic principle behind hybrid app development with Flutter is to embed a Flutter module into your existing native app as a
subproject
. Because of the cross-platform nature of Flutter, you only need to develop the Flutter module once, and it can be added to both Android and iOS projects.To launch a Flutter screen from an existing iOS/Android, you start a FlutterEngine and a FlutterViewController/FlutterActivity.
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 can be used to communicate between the native app and the Flutter module, for example when passing current user information, transmitting audio and video call data, or triggering offline push notifications. To trigger the method of the other end, use
invokeMethod
and to listen for the method callback from the other end using the pre-mounted MethodCallHandler
.Adding a Flutter Module to an Android App
This method involves adding the Flutter module as a dependency of your existing app in Gradle. There are two ways to achieve this. The first is using the AAR mechanism to create 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 other is using the source code subproject mechanism, which 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 release version of your app.
Steps:
Run the following command on your Flutter module.
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
:// Include the host app project.include ':app' // assumed existing content// Add the following lines to your projectsetBinding(new Binding([gradle: this]))evaluate(new File(settingsDir.parentFile,'tencent_chat_module/.android/include_flutter.groovy'))
Introduce an
implementation
dependency on the Flutter module from your app:dependencies {implementation project(':flutter')}
Your app now includes the Flutter module as a dependency.
Adding a Flutter Module to an iOS App
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.
Note:
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.
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 for development and debugging.
Steps:
Add the following lines to your
Podfile
:// The path of your Flutter moduleflutter_chat_application_path = '../tencent_chat_module'load File.join(flutter_chat_application_path, '.ios', 'Flutter', 'podhelper.rb')
For each Podfile target that needs to embed Flutter, call
install_all_flutter_pods(flutter_application_path)
.target 'MyApp' doinstall_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',]endendend
Run
pod install
.Note:
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/
.flutter build ios-framework --output=some/path/MyApp/Flutter/
Embed and link the generated frameworks into your existing application in Xcode.
Adding Flutter Engines
It's recommended to add the Flutter module to your existing application.
You need to add the Chat and Call modules to your native app using Flutter engines. For details, see here.
There are two options for creating and managing Flutter engines: create a single
FlutterEngine
or create two FlutterEngines
with FlutterEngineGroup
.Mode | Introduction | Pros | Cons | Sample Code |
Both Chat and Call integrate into one FlutterEngine | Convenient | Since the Call module needs to automatically display the incoming call page when a call is received, it needs to be forced to redirect to the Flutter page, resulting in a poorer experience. | ||
The Chat and Call modules are located in two separate Flutter engines | The Call module exists independently in a Flutter engine, with independent page control. When a call comes in, the Flutter page is shown, which does not affect the page where the user is currently located, providing a better experience. | Minimize for the calling page is not allowed. |
In addition to the methods above, you can also integrate a native Chat SDK and Flutter SDK. For details, see here, and sample code can be found from GitHub.
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.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.Developing the Flutter Module
To embed Flutter into your existing application, you must 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, you can add code within
lib/
.The structure of lib/
Note:
The following structure and code are for demonstration purposes only. You can modify them to meet your actual needs.
Within
lib/
, you need to create three directories: call, chat, and common. These directories are used for the Call module, Chat module, and some common classes, respectively.tencent_chat_module/├── lib/│ └── call/│ └── chat/│ └── common/
Common model classes
Create a new file named
common/common_model.dart
, as shown below. Create two classes in this file to define the communication standard between Flutter and the native application.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 code 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.
It's the core of the Chat module.
For detailed implementation, refer to the source code of the sample app, paying attention to the following three functions:
Future_handleMessage(MethodCall call): Listens for events from native app, including login and notification click events.
Future handleClickNotification(Map< String, dynamic> msg): The function invoked by the callback after clicking the notification.
Future 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.
Note:
Please configure the offline push before uploading the token and use this capability, see Integrating Offline Push.
2. Create a file,
chat_main.dart
. This is also used as the home page 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.
Detailed implementation can refer to the sample code from GitHub repo.
2.1 Create a file,
push.dart
, used for maintaining the offline push plugin. Detailed implementation can refer to the sample code from GitHub repo.2.2 Create a file,
conversation.dart
, used for implementing conversation list widget TIMUIKitConversation
. Detailed implementation can refer to the sample code from GitHub repo.2.3 Create a file,
user_profile.dart
, used to implement the user profile widget TIMUIKitProfile
. Detailed implementation can refer to the sample code from GitHub repo.2.4 Create a file,
group_profile.dart
, used to implement group profile widget TIMUIKitGroupProfile
. Detailed implementation can refer to the sample code from GitHub repo.2.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 sample code from GitHub repo.At this point, the 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
The key function of this module is to automatically show the call page by calling the native method when a new call invitation is received, as well as to accept the call request forwarded by the Chat module and actively initiate the call.
The following files and code are located in the
lib/call
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, 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 sample app, while it's recommended to focus on these two functions:_onRtcListener = TUICallingListener(...): The listener of the calling events, notify native to show this page, when receiving a new call.
Future_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 launching the call page should be added here.
Detailed implementation can refer to the sample code from GitHub repo.Configure the entry point for each module
After completing the three parts above, you can configure the entry point for each module, which serve as the entry for the Flutter engine.
1. Default entry
Open
lib/main.dart
, modify the default main functions to return an empty MaterialApp
.This method serves as the default entry point for the Flutter module. Under the management of FlutterEngineGroup in a multi-engine Flutter engine scenario, if no child Flutter engine sets any entry point, this method will not be used.
For example, in this tutorial, this default main() method is not used.
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')
to mark a method as an entry point. The method named chatMain
is the name of the entry.In the native code, this name is also used to create the corresponding
FlutterEngine
.Use the global
ChangeNotifierProvider
for state management to maintain ChatInfoModel
data and business logic.@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.@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(),),);}
At this point, the Dart code for the Flutter Module has been completed.
Now, let's take a look at the native integration for your existing app.
iOS Native development
In this section,
Swift
is used as an example, but Objective-C
is also available.Note:
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 to add a Podfile to your project.Import Flutter Module
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
.@UIApplicationMainclass 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.In the sample code, a new navigator is used for the Chat ViewController. The Call ViewController is maintained dynamically with present and dismiss.
Create a new file named FlutterUtils.swift. For detailed code, refer to the sample 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.
Listen for and forward the notification click event
The initialization/token reporting/click event corresponding to the offline push notification is handled in the Flutter Chat module. Therefore, on the native side, only the ext of the click notification event needs to be passed through.
The reason we need to do this is because the clicking event has been consumed on the native side, so it is impossible for the Flutter Push plug-in to receive this event.
Add the following code to
AppDelegate.swift
.
At this point, the implementation for iOS is complete.
Android Native Development
Here, Kotlin is used as an example, but Java is also available.
Note:
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
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
.@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
.lateinit var context : Contextlateinit var flutterEngines: FlutterEngineGroupprivate lateinit var chatFlutterEngine:FlutterEngineprivate lateinit var callFlutterEngine:FlutterEnginelateinit var chatMethodChannel: MethodChannellateinit var callMethodChannel: MethodChannel// Initialize themflutterEngines = FlutterEngineGroup(context)...
Further developed for this singleton static object
The basic implementation logic of the sample app 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.
func reportChatInfo(): Reports the current user login info and SDKAppID to the Flutter module to initialize and log in to the 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 sample app 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
.class MyApplication : MultiDexApplication() {override fun onCreate() {super.onCreate()FlutterUtils.context = this // newFlutterUtils.init() // new}}
Listen for and forward 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.
Note:
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.
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 sample app, com.tencent.chat.android.MainActivity
.
Add the following code to the Activity you set in the console in the previous step.
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 source code of the sample app for this capability.
Now, we finished the implementation for Android.
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.
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
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.Note:
Please configure the offline push before uploading the token and use this capability, referring to this documentation.
Tips for Call plug-in:
When a new call invitation is received, call the native method to check if the user is currently on the Flutter page. If not, force the page to redirect to this module to display the incoming call page.
Tips for Push plug-in:
The callback event of notification clicking is passed from the native layer and used to navigate to the 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.
In addition, the current status of the application needs to be reported to the Tencent Cloud Chat backend upon each foreground/background switch from here. For details, refer to this document.
Detailed implementation can refer to the sample code from GitHub repo.
Other widgets from TUIKit
1. Create a file,
push.dart
, used for maintaining the offline push plugin. Detailed implementation can refer to the sample code from GitHub repo.2. Create a file,
conversation.dart
, used to implement group profile widget TIMUIKitGroupProfile
. Detailed implementation can refer to the sample code from GitHub repo.3. Create a file,
user_profile.dart
, used to implement the user profile widget TIMUIKitProfile
. Detailed implementation can refer to the sample code from GitHub repo.4. Create a file,
group_profile.dart
, used to implement group profile widget TIMUIKitGroupProfile
. Detailed implementation can refer to the sample code from GitHub repo.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 sample code from GitHub repo.At this point, the Flutter module has been developed.
iOS Native development
Here, we take
Swift
as an example, while Objective-C
is also available.Note:
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 to add a Podfile to your project.Import Flutter Module
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.import UIKitimport Flutterimport FlutterPluginRegistrant@UIApplicationMainclass 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 entry point 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 sample app 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 sample app source code.Mainly focus on:
private override init(): Initializes each Flutter instance, registers method channel, and listens for 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.
Listen for and forward 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
.
At this point, the implementation for iOS is complete.
Android Native Development
Here, we take
Kotlin
as an example, while Java
is also available.Note:
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
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
.@SuppressLint("StaticFieldLeak")object FlutterUtils {}
Create a
FlutterEngine
.Define a
FlutterEngine
and corresponding MethodChannel
in FlutterUtils.kt
.lateinit var context : Contextprivate lateinit var flutterEngine:FlutterEngineflutterEngine = FlutterEngine(context)
Further developed for this singleton static object
The basic implementation logic of the sample app is that, using a new navigator for the Activity for both Chat and Chat.
Mainly focus on:
fun init(): Initialize each Flutter instance, register method channel events.
func reportChatInfo(): Reports the current user login info and SDKAppID to the Flutter module to initialize and log in to the 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 sample code from GitHub repo.
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
.class MyApplication : MultiDexApplication() {override fun onCreate() {super.onCreate()FlutterUtils.context = this // newFlutterUtils.init() // new}}
Listen for and forward 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.
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
.
Add the following code to the Activity you set in the console in the previous step.
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.
At this point, the implementation for Android is complete.
Additional solution: Initialize Tencent Cloud Chat from Native SDK
Sometimes, you may prefer to integrate a chat module to your existing UI without a complex chat page.
For example, during a game, you want to let players 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 can initialize and login using the native SDK and build in-app chat features.
Note:
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
iOS Swift is used as an example to demonstrate how to initialize and log in with the native SDK.
import ImSDK_Plusfunc 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 = trueself.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.
Initialize Flutter TUIKit
After initialization and login from the native SDK, the user info should be provided to Flutter TUIKit by invoking _coreInstance.setDataFromNative().
final CoreServicesImpl _coreInstance = TIMUIKitCore.getInstance();_coreInstance.setDataFromNative(userId: chatInfo?.userID ?? "");
This completes the tutorial for using hybrid development with Flutter to integrate Tencent Cloud Chat to your existing app.
If you have any questions, feel free to contact us.