This is more of a question than a bug report, but maybe someone has an idea about this:
I want to use flutter_cache_manager to retrieve and cache data from an API for an offline-first experience. In general no problem.
The way I plan to use it is to use the getFileStream() method of it, which returns a stream, with the first item being the cached file if available, and the second item being the freshly downloaded item, in case there is no cached version or the cached version got outdated).
What I now want to do is fire a Load event on my bloc, in which it calls getFileStream() and then for each resulting File parses the JSON result, builds an object structure and yields a new State with the resulting object.
End result should be that if there is a cached File, the view can be rendered immediately and if the File is outdated, a new version is downloaded in the background and then the view is silently updated.
I'd be thankful for any pointers :)
Hi @DASPRiD โ
You could just subscribe to that stream inside bloc and yield proper states based on the incoming data. One quick way would be around the lines of:
_sub?.cancel();
_sub = getFileStream().listen((data) {
if(state is Initial) {
if (data == null) yield Loading(); // no initial cache value
else yield Loaded(data); // initial cache value
} else {
yield Loaded(data); // fresh downloaded value
}
});
You'd also want to cancel the sub in bloc's close method.
Oh, that sounds perfect! :) Just one small problem:
I have to make the listen method async, since inside I'm doing this:
var source = await response.file.readAsString();
Now my yield complains that the parent is not async*. I could make that change, but the yield then wouldn't propagate through the mapEventToState function, would it?
@DASPRiD my example was more of a pseudo-code.
You should definitely add an event to the bloc and process it properly.
getFileStream().listen((data) => add(DataReceived(data)))
In mapEventToState process this event around the lines of my previous suggestion.
Hope that helps! ๐
Ah yeah, that totally helped, thanks a lot! Still very new to even just Flutter, even more so to blocs, so every bit helps, thanks a lot again for the really quick help, you're amazing! :)
Now just a last final unrelated question (no need to create another ticket for this I guess). This is mainly an architectural question.
I have an authentication bloc for my OAuth2 handling, which gets a user repository injected to handle actual authentication with AppAuth. All of that is working really well so far. The tricky part for me was handling the access and refresh token, but I think I managed to handle that somewhat elegantly:
The user repository has an async method getCurrentUser(), which other blocs can call to get the current user. That method will check if the access token is still valid. If so, it just returns the user. Otherwise it tries to get a new token with the refresh token. If the fresh fails due to a network error, a specific exception for that is thrown. Now the part where I'm stuck:
If the refresh token re-authentication fails, I throw a specific exception for that, so the calling bloc can handle that (e.g. emit a state so the calling widget can redirect the user back to the start page). The problem now is that my authentication bloc still has a status of "Authenticated" in it, so the drawer shows the user still as signed in.
My idea would be to inject the authentication bloc into the user repository after creating the bloc, so that the user repository can emit an event that re-authentication failed. Would you consider such cross-referencing as a sane way to handle this? Also I assume I should make the user repository a global object so all blocs use that same instance?
Again, thanks a lot for your time!
I'd recommend having a look at a package made by @felangel https://pub.dev/packages/fresh.
I'd advice against injecting blocs into repos since that's an anti-pattern. Only repos should be injected into blocs. A single instance of your user repo should be used in all the blocs that care about it for sure. I personally don't expose repositories to the widget tree, since they should only be used within blocs and not directly from the UI. ๐
Well, I like using AppAuth for this (since I'm using Code authentication with PKCE). But that package's example actually gave me a good pointer:
The user repository can just expose a stream the bloc can listen to. And yeah, I don't want to use the user repository in the widget tree, only in blocs.
Closing this for now since it seems @RollyPeres was able to point you in the right direction ๐
Feel free to comment with additional questions and I'm happy to continue the conversation ๐
Most helpful comment
Well, I like using AppAuth for this (since I'm using Code authentication with PKCE). But that package's example actually gave me a good pointer:
https://github.com/felangel/fresh/blob/master/example/lib/authentication/bloc/authentication_bloc.dart#L15-L17
The user repository can just expose a stream the bloc can listen to. And yeah, I don't want to use the user repository in the widget tree, only in blocs.