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

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

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

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

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.

Create Stream Application via Stream's Dashboard

Make sure your Dashboard is set to show for Feeds:

Set Stream Dashboard to 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.

View your application's API key

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.

Create new Feed Group

You should then see the following:

Create User Flat Feed Group

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:

Feed group overview

Stream Feed Client Setup

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

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:

  1. The name of your application
  2. Access Key
  3. Secret Key
  4. 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:

Select User Page Preview

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

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

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:

Feed Empty 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:

Feed New Activity Page

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.

Feed Profile Page With Activity

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

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:

Feed People Page to follow/unfollow

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

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:

Feed Timeline Page Other Users Posts

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

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:

Feeds Comment Page

From the timeline page you can also see how many people liked and commented on a post.

Conclusion

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:

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 →
Enterprise

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

Learn more →