StreamCallContainer(
call: widget.call,
pictureInPictureConfiguration: const PictureInPictureConfiguration(
enablePictureInPicture: true,
disablePictureInPictureWhenScreenSharing: true,
),
)
Picture in Picture (PiP)
Picture in picture (PIP) keeps the call running and visible while you navigate to other apps.
Enable Picture-in-Picture
To enable Picture-in-Picture (PiP), set the enablePictureInPicture
property to true in the PictureInPictureConfiguration
provided to the StreamCallContainer
or StreamCallContent
widget.
Additionally, you can control whether PiP remains enabled when the local device is screen sharing using the disablePictureInPictureWhenScreenSharing
parameter (disabled by default).
Keep the Connection Active in Background
For Picture-in-Picture to function properly while the app is in the background, it is important to keep the connection to Stream backend active.
This is controlled by the keepConnectionsAliveWhenInBackground
property in StreamVideoOptions
, which must be set to true
.
Additionally, to ensure the local participant remains visible and audible in PiP mode, ensure muteVideoWhenInBackground
and muteAudioWhenInBackground
are set to false (false by default).
StreamVideo(
apiKey,
user: user,
token: token,
options: const StreamVideoOptions(
muteAudioWhenInBackground: false,
muteVideoWhenInBackground: false,
keepConnectionsAliveWhenInBackground: true,
),
);
Android
Quick Setup with StreamFlutterActivity
The easiest way to add PiP support is to extend StreamFlutterActivity
instead of FlutterActivity
:
// MainActivity.kt
import io.getstream.video.flutter.stream_video_flutter.StreamFlutterActivity
class MainActivity : StreamFlutterActivity() {
// All PiP functionality is automatically handled!
// Add your custom logic here if needed
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
// Your custom Flutter engine configuration
}
}
What StreamFlutterActivity Does
StreamFlutterActivity
automatically handles:
- PiP Initialization: Sets up the PictureInPictureHelper with your Flutter engine
- Automatic Triggers: Enters PiP mode when:
- User presses the home button (
onUserLeaveHint
) - App is backgrounded during an active call (
onPause
)
- User presses the home button (
- Mode Change Notifications: Notifies Flutter when PiP mode changes
- Optimal Aspect Ratios: Automatically sets appropriate aspect ratios
- Android Version Compatibility: Works across different Android versions
Advanced: Manual Setup
If you prefer to set up PiP manually or need more control:
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
// Initialize PiP helper
PictureInPictureHelper.initializeWithFlutterEngine(flutterEngine) { this }
}
override fun onUserLeaveHint() {
super.onUserLeaveHint()
PictureInPictureHelper.handlePipTrigger(this)
}
override fun onPause() {
super.onPause()
if (!isFinishing) {
PictureInPictureHelper.handlePipTrigger(this)
}
}
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
newConfig: Configuration
) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
PictureInPictureHelper.notifyPictureInPictureModeChanged(this, isInPictureInPictureMode)
}
}
Configuration Options
PictureInPictureConfiguration
PictureInPictureConfiguration(
enablePictureInPicture: true,
disablePictureInPictureWhenScreenSharing: true,
sort: CallParticipantSortingPresets.speaker, // Custom participant sorting
androidPiPConfiguration: AndroidPictureInPictureConfiguration(
callPictureInPictureWidgetBuilder: (context, call) {
// Custom PiP overlay widget
return YourCustomPiPWidget(call: call);
},
),
)
Android Permissions
Enable PiP for your activity and update the configChanges in android/app/src/main/AndroidManifest.xml
:
<activity
android:name=".MainActivity"
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
... >
Troubleshooting
PiP Not Working?
- Check Permissions: Ensure
supportsPictureInPicture="true"
is set in AndroidManifest.xml - Check Flutter Configuration: Ensure
enablePictureInPicture: true
in your Flutter code - Check Android Version: PiP requires Android 7.0 (API level 24) or higher
- Check Device Support: Some devices/manufacturers may disable PiP
PiP Overlay Not Showing?
- Widget Tree: Ensure
StreamCallContent
orStreamPictureInPictureAndroidView
is in your widget tree - Call State: PiP only activates during active calls (connected state)
- Screen Sharing: PiP is disabled during screen sharing by default
Migration from Old API
If you’re migrating from the old PiP API:
❌ Remove These (Deprecated)
// Remove manual PiP calls
await StreamVideoFlutterBackground.setPictureInPictureEnabled(enable: true);
// Remove manual PiP triggers
override fun onUserLeaveHint() {
super.onUserLeaveHint()
PictureInPictureHelper.enterPictureInPictureIfInCall(this) // Deprecated
}
✅ Use This Instead
// Simply extend StreamFlutterActivity
class MainActivity : StreamFlutterActivity()
// Configure in Flutter
StreamCallContent(
call: call,
pictureInPictureConfiguration: PictureInPictureConfiguration(
enablePictureInPicture: true,
),
)
iOS
Local camera feed in Picture-in-Picture mode
By default, iOS does not allow access to the user’s camera while the app is in the background. To enable it, the multitasking camera access property must be set to true.
For apps linked against iOS 18 or later, this property is automatically true if voip is included in UIBackgroundModes
. Additionally, apps with the com.apple.developer.avfoundation.multitasking-camera-access
entitlement will also have multitasking camera access enabled.
If the multitasking camera access property is true for your app based on the above conditions, the local camera feed will be visible in PiP mode. However, if you prefer to disable the local feed in PiP mode, set includeLocalParticipantVideo
to false:
StreamCallContainer(
call: widget.call,
callContentWidgetBuilder: (
BuildContext context,
Call call,
) {
return StreamCallContent(
call: call,
callState: callState,
pictureInPictureConfiguration: const PictureInPictureConfiguration(
enablePictureInPicture: true,
iOSPiPConfiguration: IOSPictureInPictureConfiguration(
includeLocalParticipantVideo: false,
)
),
);
},
);
Enabling PiP support with custom call content widget
If you are not using our StreamCallContent
and instead building custom call content widget you can still enable Picture in Picture mode by adding StreamPictureInPictureUiKitView
anywhere in the widget tree. This widget will handle the Picture in Picture mode in iOS for you.
StreamCallContainer(
call: widget.call,
callContentWidgetBuilder: (
BuildContext context,
Call call,
) {
return Stack(
children: [
StreamPictureInPictureUiKitView(call: call),
// YOUR CUSTOM WIDGET
],
);
},
);
Done. Now after leaving the app, you’ll see that the call will be still alive in the background like the one below:
In-App Picture in Picture
The system-level Picture in Picture described above only works when users navigate away from your app to other applications. It does not provide a floating video view within your own app when users navigate between different screens.
If you want to keep the call visible while users navigate within your app (for example, when browsing content, accessing chat, or using other features), you’ll need to implement in-app picture-in-picture.
For a complete implementation guide with code examples, see our In-App Picture in Picture cookbook.