I have to develop a simple app with Flutter and I am trying for the first time the bloc pattern. I am using flutter_bloc package but I have two main problems and got stuck:
All the screens in the app have blocs that depends on repos to make requests to a server, and for each request I need a token. I obtain the token at the authentication_bloc (login), but I can't share that token to all the blocs unless I make them dependent on auth_bloc (the bloc who has the user model storing the token). For a simple app, maybe it works but if other bloc has more bloc dependencies besides the auth_bloc, it could become a mess injecting all the blocs as dependencies. What alternative could be a good solution?
For the navigation I have a bloc for bottom navigation bar. But I want to all the screens on tabs can call the change_tab() function at bottom bar bloc. And again is the same problem, I only can add events at bottom bar bloc if the other blocs have a dependency on bottom bar bloc. I dont want to add the auth bloc and the bottom bar bloc as dependencies on all the blocs that manages all the screens.
I appreciate advices, alternatives or documentation where I can learn to build a good architecture for apps with bloc.
Hi @limonadev 馃憢
Thanks for opening an issue!
Regarding your questions:
Your blocs should not care about tokens because that is a networking layer detail. Ideally, the bloc should invoke a method on the repository (getPosts). The repository would then invoke a get on the PostsApiClient which has a dependency on an HttpClient. You could then use http interceptors to handle automatically reading the auth token from keystore/keychain using flutter_secure_storage and appending it to the request header.
Can you provide some more detail? If you provide a TabBarBloc then all children in the subtree can access the TabBarBloc via BuildContext like BlocProvider.of<TabBarBloc>(context). You can then add the event in response to onPress or other callbacks in the presentation layer. If you want to add an event in response to a state change in another bloc, I would recommend using BlocListener like:
BlocListener<OtherBloc, OtherBlocState>(
listener: (context, state) {
if (state is DesiredState) {
BlocProvider.of<TabBarBloc>(context).add(ChangeTab(...));
}
},
child: MyChild(),
)
I would also like to add that you might not even need a bloc for the bottom navigation bar because if there's no business logic involved you should simply do the navigation within the widget itself.
Hope that helps 馃憤
Thank you very much for your answer.
change_tab() I will need to add a listener for each one, right?Again, thank you very much for your help.
No problem!
HttpClient called onTokenExpired and you can add a AuthenticationRevoked event to the AuthenticationBloc in that case.final _httpClient = HttpClient(onTokenExpired: () => _authenticationBloc.add(AuthenticationRevoked()));
Closing for now but feel free to add additional comments/questions and I'm happy to continue the conversation 馃憤
@felangel Oh my goodness, I've been racking my head on how to do exactly what you're describing with the authentication error. Would you mind providing a more detailed example? I have not been able to figure out how to make this work in my own app. I hate how I'm handling authentication errors in my application right now.
@sdstolworthy sure thing! I'll try to create a standalone example over the next few days 馃憤
Thanks @felangel
@felangel I am sorry I need to reopen the question. In your answer
final _httpClient = HttpClient(onTokenExpired: () => _authenticationBloc.add(AuthenticationRevoked()));
the _httpClient has a dependency on _authenticationBloc. So, all the http clients the app create (for each data provider) should have that dependency (unless it becomes singleton). Unless I had misunderstood.
Also, suppose I have an AuthBloc with a dependency on a UserRepository (just like the example on Firebase Auth). If the UserRepository has a dependency on some ApiClient, the order of "creation" would be:
But if I want to have a callback onTokenExpired on the http client, I should pass it on the first step (in the ApiClient). But I can't have that callback because the AuthBloc is instanced on step 3. Is a cyclic dependency! (Again, sorry if I misunderstood something).
If somehow that is solved, I still need to communicate some Event from XBloc to the AuthBloc. For example, if I have 5 blocs (BlocA, BlocB, etc), and all of them had their own repos, I would need to communicate from any of them "Hey AuthBloc, the token has expired so I need to go to the login". That can be made injecting the AuthBloc to each one, but is that the intended way to work? I mean, each time I create a new Bloc I would have to pass the AuthBloc to it.
I tried some ideas, but even if they work, I think they are not very maintainable. For example, I saved an "status" on the UserRepository and I made it Singleton. I subscribed the AuthBloc to the status and when the status if "token expired" the AuthBloc react to it. And every query from other repositories (called from other Blocs) that use the token call the Singleton UserRepository to get the token. If the token has expired the status change and the AuthBloc rebuild the login, but the query feels "incomplete" because the Bloc that called it never got a response. I think mine it's a bad way to handle this, so if you can help me with some advices or a simple example about how is the correct way I will appreciate that a lot. Thanks.
@limonadev I'm working on a sample app for this since it's a highly requested item. I'll comment with a link once it's ready 馃憤
@felangel Thank you very much! :)
Most helpful comment
@sdstolworthy sure thing! I'll try to create a standalone example over the next few days 馃憤