Flutter Picture in Picture View

Kürşat Fevzican Şayhan
5 min readJul 14, 2023

--

Picture in picture shrinks the video or pages into a look smaller that you can drag anywhere on your home screen or in your application.

Today, this feature can be used in many applications such as Video player applications. We will also look at both the pip for widgets within Apps and the pip for videos.

First, we start by adding our packages to pubspec.yaml file . The packages we will be using are pip_flutter and pip_view.

dependencies:
flutter:
sdk: flutter

cupertino_icons: ^1.0.2
pip_flutter: ^0.0.3
pip_view: ^0.9.7

Then, we enter the usesCleartextTraffic and supportsPictureInPicture permissions in the application > activity tab in the AndroidManifest.xml file.

android:supportsPictureInPicture="true"
android:usesCleartextTraffic="true"

Finally, we add multiDexEnabled true to android > defaultConfig in the app level build.gradle file and finish the settings.

Video Player — Picture in Picture

The package we will use here is the pip_flutter package, so we import this package into our code and then define the controller variable and the global key of the media player we will use.

import 'package:pip_flutter/pipflutter_player.dart';
import 'package:pip_flutter/pipflutter_player_configuration.dart';
import 'package:pip_flutter/pipflutter_player_controller.dart';
import 'package:pip_flutter/pipflutter_player_data_source.dart';
import 'package:pip_flutter/pipflutter_player_data_source_type.dart';
late PipFlutterPlayerController pipFlutterPlayerController;
final GlobalKey pipFlutterPlayerKey = GlobalKey();

Then we define the settings such as the size of the media player and the video source to be used in the init state.

@override
void initState() {
PipFlutterPlayerConfiguration pipFlutterPlayerConfiguration =
const PipFlutterPlayerConfiguration(
aspectRatio: 16 / 9,
fit: BoxFit.contain,
);
PipFlutterPlayerDataSource dataSource = PipFlutterPlayerDataSource(
PipFlutterPlayerDataSourceType.network,
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
);
pipFlutterPlayerController =
PipFlutterPlayerController(pipFlutterPlayerConfiguration);
pipFlutterPlayerController.setupDataSource(dataSource);
pipFlutterPlayerController
.setPipFlutterPlayerGlobalKey(pipFlutterPlayerKey);
super.initState();
}

After defining and setting our variables, we can now move on to our widgets.

The first variable we will use is the Media Player variable. We create our media player widget with the controller and global key we defined.

Flexible(

flex: 1,

fit: FlexFit.loose,

child: AspectRatio(

aspectRatio: 16 / 9,

child: PipFlutterPlayer(

controller: pipFlutterPlayerController,

key: pipFlutterPlayerKey,

),

),

),

Then we define the Elevated Buttons that we will activate and deactivate the Picture in Picture View.

Along with the controls we have defined;

pipFlutterPlayerController.enablePictureInPicture(pipFlutterPlayerKey);

Thanks to the command, Picture in Picture view will be enabled.

Same way

pipFlutterPlayerController.disablePictureInPicture();

It is also passive with the code.

Container(

margin: const EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(primary: Colors.deepOrangeAccent),
onPressed: () {pipFlutterPlayerController.enablePictureInPicture(pipFlutterPlayerKey);},
child: const Center(child: Text("Show PiP",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold),))),
ElevatedButton(
style: ElevatedButton.styleFrom(primary: Colors.deepOrangeAccent),
onPressed: () {pipFlutterPlayerController.disablePictureInPicture();},
child: const Center(child: Text("Disable PiP",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold),))),
],
),
),

Thus, we complete the Picture in Picture feature to be used with Media Players.

Full Code of Page :

