I have a problem implementing an app on Flutter using flutter_bloc. I understood the core concepts but I found an "edge case" where there is not examples or guides (at least that I could find):
(I will simplify the problem) I have a bloc called AuthBloc
that manages the App
. If the state is NotAuthenticated
then the App should show the LoginScreen
but if is Authenticated
the App should show the HomeScreen
. Inside the HomeScreen
I have 4 blocs, where each bloc has its states and events, but all of them has dependencies on different Repositories
to get some data from an API.
All the Repositories
need a token to make the API requests. The first problem came here. How can I get the token from all the repositories? If I use a UserRepository
to store the token, I will need to pass it as dependency to each Repository (probably works but I don't think its the right way to do it). So, what can be the right way to manage this?
The second problem is:
If somehow I can get the token on all the repositories queries, what happens when the token is revoked? The app should return to the LoginScreen
and for that I would need to notify the AuthBloc
through an event (for example InvalidTokenEvent
). And the AuthBloc
should change its state to NotAuthenticated
and that will rebuild the LoginScreen
. But the question is: How can I notify the AuthBloc
from other blocs or repositories? The first idea I had is through dependency injection: I can pass the AuthBloc to every other bloc in the constructor, so when the repository request returns a token expired, the XBloc
can call AuthBloc.add(InvalidTokenEvent)
. But again, if I have a lot of blocs I would need to do that in each bloc. So, what's the right way to do this?
Thank you for any help!
To answer your first problem:
Your repositories should not be handling the API authentication. That should be the job of the data layer under the repository layer.
What I recommend is having one instance of a http client and then provide this instance to all the repositories. This way your client can have an interceptor that handles adding the saved token to all your requests. So your repositories could look something like this:
class OrderRepository {
const OrderRepository(this.httpClient);
final HttpClient httpClient;
}
Then the rest would be as simple as initializing your httpclient then pass it to your repositories which you can provide to your app and blocs by using RepositoryProvider
or MultiRepositoryProvider
. Then do the same thing with your repositories and blocs passing your repositories to your blocs inside of your blocproviders.
To answer your second problem:
If authenticating and passing your token to all requests can be handled at the http client layer so should unauthenticating and revoking the token. Therefore to reflect this in your authentication state your AuthRepository should listen to your http client and similarly your AuthBloc should listen to your AuthRepository. You can do this by exposing a stream of your authentication status in both your http client and your AuthRepository that gets listened to in your auth bloc.
So it would look like this: http client gets a 401 error so it removes the token from storage and then adds the unauthenticated status to the stream. This stream is then exposed in auth repository and then in auth bloc have a stream subscription which listens to the authentication status to emit an unauthenticated state.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With