Home > front end >  Accessing state of bloc in different view where app is using onGenerateRoute
Accessing state of bloc in different view where app is using onGenerateRoute

Time:10-17

Context: I want to share Language across entire app. Due to that I want to have single instance of LanguageBloc which I can access across application to retrieve it's state which is LanguageModel

Problem: How to solve dependencies? Because I am using onGenerateRoute I don't know how to make one instance BlocProvider from LanguageBloc that can be accessed in different screen. I noticed I am creating each time new instance and of course new instance has no knowledge of LanguageBloc

Code:

main

void main() {
  runApp(App(Routes()));
}

class App extends StatelessWidget {
  final Routes routes;

  const App(this.routes, {super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'test',
      onGenerateRoute: routes.onGenerateRoute,
      home: RepositoryProvider(
        create: (context) => LanguageService(),
        child: InitialScreen(key: UniqueKey()),
      ),
      theme: ThemeData(
        primarySwatch: kPrimary,
        textTheme: kTextTheme,
      ),
    );
  }
}

Route

class Routes {
  Route onGenerateRoute(RouteSettings settings) {
    switch (settings.name) {
      case RoutesNames.initialScreen:
        return MaterialPageRoute(
          builder: (_) => InitialScreen(key: UniqueKey()),
        );
      case RoutesNames.welcomeScreen:
        return MaterialPageRoute(
          builder: (_) => RepositoryProvider(
            create: (context) => UserService(),
            child: WelcomeScreen(key: UniqueKey()),
          ),
        );
      case RoutesNames.privacyPolicyScreen:
        return MaterialPageRoute(
          builder: (_) => PrivacyPolicyScreen(key: UniqueKey()),
        );
      case RoutesNames.termsAndConditionsScreen:
        return MaterialPageRoute(
          builder: (_) => TermsAndConditionScreen(key: UniqueKey()),
        );
      case RoutesNames.homeScreen:
        return MaterialPageRoute(
          builder: (_) => HomeScreen(key: UniqueKey()),
        );
      default:
        return MaterialPageRoute(
          builder: (_) => WelcomeScreen(key: UniqueKey()),
        );
    }
  }
}

InitialScreen

class InitialScreen extends StatelessWidget {
  const InitialScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => LanguageBloc(
        languageService: RepositoryProvider.of<LanguageService>(context),
      )..add(const LanguageInitialEvent()),
      child: Scaffold(
        body: InitialScreenView(
          key: UniqueKey(),
        ),
      ),
    );
  }
}

class InitialScreenView extends StatelessWidget {
  const InitialScreenView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<LanguageBloc, LanguageState>(
      builder: (builderContext, state) {
        if (state is LanguageSuccessfulState) {
          SchedulerBinding.instance.addPostFrameCallback((_) {
            Navigator.pushReplacementNamed(context, RoutesNames.welcomeScreen);
          });
        }
      },
    );

WelcomeScreen

class WelcomeScreen extends StatelessWidget {
  WelcomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => EnterBloc(
        userService: RepositoryProvider.of<UserService>(context),
      )..add(const EnterInitialEvent()),
      child: Scaffold(
        body: WelcomeScreenView(),
      ),
    );
  }
}

class WelcomeScreenView extends StatelessWidget {
  WelcomeScreenView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<EnterBloc, EnterState>(
      builder: (builderContext, state) {
        //...
      }
    );

Question: How in WelcomeScreenView.build I can access LanguageState?

What I have tried:

  • I tried using MultiBlocProvider in front of MaterialApp so I would create only one instance. Sadly that gives null value and it cannot show first screen
  • I tried using context.select<LanguageBloc, LanguageModel?>((LanguageBloc bloc) but first it was giving me null value, but later after a lot of tweaking it gave me Not registed this is probably because I create new instance
  • this is what I want to achieve and I tried make my solution based on this How to use bloc pattern between two screens so I removed BlocProvider in WelcomeScreen and also in InitialScreen. I made MultiBlocProvider in App and register there LanguageBloc and EnterBloc but this gives me type 'Null' is not a subtype of type 'LanguageBloc' in type cast

CodePudding user response:

Move the repository to the first level and on the second level place the MultiBlocProvider, to not have other bloc instances, remove the bloc create and repository create from the screens and keep only the blocBuilder. With this, both repositories and blocs will be available in the context of the entire application.

class App extends StatelessWidget {
  final Routes routes;

  const App(this.routes, {super.key});

  @override
  Widget build(BuildContext context) {
    return MultiRepositoryProvider(
      providers: [
        RepositoryProvider<LanguageService>(
          create: (context) => LanguageService(),
        ),
        RepositoryProvider<UserService>(
          create: (context) => UserService(),
        ),
      ],
      child: MultiBlocProvider(
        providers: [
          BlocProvider<LanguageBloc>(
              create: (context) => LanguageBloc(
                    languageService:
                        RepositoryProvider.of<LanguageService>(context),
                  )..add(const LanguageInitialEvent())),
          BlocProvider<EnterBloc>(
              create: (context) => EnterBloc(
                    userService: RepositoryProvider.of<UserService>(context),
                  )..add(const EnterInitialEvent())),
        ],
        child: MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'test',
          onGenerateRoute: routes.onGenerateRoute,
          home: InitialScreen(key: UniqueKey()),
          theme: ThemeData(
            primarySwatch: kPrimary,
            textTheme: kTextTheme,
          ),
        ),
      ),
    );
  }
}
  • Related