# In-App Picture in Picture

This cookbook shows how to implement an in-app picture-in-picture (PiP) mode that keeps a call floating within your app while users navigate to other screens. Unlike system-level PiP, this implementation keeps the call visible within your app's UI.

<Admonition type="note">

For system-level picture-in-picture that works across different apps, see our [Picture in Picture guide](https://getstream.io/video/docs/flutter/advanced/picture-in-picture/).

</Admonition>

## Overview

In-app picture-in-picture allows users to continue their video call in a small floating window while they navigate to other parts of your application. This is useful for maintaining call visibility during tasks like browsing content, reading messages, or accessing other features.

## Implementation

The implementation below demonstrates one way to add in-app PiP functionality using the Provider package. This is just a sample implementation - you can adapt it to use other state management solutions like Riverpod, Bloc, or even a simple InheritedWidget based on your app's architecture and needs.

### Step 1: Create the PiP Notifier

First, create a `ChangeNotifier` to manage the PiP state:

```dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stream_video_flutter/stream_video_flutter.dart';

class InAppPictureInPictureNotifier extends ChangeNotifier {
  Call? _call;
  Call? get call => _call;

  void show(Call call) {
    _call = call;
    notifyListeners();
  }

  void hide() {
    _call = null;
    notifyListeners();
  }
}
```

### Step 2: Create the PiP Scope Widget

Next, create a widget that provides the in-app PiP functionality:

```dart
class InAppPictureInPictureScope extends StatelessWidget {
  const InAppPictureInPictureScope({
    super.key,
    required this.child,
    this.pipViewWidth = 160,
    this.pipViewHeight = 200,
  });

  final Widget child;
  final double pipViewWidth;
  final double pipViewHeight;

  static void showPictureInPicture(BuildContext context, Call call) {
    return context.read<InAppPictureInPictureNotifier>().show(call);
  }

  static void hidePictureInPicture(BuildContext context) {
    return context.read<InAppPictureInPictureNotifier>().hide();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => InAppPictureInPictureNotifier(),
      child: FloatingViewContainer(
        floatingViewWidth: pipViewWidth,
        floatingViewHeight: pipViewHeight,
        floatingView: Consumer<InAppPictureInPictureNotifier>(
          builder: (context, callNotifier, _) {
            final call = callNotifier.call;

            if (callNotifier.call == null) {
              return const SizedBox.shrink();
            }

            return SizedBox(
              width: pipViewWidth,
              height: pipViewHeight,
              child: StreamBuilder<CallState>(
                  stream: call!.state.valueStream,
                  builder: (context, snapshot) {
                    final callState = snapshot.data;

                    if (callState == null || callState.status.isDisconnected) {
                      return const SizedBox.shrink();
                    }

                    return StreamCallParticipants(
                      call: call,
                      participants: callState.callParticipants,
                      layoutMode: ParticipantLayoutMode.pictureInPicture,
                    );
                  }),
            );
          },
        ),
        child: child,
      ),
    );
  }
}
```

### Step 3: Setup Your App

Wrap your app's main content with the `InAppPictureInPictureScope`:

```dart
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: InAppPictureInPictureScope(
        pipViewWidth: 160,
        pipViewHeight: 200,
        child: MainScreen(),
      ),
    );
  }
}
```

### Step 4: Control PiP from Your Call Screen

Use the static methods to show and hide the picture-in-picture mode:

```dart
class CallScreen extends StatefulWidget {
  final Call call;

  const CallScreen({super.key, required this.call});

  @override
  State<CallScreen> createState() => _CallScreenState();
}

class _CallScreenState extends State<CallScreen> {
  @override
  void initState() {
    super.initState();
    // Hide PiP when user returns to the call screen
    InAppPictureInPictureScope.hidePictureInPicture(context);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Video Call'),
        leading: IconButton(
          icon: Icon(Icons.arrow_back),
          onPressed: () {
            // Show PiP when navigating away from call screen
            InAppPictureInPictureScope.showPictureInPicture(context, widget.call);
            Navigator.pop(context);
          },
        ),
      ),
      body: StreamCallContainer(
        call: widget.call,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // End the call and navigate away
          widget.call.leave();
          Navigator.pop(context);
        },
        child: Icon(Icons.call_end),
        backgroundColor: Colors.red,
      ),
    );
  }
}
```


---

This page was last updated at 2026-08-13T00:26:31.397Z.

For the most recent version of this documentation, visit [https://getstream.io/video/docs/flutter/ui-cookbook/in-app-picture-in-picture/](https://getstream.io/video/docs/flutter/ui-cookbook/in-app-picture-in-picture/).