I noticed, in the BLoC get started example: Login Page We have code like this :
void main() {
BlocSupervisor.delegate = SimpleBlocDelegate();
final userRepository = UserRepository();
runApp(
BlocProvider<AuthenticationBloc>(
builder: (context) {
return AuthenticationBloc(userRepository: userRepository)
..dispatch(AppStarted());
},
child: App(userRepository: userRepository),
),
);
}
class App extends StatelessWidget {
final UserRepository userRepository;
App({Key key, @required this.userRepository}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationUninitialized) {
return SplashPage();
}
if (state is AuthenticationAuthenticated) {
return HomePage();
}
if (state is AuthenticationUnauthenticated) {
return LoginPage(userRepository: userRepository);
}
if (state is AuthenticationLoading) {
return LoadingIndicator();
}
},
),
);
}
}
My question is, what is the considerations of putting a userRepository at the top of the app, and then pass the userRepository into blocs. If I put the userRepository in LoginPageBloc's definition like
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final UserRepository userRepository = userRepository();
What would be different in this approach ? My consideration is , since userRepository is a singleton, it should be able to access from everywhere in the app. The downside might be we cannot control when the repository is created/destroyed. But I think Repository should exist as long as the app is alive, and we can initilize all repositories in the App Initialization Page.
The reason I don't feel very comfortable with the former approach is : I have to define the userRepository from somewhere I don't use the repository at all, and it is hard to manage if new repositorys were involved in blocs : I have to declare the repository at the top of the app and findout where to pass them in like a normal dependency injection trouble.
Welcome everyone for your opinions !
Hi @imaffe 馃憢
Thanks for opening an issue!
Regarding your question, there are several reasons why would shouldn't instantiate the UserRepository directly inside of you bloc:
Separation of Concerns - If you inject UserRepository into the bloc rather than instantiating it, you can swap out implementations by having a common interface (abstract class) which you implement.
Testability - you will not be able easily test the bloc because you won't be able to inject a mock UserRepository (related to 1).
Concurrency Issues - if you always instantiate a new UserRepository directly where you need it you will be working with different instances of the repository and you'll be at risk of introducing bugs if your UserRepository has any state since the state will not be synchronized between the different instances
If you don't like manually injecting the UserRepository via constructor, you can also provide the Repository via RepositoryProvider like so:
@override
Widget build(BuildContext context) {
return RepositoryProvider<UserRepository>(
builder: (context) => UserRepository(),
child: MyChild(),
);
}
Then later in MyChild or any of MyChild's children you can access the UserRepository via
RepositoryProvider.of<UserRepository>(context);
If you provided your UserRepository at the root of your app you can create blocs anywhere in the widget tree and inject the UserRepository instance like so:
@override
Widget build(BuildContext context) {
return BlocProvider<MyBloc>(
builder: (context) => MyBloc(userRepository: RepositoryProvider.of<UserRepository>(context)),
child: MyChild(),
);
}
I would highly recommend using some sort of dependency injection to promote reusability, testability and maintainability. Hope that helps 馃憤
1 and 2 is really helpful for me understanding why DI is better in this case. For 3, I would implement Repository as a singleton so if I instantiate it in multiple places, I would have the same instance. Just for clarification in case someone else saws it. Your answer is really helpful and clear, thanks so much !
Most helpful comment
1 and 2 is really helpful for me understanding why DI is better in this case. For 3, I would implement Repository as a singleton so if I instantiate it in multiple places, I would have the same instance. Just for clarification in case someone else saws it. Your answer is really helpful and clear, thanks so much !