Riverpod 2.0结合Clean Architecture获取Entity失败问题求助
问题解决建议
一、数据源与仓库实例化优化
当前在Notifier内部直接实例化数据源、仓库和用例的方式不符合依赖注入原则,既不利于测试,也违背了Clean Architecture的依赖倒置理念。正确做法是通过Riverpod Provider统一提供这些依赖:
1. 定义各层依赖的Provider
import 'package:http/http.dart' as http; import 'package:riverpod_annotation/riverpod_annotation.dart'; // 导入你的数据源、仓库、用例类 part 'dependencies_providers.g.dart'; // 本地数据源Provider @riverpod AvailableLanguagesLocalDataSource availableLanguagesLocalDataSource(AvailableLanguagesLocalDataSourceRef ref) { return AvailableLanguagesLocalDataSourceImpl(); } // 远程数据源Provider @riverpod AvailableLanguagesRemoteDataSource availableLanguagesRemoteDataSource(AvailableLanguagesRemoteDataSourceRef ref) { return AvailableLanguagesRemoteDataSourceImpl(client: http.Client()); } // 仓库实现Provider @riverpod AvailableLanguagesRepository availableLanguagesRepository(AvailableLanguagesRepositoryRef ref) { final localDS = ref.watch(availableLanguagesLocalDataSourceProvider); final remoteDS = ref.watch(availableLanguagesRemoteDataSourceProvider); return AvailableLanguagesRepositoryImpl( availableLanguagesLocalDataSource: localDS, availableLanguagesRemoteDataSource: remoteDS, ); } // 用例Provider @riverpod AvailableLanguagesUseCase availableLanguagesUseCase(AvailableLanguagesUseCaseRef ref) { final repo = ref.watch(availableLanguagesRepositoryProvider); return AvailableLanguagesUseCase(availableLanguagesRepository: repo); }
2. 修改Notifier获取依赖
@riverpod class AvailableLanguagesAsyncNotifier extends _$AvailableLanguagesAsyncNotifier { @override Future<AvailableLanguagesEntity> build() async { return getAvailableLanguages(); } Future<AvailableLanguagesEntity> getAvailableLanguages() async { final useCase = ref.watch(availableLanguagesUseCaseProvider); final result = await useCase.getAvailableLanguages(); // 抛出错误让AsyncValue自动处理error状态 return result.fold((failure) => throw failure, (entity) => entity); } }
二、页面直接获取Entity的问题
之前的核心问题是AsyncValue与自定义State双重嵌套:AsyncValue的data字段包裹了AvailableLanguagesState,而该State又有自己的data/loading/error状态,导致页面需要两层解析。优化方案是让UseCase返回原始的Either<Failure, Entity>,由AsyncNotifier统一处理加载/错误状态:
1. 修改UseCase返回类型
class AvailableLanguagesUseCase { final AvailableLanguagesRepository availableLanguagesRepository; AvailableLanguagesUseCase({required this.availableLanguagesRepository}); // 返回Either而非自定义State,将状态处理交给Notifier Future<Either<Failure, AvailableLanguagesEntity>> getAvailableLanguages() async { return availableLanguagesRepository.getAvailableLanguages(); } }
2. 调整页面监听逻辑
现在AsyncNotifier的状态是Future<AvailableLanguagesEntity>,页面可以直接通过AsyncValue的maybeWhen拿到实体:
class InitialSetupPage extends StatelessWidget { const InitialSetupPage({super.key}); @override Widget build(BuildContext context) { return Consumer( builder: (context, ref, child) { final languageState = ref.watch(availableLanguagesAsyncNotifierProvider); return languageState.maybeWhen( loading: () => const ColoredBox(color: Colors.purple), data: (entity) => ColoredBox( color: Colors.green, child: Center( child: Text(entity.availableLanguages.first), // 直接访问Entity字段 ), ), error: (error, stackTrace) => const ColoredBox(color: Colors.red), orElse: () => const ColoredBox(color: Colors.lightBlue), ); }, ); } }
补充说明
- 自定义的
AvailableLanguagesState可以直接移除,因为AsyncValue已经提供了loading/data/error三种基础状态,完全满足业务需求。 - 如果需要自定义错误信息,可以在Notifier抛出错误时封装自定义异常,页面监听时针对性处理。
内容的提问来源于stack exchange,提问作者patana93
相关产品推荐
相关产品推荐

