You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Flutter应用中实现画中画(Picture-in-Picture)功能?

Flutter实现画中画(Picture-in-Picture)功能指南

嘿,刚好我之前做过类似的需求,完全可以实现像YouTube那样切换应用后悬浮播放视频的画中画效果!不过要注意,画中画是系统级特性,Android和iOS的配置和实现逻辑略有不同,咱们一步步来拆解:

一、先搞定平台端的基础配置

不管用插件还是原生集成,第一步都得先给对应平台开权限、做配置:

Android 端(Android 8.0+,API 26及以上)

  1. 打开android/app/src/main/AndroidManifest.xml,给你的主Activity添加这两个属性:
<activity
    android:name=".MainActivity"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">

supportsPictureInPicture是告诉系统这个Activity支持画中画,configChanges是避免模式切换时Activity重启。

  1. 如果你的应用需要在后台保持播放,还要确保申请了android.permission.FOREGROUND_SERVICE权限(可选,按需添加)。

iOS 端(iOS 14+)

  1. 打开ios/Runner/Info.plist,添加画中画权限描述:
<key>NSPictureInPictureUsageDescription</key>
<string>需要使用画中画功能悬浮播放视频</string>

这一步是必须的,不然系统会拒绝应用使用画中画。

二、Flutter 端实现方案

最省心的方式是用成熟的第三方插件,不用自己写太多原生代码,这里推荐两个常用的:

方案1:用 flutter_picture_in_picture 插件

这个插件封装了Android和iOS的画中画逻辑,用法很简单:

  1. pubspec.yaml里添加依赖:
dependencies:
  flutter_picture_in_picture: ^latest_version

记得替换成最新版本号。

  1. 核心代码示例:
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

火山引擎 最新活动