【问题标题】:Manage Global Events by bloc按集团管理全局事件
【发布时间】:2021-07-26 11:38:06
【问题描述】:

我正在尝试寻找管理异步查询的解决方案。例如,我有网上商店,我想在更改城市时更新所有产品和类别。而且我不想将所有异步逻辑保留在 ui 上。为了达到这个结果,我创建了这个集团:

class AppEvent {
  String message;

  AppEvent({this.message = ''});
}

class EventsBlock extends Bloc<AppEvent, AppEvent> {
  EventsBlock() : super(AppEvent());
 
  @override
  Stream<AppEvent> mapEventToState(AppEvent event) async* {
    yield event;
  }
}

final events = EventsBlock();

然后,我可以这样使用它:

class CityCubit() {
  CityCubit() : super(CityState());  
  
  Future<void> changeCity() async {
    await api.changeCity();
    events.add(AppEvent(message: 'cityChanged'));   
  }
}

class CategoryCubit extends Cubit<CategoryState> {
  CategoryCubit() : super(CategoryEmptyState()) {
    events.stream.listen((e) {
        if(e.message == 'cityChanged') {
            fetchCategories();
        }
      });
  };

  Future<void> fetchCategories() async {
    //fetch categories
  }
}

class ProductCubit extends Cubit<ProductState> {
  ProductCubit() : super(ProductEmptyState()) {
    events.stream.listen((e) {
        if(e.message == 'cityChanged') {
            fetchProducts();
        }
      });
  };

  Future<void> fetchProducts() async {
    //fetch products
  }
}

这有点像 eventBus 模式。但我不确定这是使用 bloc 的正确方法。我尝试过使用 redux + redux saga,但它有很多样板文件,我相信 Flutter 有更好的解决方案来管理这样的事情。

【问题讨论】:

    标签: flutter dart bloc


    【解决方案1】:

    我最终得到了这段代码:

    class CityChangedEvent {
      int cityId;
    
      CityChangedEvent(this.cityId);
    }
    
    EventBus eventBus = EventBus();
    
    mixin EventBusMixin {
      StreamSubscription<T> listenEvent<T>(void Function(T) subscription) =>
          eventBus.on<T>().listen(subscription);
    
      void shareEvent<S>(S event) => eventBus.fire(event);
    }
    
    class CityCubit extends CityCubit<CityState> with EventBusMixin {
      CityCubit() : super(CityInitialState());        
    
      Future<void> changeCity(cityId) async {
        try {
          emit(ChangeCityLoadingState());
          final result = await api.changeCity(cityId);
          if(result.success) {
            emit(ChangeCitySuccessState());
            shareEvent(CityChangedEvent(cityId));   
          }
        } catch (_) {
          emit(ChangeCityErrorState());
        }
      }
    }
    
    class CategoryCubit extends Cubit<CategoryState> with EventBusMixin {
      CategoryCubit() : super(CategoryEmptyState()) {
         listenEvent<CityChangedEvent>((e) {
            fetchCategories(e.cityId);
         );
      }
    
      Future<void> fetchCategories(cityId) async {
        try {
          emit(CategoryLoadingState());
          final categoriesList = await fetchCategoriesApi();
          emit(CategoryLoadedState(categories: categoriesList));
        } catch (_) {
          emit(CategoryErrorState());
        }
      }
    }
    

    现在,我可以在块之间进行通信,而无需实例化或注入它们的实例。感谢这个库https://pub.dev/packages/event_bus

    【讨论】:

      【解决方案2】:

      您的总体想法没问题,但我看不出真正需要 EventsBloc 类。事实上,你对这个集团的事件和状态使用同一个类,并简单地产生你收到的事件,这有点奇怪。就像EventsBloc 可以是一个简单的流。

      这是一个可行的方法,将CityCubit 变成一个实际的 bloc(并且还有一些错误处理,这是你可以用 bloc 优雅地做的事情):

      abstract class CityState {}
      class CityInitial extends CityState {}
      class CityLoadingCities extends CityState {}
      class CityCitiesLoaded extends CityState {}
      class CityLoadError extends CityState {}
      
      abstract class CityEvent {}
      class CityLoadCities extends CityEvent {}
      
      class CityBloc<CityEvent, CityState> {
        CityBloc() : super(CityInitial());  
      
        @override
        Stream<AppEvent> mapEventToState(CityEvent event) async* {
          if(event is CityLoadCities) {
            yield CityLoadingCities();
            try {
              await api.changeCity();
              yield CityCitiesLoaded();
            } catch (error) {
              yield CityLoadError();
            }
          }
        }
        
        void changeCity() {
          add(CityLoadCities());   
        }
      }
      

      现在您可以在任何其他集团内执行此操作:

      instanceOfCityBloc.listen((cityState) {
        if(cityState is CityCitiesLoaded){
          // do stuff
        }
      });
      

      【讨论】:

      • 问题是我需要在每个页面(类别、产品等)上注入 cityBloc 的实例。我必须在小部件构建中完成,将其传递给其他 bloc/cubit 构造函数
      • 如果我将此块设为单例并直接在其他类中侦听事件怎么办?会是正确的方式吗?
      • 我想我找到了解决方案pub.dev/packages/event_bus
      猜你喜欢
      • 2021-08-15
      • 1970-01-01
      • 2017-06-05
      • 2014-07-06
      • 1970-01-01
      • 2021-05-25
      • 1970-01-01
      • 2023-03-04
      • 2012-07-21
      相关资源
      最近更新 更多