6

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.

Akif
  • 7,098
  • 7
  • 27
  • 53
  • actually you are trying to access a class property from another class right? – Yaya Oct 22 '20 at 12:59
  • At first define User object member variable in event 'GetAllDataEvent' class. And pass the user object by calingl 'add(GetAllDataEvent(user: userObj))' in the state where UserBloc is loaded. And get a user object by calling 'event.user' in 'if (event is GetAllDataEvent)' – KuKu Oct 22 '20 at 13:20
  • thank you so much. its working. but there is another official way to communicate between Blocs. by the way, thanks a lot – IMS Mobile Dev Oct 22 '20 at 16:51

2 Answers2

12

UPDATE (Best Practice): please refer to the answer here enter link description here so the best way for that is to hear the changes of another bloc inside the widget you are in, and fire the event based on that. so what you will do is wrap your widget in a bloc listener and listen to the bloc you want.

    class SecondPage extends StatelessWidget {
      const SecondPage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return BlocListener<FirstBloc, FirstBlocState>(
          listener: (context, state) {
             if(state is StateFromFirstBloc){
             BlocProvider.of<SecondBloc>(context).add(SecondBlocEvent());}//or whatever you want
          },
          child: ElevatedButton(
            child:   Text('THIS IS NEW SCREEN'),
            onPressed: () {
              BlocProvider.of<SecondBloC>(context).add(SecondBloCEvent());
            },
          ),
        );
      }
    }

the lovely thing about listener is that you can listen anywhere to any bloc and do whatever you want here is the official documentation for it

OLD WAY (NOT Recommended)

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();
  }
}

sorry for the late update for this answer and thanks to @MJ studio

Baraa Aljabban
  • 992
  • 13
  • 22
  • most Welcome buddy!. – Baraa Aljabban Nov 05 '20 at 08:42
  • 1
    The correct way is actually: otherBloc.stream.listen((state) {}) – Chwizdo Mar 17 '22 at 10:12
  • 1
    The documentation literate advises to NOT do that. It's better to use the two other solutions described below that part. – Philipp May 25 '22 at 13:08
  • 1
    This is perfectly misunderstanding what the docs say... – MJ Studio Sep 23 '22 at 09:09
  • may you please explain more @MJStudio – Baraa Aljabban Sep 23 '22 at 12:45
  • In your link, the document says about the bloc has another bloc as a dependency like the following. ***Because blocs expose streams, it may be tempting to make a bloc which listens to another bloc. You should not do this. There are better alternatives than resorting to the code below:*** So, the docs guides do not following this way. Instead, presentation layer communication or domain layer communication is encouraged – MJ Studio Sep 24 '22 at 12:58
  • got it thanks @MJStudio, I'll update the answer based on that – Baraa Aljabban Nov 07 '22 at 02:00
5

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());
        },
      ),
    );
  }
}
rootOfSound
  • 109
  • 1
  • 5