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

如何避免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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 19:07:10