返回首页

BLoC Flutter 中的 Use-cases:架构重构

本文描述了在 Flutter 中通过将业务逻辑提取到 use-cases 来重构 BLoC。提供了重构前后的代码示例,以及测试和可扩展性的优势。该方法基于 clean architecture。

BLoC + use-cases:如何简化 Flutter 架构
Advertisement 728x90

Flutter 中 BLoC 与用例分离:清洁架构实战

Flutter 的 BLoC 模式常常演变成“上帝对象”:事件处理器混杂服务调用、验证和状态发射。这让维护、测试和扩展变得噩梦般艰难。将业务逻辑移入用例,能恢复正确的层级分离,遵循清洁架构原则:表示层(UI 和 BLoC)、领域层(用例、实体)和数据层(仓库、服务)。

用例实现特定场景:它协调服务、处理错误、应用业务规则,并保持与 UI 无关。BLoC 则专注单向数据流——处理事件并发射状态。

传统 BLoC:逻辑与 UI 纠缠不清

典型 BLoC 通过依赖注入直接调用服务,在处理器中执行副作用。下面是一个简化的 ItemsBloc 示例:

Google AdInline article slot
class ItemsBloc extends Bloc<ItemsEvent, ItemsState> {
  final FetchItemsService _service;

  ItemsBloc(this._service) : super(Initial()) {
    on<FetchItemsEvent>(_onFetchItemsEvent);
  }

  Future<void> _onFetchItemsEvent(FetchItemsEvent event, Emitter<ItemsState> emit) async {
    emit(Loading());
    try {
      final items = await _service.fetchItems();
      emit(Loaded(items: items));
    } catch (error) {
      emit(Error(error: error));
    }
  }
}

问题:强耦合、难测试的代码,以及违反单一职责原则(SRP)。BLoC 知道太多数据、网络和业务逻辑细节。

重构后:BLoC 变身纯状态管理者

重构后,BLoC 只负责事件到状态的映射,无任何依赖:

class ItemsBloc extends Bloc<ItemsEvent, ItemsState> {
  ItemsBloc() : super(Initial()) {
    on<SetLoading>(_onSetLoading);
    on<SetLoaded>(_onSetLoaded);
    on<SetError>(_onSetError);
  }

  Future<void> _onSetLoading(SetLoading event, Emitter<ItemsState> emit) async {
    emit(Loading());
  }

  Future<void> _onSetLoaded(SetLoaded event, Emitter<ItemsState> emit) async {
    emit(Loaded(items: event.items));
  }

  Future<void> _onSetError(SetError event, Emitter<ItemsState> emit) async {
    emit(Error(error: event.error));
  }
}

用例负责协调:

Google AdInline article slot
class FetchItemsUseCase {
  final ItemsBloc bloc;
  final FetchItemsService service;

  FetchItemsUseCase({required this.bloc, required this.service});

  Future<void> call() async {
    bloc.add(SetLoading());
    try {
      final items = await service.fetchItems();
      bloc.add(SetLoaded(items: items));
    } catch (error) {
      bloc.add(SetError(error: error.toString()));
    }
  }
}

BLoC 缩减到 20 行,完全可测试:模拟事件并验证状态。

用例作为协调者的优势

用例不只是代理——它组合仓库数据、应用规则、记录活动并处理同步。它依赖抽象(接口),而非具体实现。

关键优势:

Google AdInline article slot
  • 可测试性:为用例单元测试模拟服务/仓库。
  • 可扩展性:新增场景无需改动 BLoC(开闭原则)。
  • DI 灵活性:向用例注入服务;BLoC 保持独立。
  • 可读性:业务场景隔离在专用类中。

真实项目中,此重构缩短开发时间 30%、减少 bug、简化测试。

数据流与权衡

UI → 用例 → 服务/仓库 → 事件至 BLoC → UI 状态。用例了解 BLoC(为简便的清洁架构妥协),但 UI 无需 bloc.add 调用。

BLoC 实现单向数据流:行为可预测、测试轻松。用例独立测试:模拟响应并验证调用。

核心要点

  • 层级分离:BLoC 作状态管理者,用例管领域逻辑。
  • SRP 实践:每个类只干一件事。
  • 简化测试:BLoC 中纯函数,用例用模拟。
  • 可扩展:兼容 Cubit、Riverpod 等其他状态管理器。

此方法适用于任何 BLoC 膨胀的 Flutter 项目。度量前后指标:类大小、测试覆盖率、功能交付时间。

— Editorial Team

Advertisement 728x90

继续阅读