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

如何将Riverpod控制器获取的数据存储到另一个Provider中?

问题描述

我有一个用于调用API获取数据并在页面展示的Riverpod控制器,代码如下:

final imagesByCamIdControllerProvider = StateNotifierProvider.autoDispose<
ImagesByCamIdController, ImagesByCamIdState>((ref) {
final cameraService = ref.watch(cameraDetailsProvider);
return ImagesByCamIdController(const ImagesByCamIdState(), cameraService);
});
class ImagesByCamIdController extends StateNotifier<ImagesByCamIdState> {
ImagesByCamIdController(super.state, this.service);
final CameraDetailsRepositoryImpl service;
Future getImagesByCamId(projectId, cameraId, {searchDate = ''}) async {
state = state.copyWith(isFetching: true);
final result = await service.imagesByCameraId(projectId, cameraId,
    searchDate: searchDate);
result.fold((l) {
  // error handle
  state = state.copyWith(isFetching: false, errorMessage: l.message);
}, (r) {
  state =
      state.copyWith(isFetching: false, imagesByCamId: AsyncValue.data(r));
});
}}

我在页面中的使用方式如下:

@override
void initState() {
super.initState();
   WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
  ref.read(imagesByCamIdControllerProvider.notifier).getImagesByCamId(
      widget.projectId, widget.cameraId,
      searchDate: _searchDate);
});
}

在Widget build中赋值:

final imagesByCameraIdData = ref.watch(
    imagesByCamIdControllerProvider.select((value) => value.imagesByCamId));

现在我希望将该Riverpod控制器获取的数据存储到另一个Provider(如StateNotifierProvider)中,使用该Provider来展示图片和修改状态,避免每次修改状态时重复发起API请求。请问该如何实现这一需求?

实现方案

核心思路是创建一个持久化的状态存储Provider,让原数据请求控制器在获取到API结果后同步数据到这个Provider,后续页面直接监听该存储Provider的状态,无需重复触发API调用。

步骤1:创建数据存储的StateNotifier与Provider

先定义专门用于存储图片数据的状态类和控制器,这个Provider不需要自动销毁,保证数据能在页面间或状态更新时保留:

// 替换成你实际的图片数据模型
class ImageModel {
  final String url;
  final String id;

  ImageModel({required this.url, required this.id});
}

// 存储图片数据的状态类
class StoredImagesState {
  final AsyncValue<List<ImageModel>> images;
  final bool isLoading;
  final String? errorMessage;

  StoredImagesState({
    this.images = const AsyncValue.loading(),
    this.isLoading = false,
    this.errorMessage,
  });

  StoredImagesState copyWith({
    AsyncValue<List<ImageModel>>? images,
    bool? isLoading,
    String? errorMessage,
  }) {
    return StoredImagesState(
      images: images ?? this.images,
      isLoading: isLoading ?? this.isLoading,
      errorMessage: errorMessage ?? this.errorMessage,
    );
  }
}

// 创建全局存储用的StateNotifierProvider
final storedImagesProvider = StateNotifierProvider<StoredImagesController, StoredImagesState>((ref) {
  return StoredImagesController(const StoredImagesState());
});

class StoredImagesController extends StateNotifier<StoredImagesState> {
  StoredImagesController(super.state);

  // 更新图片数据
  void updateImages(AsyncValue<List<ImageModel>> newImages) {
    state = state.copyWith(images: newImages, isLoading: false);
  }

  // 设置加载状态
  void setLoading(bool isLoading) {
    state = state.copyWith(isLoading: isLoading);
  }

  // 设置错误信息
  void setError(String? message) {
    state = state.copyWith(errorMessage: message, isLoading: false);
  }
}

步骤2:修改原控制器,同步数据到存储Provider

调整原ImagesByCamIdController,在处理API结果时,调用存储Provider的方法同步状态:

final imagesByCamIdControllerProvider = StateNotifierProvider.autoDispose<
ImagesByCamIdController, ImagesByCamIdState>((ref) {
final cameraService = ref.watch(cameraDetailsProvider);
// 获取存储Provider的控制器引用,用于后续同步数据
final storedNotifier = ref.read(storedImagesProvider.notifier);
return ImagesByCamIdController(const ImagesByCamIdState(), cameraService, storedNotifier);
});

class ImagesByCamIdController extends StateNotifier<ImagesByCamIdState> {
ImagesByCamIdController(super.state, this.service, this.storedNotifier);
final CameraDetailsRepositoryImpl service;
final StoredImagesController storedNotifier;

Future getImagesByCamId(projectId, cameraId, {searchDate = ''}) async {
state = state.copyWith(isFetching: true);
// 同步加载状态到存储Provider
storedNotifier.setLoading(true);

final result = await service.imagesByCameraId(projectId, cameraId,
    searchDate: searchDate);
result.fold((l) {
  // 同步错误信息
  storedNotifier.setError(l.message);
  state = state.copyWith(isFetching: false, errorMessage: l.message);
}, (r) {
  final asyncData = AsyncValue.data(r);
  // 同步获取到的图片数据
  storedNotifier.updateImages(asyncData);
  state = state.copyWith(isFetching: false, imagesByCamId: asyncData);
});
}}

步骤3:页面直接使用存储Provider展示数据

页面中不再需要在initState调用原控制器的请求方法(除非需要主动刷新),直接监听storedImagesProvider的状态即可:

@override
Widget build(BuildContext context, WidgetRef ref) {
  final storedState = ref.watch(storedImagesProvider);

  return storedState.images.when(
    loading: () => const Center(child: CircularProgressIndicator()),
    error: (err, stack) => Center(child: Text('加载失败:${storedState.errorMessage}')),
    data: (images) => GridView.builder(
      padding: const EdgeInsets.all(16),
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        crossAxisSpacing: 12,
        mainAxisSpacing: 12,
      ),
      itemCount: images.length,
      itemBuilder: (context, index) {
        final image = images[index];
        return Image.network(image.url, fit: BoxFit.cover);
      },
    ),
  );
}

步骤4:按需触发API刷新

如果需要重新发起API请求(比如切换搜索日期、更换摄像头),仍然可以调用原控制器的方法,数据会自动同步到存储Provider:

ElevatedButton(
  onPressed: () {
    ref.read(imagesByCamIdControllerProvider.notifier).getImagesByCamId(
      widget.projectId,
      widget.cameraId,
      searchDate: _newSearchDate,
    );
  },
  child: const Text('刷新数据'),
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 03:42:18