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 meNot 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
inWelcomeScreen
and also inInitialScreen
. I madeMultiBlocProvider
in App and register thereLanguageBloc
andEnterBloc
but 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,
),
),
),
);
}
}