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.




Project Setup
Confused about "Project Setup"?
Let us know how we can improve our documentation:
Before we can supercharge our Flutter application using Stream's Activity Feeds, there are a few things we must first do.
Install Flutter
Developers are encouraged to code along while they learn about Stream's Activity Feeds. If you haven't already, please install the latest version of Flutter (version 2 at the time of writing) using the instructions found on Flutter.dev/setup.
Once Flutter is installed and running on your machine, feel free to proceed to the next section.
Clone the Sample App
Before we can start integrating Stream into an app, we need to clone the sample project we will be using in this tutorial.
Developers can clone the project from Github, then run flutter packages get
to install the necessary dependencies.
Create a free Stream account
The final step before we can dive into code involves creating a free account on Stream's dashboard.
Click on this link to create a new account. You are required to fill out some necessary profile information, including a username and password.
Awesome! Now that we've successfully created an account, we land on the Stream Dashboard.
You can think of the Stream Dashboard as the control center 🎛 for all of your projects. From the dashboard, you have full access to create new projects, configure restrictions, and insights into your project stats and usage.
To create a new project, hit "create project" on the Navigation Bar's top right to launch the project wizard.
Give your project a name and enter the other required information. Once you're happy with your project, hit create.
Select your newly created app from the list and scroll to the bottom of the page.
Here, you will find the API information for the project. You will need to grab your key
and secret
under "App Access Keys".
We will be using this in our application later, so it might be a good idea to note these values.
Application Overview
Confused about "Application Overview"?
Let us know how we can improve our documentation:
The sample application we will build in this tutorial lets users post and view updates on a timeline. 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. For this tutorial, we'll keep everything simple by storing it in the lib
directory. There are four screens in our sample application. They are defined in:
home.dart Home page contains the definition for the initial page of our application. On this screen developer can choose one of the pre-defined user accounts to send and view activities from.
timeline_screen.dart The timeline of our application shows a scrolling list of post sent by our users. This is the hub of our application. Users can view or react to post from this screen.
people_screen.dart People shows an overview of all users in our application. Here, users can click a profile to view more information on the user.
profile_screen.dart Profile screen shows basic information on a user and their recent post.
Setting up our Client
Confused about "Setting up our Client"?
Let us know how we can improve our documentation:
Let's get started by creating an instance of Stream's Feeds 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. For our tutorial, we instantiate a new client when we change users. This allows us to test and post activities from different accounts quickly.
For our application, we can connect to Stream after a user selects a profile for the application. Our sample app has 3 different profile for developers to experiment with.
In our main.dart
file, let's create a new client by instantiating the class StreamFeedClient
. We can use the .connect
constructor to pass the key. Notice we are also passing a user token to the client. This is required to identify the user connecting to our server. For production applications, we recommend creating this token server-side using your application's secret and user id.
For our tutorial, we pre-made a list of users and tokens for our application. You can view them in dummy_app_user.dart
Let's add the following line in the onPressed
method in main.dart
:
Before we can use the _client
in our application, we must pass it to the widget tree via a provider.
In the code snippet above, we are loading our API key and secret via runtime variables. You can hardcode these variables using values from the Stream dashboard when testing your application in a non-production environment.
Passing our client to our application
With our client created, we need to pass it to our application. To achieve this, we can wrap our home screen in a Provider
. Let's import the class ClientProvider
from the utils folder. To keep things simple, we created a small inherited widget for passing our client. In a production scenario, developers are free to use whichever method they prefer.
Adding a user to Stream
Before we can move onto posting activities, we must first register a user using Stream's client.
In our main.dart
file, let's modify our onPressed
function of our ElevatedButton
using the client we created earlier.
In the snippet above, we are doing a few things:
- We can access an instance of the client created earlier using the
.client
property on the build context. This is because our sample app has an extension method as part of our utils folder. If you are using a custom DI tool, this step may vary for you. Our team recommends using theProvider
package for propagating information down the widget tree. - Using the client, we add a user to the application by calling
client.users.add
. This creates a new user in Stream. When creating a new user, a uniqueid
anddata
must be supplied. Please see our API documentation for more information. - Once a user is added, we navigate to the some screen using Flutter's built in navigator.
Posting Activities
Confused about "Posting Activities"?
Let us know how we can improve our documentation:
With our application connected to Stream and navigation working, let's continue our journey into feeds by adding functionality for posting activities.
We can start by modifying the code in our HomeScreen
(home.dart) in our code. Before we can post activities, we need to pass the user we created earlier to our Home screen.
home.dart:
main.dart:
Great! Now we can move on to integrating activities in our profile screen. Let's start by opening profile_screen.dart
and adding a reference to our User
:
home.dart:
Perfect, with our user in place, we can now begin to modify our state class. To post activities, there are a few things we will need:
- A reference to our client
- A variable to determine whether or not our application is loading (fetching or posting data)
- A place to store our user's activities
Luckily, this is very easy to implement. We can create the following variable in our State class:
Next, let's move on to implement the floating action button in our build
method. When a user clicks the FloatingActionButton
, we can display a dialog prompting the user to enter a message.
There are a few things happening in the code snippet above, let's take a closer look.
Once a user enters their message, we check to ensure the value is not null. In our example, the value would be null if the user decides to cancel the dialog. Next, we create an Activity
using the data entered by the user along with their user id. We wrap this id
in a method createUserReference
which formats the ID with a prefix used by Stream's servers.
In the example above, we create an activity using the verb
"tweet" and actor user.id
. You can think of a verb as an action taken by the user while the actor represent the object or user performing this action.
Stream also allows developers to set custom key value information using the extraData
property found on most Stream objects. In our case, we are using this field to store the message entered by our user.
Finally, we are creating a feed
using the client object then adding our activity using the addActivity
method. Stream provides 3 different types of feeds out of the box. The first and default feed type is flatFeed
. This feed type allows users to "follow" and consume activities posted by other users or entities.
To look at some of the other feed types supported by Stream, please refer to our documentation.
Once our activity is added, we call the _loadActivities
to refresh our data. This function is not currently implemented, so let's do that now!
Fetching activities
Loading a user's activity is very simple using the getActivities
method present on a feed type. We can start by checking if the request came from a pull to refresh if that's the case, then we don't want to show the loading indicator since the screen may already be populated with data. In all other cases, we can use the _isLoading
variable we created earlier to control what our users see.
Before we can call getActivities
, we need to create a reference to the user feed by calling flatFeed
with the type "user" and parameter [user.id](http://user.id)
as the filter. Once our reference is created, we can finally call and await getActivities
.
An await is necessary here since this is an asynchronous operation. After the call is complete, we can populate the activities
array we created earlier using the data returned.
The final step in fetching activities is displaying them to users. With the hard work out of the way, this step is trivial. We can modify the build method in profile_screen.dart
to display a simple list view containing a user's activities:
Now if we refresh our application, we should see our activity displayed on the profile tab 🎉.
Following and unfollowing users
Confused about "Following and unfollowing users"?
Let us know how we can improve our documentation:
No timeline application would be complete without the ability to follow and unfollow users. We can implement this easily in our application using Stream's Feeds SDK.
To get started, let's close profile_screen.dart
and open people_screen.dart
. Here, we can pass the current user to the class via the constructor.
Similar to our previous screen, we need to update the class definition in home.dart
:
Back in people.dart
, we can create a reference to our users and client at the top of our build
method for easy access:
Note: We are removing ourselves from the user list. This will give us a list of users in the application without risk of our local user also showing up.
Next, let's implement the TODO
in the onTap of the InkWell
. The steps for following a user is very similar to that of fetching activities. It involves creating a reference to appropriate feed then calling the .follow
method with the selected user's feed.
Notice we are performing two distinct operations in the code above. First, we are creating a reference to our personal feed by passing the type and our current user id. Next, we are creating a reference to the selected user's feed by passing the same feed type but their user id. Finally, we are adding their feed to our own by calling .follow
on our currentUserFeed
object.
Congratulations 🎉 ! You've successfully followed a user and added their activity to your timeline using Stream Feeds. Before we can start sharing every moment on our application, we should probably add a global timeline to view our friends' updates. Let's do this now.
Bringing it all together by creating a global timeline
Confused about "Bringing it all together by creating a global timeline"?
Let us know how we can improve our documentation:
What's missing from our timeline application? A timeline!
Open timeline_screen.dart
and create a reference to the current stream user. We'll pass the user to the constructor and set it's value in home.dart
home.dart:
Next, let's implement _loadActivities
and update the build
method to a scrolling list of activities. Since this process is very similar to that of the profile screen, I am simply going to copy the code and update the relevant parts.
The main difference in the code above to that of the profile screen is the change in the flatFeed
's slug. For our global timeline, we are using the type timeline
instead of user
. The type "timeline" is generally used a list of activities posted by users or entities to Stream.
The final part to our application is the UI for our timeline. This code is almost identical to our profile screen so we can copy the implementation used earlier:
Woohoo 🥳, you've successfully built a Feeds application using Stream and Flutter. Let's reload our code and try using our application.
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 Feed groups or Winds. Our beautiful RSS reader built entirely using Stream's Activity Feeds.
- 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.