如何将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
相关产品推荐
相关产品推荐

