In App Calling
In-App Call SDK Setup Guide
Dependency Setup
Add the following dependency to your project:
in_app_call:
git:
url: [email protected]:v3/hubtel/Mobile-Apps/Platform-Library-Flutter-In-App-Call-SDK
ref: 1.0.0 # use the latest version tag
Permissions
Android Permissions
To support video calling, add these permissions to your AndroidManifest.xml
:
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
⚠️ For Android 13+, request the POST_NOTIFICATIONS permission at runtime.
iOS Permissions
To support video calling, add these permissions to your Info.plist
:
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your camera for video calls.</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your microphone for voice and video calls.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
<string>processing</string>
<string>remote-notification</string>
<string>voip</string>
</array>
Initializing the Call SDK
Before starting an audio call, initialize the InAppCalling
service with the local participant (caller) using InAppCall.init
.
Example usage:
Future<void> initializeInAppCalling() async {
final manager = Manager();
final userFuture = await manager.getUser();
final userName = userFuture.result?.user?.name ?? '';
final phoneNumber = userFuture.result?.user?.phoneNumber ?? '';
await InAppCalling.init(
participant: Participant(
phoneNumber: phoneNumber,
name: userName,
packageId: PackageId.sales,
), // create participant
);
}
Handling Incoming Call Notifications
iOS Setup
In your AppDelegate.swift
, import the necessary modules:
import UIKit
import Flutter
import stream_video_push_notification
Then, register the app for push notifications within your AppDelegate
class:
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// Register for push notifications
StreamVideoPKDelegateManager.shared.registerForPushNotifications()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
Handling Different App States
There are three possible states your app can be in when receiving an incoming call:
- Foreground
- Background
- Terminated
Foreground Handling:
When your app is in the foreground, handle notifications using InAppCalling.handleVoipPushNotification(message.data)
.
Ensure to pass it to Firebase.onMessage
:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
FirebaseMessaging.onMessage.listen(_foregroundMessageHandler);
//...
}
//...
Future<void> _foregroundMessageHandler(RemoteMessage message) async {
await InAppCalling.handlePushNotification(message.data);
}
Background Handling:
For background states, reinitialize the app in a separate isolate. The handling function should be top-level and marked with @pragma('vm:entry-point')
:
void main() async {
//...
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
//...
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// Initialize Firebase
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
try {
await initializeInAppCalling();
await InAppCalling.handlePushNotification(message.data);
} catch (e, stk) {
debugPrint('Error handling remote message: $e');
debugPrint(stk.toString());
}
InAppCalling.reset();
}
Terminated State Handling:
Handle calls in the terminated state by initializing the app and consuming the incoming call:
//Usually in the FutureBuilder of your AppStartUp
Future<AppInitializer> initializeApp(BuildContext context) async {
if (HubtelSalesApp.navigatorKey.currentContext == null) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await initializeInAppCalling();
InAppCalling.consumeIncomingCall();
});
}
...
}
Navigation to Call Screen
To open the call screen when a call starts, pass InAppCalling.navigatorKey
to your MaterialApp
:
class MyApp extends StatelessWidget {
MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
//...
navigatorKey: InAppCalling.navigatorKey,
);
}
}
Placing a Call
To place a call to another user, use the InAppCalling.call
method with the required parameters. It accepts the ID/phone number of the other user and the package ID of the app the user is on.
Example:
//...
await InAppCalling.call(
id: dialogTextController.text,
calleePackageId: PackageId.library,
);
CHAT SAMMIAT