-
Notifications
You must be signed in to change notification settings - Fork 3.3k
[video_player] #60048 ios picture in picture #3500
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 12 commits
595d3df
4831f57
976b630
a7d5dd5
876dfe8
a4ca9e7
49f769a
5477f0b
0764e27
65fdf65
d4d9ff9
ff6d02f
ae7500a
4eeb6e1
9110d0f
211c6b3
5a8735b
4583229
70284aa
7289998
ee96f77
da13c09
4d83a52
ac920fe
8394840
19b1440
1963d27
f71a27a
b3e05a0
1325254
72ba79a
8c67ab8
f935a16
42a7815
fa93319
b0103d0
236ab51
1cf961d
5c18256
ddc7139
45494b9
0597f32
5c1f759
e94ccaa
9dc85a0
2b72605
601c506
4574fa9
1135695
cd7fede
e9aba08
30c0b7a
c0ccbfc
8ee1dcb
58db59a
16ae8cb
a15c3b7
87b92c2
5183028
002faa0
148cd55
d88cf33
45f96f5
9459bd5
d9f883b
c9993dc
3e401c1
b250e9c
01aaf8d
ee6db47
2e3196b
29c1d3b
6d8fb1e
114f5b8
5861e1d
114f9ac
3c1b4e6
3985a77
1649be4
bc56139
e1348c8
7e70da0
4abe6f1
2006494
58dabf4
881e80e
917bdd4
fba55da
b0f42d8
e0231d8
c58b722
376bcf5
5dd6e13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -135,4 +135,29 @@ and so on. | |
|
||
To learn about playback speed limitations, see the [`setPlaybackSpeed` method documentation](https://pub.dev/documentation/video_player/latest/video_player/VideoPlayerController/setPlaybackSpeed.html). | ||
|
||
### Picture-in-Picture | ||
|
||
#### iOS | ||
On iOS the picture-in-picture is linked to the AVPlayerController. | ||
If you want to enable picture-in-picture make sure to enable the `audio` capability (in Xcode's UI it will say **Audio, AirPlay, and Picture in Picture**). | ||
Not setting this capability but calling `setPictureInPictureOverlayRectMessage` and `setPictureInPicture` will not start the picture-in-picture. | ||
|
||
```xml | ||
stuartmorgan-g marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<key>UIBackgroundModes</key> | ||
<array> | ||
<string>audio</string> | ||
</array> | ||
``` | ||
|
||
Example: | ||
 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. READMEs should never have references to non-pinned URLs, as it means old versions' READMEs can change or break at any time. E.g., renaming the plugin in the future would break all previous READMEs. This will need to be added in a follow-up PR, using a hash instead of |
||
|
||
#### Android | ||
|
||
On Android there is no link to the video player. Your complete app will be minimized ([picture-in-picture Android documentation](https://developer.android.com/guide/topics/ui/picture-in-picture)) | ||
vanlooverenkoen marked this conversation as resolved.
Show resolved
Hide resolved
vanlooverenkoen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
You have multiple options on Android: | ||
- [simple_pip_mode](https://pub.dev/packages/simple_pip_mode) | ||
vanlooverenkoen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- Create your own plugin that follows the android documentation | ||
|
||
Furthermore, see the example app for an example playback speed implementation. | ||
vanlooverenkoen marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file should be listed in a |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -207,6 +207,11 @@ class _BumbleBeeRemoteVideo extends StatefulWidget { | |
class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { | ||
late VideoPlayerController _controller; | ||
|
||
final GlobalKey<State<StatefulWidget>> _playerKey = | ||
GlobalKey<State<StatefulWidget>>(); | ||
final Key _pictureInPictureKey = UniqueKey(); | ||
bool _enableStartPictureInPictureAutomaticallyFromInline = false; | ||
|
||
Future<ClosedCaptionFile> _loadCaptions() async { | ||
final String fileContents = await DefaultAssetBundle.of(context) | ||
.loadString('assets/bumble_bee_captions.vtt'); | ||
|
@@ -243,17 +248,95 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { | |
children: <Widget>[ | ||
Container(padding: const EdgeInsets.only(top: 20.0)), | ||
const Text('With remote mp4'), | ||
FutureBuilder<bool>( | ||
key: _pictureInPictureKey, | ||
future: _controller.isPictureInPictureSupported(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't most of the UI need to be conditional on this? It looks like the button below to trigger PiP is unconditionally enabled and will call a method that isn't implemented on most platforms, for instance. |
||
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) => | ||
Text(snapshot.data ?? false | ||
? 'Picture-in-picture is supported' | ||
: 'Picture-in-picture is not supported'), | ||
), | ||
Row( | ||
children: <Widget>[ | ||
const SizedBox(width: 16), | ||
const Expanded( | ||
child: Text( | ||
'Start picture-in-picture automatically when going to background'), | ||
), | ||
Switch( | ||
value: _enableStartPictureInPictureAutomaticallyFromInline, | ||
onChanged: (bool newValue) { | ||
setState(() { | ||
_enableStartPictureInPictureAutomaticallyFromInline = | ||
newValue; | ||
}); | ||
_controller.setAutomaticallyStartsPictureInPicture( | ||
enableStartPictureInPictureAutomaticallyFromInline: | ||
_enableStartPictureInPictureAutomaticallyFromInline); | ||
}, | ||
), | ||
const SizedBox(width: 16), | ||
], | ||
), | ||
MaterialButton( | ||
color: Colors.blue, | ||
onPressed: () { | ||
final RenderBox? box = | ||
_playerKey.currentContext?.findRenderObject() as RenderBox?; | ||
if (box == null) { | ||
return; | ||
} | ||
final Offset offset = box.localToGlobal(Offset.zero); | ||
_controller.setPictureInPictureOverlayRect( | ||
rect: Rect.fromLTWH( | ||
offset.dx, | ||
offset.dy, | ||
box.size.width, | ||
box.size.height, | ||
), | ||
); | ||
}, | ||
child: const Text('Set picture-in-picture overlay rect'), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this something that clients have to call manually? Shouldn't the controller be determining this information from the widget as needed? |
||
), | ||
MaterialButton( | ||
color: Colors.blue, | ||
onPressed: () { | ||
if (_controller.value.isPictureInPictureActive) { | ||
_controller.stopPictureInPicture(); | ||
} else { | ||
_controller.startPictureInPicture(); | ||
} | ||
}, | ||
child: Text(_controller.value.isPictureInPictureActive | ||
? 'Stop picture-in-picture' | ||
: 'Start picture-in-picture'), | ||
), | ||
Container( | ||
padding: const EdgeInsets.all(20), | ||
child: AspectRatio( | ||
aspectRatio: _controller.value.aspectRatio, | ||
child: Stack( | ||
key: _playerKey, | ||
alignment: Alignment.bottomCenter, | ||
children: <Widget>[ | ||
VideoPlayer(_controller), | ||
ClosedCaption(text: _controller.value.caption.text), | ||
_ControlsOverlay(controller: _controller), | ||
VideoProgressIndicator(_controller, allowScrubbing: true), | ||
if (_controller.value.isPictureInPictureActive) ...<Widget>[ | ||
Container(color: Colors.white), | ||
// TODO(goderbauer): Make this const when this package requires Flutter 3.8 or later. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is already the case. |
||
// ignore: prefer_const_constructors | ||
Column( | ||
mainAxisAlignment: MainAxisAlignment.center, | ||
children: const <Widget>[ | ||
Icon(Icons.picture_in_picture), | ||
SizedBox(height: 8), | ||
Text('This video is playing in picture-in-picture.'), | ||
], | ||
), | ||
] else ...<Widget>[ | ||
VideoProgressIndicator(_controller, allowScrubbing: true), | ||
_ControlsOverlay(controller: _controller), | ||
], | ||
], | ||
), | ||
), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
## 2.5.0 | ||
|
||
* Adds support for picture-in-picture on iOS. | ||
|
||
## 2.4.4 | ||
|
||
* Updates pigeon to fix warnings with clang 15. | ||
|
Uh oh!
There was an error while loading. Please reload this page.