Im making a type of chat app, and has a need for multiple blocs/repositories/etc. This has lead to my widget tree looking like this:
And also has lead to a nasty looking build method in my main.dart:
@override
Widget build(BuildContext context) {
return MultiRepositoryProvider(
providers: [
RepositoryProvider(
create: (_) => AuthRepository(),
),
RepositoryProvider(
create: (_) => FirestoreRepository(),
),
RepositoryProvider(
create: (context) => StorageRepository(),
),
RepositoryProvider(
create: (context) => MessagingRepository(),
),
],
child: MultiBlocProvider(
providers: [
BlocProvider<AuthBloc>(
create: (context) => AuthBloc(
authRepository: context.read<AuthRepository>(),
),
),
BlocProvider<BottomnavbarCubit>(
create: (context) => BottomnavbarCubit(),
child: MainPage(),
),
BlocProvider<SignupCubit>(
create: (context) => SignupCubit(
authRepository: context.read<AuthRepository>(),
),
),
BlocProvider<LocaluserCubit>(
create: (context) => LocaluserCubit(),
child: MaterialApp(),
),
BlocProvider<SigninCubit>(
create: (context) => SigninCubit(
authRepository: context.read<AuthRepository>(),
),
),
BlocProvider<UpdateCubit>(
create: (context) => UpdateCubit(
firestoreRepository: context.read<FirestoreRepository>(),
),
),
BlocProvider<ProfileBloc>(
create: (context) => ProfileBloc(
authBloc: BlocProvider.of<AuthBloc>(context),
databaseRepository: context.read<FirestoreRepository>(),
)),
BlocProvider<VoteBloc>(
create: (context) => VoteBloc(
firestoreRepository: context.read<FirestoreRepository>())),
BlocProvider<MessageBloc>(
create: (context) => MessageBloc(
firestoreRepository: context.read<FirestoreRepository>())),
BlocProvider<LeaderboardBloc>(
create: (context) => LeaderboardBloc(
databaseRepository: context.read<FirestoreRepository>())),
BlocProvider<CommentBloc>(
create: (context) => CommentBloc(
firestoreRepository: context.read<FirestoreRepository>())),
BlocProvider<EditbioCubit>(
create: (context) => EditbioCubit(),
),
BlocProvider<CommentCubit>(
create: (context) => CommentCubit(),
),
BlocProvider<SearchBloc>(
create: (context) => SearchBloc(),
),
BlocProvider(
create: (context) => SwipeBloc(
firestoreRepository: context.read<FirestoreRepository>(),
profileBloc: context.read<ProfileBloc>(),
),
),
BlocProvider<StingrayBloc>(
create: (context) => StingrayBloc(
authBloc: BlocProvider.of<AuthBloc>(context),
databaseRepository: context.read<FirestoreRepository>(),
)),
],
child: MultiProvider(
providers: [
Provider<AuthRepository>(
create: (_) => AuthRepository(),
),
StreamProvider<List<Stingray?>>.value(
value: FirestoreRepository().stingrays,
initialData: [],
),
],
child: MaterialApp(
title: 'Fishbowl',
theme: theme(),
onGenerateRoute: AppRouter.onGenerateRoute,
initialRoute: Wrapper.routeName,
),
),
),
);
}
Is this normal? I looked into GetIt, but i read that it is difficult to do dependency injection with blocs. Not sure if this is a fixable issue, or if this is just how working with multiple blocs work. Thank you!
Q: Is this normal?
A: Yes, this is perfectly normal. TheMultiBlocProvider
is used in the same way and this is the resulting tree structure.
Q: I looked into GetIt, but i read that it is difficult to do dependency injection with blocs
A: No, it's not difficult to do DI with Blocs. Rather its very simple. And, it's just like injecting other instance. However, there is a cost and problem associated with it.
You can inject using below code:
return BlocProvider(
create: (ctx) => getIt<SomeBloc>(),
child: SomeChildWidget(),
);
To use getIt
with Bloc
, there is only one problem that must be addressed properly.
Bloc
's cannot and must not be dependency injected in Flutter using getIt
. The reason being Bloc
maintains the state changes and creation and destruction of Bloc
's and Cubit
's. If you just inject it's instance, then BlocProvider
will always get the same instance with previous states and all. This will cause us to manually reset the state of Bloc
leading to unnecessary state management which is against the Bloc
's principles of state management. Event
s should update states not us.
Bloc
should not me DI
ed. However, the dependencies used by Bloc
can be DI
ed. FOr instance, repository
and data sources
can be injected into the Bloc
. But never the Bloc
itself.
We use Bloc
extensively with GetIt
, just that we do not use it on the Bloc
instance itself, rather with the dependencies needed for the Bloc
's.
UPDATE::
We can use @injectable
to make new instances of BLoC
's as well. However, when we need to pass in parameters in constructor as per usage to BLoC
, I personally refrain from creating them manually.