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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 14:25:23