I am building a Flutter Android app using stream_video_flutter and stream_video_push_notification.
I want incoming calls to behave similarly to WhatsApp:
App is in the background or completely closed.
Incoming Stream Video call arrives.
Android displays the incoming-call/full-screen notification.
User taps Answer.
The call should be accepted/joined immediately in the background.
The Flutter application UI should NOT open first.
The user should only see my Flutter CallScreen when I explicitly decide to show it.
Currently, when the user taps Answer, the Flutter app launches first and then my Dart code calls consumeAndAcceptActiveCall(). This causes the app UI to open before the call is handled.
My current setup is approximately:
FirebaseMessaging.onBackgroundMessage(
firebaseMessagingBackgroundHandler,
);
Future<void> setupStreamAfterLogin() async {
final auth = await _getStreamAuth();
if (auth == null) return;
final client = _createStreamClient(
auth,
background: false,
);
if (Platform.isAndroid) {
await StreamBackgroundService.init(client);
}
globalRingingEventsSub = client.observeCoreRingingEvents(
onCallAccepted: (call) {
appLaunchedForCall = true;
// I don't want to open CallScreen here.
debugPrint('CALL ACCEPTED: ${call.id}');
},
);
globalIncomingCallSub = client.state.incomingCall.listen((call) {
if (call != null) {
openCallScreen(call);
}
});
if (Platform.isAndroid) {
await StreamVideoPushNotificationManager
.ensureFullScreenIntentPermission();
await client.consumeAndAcceptActiveCall(
onCallAccepted: (call) {
appLaunchedForCall = true;
// I don't want this to launch the Flutter UI.
debugPrint('CALL ACCEPTED: ${call.id}');
},
);
}
}
My Stream client is created with:
stream.StreamVideo _createStreamClient(
StreamAuth auth, {
required bool background,
}) {
final user = stream.User.regular(
userId: auth.uid,
name: auth.name,
extraData: {
'email': auth.email,
},
);
final pushProvider =
StreamVideoPushNotificationManager.create(
androidPushProvider:
const StreamVideoPushProvider.firebase(
name: 'notifi_call',
),
iosPushProvider:
const StreamVideoPushProvider.apn(
name: 'notifi_call',
),
pushConfiguration:
const StreamVideoPushConfiguration(
android: AndroidPushConfiguration(
incomingCallNotification:
IncomingCallNotificationParams(
textAccept: 'Answer',
textDecline: 'Decline',
showCallHandle: false,
),
ringtonePath: 'ringtone_default',
showFullScreenOnLockScreen: true,
),
),
);
return stream.StreamVideo.create(
streamApiKey,
user: user,
userToken: auth.token,
options: stream.StreamVideoOptions(
keepConnectionsAliveWhenInBackground: true,
muteAudioWhenInBackground: false,
muteVideoWhenInBackground: false,
),
pushNotificationManagerProvider: pushProvider,
);
}
I also have an FCM background handler:
@pragma('vm:entry-point')
Future<void> firebaseMessagingBackgroundHandler(
RemoteMessage message,
) async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: firebaseOptions,
);
try {
final data = message.data;
final sender = data['sender']?.toString();
final type = data['type']?.toString();
if (sender != 'stream.video' || type != 'call.ring') {
return;
}
final auth = await _getStreamAuth();
if (auth == null) {
return;
}
final streamVideo = _createStreamClient(
auth,
background: true,
);
await streamVideo.connect();
final subscription =
streamVideo.observeCoreRingingEventsForBackground();
streamVideo.disposeAfterResolvingRinging(
disposingCallback: () async {
await subscription?.cancel();
},
);
await streamVideo.handleRingingFlowNotifications(data);
} catch (e, stackTrace) {
debugPrint('FCM BACKGROUND ERROR: $e');
debugPrint('$stackTrace');
}
}
I am not trying to manually create a custom VoIP implementation. I want to use the Stream Video Flutter SDK and its Android push-notification functionality.
My question is:
What is the correct Stream Video Flutter/Android configuration to make the Answer action accept/join the call while the app is backgrounded or killed, without first launching my Flutter UI?
Specifically:
Is consumeAndAcceptActiveCall() supposed to launch the Flutter activity?
Is there a Stream API/configuration that handles the Answer action entirely on the Android/native side?
Should observeCoreRingingEvents() be used differently for background calls?
Does stream_video_push_notification support this WhatsApp-style flow?
Are additional AndroidManifest.xml, MainActivity.kt, foreground-service, or notification configuration changes required?
What is the recommended architecture for opening the Flutter CallScreen only after the native Answer action has already accepted the call?
Versions:
environment:
sdk: ^3.12.2
dependencies:
stream_video_flutter: ^1.6.0
stream_video_push_notification: ^1.6.0
firebase_core: ^4.14.0
firebase_messaging: ^16.6.0
Android:
compileSdk = 36
targetSdk = 36
minSdk = 24
Kotlin = 2.3.20
Android Gradle Plugin = 9.0.1
Any guidance from someone familiar with the Stream Video Flutter push-notification/background-call implementation would be appreciated.