如何在Riverpod的Provider内部提供异步Future类型的数据库实例?
解决Riverpod结合Clean Architecture时异步数据库实例的提供问题
你的核心问题是:databaseFutureProvider是FutureProvider,直接ref.watch它得到的是AsyncValue<Database>类型的对象,而ToDoDatasourceImpl需要的是直接的Database实例,类型不匹配导致报错。下面提供两种可靠的解决思路:
方案一:将依赖异步的Provider改为FutureProvider(推荐)
既然数据源和仓库的创建依赖异步初始化的数据库,直接把它们也声明为FutureProvider,通过.future属性等待数据库实例就绪:
final databaseFutureProvider = FutureProvider<Database>((ref) async { final db = await DatabaseHelper().createDatabase(); return db; }); final toDoDatasourceProvider = FutureProvider<ToDoDatasource>((ref) async { // 通过.future获取Future<Database>,await拿到实际实例 final db = await ref.watch(databaseFutureProvider.future); return ToDoDatasourceImpl(db: db); }); final toDoRepositoryProvider = FutureProvider<ToDoRepository>((ref) async { final ds = await ref.watch(toDoDatasourceProvider.future); return ToDoRepositoryImpl(ds); });
这种方式贴合异步依赖的语义,能自然处理数据库加载中的等待状态,以及加载失败的错误捕获。在UI层使用时,只需通过AsyncValue.when处理加载、成功、错误三种状态即可。
方案二:保持同步Provider,处理AsyncValue状态
如果希望数据源和仓库保持为同步Provider,可以通过AsyncValue.when方法根据数据库的状态返回对应实例或占位值:
final databaseFutureProvider = FutureProvider<Database>((ref) async { final db = await DatabaseHelper().createDatabase(); return db; }); final toDoDatasourceProvider = Provider<ToDoDatasource?>((ref) { final dbAsync = ref.watch(databaseFutureProvider); // 根据异步状态返回数据源实例或null return dbAsync.when( data: (db) => ToDoDatasourceImpl(db: db), loading: () => null, error: (_, __) => null, ); }); final toDoRepositoryProvider = Provider<ToDoRepository?>((ref) { final ds = ref.watch(toDoDatasourceProvider); // 数据源就绪时创建仓库,否则返回null return ds != null ? ToDoRepositoryImpl(ds) : null; });
这种方式需要在UI层处理null的情况,确保在数据库未就绪时显示加载提示或错误信息,避免空指针异常。
不推荐的方式:直接使用requireValue
虽然可以通过ref.watch(databaseFutureProvider).requireValue强制获取数据库实例,但这种方式会在数据库未加载完成或加载失败时直接抛出异常,仅适用于确定数据库已经初始化完成的场景,不建议作为常规方案使用。
内容的提问来源于stack exchange,提问作者Maciej Pszczolinski
相关产品推荐
相关产品推荐

