How to access data in Bloc's state from another bloc

Viewed 4913

I am developing a Flutter application using Bloc pattern. After success authentication, UserSate has User object. In all other Blocs, I need to access User object in UserState. I tried with getting UserBloc on other Bloc's constructor parameters and accessing User object. But it shows that User object is null. Anyone have a better solution?

class SectorHomeBloc extends Bloc<SectorHomeEvent, SectorHomeState> {
  final OutletRepository outletRepository;
  UserBloc userBloc;
  final ProductRepository productRepository;
  final ProductSubCategoryRepository productSubCategoryRepository;
  final PromotionRepository promotionRepository;
  final ProductMainCategoryRepository mainCategoryRepository;

  SectorHomeBloc({
    @required this.outletRepository,
    @required this.userBloc,
    @required this.productSubCategoryRepository,
    @required this.productRepository,
    @required this.promotionRepository,
    @required this.mainCategoryRepository,
  });
  @override
  SectorHomeState get initialState => SectorHomeLoadingState();

  @override
  Stream<SectorHomeState> mapEventToState(SectorHomeEvent event) async* {
    try {
      print(userBloc.state.toString());
      LatLng _location = LatLng(
          userBloc.state.user.defaultLocation.coordinate.latitude,
          userBloc.state.user.defaultLocation.coordinate.longitude);
      String _token = userBloc.state.user.token;

      if (event is GetAllDataEvent) {
        yield SectorHomeLoadingState();
        List<Outlet> _previousOrderedOutlets =
            await outletRepository.getPreviousOrderedOutlets(
                _token, _location, event.orderType, event.sectorId);

        List<Outlet> _featuredOutlets =
            await outletRepository.getFeaturedOutlets(
                _token, _location, event.orderType, event.sectorId);
        List<Outlet> _nearestOutlets = await outletRepository.getOutletsNearYou(
            _token, _location, event.orderType, event.sectorId);

        List<Product> _newProducts = await productRepository.getNewItems(
            _token, _location, event.orderType, event.sectorId);

        List<Product> _trendingProducts =
            await productRepository.getTrendingItems(
                _token, _location, event.orderType, event.sectorId);

        List<Promotion> _promotions = await promotionRepository
            .getVendorPromotions(_token, event.sectorId);
        yield SectorHomeState(
          previousOrderedOutlets: _previousOrderedOutlets,
          featuredOutlets: _featuredOutlets,
          nearByOutlets: _nearestOutlets,
          newItems: _newProducts,
          trendingItems: _trendingProducts,
          promotions: _promotions,
        );
      }
    } on SocketException {
      yield SectorHomeLoadingErrorState('could not connect to server');
    } catch (e) {
      print(e);
      yield SectorHomeLoadingErrorState('Error');
    }
  }
}

The print statement [print(userBloc.state.toString());] in mapEventToState method shows the initial state of UserSate. But, at the time of this code executing UserState is in UserLoggedInState.

2 Answers

there is an official way to do this as in the documentation, called Bloc-to-Bloc Communication and here is the example for this as in the documentation

class MyBloc extends Bloc {
  final OtherBloc otherBloc;
  StreamSubscription otherBlocSubscription;

  MyBloc(this.otherBloc) {
    otherBlocSubscription = otherBloc.listen((state) {
        // React to state changes here.
        // Add events here to trigger changes in MyBloc.
    });
  }

  @override
  Future<void> close() {
    otherBlocSubscription.cancel();
    return super.close();
  }
}

The accepted answer actually has a comment in the above example in the official docs saying "No matter how much you are tempted to do this, you should not do this! Keep reading for better alternatives!"!!!

Here's the official doc link, ultimately one bloc should not know about any other blocs, add methods to update your bloc and these can be triggered from blocListeners which listen to changes in your other blocs: https://bloclibrary.dev/#/architecture?id=connecting-blocs-through-domain

class MyWidget extends StatelessWidget {
  const MyWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocListener<WeatherCubit, WeatherState>(
      listener: (context, state) {
        // When the first bloc's state changes, this will be called.
        //
        // Now we can add an event to the second bloc without it having
        // to know about the first bloc.
        BlocProvider.of<SecondBloc>(context).add(SecondBlocEvent());
      },
      child: TextButton(
        child: const Text('Hello'),
        onPressed: () {
          BlocProvider.of<FirstBloc>(context).add(FirstBlocEvent());
        },
      ),
    );
  }
}
Related