Closed Tregan closed 5 years ago
Hi @Tregan π Thanks for opening an issue and for giving the bloc library a try!
Can you please provide a bit more information about what your concerns are? Iβm not sure I understand what you mean by a queue of requests. If you can provide a code snippet of how youβre handling the issue without bloc it would be super helpful! Thanks π
Our app is currently made in Unity. Unity is way overkill for what we need, but at the time it was the only proper cross-platform solution around. The app supports offline-mode, so that means we have to fetch a lot of content once the user logs in, for example topics, questions, statistics, images, opponents. We need those requests to finish in a specific order (most important to least important), so we add everything to a queue. Once a request is finished, we fire an event so the listeners know that they have to refresh the views, and then next queued request is started.
What we're struggling with is how we'd tackle something like this in Flutter. Would we need a separate bloc/event/state class per request? What class would be keeping track of the requests? The repository, or the bloc, or something else? If we use 1 bloc for all requests, how would we handle events and states? What if some request is already running and we want to queue/start another one?
Looking at the weather example, the repository has 1 function that uses 2 api calls. Our repository would have a lot of functions with most of them using 1 api call, which could all be called in succession (queue) or while another request is already running.
An example of what we want: User enters credentials -> fetch session id -> fetch user data -> fetch organization -> fetch additional user data from 5 api routes -> go to home page -> fetch content (queue [all of these are paginated requests]: topics, certificates of those topics, questions of those topics, statistics of those questions, images of those questions, opponents of those topics)
Sorry if this only confuses you more, haha. We're probably still stuck in C# π Really appreciate the help though!
Yours, Bas
-Edit- Added example flow
I'm not @felangel, but I can offer a little insight from my time using his library.
What class would be keeping track of the requests? The repository, or the bloc, or something else?
The bloc
should call functions in the repository that, in turn, probably look through internal sources
classes which check local memory, maybe a local file store cache (all this stuff is optional), and finally, at the bottom of the stack of sources
, is an API source that makes network requests. This code is pulled straight from a side project of my own and lives in my BaseRepository
class, and loops over its known sources to find data:
abstract class BaseRepository<T> {
List<Source> sources;
Future<List<T>> getItems(Map<String, dynamic> params) async {
List<T> items = [];
List<Source> emptySources = [];
// First check local memory, then fall back to a network request to the API
for (var source in sources) {
List<T> _items = await source.getItems(params);
if (_items != null) {
items = _items;
break;
} else {
// Note which sources came up empty. Only local storage sources actually matter
// in this list
emptySources.add(source);
}
}
// Classic caching of data locally to prevent network calls next time
for (final Source source in emptySources) {
source.setItems(items);
}
return items;
}
}
With the above example (if it makes any sense), hopefully you can see that you would not want a separate bloc and repository for request. But you probably do want a separate repository per model type (use generics to write it once!), and then a unique bloc
for each logical section of your app. In my experience so far, I just have to play with that to figure out where to draw the lines between blocs. Maybe @felangel has more concrete rules of thumb.
Hope this clarifies some of it!
@craiglabenz That already made things so much more clear, thanks! Let's see if I indeed understand it: Say we have 3 models user, organization, topic, question.
Then all these blocs are made available by the root MaterialApp using BlocProviderTree, so pages that need them can access them whenever. Whenever a user logged in and we have their id, we can dispatch FetchUser(userId) -> UserLoaded state is fired -> dispatch FetchOrganization(userId) -> OrganizationLoaded is fired. Same for the topics and questions. Does that sound about right? π Or would it be better to, for example, have questions in their own repository that the topic knows about?
At a glance, that all sounds exactly right, @Tregan. I also found that keeping all of my blocs at the top level and making them available anywhere via the BlocProvider
widget was the best situation for my app.
Thanks @craiglabenz for your detailed answer! I really appreciate the help π
I would just like to add that you should consider scoping the blocs only to the part of the widget tree that needs them instead of declaring them all at the root level.
In addition, your repositories should represent the different domains of your application whereas the blocs should be more feature-driven (as @craiglabenz mentioned). You might have a UserRepository
and a OrganizationRepository
with many blocs for each of your features that have a dependency on one or more repository.
If you haven't already, I highly recommend checking out the Bloc Architecture Docs.
Hope that helps! Closing this for now but feel free to comment with additional questions and I'm happy to continue the conversation π
First of all: thanks for the replies guys, this is really helping a lot π
I would just like to add that you should consider scoping the blocs only to the part of the widget tree that needs them instead of declaring them all at the root level.
Makes sense, I don't really want the full application to have access to everything, but a lot of pages will need access to a lot of different blocs. Would it be a good idea to add the blocs to required parameters of the pages, so that they are passed to the pages by app in MaterialApp onGenerateRoute? Then the page state could ofcourse access the bloc using widget.blocVariable
Heyo!
So, we've decided to start rewriting our app using Flutter, using Bloc for state management. We're kind of stuck with the api part. I've checked out the weather example, but what could we do to implement a queue of requests? It seems that the bloc would get really big if we have events and states for every request (to give an idea: we have about 50 api routes)
Thanks in advance!
Yours, Bas