6

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().

Abdi mussa
  • 171
  • 11
  • Does this answer your question? [Go\_router pass an object/bloc to new route](https://stackoverflow.com/questions/74581819/go-router-pass-an-object-bloc-to-new-route) – mirkancal Aug 10 '23 at 17:04
  • Not really as I won't be able to access it as context.read() from the other page – Abdi mussa Aug 11 '23 at 07:52
  • 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. – mirkancal Aug 11 '23 at 10:15
  • 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. – Abdi mussa Aug 11 '23 at 10:39
  • 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. – mirkancal Aug 12 '23 at 10:18
  • 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. – Abdi mussa Aug 14 '23 at 07:19
  • 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://stackoverflow.com/a/74856689/9779791 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() – mirkancal Aug 15 '23 at 10:58

2 Answers2

2

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>();
P.J.Radadiya
  • 1,493
  • 1
  • 12
  • 21
-1

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

krishnaacharyaa
  • 14,953
  • 4
  • 49
  • 88
  • 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 screen – Abdi mussa Dec 15 '22 at 12:28
  • `flutter_bloc` takes care of that for you, by loading the `bloc` only when initialized. I have edited my answer . Please follow up – krishnaacharyaa Dec 15 '22 at 13:09
  • 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. – Abdi mussa Dec 16 '22 at 07:20
  • 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(context).randomFunction()` – krishnaacharyaa Dec 16 '22 at 07:56
  • 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 place – Abdi mussa Dec 16 '22 at 08:05
  • 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. – mirkancal Aug 10 '23 at 16:40