You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中为Chewie播放器添加跨平台画中画(PIP)视图?

Flutter实现跨平台画中画(PIP)+ Chewie播放器方案

不需要单独做原生开发,现有Flutter生态的包可以满足需求,结合你正在使用的Chewie播放器,就能快速实现双平台PIP功能及播放控制。

一、推荐依赖包

  • chewie: 你已在使用的视频播放器,提供播放/暂停/切换等控制能力
  • video_player: Chewie的底层依赖,负责原生视频渲染
  • flutter_pip: 简化跨平台PIP模式的开启与生命周期管理

二、双平台配置

Android端配置

  1. 在android/app/src/main/AndroidManifest.xml中,给主Activity添加PIP支持:
<activity
    android:name=".MainActivity"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
  1. 确保应用目标SDK版本≥26(PIP最低支持版本)

iOS端配置

  1. 在ios/Runner/Info.plist中添加PIP权限说明:
<key>NSPictureInPictureUsageDescription</key>
<string>需要使用画中画模式播放视频</string>
  1. 开启后台音频播放权限:Xcode中选择你的项目 → Targets → Signing & Capabilities → 添加Background Modes → 勾选Audio, AirPlay, and Picture in Picture

三、核心代码示例

以下是整合Chewie与PIP功能的完整示例,包含播放控制和PIP切换:

import 'package:flutter/material.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
import 'package:flutter_pip/flutter_pip.dart';

class PIPVideoPlayer extends StatefulWidget {
  const PIPVideoPlayer({super.key});

  @override
  State<PIPVideoPlayer> createState() => _PIPVideoPlayerState();
}

class _PIPVideoPlayerState extends State<PIPVideoPlayer> {
  late VideoPlayerController _videoController;
  late ChewieController _chewieController;
  final List<String> _videoUrls = [
    "https://example.com/video1.mp4",
    "https://example.com/video2.mp4",
    "https://example.com/video3.mp4",
  ];
  int _currentVideoIndex = 0;

  @override
  void initState() {
    super.initState();
    _initializeVideo(_currentVideoIndex);
  }

  void _initializeVideo(int index) async {
    _videoController = VideoPlayerController.networkUrl(Uri.parse(_videoUrls[index]))
      ..initialize().then((_) {
        setState(() {
          _chewieController = ChewieController(
            videoPlayerController: _videoController,
            autoPlay: true,
            looping: false,
            allowFullScreen: true,
            materialProgressColors: ChewieProgressColors(
              playedColor: Colors.blue,
              handleColor: Colors.blueAccent,
            ),
          );
        });
      });
  }

  void _switchVideo(int direction) {
    _currentVideoIndex += direction;
    if (_currentVideoIndex < 0) _currentVideoIndex = _videoUrls.length - 1;
    if (_currentVideoIndex >= _videoUrls.length) _currentVideoIndex = 0;
    
    _chewieController.dispose();
    _videoController.dispose();
    _initializeVideo(_currentVideoIndex);
  }

  void _enterPIPMode() async {
    if (await FlutterPip.isPipAvailable) {
      await FlutterPip.enterPipMode(
        aspectRatio: const Rational(16, 9),
        autoEnter: true,
      );
    }
  }

  @override
  void dispose() {
    _videoController.dispose();
    _chewieController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("PIP视频播放器")),
      body: Column(
        children: [
          if (_videoController.value.isInitialized)
            Expanded(
              child: Chewie(controller: _chewieController),
            ),
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                IconButton(
                  icon: const Icon(Icons.skip_previous),
                  onPressed: () => _switchVideo(-1),
                ),
                IconButton(
                  icon: Icon(
                    _chewieController.isPlaying ? Icons.pause : Icons.play_arrow,
                  ),
                  onPressed: () {
                    setState(() {
                      _chewieController.isPlaying
                          ? _chewieController.pause()
                          : _chewieController.play();
                    });
                  },
                ),
                IconButton(
                  icon: const Icon(Icons.skip_next),
                  onPressed: () => _switchVideo(1),
                ),
                const SizedBox(width: 20),
                ElevatedButton(
                  onPressed: _enterPIPMode,
                  child: const Text("开启画中画"),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

四、关键说明

  • 进入PIP模式后,Flutter的UI会暂停渲染,此时播放控制可以通过系统的PIP控件或监听FlutterPip的生命周期回调来处理(比如在PIP状态下切换视频)
  • 若需要自定义PIP内的控制逻辑,可以通过原生通道扩展,但上述方案已覆盖基础需求

内容的提问来源于stack exchange,提问作者appdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 12:00:50