Flutter Feed SDK for Activity Feeds
In this tutorial, we’re going to build an app using Stream’s Activity Feed Client with Dart. By the end of this tutorial, you'll learn how to setup a timeline feed, follow other feeds and post new activities.




Application Overview
Confused about "Application Overview"?
Let us know how we can improve our documentation:
The sample Flutter application you will build in this tutorial lets users post text and photos, view other users' posts in a timeline, and follow/unfollow specific users. The user experience of our application is very similar to that of Twitter or Instagram.
Let's look at the structure of the sample application. It'll contain the following pages:
- Select User Page: displays a list of demo users to use.
- Home Page: provides navigation to the rest of the app through a PageView.
- Timeline Page: shows a scrolling list of posts sent by the current user and the users they follow. This is the hub of our application. Users can view or react to posts from this screen.
- Profile Page: shows the current users' posts.
- People Page: shows all demo users and enables the current user to follow/unfollow them.
- Comments Page: displays all comments on a post and allows a user to add a new comment. Each comment can also be liked by the current user.
- Compose Activity Page: a page to create and post new activities.
Flutter Project Setup
Confused about "Flutter Project Setup"?
Let us know how we can improve our documentation:
Before you can supercharge your Flutter application using Stream's Activity Feeds, there are a few things you must first do.
Install Flutter
Developers are encouraged to code along while learning about Stream's Activity Feeds. If you haven't already, please make sure you are using the latest version of Flutter from the stable channel.
See the official Flutter installation instructions.
Complete Sample Source Code
The full example code for this tutorial can be found on Github.
Clone the repository and run the following:
This should be all that is needed to run the sample application. However, we recommend following this tutorial by creating your own Flutter and Stream Feed project.
Create a New Flutter Project
Create a new Flutter project; you can call it whatever you want.
Add Dependencies
Add the following dependencies:
You can delete everything in main.dart
and add the following imports:
Stream Setup
Confused about "Stream Setup"?
Let us know how we can improve our documentation:
Creating an app on Stream is easy and doesn’t take long. It’s also free and doesn’t require a credit card 💸 with the 30-day trial, or you can register for a Maker Account and access Stream for free indefinitely for qualifying teams and individuals.
Click on this link to create a new account.
Awesome! Now that you’ve successfully created an account, you should see the Stream Dashboard.
You can think of the dashboard as the control center for all your projects. From the dashboard, you have full access to create new projects, configure restrictions, and gain insight into your project stats and usage.
Create Stream App
To create a new Stream app, hit "Create App" on the Navigation Bar's top right to launch the project wizard.
Give your project a name and fill in the other required information; you can set the environment to Development. Once you're happy with your project, hit create.
Make sure your Dashboard is set to show for Feeds:
Select your newly created app from the application dropdown menu at the top.
On this page you will find the API information for the project. Take note of your app key
and secret
, you'll need these later.
Stream Feeds Introduction
See our Feed 101 documentation for a good introduction to feeds.
As a quick summary, a feed can be seen as a stack of activities, and activities are pushed to a feed. An activity can be a tweet or a post, for example.
A single application may have multiple feeds. For example, you might have a user's feed (what they posted), their timeline feed (what the people they follow posted), and a notification feed (to alert them of engagement with activities they posted).
There are three different types of feed groups:
- Flat: default feed type and the only feed type that you can follow
- Aggregated: helpful if you want to group activities together
- Notification: contains Activity Groups, each with a seen and read status field
Create Stream Feed Groups
In this sample application, we’ll need two flat feeds:
- “user” and
- “timeline”
The “user” feed is where individual users will post activities to, and the “timeline” feed will subscribe/follow specific users’ “user” feeds.
By the end of this tutorial, your “user” feed will display all of your posts/activities, and the “timeline” feed will display all the activities of the users you’ve followed.
From your Stream dashboard, you’ll need to create these feed groups. Navigate to Feed Groups, and select Add Feed Group.
You should then see the following:
The screenshot above shows the process of creating the “user” flat feed. Follow the same steps to create a “timeline” flat feed. After you've completed these steps, you should see the following two feeds in your dashboard:
Stream Feed Client Setup
Confused about "Stream Feed Client Setup"?
Let us know how we can improve our documentation:
Let's start by creating an instance of the Stream Feed client in our code. Stream's Flutter client acts as a gateway between your application and Stream's servers.
To create a client, developers must pass an API
key to the constructor.
Open main.dart
, and make sure you have the following imports:
Then create a new client by instantiating the class StreamFeedClient
within the main
function, and replace the apiKey
value with your newly created key:
The FeedProvider
widget is an InheritedWidget that exposes the FeedBloc
instance to your entire application. The FeedBloc
and StreamFeedClient
classes are used to manage the state of your Stream Feed application and to interact with your Stream server application. We’ll explore these more in later sections.
Demo Users
As this is a tutorial, we will create a hardcoded list of DemoUser
s.
The DemoUser class consists of User and Token fields - these classes come from the Stream Feed package. The User class takes in a mandatory id
and an optional data
argument to set user-specific data. The data
map can contain any mappable object you want to store on your user object.
Please note that in this tutorial the user tokens are hardcoded. However, this should never be done in a production environment. See our documentation on user tokens.
You can generate a token using any of our backend SDKs, manually using our online token generator, or using the stream-cli.
Stream CLI - Generate User Tokens
Let’s look at generating user tokens using the Stream CLI.
For installation instructions, see the Github repo.
Configure an application by running:
This will ask for:
- The name of your application
- Access Key
- Secret Key
- Optional base URL
Fill these in with the information you have on your dashboard. The name you give is only for the CLI configuration so that you can identify your different applications.
After configuring an application, you can easily generate a new frontend token. Let’s generate a token for the user Sacha, with ID sachaarbonel:
This generates a token that does not have an expiration date. You can now replace the token for the user with ID sachaarbonel in your demo users.
Now, do the same for all the other demo users.
If you have multiple applications configured in you CLI, you can specify which one to use with --app
, for example:
For more options, run:
User Data Extensions
Earlier, you set a few different parameters on the User.data
object. Now create an extension class that will allow you to access those fields easily:
This will come in handy later when we need to display this data.
Set and Connect a User - Select User Page
Next, create a SelectUserPage that allows you to select one of these demo users and connect them:
This code is mostly just UI. The important part is the call to:
This sets the current user by passing in a User and Token object.
If that is successful, the application will navigate to the HomePage. You will create the HomePage next, however if you wanted to run the application now by creating a temporary empty HomePage widget, it should look something like this:
App Navigation - Home Page
The Home Page is the base landing page of your application. From this page, a user will be able to navigate to all the other pages using a PageView.
This page uses a PageView to access three other pages:
- Timeline Page
- Profile Page
- People Page
For now, you can create your own empty TimelinePage and PeoplePage widgets.
You’ll implement these later. First, let’s start with the ProfilePage.
User Feed - Profile Page
Confused about "User Feed - Profile Page"?
Let us know how we can improve our documentation:
This page displays the feed of activities/posts by the current user.
The most important part is the FlatFeedCore widget. This allows you to easily access the feed data for a particular user and feed group by specifying the feedGroup
and userId
attributes.
Note that you can easily access the StreamFeedClient using context.feedClient
.
Next, there are four builders that you need to set:
loadingBuilder
: widget to return when the data is loading.emptyBuilder
: widget to return when there is no data to display.errorBuilder
: widget to return when an error occurred.feedBuilder
: widget to return when the feed data has been retrieved.
In the feedBuilder
, you get a list of activities that you can display. In this example, you’re creating a ListView.seperated.
Within the ListView, you’re returning a ListActivityItem, which we’ll explore later. We’ll also explore the ComposeActivityPage in a later step. We navigate to this page when tapping the FloatingActionButton.
Refresh Activities
To enable your users to refresh the list of activities, we wrap the ListView with a RefreshIndicator.
In the onRefresh
argument, you call FeedBloc.refreshPaginatedEnrichedActivities
, specifying the feedGroup
and the flags
to use. This creates the UI and logic to enable a user to drag down to refresh the data.
Activity Enrichment
The flags
argument determines the activity enrichment that you require. Enrichment is an interesting concept that you can read more about here.
Not specifying any flags will simply return the activities without any reaction data, for example, “likes” or “comments”.
In this example, you’re specifying the following flags:
These two flags will ensure that the activity object contains the number of reactions added to it and whether the current user has added any reactions.
Note you’re using the same flags for onRefresh
, within the FlatFeedCore
widget, and when calling loadMoreEnrichedActivities
. If you don’t use the same flags, you may receive activities with conflicting reactions when doing a refresh or when loading more activities.
Display Activity and Add “Like” Reactions
It's time to add the ListActivityItem widget, which does the following:
- displays an activity/post
- enables a user to like a post (add a reaction to it), and
- navigates to the comments screen on tap
From the activity
passed into the widget, you can retrieve the reaction counts and the current user’s reactions from the activity - as determined by the EnrichmentFlags you set earlier.
Within the onPressed
method you’re adding or removing a “like” reaction to the current activity, depending on whether the reaction already exists or not.
You will create the CommentsPage later on - where you’ll be able to add a “comment” to a post. For now, we’re simply showing the total number of comments and adding navigation to the comment page.
For now, you can create an empty CommentsPage that takes in an EnrichedActivity.
Activities Pagination
Within the ProfilePage widget, the _loadMore
method handles loading more activities. This method gets called from the ListView widget when the third last widget is built. There is some additional logic with the _isPaginating
boolean to ensure that you only perform one API call to load more data.
Note that this is only an example implementation. You can implement pagination in whatever way you want and load more activities by calling:
In this call you specify the feed group and the enrichment flags needed.
Posting Activities - Compose Activity Page
Confused about "Posting Activities - Compose Activity Page"?
Let us know how we can improve our documentation:
Now that we’re able to display activities, the next step is to create new ones.
We previously created a FloatingActingButton that navigates the user to a page, called ComposeActivityPage. Let’s create that page now.
Make sure to have an import for the image_picker package:
Depending on the platform you target you will also need to configure you app permissions. If this step is skipped your application may crash when trying to access the device image gallery.
For information, see the package installation instructions.
The code above makes use of two controllers:
- UploadController to manage file uploads to Stream’s CDN. For this application, we can upload images.
- TextEditingController allows a user to type in text that will be added to an activity.
The IconButton’s onPressed
callback uses the image_picker package to select an image from the device’s gallery and uses the upload controller to upload the image.
The uploaded images are retrieved using the UploadListCore widget, and rendered with FileUploadStateWidget - these widgets come from Stream’s core package.
Within the ActionChip’s onPressed
callback you call the _post
method, which performs the following:
The above code reads like this: post an activity to the user feed group, with this object and data:
- The object can be more complex than a simple text value. However, that will need to be a separate tutorial. See our documentation on enrichment and collections for more details.
- The data field is a map of type
Map<String, Object>?
, and similar to the User class, we can provide any mappable content. For this example, we’re using the data to store the uploaded image URLs. - After creating the new activity, we clear the upload controller.
Your HomePage should now look like this, when navigating to the profile page:
As you can see, we've not yet added any activities to our Stream Feed app. Let's do that now.
Press the floating action button to go to the new activity page, and create a post:
Note: be sure to have set the correct app permissions to access the device's image gallery.
Pressing the Post button will navigate you back to the Profile Page, and you should see your newly added post.
Congratulations! You're on your way to mastering activity feeds. You can try to add as many posts as you want, and experiment with the pagination you've already created.
Following and Unfollowing Users - People Page
Confused about "Following and Unfollowing Users - People Page"?
Let us know how we can improve our documentation:
At this stage, your application will allow you to post and view the current user’s activities. Not very exciting on its own, so let’s fix that. We want a way to see other users’ posts, and no timeline application would be complete without the ability to follow and unfollow users.
For this functionality, you will need to create a new page in your application, called PeoplePage. This page is accessible from the HomePage’s PageView.
Here we’re simply using the list of demo users we’ve made and removing the current user from that list. Then we’re displaying a FollowUserTile, which we’ll create next:
This widget uses the FeedBloc to see if the current user is following a specific user and to follow/unfollow a user; see the following methods:
_checkIfFollowing
_follow
_unfollow
Note that with these methods you can optionally supply the feed groups. By default these are set to “user” and “timeline”.
What this essentially means is that when you “follow” a user, you are in fact subscribing your “timeline” feed to their “user” feed. Everything that the user posts to their “user” feed will now show up in your “timeline” feed.
Now, when navigating to the people page you should see all the demo users (except for who you are logged in as) and be able to follow/unfollow them:
Experiment by following different users from different accounts, and also make sure to create posts from all the different accounts.
Congratulations 🎉 ! You've successfully followed a user and added their activity to your timeline using Stream Feeds. You've also created posts as different users. You will now create the timeline page that will show the posts of the users you've followed.
Timeline Feed - Timeline Page
Confused about "Timeline Feed - Timeline Page"?
Let us know how we can improve our documentation:
The timeline page will be very similar to the profile page, the only difference being that it’ll be using a different feed group - the “timeline”.
As you can see, you set the _feedGroup
variable to “timeline”. Other than that, it’s the same as we saw on the profile page.
This is all that is needed! You’ll now see all posts for the users you follow by navigating to the timeline page:
Fantastic! You can see the posts of the users you follow - you can also like their posts!
But, you may have noted that the current user’s posts aren’t showing in their timeline. This is because you’ll also need to follow your own “user” feed for that to show up.
In the SelectUserPage widget, after calling:
Add this code to follow your own feed:
Hot restart the application and select the same user, their posts should now show up in their timeline feed, along with all the users they follow.
Something else you may have noted is that the profile page updates the moment you add a new activity, but the timeline page doesn’t. This isn't a bug, the timeline page will require you to reload the data in order to see the latests activities (from yourself and other users). On the timeline page you can do this by dragging down and initiating a refresh, or you can navigate to a different page of the app and back (which will cause the whole page to rebuild as we're not caching it).
Add Reactions and Child Reactions - Comments Page
Confused about "Add Reactions and Child Reactions - Comments Page"?
Let us know how we can improve our documentation:
Your application is almost finished. Now you only need to enable users to add comments to posts. We’ve seen how to do this with “likes” by adding reactions to activities.
We can do the exact same thing for a comment, the only difference is that we’ll need to supply some extra information - the comment we’re adding.
We also want to allow adding a “like” reaction to a comment. You may be scratching your head right now. Essentially, this will require you to add a reaction to a reaction - a child reaction!
Let’s create the CommentsPage:
In the above code, you’re using a ReactionListCore
to retrieve the list of reactions that meet certain criteria. That criteria are:
lookupValue
: which is the current activity’s id.kind
: which is the type of reaction, a “comment” in this scenario.
This reads as follows: “Retrieve all comments for the activity with this id.”
As we saw previously, we have several different builders to handle the different data states, for example error and loading. Let’s take a closer look at the reactionsBuilder
.
The reactionsBuilder
builder returns the list of reactions that meet the above defined criteria. We can then do with that data whatever we want. In this sample, we’re displaying the list of comments in a list view.
Each comment also has a “like” button, that when pressed calls _addOrRemoveLike
. In this method we add a child reaction to the current reaction, or if the reaction already exists we remove it.
Finally, you need to create the AddCommentBox widget, to enable users to create new comments:
This widget manages a TextEditingController, with a TextField, that onSubmitted
calls the _addComment
method. This simply adds a new reaction to the current activity. The kind of reaction is a “comment”, and you specify the data
argument to contain the actual comment text.
From the timeline page, select one of the posts and add a comment:
From the timeline page you can also see how many people liked and commented on a post.
Conclusion
Confused about "Conclusion"?
Let us know how we can improve our documentation:
Woohoo 🥳, you've successfully built a Feeds application using Stream and Flutter.
This is just the tip of the iceberg when it comes to Activity Feeds and what is possible using Stream. We support different feed groups for a wide variety of scenarios and use cases.
Check out our Instagram clone we made using Stream Feeds and Flutter: https://getstream.io/blog/instagram-clone-flutter/
Also see our ever-growing samples repository to see what else you can make with Stream and Flutter: https://github.com/GetStream/flutter-samples
Other resources you may find useful:
- Feed Types (flat, aggregated, notification) - https://getstream.io/activity-feeds/docs/javascript/creating_feeds/?language=dart
- Winds - https://github.com/GetStream/Winds
- Supercharge Feeds with Algolia and GraphQL - https://getstream.io/blog/supercharge-feeds-with-algolia-and-graphql/
Final Thoughts
In this tutorial we saw how easy it is to use Stream API and the Flutter library to add a fully featured timeline to an application.
Adding feeds to an app can take weeks or months, even if you're a Flutter developer. Stream makes it easy and gives you the tools and the resources to improve user engagement within your app. Time to add a feed!
Give us Feedback!
Did you find this tutorial helpful in getting you up and running with Flutter for adding feeds to your project? Either good or bad, we’re looking for your honest feedback so we can improve.