class PictureInPicturePage extends StatefulWidget {
@override
State<PictureInPicturePage> createState() => _PictureInPicturePageState();
}
class _PictureInPicturePageState extends State<PictureInPicturePage> {

late PipFlutterPlayerController pipFlutterPlayerController;
final GlobalKey pipFlutterPlayerKey = GlobalKey();

@override
void initState() {
PipFlutterPlayerConfiguration pipFlutterPlayerConfiguration =
const PipFlutterPlayerConfiguration(
aspectRatio: 16 / 9,
fit: BoxFit.contain,
);
PipFlutterPlayerDataSource dataSource = PipFlutterPlayerDataSource(
PipFlutterPlayerDataSourceType.network,
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
);
pipFlutterPlayerController =
PipFlutterPlayerController(pipFlutterPlayerConfiguration);
pipFlutterPlayerController.setupDataSource(dataSource);
pipFlutterPlayerController
.setPipFlutterPlayerGlobalKey(pipFlutterPlayerKey);
super.initState();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepOrangeAccent,
title: Text("Flutter PiP View"),
),
body: Column(
children: [
const SizedBox(height: 20),
Flexible(
flex: 1,
fit: FlexFit.loose,
child: AspectRatio(
aspectRatio: 16 / 9,
child: PipFlutterPlayer(
controller: pipFlutterPlayerController,
key: pipFlutterPlayerKey,
),
),
),
Container(

margin: const EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(primary: Colors.deepOrangeAccent),
onPressed: () {pipFlutterPlayerController.enablePictureInPicture(pipFlutterPlayerKey);},
child: const Center(child: Text("Show PiP",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold),))),
ElevatedButton(
style: ElevatedButton.styleFrom(primary: Colors.deepOrangeAccent),
onPressed: () {pipFlutterPlayerController.disablePictureInPicture();},
child: const Center(child: Text("Disable PiP",style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold),))),
],
),
),
],
),
);
}
}
Video Player PiP View Gif

Widgets — Picture in Picture

We will use the pip_view library to put the widgets in picture in picture view. First, we start by importing the library.

import 'package:pip_view/pip_view.dart';

I thought of 3 pages as a scenario in our code. Our first page will be the page that will activate the picture in picture view, and the second page will be the page that will open after the first page switches to the picture in picture view. The third page will be the last page we will redirect from the second page.

Let’s start with our PiP page. The widget that we will use as the parent at the top of the page is the PIPView variable. Everything we take as a child inside the variable will appear in PIP view. There are floatingHeight and floatingWidth variables in the PIPView widget. Thanks to these variables, we can adjust the size of the widget in pip view.

PIPView.of(context)!.presentBelow(Screen_One());

Thanks to our code , the PIP view will be opened with the button we clicked at the bottom of the page . When the PIP view is opened with the widget we directed in presentBelow, the widget we defined will be opened.

PiP View Page Full Code :

class Pip_Example extends StatelessWidget {
const Pip_Example({super.key});
@override
Widget build(BuildContext context) {
return PIPView(
floatingHeight: 450,

builder: (context,isFloating){
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepOrangeAccent,
title: Text("Pip Example"),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset("assets/cyan.png",height: 750,width: 750,),
SizedBox(height: 20,),
ElevatedButton(
style: ElevatedButton.styleFrom(primary: Colors.deepOrangeAccent),
onPressed: (){PIPView.of(context)!.presentBelow(Screen_One());},
child: Text("Float"))
],
),
),
);
},
);
}
}

The full code of the first page we will redirect after the PiP view is opened :

class Screen_One extends StatelessWidget {
const Screen_One({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepOrangeAccent,
title: Text("Flutter PiP View - Screen One"),
centerTitle: false,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Secret PiP Page",style: TextStyle(fontSize: 20),),
SizedBox(height: 50,),
Image.asset("assets/yellow.png",height: 500,width: 500,),
ElevatedButton(
style: ElevatedButton.styleFrom(primary: Colors.deepOrangeAccent),
onPressed: (){Navigator.push(context, MaterialPageRoute(builder: (context)=>Screen_Two()));},
child: Text("Go To Screen Two !"))
],
),

),
);
}
}

And the other page’s full code :

class Screen_Two extends StatelessWidget {
const Screen_Two({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepOrangeAccent,
title: Text("Flutter PiP View - Screen Two"),
centerTitle: false,
),
body: Center(
child: Image.asset("assets/red.png",height: 500,width: 500,),

),
);
}
}
Widgets PiP View Gif

--

--

Kürşat Fevzican Şayhan
Kürşat Fevzican Şayhan

Written by Kürşat Fevzican Şayhan

Hi, I am developing mobile and desktop applications. I use Flutter/Dart language in mobile programming and .Net/C# language in desktop programming.

No responses yet