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

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.

Create Stream Application via Stream's Dashboard

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".

View your application's API key

We will be using this in our application later, so it might be a good idea to note these values.

Application Overview

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

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.

View your application's API key

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:

  1. 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 the Provider package for propagating information down the widget tree.
  2. 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 unique id and data must be supplied. Please see our API documentation for more information.
  3. Once a user is added, we navigate to the some screen using Flutter's built in navigator.

Posting Activities

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.



Sample App Home Screen

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:


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:

  1. A reference to our client
  2. A variable to determine whether or not our application is loading (fetching or posting data)
  3. 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!

Post Sample Activity dialog

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 🎉.

Profile activities

Following and unfollowing users

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.

Follow User

Bringing it all together by creating a global timeline

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


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.

Types overview

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.

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.

Next Steps

Open your Stream Account to try out all our Activity Feeds product has to offer. No commitment or credit card required. If you want a custom plan or have questions, we are eager to talk with you.

Chat Messaging

Build any kind of chat messaging experience without scalability or reliability issues.

Learn more →

99.999% Uptime SLA, Industry leading compliance and security best practices.

Learn more →