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
MultiBlocProviderin 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 meNot registedthis 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
BlocProviderinWelcomeScreenand also inInitialScreen. I madeMultiBlocProviderin App and register thereLanguageBlocandEnterBlocbut this gives metype '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,
),
),
),
);
}
}
