Convenient way to share BLoC between multiple screen
Asked Answered
M

3

6

I am looking for the most convenient way to share bloc in different screens/pages without providing at top of the material app. For example I have a Customer bloc, which is required in CustomerListScreen and CustomerDetailsScreen. The bloc is created in CustomerListScreen and passed it to the CustomerDetailsScreen while navigating.

Navigator.context, MaterialPageRoute(builder: (context) => CustomerDetailsScreen(context.read<CustomerBloc>())));

This is the procedure I am following right now. Looking for any better approach available out there..

Mlle answered 3/8, 2022 at 6:42 Comment(0)
E
6

You could make the Bloc a required field of the page, something like this:

class CustomerDetailsScreen extends StatelessWidget { 
  CustomerDetailsScreen(this.mybloc);

  final Bloc mybloc;

  @override
  Widget build(BuildContext context) {
    return  BlocProvider.value(
      value: mybloc,
      child: Text('Body...'),
    );
  }
}

Now, even if you use a package like AutoRoute you will still be able to provide the bloc to the page route.

Even though I don't like this solution because what if you navigated through a url then you can't pass the bloc to it, for this i recommend to use nested navigation read this

it will look something like this if you use AutoRoute

@MaterialAutoRouter(              
  replaceInRouteName: 'Page,Route',              
  routes: <AutoRoute>[              
    AutoRoute(                            
      page: BlocProviderPage,              
      children: [              
        AutoRoute(page: CustomerListScreen),              
        AutoRoute(page: CustomerDetailsScreen),          
      ],              
    ),             
  ],              
)              
class $AppRouter {} 
class BlocProviderPage extends StatelessWidget {          
  @override          
  Widget build(BuildContext context) {          
    return BlocProvider(
            create: (context) => MyBloc(),       
            child: AutoRouter(),          
    );          
  }          
}  

This way both pages will have access to the bloc and you cant navigate to them without a BlocProviderPage being a parent of them

Electrocardiogram answered 3/8, 2022 at 8:20 Comment(1)
This was perfect for my use case! I'm using auto_route with bloc, and although I've worked with nested routes before (in a screen that contains a BottomNavBar), this solution didn't come to mind when I wanted to share a cubit in a case similar to this. Thanks!Jonjona
S
10

Use BlocProvider.value.

BlocProvider can be used to provide an existing bloc to a new portion of the widget tree. This will be most commonly used when an existing bloc needs to be made available to a new route. In this case, BlocProvider will not automatically close the bloc since it did not create it.

BlocProvider.value(
  value: context.read<CustomerBloc>(),
  child: CustomerDetailsScreen(),
);
Sterne answered 3/8, 2022 at 7:54 Comment(4)
thanks for your ans brother, but its not possible in case of using any navigation related packages like AutoRoute or something like that.Mlle
That's another question, but use AutoRouteWrapper (pub.dev/packages/auto_route#wrapping-routes) for that.Sterne
I am not sure this can be used to provide the bloc to another screen. When navigating to a new screen, the context changes completely doesn't it?. A new tree is generated. Hence you need to pass it as argument to the new screen?Addictive
@Faz, the other piece for this puzzle is to involve those Blocs you want to be inherited in a higher level point, such as App. That point of context is above what changes, so then this snippet in the solution is able to get those Blocs from the context. Essentially that's what happens in ArinFaraj's answer too: the AppRouter level is above the pages and hence will be "shared".Millesimal
E
6

You could make the Bloc a required field of the page, something like this:

class CustomerDetailsScreen extends StatelessWidget { 
  CustomerDetailsScreen(this.mybloc);

  final Bloc mybloc;

  @override
  Widget build(BuildContext context) {
    return  BlocProvider.value(
      value: mybloc,
      child: Text('Body...'),
    );
  }
}

Now, even if you use a package like AutoRoute you will still be able to provide the bloc to the page route.

Even though I don't like this solution because what if you navigated through a url then you can't pass the bloc to it, for this i recommend to use nested navigation read this

it will look something like this if you use AutoRoute

@MaterialAutoRouter(              
  replaceInRouteName: 'Page,Route',              
  routes: <AutoRoute>[              
    AutoRoute(                            
      page: BlocProviderPage,              
      children: [              
        AutoRoute(page: CustomerListScreen),              
        AutoRoute(page: CustomerDetailsScreen),          
      ],              
    ),             
  ],              
)              
class $AppRouter {} 
class BlocProviderPage extends StatelessWidget {          
  @override          
  Widget build(BuildContext context) {          
    return BlocProvider(
            create: (context) => MyBloc(),       
            child: AutoRouter(),          
    );          
  }          
}  

This way both pages will have access to the bloc and you cant navigate to them without a BlocProviderPage being a parent of them

Electrocardiogram answered 3/8, 2022 at 8:20 Comment(1)
This was perfect for my use case! I'm using auto_route with bloc, and although I've worked with nested routes before (in a screen that contains a BottomNavBar), this solution didn't come to mind when I wanted to share a cubit in a case similar to this. Thanks!Jonjona
D
0

Change the context name to other name and pass that context like below

 @override
 Widget build(BuildContext context) {
 return MultiBlocProvider(
    providers: [
      BlocProvider<AuthBloc>(
        create: (BuildContext context) => AuthBloc(),
      ),
    ],
    child: Scaffold(
        appBar: AppBar(
          title: const Text("Log In"),
          centerTitle: true,
          backgroundColor: ContactColors.primaryColor,
        ),
        backgroundColor: ContactColors.offwhite,
        body: BlocConsumer<AuthBloc, AuthState>(listener: (c, state) {
          if (state is AuthError) {
            print("error state");
          } else if (state is AuthSuccess) {
            print("success");
          }
        }, builder: (c, state) {
          if (state is AuthLoading) {
            print("loader");
            return loginbox(c);
          } else {
            return loginbox(c);
          }
          // return widget here based on BlocA's state
        })));
}
Dean answered 29/11, 2022 at 9:37 Comment(1)
Passing context to other view is not a good practiceMlle

© 2022 - 2024 — McMap. All rights reserved.