如何在Flutter应用中实现画中画(Picture-in-Picture)功能?
Flutter实现画中画(Picture-in-Picture)功能指南
嘿,刚好我之前做过类似的需求,完全可以实现像YouTube那样切换应用后悬浮播放视频的画中画效果!不过要注意,画中画是系统级特性,Android和iOS的配置和实现逻辑略有不同,咱们一步步来拆解:
一、先搞定平台端的基础配置
不管用插件还是原生集成,第一步都得先给对应平台开权限、做配置:
Android 端(Android 8.0+,API 26及以上)
- 打开
android/app/src/main/AndroidManifest.xml,给你的主Activity添加这两个属性:
<activity android:name=".MainActivity" android:supportsPictureInPicture="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
supportsPictureInPicture是告诉系统这个Activity支持画中画,configChanges是避免模式切换时Activity重启。
- 如果你的应用需要在后台保持播放,还要确保申请了
android.permission.FOREGROUND_SERVICE权限(可选,按需添加)。
iOS 端(iOS 14+)
- 打开
ios/Runner/Info.plist,添加画中画权限描述:
<key>NSPictureInPictureUsageDescription</key> <string>需要使用画中画功能悬浮播放视频</string>
这一步是必须的,不然系统会拒绝应用使用画中画。
二、Flutter 端实现方案
最省心的方式是用成熟的第三方插件,不用自己写太多原生代码,这里推荐两个常用的:
方案1:用 flutter_picture_in_picture 插件
这个插件封装了Android和iOS的画中画逻辑,用法很简单:
- 在
pubspec.yaml里添加依赖:
dependencies: flutter_picture_in_picture: ^latest_version
记得替换成最新版本号。
- 核心代码示例:
import 'package:flutter_picture_in_picture/flutter_picture_in_picture.dart'; import 'package:video_player/video_player.dart'; class VideoScreen extends StatefulWidget { @override _VideoScreenState createState() => _VideoScreenState(); } class _VideoScreenState extends State<VideoScreen> { late VideoPlayerController _controller; bool _isInPipMode = false; @override void initState() { super.initState(); _controller = VideoPlayerController.networkUrl(Uri.parse("你的视频地址")) ..initialize().then((_) { setState(() {}); }); // 监听画中画模式变化 FlutterPictureInPicture.pipStream.listen((event) { setState(() { _isInPipMode = event.isActive; }); }); } // 触发进入画中画 void _enterPip() async { if (await FlutterPictureInPicture.isPipAvailable) { await FlutterPictureInPicture.startPictureInPicture( id: "video_player", width: 300, height: 180, // 传递自定义参数给原生层,比如视频地址 data: {"url": "你的视频地址"}, ); } } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: _controller.value.isInitialized ? AspectRatio( aspectRatio: _controller.value.aspectRatio, child: VideoPlayer(_controller), ) : CircularProgressIndicator(), ), floatingActionButton: FloatingActionButton( onPressed: _isInPipMode ? null : _enterPip, child: Icon(_isInPipMode ? Icons.picture_in_picture : Icons.play_arrow), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } }
方案2:原生集成(适合定制需求高的场景)
如果插件满足不了你的定制化需求,比如要自定义画中画窗口的样式,那就需要自己通过MethodChannel调用原生代码:
- Android 端:在MainActivity里实现
enterPictureInPictureMode()方法,通过MethodChannel暴露给Flutter调用; - iOS 端:用
AVPictureInPictureController管理画中画,同样通过MethodChannel和Flutter通信。
三、关键注意事项
- 画中画模式下,Flutter的UI会暂停渲染,所以视频播放逻辑最好放在原生层(插件已经帮你处理了这点);
- Android上,画中画窗口的大小、位置是系统控制的,iOS可以有限度地调整;
- 测试时记得用真机,模拟器的画中画支持可能有问题。
这样配置和代码写完后,就能实现切换到其他应用时视频悬浮播放的效果啦!
内容的提问来源于stack exchange,提问作者Sam




