This content is not available in your language yet.
In this tutorial, we’re going to be implementing an app which fetches data over the network and loads it as a user scrolls using Flutter and the bloc library.
We’ll start off by creating a brand new Flutter project
We can then go ahead and replace the contents of pubspec.yaml with
and then install all of our dependencies
Project Structure
The application uses a feature-driven directory structure. This project structure enables us to scale the project by having self-contained features. In this example we will only have a single feature (the post feature) and it’s split up into respective folders with barrel files, indicated by the asterisk (*).
REST API
For this demo application, we’ll be using jsonplaceholder as our data source.
Great, now that we know what our data is going to look like, let’s create the model.
Data Model
Create post.dart and let’s get to work creating the model of our Post object.
Post is just a class with an id, title, and body.
Now that we have our Post object model, let’s start working on the Business Logic Component (bloc).
Post Events
Before we dive into the implementation, we need to define what our PostBloc is going to be doing.
At a high level, it will be responding to user input (scrolling) and fetching more posts in order for the presentation layer to display them. Let’s start by creating our Event.
Our PostBloc will only be responding to a single event; PostFetched which will be added by the presentation layer whenever it needs more Posts to present. Since our PostFetched event is a type of PostEvent we can create bloc/post_event.dart and implement the event like so.
To recap, our PostBloc will be receiving PostEvents and converting them to PostStates. We have defined all of our PostEvents (PostFetched) so next let’s define our PostState.
Post States
Our presentation layer will need to have several pieces of information in order to properly lay itself out:
PostInitial- will tell the presentation layer it needs to render a loading indicator while the initial batch of posts are loaded
PostSuccess- will tell the presentation layer it has content to render
posts- will be the List<Post> which will be displayed
hasReachedMax- will tell the presentation layer whether or not it has reached the maximum number of posts
PostFailure- will tell the presentation layer that an error has occurred while fetching posts
We can now create bloc/post_state.dart and implement it like so.
Now that we have our Events and States implemented, we can create our PostBloc.
Post Bloc
For simplicity, our PostBloc will have a direct dependency on an http client; however, in a production application we suggest instead you inject an api client and use the repository pattern docs.
Let’s create post_bloc.dart and create our empty PostBloc.
Next, we need to register an event handler to handle incoming PostFetched events. In response to a PostFetched event, we will call _fetchPosts to fetch posts from the API.
Our PostBloc will emit new states via the Emitter<PostState> provided in the event handler. Check out core concepts for more information.
Now every time a PostEvent is added, if it is a PostFetched event and there are more posts to fetch, our PostBloc will fetch the next 20 posts.
The API will return an empty array if we try to fetch beyond the maximum number of posts (100), so if we get back an empty array, our bloc will emit the currentState except we will set hasReachedMax to true.
If we cannot retrieve the posts, we emit PostStatus.failure.
If we can retrieve the posts, we emit PostStatus.success and the entire list of posts.
One optimization we can make is to throttle the PostFetched event in order to prevent spamming our API unnecessarily. We can do this by using the transform parameter when we register the _onPostFetched event handler.
Our finished PostBloc should now look like this:
Great! Now that we’ve finished implementing the business logic all that’s left to do is implement the presentation layer.
Presentation Layer
In our main.dart we can start by implementing our main function and calling runApp to render our root widget. Here, we can also include our bloc observer to log transitions and any errors.
In our App widget, the root of our project, we can then set the home to PostsPage
In our PostsPage widget, we use BlocProvider to create and provide an instance of PostBloc to the subtree. Also, we add a PostFetched event so that when the app loads, it requests the initial batch of Posts.
Next, we need to implement our PostsList view which will present our posts and hook up to our PostBloc.
Moving along, our build method returns a BlocBuilder. BlocBuilder is a Flutter widget from the flutter_bloc package which handles building a widget in response to new bloc states. Any time our PostBloc state changes, our builder function will be called with the new PostState.
Whenever the user scrolls, we calculate how far you have scrolled down the page and if our distance is ≥ 90% of our maxScrollextent we add a PostFetched event in order to load more posts.
Next, we need to implement our BottomLoader widget which will indicate to the user that we are loading more posts.
Lastly, we need to implement our PostListItem which will render an individual Post.
At this point, we should be able to run our app and everything should work; however, there’s one more thing we can do.
One added bonus of using the bloc library is that we can have access to all Transitions in one place.
The change from one state to another is called a Transition.
Even though in this application we only have one bloc, it’s fairly common in larger applications to have many blocs managing different parts of the application’s state.
If we want to be able to do something in response to all Transitions we can simply create our own BlocObserver.
Now every time a Bloc Transition occurs we can see the transition printed to the console.
That’s all there is to it! We’ve now successfully implemented an infinite list in flutter using the bloc and flutter_bloc packages and we’ve successfully separated our presentation layer from our business logic.
Our PostsPage has no idea where the Posts are coming from or how they are being retrieved. Conversely, our PostBloc has no idea how the State is being rendered, it simply converts events into states.
The full source for this example can be found here.