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).

StreamCallContainer(
    call: widget.call,
    pictureInPictureConfiguration: const PictureInPictureConfiguration(
        enablePictureInPicture: true,
        disablePictureInPictureWhenScreenSharing: true,
    ),
)

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)
  • 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?

  1. Check Permissions: Ensure supportsPictureInPicture="true" is set in AndroidManifest.xml
  2. Check Flutter Configuration: Ensure enablePictureInPicture: true in your Flutter code
  3. Check Android Version: PiP requires Android 7.0 (API level 24) or higher
  4. Check Device Support: Some devices/manufacturers may disable PiP

PiP Overlay Not Showing?

  1. Widget Tree: Ensure StreamCallContent or StreamPictureInPictureAndroidView is in your widget tree
  2. Call State: PiP only activates during active calls (connected state)
  3. 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:

Picture in Picture example

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.

© Getstream.io, Inc. All Rights Reserved.