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.
Environment requirements
|
| Flutter 2.2.0 or later for the Chat SDK; Flutter 2.10.0 or later for the TUIKit integration component library. |
| Android Studio 3.5 or later; devices with Android 4.1 or later for apps. |
| Xcode 11.0 or later. Ensure that your project has a valid developer signature. |
Tencent Cloud Chat SDK | |
Sample Code:
The source code of the sample app can be found at GitHub repo. 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.
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 project
setBinding(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 module
flutter_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' 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
.
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
.
|
| Both Chat and Call integrate into one FlutterEngine | | 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.
You can refer to the sample code from GitHub repo this module. 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.
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
This module is used for voice call and video call, provided by our calling plugin. 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.
Import Flutter Module
Please refer to this part, 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
.
@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.
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
Please refer to this part, 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
.
@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 : 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)
...
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
FlutterUtils.init()
}
}
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.
You can refer to the sample code from GitHub repo this module. 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.
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.
Import Flutter Module
Please refer to this part, 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.
import UIKit
import Flutter
import FlutterPluginRegistrant
@UIApplicationMain
class AppDelegate: FlutterAppDelegate {
lazy var flutterEngine = FlutterEngine(name: "tencent cloud chat")
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
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
Please refer to this part, 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
.
@SuppressLint("StaticFieldLeak")
object FlutterUtils {}
Create a FlutterEngine
.
Define a FlutterEngine
and corresponding MethodChannel
in FlutterUtils.kt
.
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 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
FlutterUtils.init()
}
}
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_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.
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 ?? "");
You can refer to the sample code from GitHub repo this module. 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.
Reference
Was this page helpful?