How to pass a bloc to another screen using go_router on flutter
Asked Answered
G

2

11

On Flutter I would like to pass a local bloc to another screen. This is how I used to pass a bloc to a new route when using the default navigator.

Navigator.of(context).push(
      MaterialPageRoute<void>(
        builder: (BuildContext context) =>  BlocProvider.value(
          value: localBloc,
          child: MyPage(),
      ),
    ));

But now, I'm using the go_router package for navigation. How can I provide the local bloc to the screen I want using BlocProvider.value().

Gwynethgwynne answered 5/10, 2022 at 7:6 Comment(9)
Does this answer your question? Go_router pass an object/bloc to new routeRuffner
Not really as I won't be able to access it as context.read<LocalBloc>() from the other pageGwynethgwynne
To use context.read, you need to have blocprovider in your widget tree. In the answer I shared, it's passing bloc to the page via constructor on go router. To use context.read, you need to wrap your page with blocprovider on router(in exact same place). Same as your code example.Ruffner
The problem is the bloc might not be available inside the router file as it is a local (not global) bloc, so it's defined inside some page.Gwynethgwynne
I'm not sure if I understand correctly. You have a bloc in some page, you want to access from other page via context.read. For context.read, you need to have bloc provider. So when you define your route, wrap it with blocprovider.value and pass the bloc. You need to use blocprovider.value and pass the same instance that you get from router's state.extra in the other question. What exactly is the problem in that flow? If you pass to router's extra param, you can grab it on router.Ruffner
The problem is when I define the route, the bloc isn't accessible as it's not a global bloc but a local one. Like say I've a Cart Page and a Checkout Page. Now a Cart Bloc is defined in Cart Page(so local bloc). When I navigate to Checkout Page, I want to be able to pass that Cart Bloc as BlocProvider.value(). But that isn't possible. I know it seems like it's very possible, but it isn't. If you think it is, give me a reproducible code.Gwynethgwynne
I understand that it's not a global bloc. That's why you need to pass via router. My reproducible code is exact same as this code: https://mcmap.net/q/1023024/-how-to-pass-a-bloc-to-another-screen-using-go_router-on-flutter What exactly is wrong with this code piece? You are using blocprovider.value when you are defining your routes. value param for blocprovider is coming from router's state. Which is state.extra! as YourBlocClass. Once your page is wrapped with BlocProvider.value, you should be able to access using BlocProvider.of(context) or context.read()Ruffner
the issue is that it is not workingWhereby
just use mobx bloc is such a hassleGeest
K
10

You need to pass the bloc to the route using BlocProvider.value.

context.goNamed('/', extra: HomeBloc());

GoRoute(
  path: '/',
  builder: (context, state) { 
    return BlocProvider.value(
      value: state.extra! as HomeBloc,
      child: HomeScreen(),
    );
  }
),

And then inside HomeScreen you can get bloc using.

final bloc = context.read<HomeBloc>();
Keble answered 19/12, 2022 at 22:29 Comment(0)
D
-3

BlocProviders are lazy loaded by default.

It automatically handles closing the instance when used with Create. By default, Create is called only when the instance is accessed. To override this behavior, set lazy to false.

Therfore instead of passing bloc between routes,The best practice is to:

Provide all the BlocProvider in the main.dart using the MultiBlocProvider

And add it before App() so that all the child can get access to it .

  runApp(
      MultiBlocProvider(
          providers: [
            BlocProvider<BlocA>(
              create: (context) => BlocA(),
            ),
             BlocProvider<BlocB>(
              lazy: false                   // if you want the bloc to be loaded here.
              create: (context) => BlocB(),
            ),

          ],
          child: App()
      )
  );

And access it using BlocProvider.of<BlocA>(context).add([BlocAEvent]); in your desired screen

Disarray answered 15/12, 2022 at 4:15 Comment(6)
What if I wanted the bloc to only be initialized when I am at a certain screen, for example when at search screen. Then wanted to pass the bloc to a search detail screenGwynethgwynne
flutter_bloc takes care of that for you, by loading the bloc only when initialized. I have edited my answer . Please follow upDisarray
Lets say I've a screen that I want the state of it to be initialized each time I open that screen, if the bloc was associated with that screen, upon each routing to that screen the bloc will be created with the initial state. But if I defined it globally, the state will persist even when I re-enter the screen.Gwynethgwynne
create a function randomFunction() in bloc and call it while initializing the bloc, and then every time you open the desired screen, run BlocProvider.of<BlocA>(context).randomFunction()Disarray
Yeah, you see this would've been automatic if the bloc was associated with the screen. And I think this was why such an option was given in the first placeGwynethgwynne
You can use get_it, injectable for that case too, just decorate them as @singleton. I'm using for general purpose ones like purchasebloc, authbloc etc. ButI was(probably OP too) looking a way to initiate new bloc everytime you open the page. Let's say chat page.Ruffner

© 2022 - 2024 — McMap. All rights reserved.