如何避免Widget重建时ChewieController控件出现闪烁?
解决ChewieController重试时进度条闪烁问题
问题根源
你当前在errorBuilder里调用setState会触发整个页面组件重建,Chewie实例被重新初始化,导致进度条等控件反复渲染出现闪烁;另外直接在errorBuilder里做状态更新也不符合组件设计逻辑。
解决方案
1. 用ValueNotifier管理ChewieController实例
通过ValueNotifier封装ChewieController,更新控制器时仅通知Chewie组件刷新,而非触发全局setState,从根源避免页面整体重建导致的闪烁。
2. 分离错误重试逻辑,不在errorBuilder内处理状态更新
将重试逻辑移到视频初始化方法中,或通过控制器的错误回调处理,避免在UI构建周期内触发状态变更。
3. 务必释放旧控制器资源
每次重新初始化前,dispose旧的ChewieController和VideoPlayerController,防止内存泄漏。
代码示例
// 用ValueNotifier管理ChewieController,避免全局setState触发全页面重建 final ValueNotifier<ChewieController?> _chewieControllerNotifier = ValueNotifier(null); int numberTryInit = 0; final int maxNumberTryInit = 3; // 初始化视频控制器的方法 Future<void> _initVideoController() async { // 先释放旧资源,防止内存泄漏 _chewieControllerNotifier.value?.dispose(); final VideoPlayerController videoPlayerController = VideoPlayerController.network( widget.videoContent.url, // 替换为你的视频地址 ); try { await videoPlayerController.initialize(); // 创建新的ChewieController并更新ValueNotifier _chewieControllerNotifier.value = ChewieController( videoPlayerController: videoPlayerController, autoInitialize: true, allowFullScreen: true, aspectRatio: widget.videoContent.isVertical ? 0.5 : 16 / 9, looping: false, autoPlay: true, showControlsOnInitialize: false, errorBuilder: (context, errorMessage) { // 仅负责显示错误/加载UI,不处理状态更新 if ((errorMessage == "Failed to load video: Operation Stopped" || errorMessage == "Failed to load video: unknown error") && numberTryInit <= maxNumberTryInit) { return const Center(child: CircularProgressIndicator()); } return Padding( padding: const EdgeInsets.all(20.0), child: Center( child: CustomText.title( text: "error_load_video".tr(), textAlign: TextAlign.center, ), ), ); }, showOptions: false, cupertinoProgressColors: ChewieProgressColors( handleColor: Colors.white, playedColor: AppProperties.accentColor, bufferedColor: const Color.fromRGBO(200, 200, 200, 0.8), ), materialProgressColors: ChewieProgressColors( handleColor: Colors.white, playedColor: AppProperties.accentColor, bufferedColor: const Color.fromRGBO(200, 200, 200, 0.8), ), placeholder: const Center(child: CircularProgressIndicator()), ); // 监听视频控制器错误,触发重试逻辑 videoPlayerController.addListener(() { if (videoPlayerController.value.hasError && numberTryInit <= maxNumberTryInit) { numberTryInit++; // 延迟重试,避免频繁请求 Future.delayed(const Duration(seconds: 1), _initVideoController); } }); } catch (e) { // 初始化失败时触发重试 if (numberTryInit <= maxNumberTryInit) { numberTryInit++; Future.delayed(const Duration(seconds: 1), _initVideoController); } else { // 重试耗尽,更新状态显示错误 _chewieControllerNotifier.value = null; } } } @override void initState() { super.initState(); _initVideoController(); } @override void dispose() { // 销毁资源 _chewieControllerNotifier.value?.dispose(); _chewieControllerNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return SafeArea( child: ValueListenableBuilder<ChewieController?>( valueListenable: _chewieControllerNotifier, builder: (context, controller, child) { if (controller == null) { return Padding( padding: const EdgeInsets.all(20.0), child: Center( child: CustomText.title( text: "error_load_video".tr(), textAlign: TextAlign.center, ), ), ); } return Chewie(controller: controller); }, ), ); }
关键优化点
- 使用
ValueNotifier+ValueListenableBuilder,仅当控制器实例变化时才更新Chewie组件,避免全局setState导致的全页面重建。 - 将重试逻辑移到视频控制器的监听或初始化方法中,不在
errorBuilder内触发状态变更,符合UI构建逻辑。 - 每次重试前释放旧控制器资源,防止内存泄漏。
- 重试时保持加载指示器显示,避免界面空白或跳动,进一步减少视觉闪烁。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

