Flutter中PlatformView视频播放器尺寸变更后停止播放求助
Flutter PlatformView 尺寸变更时维持视频播放状态问题

复现代码
import 'package:flutter/material.dart'; import 'package:native_video_player/native_video_player.dart'; import 'package:video_player/video_player.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { late VideoPlayerController _controller; late NativeVideoPlayerController controllerPV; double sizedHeight=250.0; @override void initState() { super.initState(); _controller = VideoPlayerController.networkUrl( Uri.parse('https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/WhatCarCanYouGetForAGrand.mp4'), videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true), ); _controller.addListener(() { setState(() {}); }); _controller.setLooping(true); _controller.initialize(); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Demo', theme: ThemeData(primarySwatch: Colors.blue), home:Scaffold( appBar: AppBar(title: const Text('Video Player')), body: Column( children: [ SizedBox( height: sizedHeight, child: AspectRatio( aspectRatio: 16/9, child: VideoPlayer(_controller), ), ), ElevatedButton( onPressed: () { setState(() { if (_controller.value.isPlaying) { _controller.pause(); controllerPV.pause(); } else { _controller.play(); controllerPV.play(); } }); }, child: Icon(_controller.value.isPlaying ? Icons.pause : Icons.play_arrow), ), SizedBox( height: sizedHeight, child: AspectRatio( aspectRatio: 16/9, child: NativeVideoPlayerView( onViewReady: (controller) async { controllerPV=controller; final videoSource = await VideoSource.init( path: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/WhatCarCanYouGetForAGrand.mp4', type: VideoSourceType.network, ); await controller.loadVideoSource(videoSource); }, ), ), ), ElevatedButton( onPressed: () { setState(() { sizedHeight=220.0; }); }, child: const Icon(Icons.lock_reset_outlined), ), ], ), ) ); } }
问题描述
我在代码中同时使用两个播放器播放同一视频:
- 第一个基于
video_player: ^2.8.2,尺寸变更时视频能正常播放无中断; - 第二个基于
native_video_player: ^1.3.1,通过PlatformView实现(Android端采用ExoPlayer),尺寸变更时视频会停止播放。
其他采用PlatformView的Flutter视频播放器也存在该问题,我自己开发的基于Android视频编辑器API的视频编辑器,用PlatformView展示视频视图时也碰到了同样情况。代码中通过按钮修改播放器尺寸来模拟场景,希望找到在Flutter的PlatformView中实现尺寸变更时维持视频播放器状态的方案。
解决思路与方案
1. 防止PlatformView不必要重建
使用AutomaticKeepAliveClientMixin保持Widget状态,避免尺寸变化触发的Widget重建导致PlatformView销毁:
class _MyAppState extends State<MyApp> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); // 必须调用 // 原build代码保持不变 } }
2. 自定义保持状态的PlatformView容器
将NativeVideoPlayerView封装到保持状态的Widget中,确保尺寸变化时仅更新容器尺寸,不重建PlatformView:
class KeepAliveVideoPlayer extends StatefulWidget { final double height; final void Function(NativeVideoPlayerController) onViewReady; const KeepAliveVideoPlayer({ Key? key, required this.height, required this.onViewReady, }) : super(key: key); @override State<KeepAliveVideoPlayer> createState() => _KeepAliveVideoPlayerState(); } class _KeepAliveVideoPlayerState extends State<KeepAliveVideoPlayer> with AutomaticKeepAliveClientMixin { @override bool get wantKeepAlive => true; @override Widget build(BuildContext context) { super.build(context); return SizedBox( height: widget.height, child: AspectRatio( aspectRatio: 16/9, child: NativeVideoPlayerView( onViewReady: widget.onViewReady, ), ), ); } }
替换原代码中对应的NativeVideoPlayer部分为这个容器即可。
3. 原生层优化(针对自定义插件)
如果使用的是自己开发的插件,在Android端修改PlatformView实现:
- 监听视图尺寸变化回调,直接更新ExoPlayer的Surface尺寸;
- 确保ExoPlayer实例在PlatformView生命周期内唯一,仅在视图销毁时释放资源。
4. 兜底方案:记录并恢复播放状态
如果无法避免重建,可在尺寸变化前记录播放状态,重建后恢复:
ElevatedButton( onPressed: () { setState(() { // 记录当前状态 final currentPosition = controllerPV.position; final wasPlaying = controllerPV.isPlaying; sizedHeight=220.0; // 等待重建完成后恢复 WidgetsBinding.instance.addPostFrameCallback((_) async { if (wasPlaying) { await controllerPV.seekTo(currentPosition ?? Duration.zero); controllerPV.play(); } }); }); }, child: const Icon(Icons.lock_reset_outlined), ),
内容的提问来源于stack exchange,提问作者Lins Louis
相关产品推荐
相关产品推荐

