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

使用Riverpod监听Image.network加载状态并存入state的最优方案是什么?

Flutter 监听Image.network加载状态并存入Riverpod的最优实现

原有实现的核心问题

  • 仅覆盖加载成功场景:loadingBuilder的loadingProgress == null仅代表加载完成且成功,加载失败、资源无效、网络错误等场景完全没有处理,状态会一直停留在初始值,不符合业务预期
  • 存在build阶段改状态的风险:在widget的build参数中直接修改Riverpod状态,属于build执行过程中触发状态更新,非常容易触发setState during build异常,导致页面闪退或者无限重绘
  • 状态修改方式不规范:Riverpod 1.0之后StateProvider的状态修改需要通过.notifier访问控制器修改,直接读provider修改状态的写法已经废弃,会导致编译警告或者运行异常

最优实现方案

核心采用Flutter官方提供的ImageStreamListener监听图片加载全周期状态,所有状态更新放到帧回调中执行,规避build阶段改状态的问题。

第一步:Provider定义

Riverpod 2.x 推荐写法(更易维护)

final validPhotoProvider = NotifierProvider<ValidPhotoNotifier, bool>(ValidPhotoNotifier.new);

class ValidPhotoNotifier extends Notifier<bool> {
  @override
  bool build() => false;

  void onLoadSuccess() => state = true;
  void onLoadFailed() => state = false;
}

Riverpod 1.x 兼容写法

final validPhotoProvider = StateProvider<bool>((ref) => false);

第二步:图片加载及状态监听实现

ConsumerStatefulBuilder(
  builder: (context, ref, child) {
    final imageProvider = NetworkImage(photoUrl);
    // 注册全周期加载监听
    imageProvider.resolve(const ImageConfiguration()).addListener(
      ImageStreamListener(
        // 加载成功回调
        (image, synchronousCall) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            // 2.x版本写法
            ref.read(validPhotoProvider.notifier).onLoadSuccess();
            // 1.x版本写法
            // ref.read(validPhotoProvider.notifier).state = true;
          });
        },
        // 加载失败回调
        onError: (error, stackTrace) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            // 2.x版本写法
            ref.read(validPhotoProvider.notifier).onLoadFailed();
            // 1.x版本写法
            // ref.read(validPhotoProvider.notifier).state = false;
          });
        },
      ),
    );

    return Image(
      image: imageProvider,
      loadingBuilder: (context, child, loadingProgress) {
        if (loadingProgress == null) return child;
        return const CircularProgressIndicator();
      },
      errorBuilder: (context, error, stackTrace) {
        // 自定义加载失败占位UI
        return const Icon(Icons.broken_image, size: 48);
      },
    );
  },
)

方案优势

  • 全场景覆盖:加载成功、加载失败都有对应回调,状态不会出现遗漏
  • 无异常风险:所有状态更新都放在addPostFrameCallback中执行,完全规避build过程中修改状态的异常
  • 逻辑可复用:监听逻辑和UI渲染分离,后续可以封装成通用组件直接使用
  • 状态更新精确:仅当加载状态真实变化时才会更新Riverpod状态,不会出现重复触发无意义rebuild的问题

可选优化点

  • 内存泄漏防护:将ImageStreamListener实例存为变量,在页面/组件销毁时调用removeListener移除监听,避免内存泄漏
  • 多图片场景适配:如果需要同时监听多张图片的加载状态,可以给Provider加family修饰符,传入图片url作为唯一标识区分不同图片的状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 13:15:05