25

My question is about navigation used with the BLoC pattern.

In my LoginScreen widget I have a button that adds an event into the EventSink of the bloc. The bloc calls the API and authenticates the user. Where in the LoginScreen Widget do I have to listen to the stream, and how do I navigate to another screen after it returns a success status?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sebastian
  • 3,666
  • 2
  • 19
  • 32
  • 1
    i guess you can listen to that in `initState` and navigate with `Navigator.push()` based on the result of the stream – nonybrighto Jul 28 '18 at 04:02
  • Duplicate of https://stackoverflow.com/questions/51333474/right-way-to-handle-navigation-using-bloc See my answer to that question. – boformer Jul 28 '18 at 14:56
  • @nonybrighto The problem I have with that solution is that I'm using a BlocProvider which is an Inherited widget, son to get access to the bloc I need the context inside the build function. So I can't access the bloc in `initState`. Any thoughts on how to get around that? – Sebastian Jul 28 '18 at 21:11
  • @boformer I saw your response but I can't fully understand it. Are you using an InheritedWidget to access the bloc? Do you have any github repo where I can see the full code? Thanks for your answer – Sebastian Jul 28 '18 at 21:14
  • aw! Thats true! I guess you can go with the callback method in the link provided by @boformer – nonybrighto Jul 28 '18 at 23:31
  • @nonybrighto By reading the docs I came across with this line of code "BuildContext get context => _element;" That way you may have access to the context. I have to try it. If I have success I'll post it as an answer – Sebastian Jul 29 '18 at 00:14
  • @Sebastian Instead of passing in the Bloc as a constructor parameter, you can also inherit it from your `InheritedWidget`. Use the `context` in your `build` method to get your Block: `var myBloc = MyInheritedWidget.of(context)` – boformer Jul 29 '18 at 12:56
  • 1
    I think this answer is exactly what you're after: https://stackoverflow.com/a/52167972/4743190 – Mantoska Sep 04 '18 at 15:25
  • @Mantoska Not exactly. He is making use of a BlocProvider which requires a context, but the answer to your link doesn't have that. I'll be posting an alternative method i made – nonybrighto Sep 05 '18 at 23:37
  • 1
    Possible duplicate of [Right way to handle navigation using BLoC](https://stackoverflow.com/questions/51333474/right-way-to-handle-navigation-using-bloc) – Pablo Cegarra Feb 19 '19 at 10:09
  • @Sebastian just put it on the didChangeDependecies method – Jose Jet Apr 03 '19 at 07:47
  • @rémi-rousselet answer makes the most sense to me and is the most comprehensive, covering the pattern you need to use if you are using InheritedWdiget or something based on it like Provider: https://stackoverflow.com/a/54109955/85472 – Maks Aug 02 '19 at 01:01

4 Answers4

11

Use BlockListener.

 BlocListener(
  bloc: _yourBloc,
  listener: (BuildContext context, YourState state) {
    if(state is NavigateToSecondScreen){
      Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {return SecondScreen();}));
    }
  },
  child: childWidget
)
7

The navigator is not working in blocBuilder, because in blocBuilder, you can only return a widget

But BlocListener solved it for me.

Add this code:

BlocListener(
  bloc: _yourBloc,
  listener: (BuildContext context, YourState state) {
    if(state is NavigateToSecondScreen){
      Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {return SecondScreen();}));
    }
  },
  child: childWidget
)
Hossein Sohan
  • 603
  • 7
  • 11
5

First of all: if there isn't any business logic, then there isn't any need to go to YourBloc class.

But from time to time some user's activity is required to perform some logic in Bloc class and then the Bloc class has to decide what to do next: just rebuild widgets or show dialog or even navigate to next route. In such a case, you have to send some State to UI to finish the action.

Then another problem appears: what shall I do with widgets when Bloc sends State to show a toast?

And this is the main issue with all of this story.

A lot of answers and articles recommend to use flutter_block. This library has BlocBuilder and BlocListener. With those classes you can solve some issues, but not 100% of them.

In my case I used BlocConsumer which manages BlocBuilder and BlocListener and provides a brilliant way to manage states.

From the documentation:

BlocConsumer<BlocA, BlocAState>(
  listenWhen: (previous, current) {
    // Return true/false to determine whether or not
    // to invoke listener with state
  },
  listener: (context, state) {
    // Do stuff here based on BlocA's state
  },
  buildWhen: (previous, current) {
    // Return true/false to determine whether or not
    // to rebuild the widget with state
  },
  builder: (context, state) {
    // Return widget here based on BlocA's state
  }
)

As you can see with BlocConsumer, you can filter states: you can easily define states to rebuild widgets and states to show some popups or navigate to the next screen.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Volodymyr Yatsykiv
  • 3,181
  • 1
  • 24
  • 28
0

Something like this:

if (state is PhoneLoginCodeSent) {
    // Dispatch here to reset PhoneLoginFormState
    SchedulerBinding.instance.addPostFrameCallback((_) {
        Navigator.of(context).push(
            MaterialPageRoute(
                builder: (context) {
                    return VerifyCodeForm(phoneLoginBloc: _phoneLoginBloc);
                },
            ),
        );
        return;
    });
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131