如何在Flutter中为Chewie播放器添加跨平台画中画(PIP)视图?
Flutter实现跨平台画中画(PIP)+ Chewie播放器方案
不需要单独做原生开发,现有Flutter生态的包可以满足需求,结合你正在使用的Chewie播放器,就能快速实现双平台PIP功能及播放控制。
一、推荐依赖包
chewie: 你已在使用的视频播放器,提供播放/暂停/切换等控制能力video_player: Chewie的底层依赖,负责原生视频渲染flutter_pip: 简化跨平台PIP模式的开启与生命周期管理
二、双平台配置
Android端配置
- 在
android/app/src/main/AndroidManifest.xml中,给主Activity添加PIP支持:
<activity android:name=".MainActivity" android:supportsPictureInPicture="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
- 确保应用目标SDK版本≥26(PIP最低支持版本)
iOS端配置
- 在
ios/Runner/Info.plist中添加PIP权限说明:
<key>NSPictureInPictureUsageDescription</key> <string>需要使用画中画模式播放视频</string>
- 开启后台音频播放权限: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
相关产品推荐
相关产品推荐

