Stream Flutter: Building a Social Network with Flutter – Activity Feeds
Tags: flutter, feeds, tutorial, chat
Published: 2020-03-03 22:23:50

In this post, we'll be creating a simple social network, called Stream Flutter, that allows a user to post messages to followers. 

The app will allow a user to post a message to their followers. Stream's [Activity Feed API](https://getstream.io/activity-feeds/) combined with Google's [Flutter](https://flutter.dev/) makes it straightforward to build this sort of complex interaction. All source code for this application is available on [GitHub](https://github.com/psylinse/flutter_the_stream). This application is fully functional on both iOS and Android. 

For brevity, when we need to drop down to native code, we'll only focus on Android. You can find the corresponding iOS code to see how things are implemented. To keep things focused, we'll be showing the more essential code snippets to get the idea of each piece across. Often there is the context around those code snippets, which are necessary, such as layout or navigation. Please refer to the full source if you're confused on how something works, or how we got to a screen. Each snippet will be accompanied by a comment explaining which file and line it came from.

To build our social network, we'll need both a backend and a mobile application. Most of the work is done in the mobile app, but we need the backend to create frontend tokens for interacting with the Stream API securely.

For the backend, we'll rely on [Express](https://expressjs.com/) (Node.js) leveraging Stream's [JavaScript library](https://github.com/GetStream/stream-js).

For the frontend, we'll build it with Flutter wrapping Stream's [Java](https://github.com/GetStream/stream-java) and [Swift](https://github.com/getstream/stream-swift) libraries. 

To post an update, the app will perform these steps:

* User types their name into our mobile application to log in.
* The mobile app registers the user with our backend and receives a Stream Activity Feed [frontend token](https://getstream.io/blog/integrating-with-stream-backend-frontend-options/).
* User types in their message and hits "Post". The mobile app uses the Stream token to create a Stream activity by using Flutter's [platform channels ](https://flutter.dev/docs/development/platform-integration/platform-channels) to connect to [Stream's REST API](https://getstream.io/docs_rest/) via [Java](https://github.com/GetStream/stream-java) or [Swift](https://github.com/getstream/stream-swift).
* User views their posts. The mobile app does this by retrieving its `user` feed via Stream.

If another user wants to follow a user and view their messages, the app goes through this process:
* Login (see above).
* User navigates to the user list and selects a user to follow. The mobile app communicates with Stream API directly to create a [follower relationship](https://getstream.io/get_started/#follow) on their `timeline` feed.
* User views their timeline. The mobile app uses Stream API to retrieve their `timeline` feed, which is composed of all the messages from who they follow.

The code is split between the Flutter mobile application contained in the `mobile` directory, and the Express backend is in the `backend` directory. See the `README.md` in each folder to see installing and running instructions. If you'd like to follow along with running code, make sure you get both the backend and mobile app running before continuing.

## Prerequisites

Basic knowledge of [Node.js](https://nodejs.org/en/) (JavaScript), [Flutter](https://flutter.dev/) ([Dart](https://dart.dev/)), [Kotlin](https://kotlinlang.org/) is required to follow this tutorial. This code is intended to run locally on your machine. 

If you'd like to follow along, you'll need an account with [Stream](https://getstream.io/accounts/signup/). Please make sure you can run a Flutter app, at least on Android. If you haven't done so, make sure you have Flutter [installed](https://flutter.dev/docs/get-started/install). If you're having issues building this project, please check if you can create run a simple application by following the instructions [here](https://flutter.dev/docs/get-started/test-drive).

Once you have an account with Stream, you need to set up a development app:

![](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/68195bcb5b82c31441bdfdaa5fc84589/create-app.png?auto=compress%2Cformat&ixlib=php-1.2.1)

You'll need to add the credentials from the Stream app to the source code for it to work. See both the `mobile` and `backend` readmes. 

Let's get to building!

## User posts a status update
We'll start by allowing a user to post messages. 

### Step 1: Login
To communicate with the Stream API, we need a secure frontend token that allows our mobile application to authenticate with Stream directly. This avoids having to proxy through the `backend`. To do this, we'll need a backend endpoint that uses our Stream account secrets to generate this token. Once we have this token, we don't need the backend to do anything else, since the mobile app has access to the full Stream API. 

First, we'll be building the login screen which looks like:

![](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/856544bef087b2e4350a8166f15994c8/login.png?auto=compress%2Cformat&ixlib=php-1.2.1)

To start, let's lay out our form in Flutter. In our `main.dart` file, we'll create a simple check to see if we're logged in, and if we don't have one, show the user a login form:

https://gist.github.com/nparsons08/94799d558ef55291a8ea0cbed01b6212

The `_account` variable is as simple `Map<String, String>` object, which will contain the backend `authToken` and a Stream `feedToken`. The `authToken` is used to make further requests the backend, which we'll use later to retrieve a list of users. The `feedToken` is the Stream frontend token, which allows access to the Stream API. 

To set the `_account` variable, we'll take the string typed in by the user once they've pressed "Login" and pass it to the `ApiService` to perform our authentication. Here's our `_login(..)` function:

https://gist.github.com/nparsons08/2a9ee3cbdc4f97a40b6a248d01bc2937

We use the user's typed in name to get our credentials from the backend and store it in our `_account` variable. This is two calls, one to get our `backend` authentication token and the other to get our Stream API token. To do this, let's look at our implementation of `ApiService#login`:

https://gist.github.com/nparsons08/6ec1f8a3303b8e6a89897311a3c44b66

Two things happen here. First, we register a user with the backend and get an `authToken`. Using this `authToken`, we ask the backend to create our Stream Activity Feed frontend token. 

The user registration endpoint in the `backend` simply stores the user in memory and generates a simple token for auth. This is not a real implementation and should be replaced by; however, authentication and user management work for your application. Because of this, we won't go into detail here (please refer to the source code if you're interested). 

To generate our Stream token, let's look at what the backend is doing to make that:

https://gist.github.com/nparsons08/bbef32d996250c3dc8abe925f9fa2d78

This code uses our secret account credentials to create a Stream user and register the user's name. The `getOrCreate` call creates the user with a name (or simply retrieves that user if we already registered them). Once we've created the user, we return the necessary credentials to the mobile app.

Once we're logged in, we're ready to post our first message!

### Step 2: Posting a Message

Now we'll build the form to post a status message to our Stream activity feed. We won't dive into navigation and layout in this tutorial. Please refer to the source if you're curious about how we get to this screen. We'll need to build a form that takes what the user wants to say to their followers and submit that to Stream.

First, the form:

https://gist.github.com/nparsons08/15f7a47067b296e0caa1b9c223c30c5f

Which will produce a flutter view that looks like this:

![](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/1c87ff61a699ec34252fa21c072faedc/post-form.png?auto=compress%2Cformat&ixlib=php-1.2.1)

This is a straightforward Flutter widget, which uses a `TextEditingController` to track a user's input and uses a `MaterialButton` to trigger the post.

Now let's look at the implementation of `_postMessage` which is how we create the [activity](https://getstream.io/docs/#adding-activities), with our message, in Stream:

https://gist.github.com/nparsons08/0be616893dbefb060c8dbf68321fc6f1

We simply take the typed in text and pass it to our `ApiService` and pop the navigation stack to return to the previous screen. Here is the implementation of `ApiService#postMessage`:

https://gist.github.com/nparsons08/abb13703b980da41af4be00587884736

Since we're going to leverage the Streams' [Java](https://github.com/getstream/stream-java) library (and [Swift](https://github.com/getstream/stream-java) on iOS), we make a call to the native implementation via Flutter's built-in [Platform Channels](https://flutter.dev/docs/development/platform-integration/platform-channels). We won't go into detail on how this call happens, so either refers to the source code or read up on Flutter's docs on how to do this. Here is the Kotlin implementation (the iOS implementation is available in the source):

https://gist.github.com/nparsons08/ef935769d4647825661015622cc2d868

Here we use [Stream Java's](https://github.com/GetStream/stream-java) `CloudClient` from the [Cloud package](https://getstream.github.io/stream-java/io/getstream/cloud/package-summary.html). This set of classes takes our frontend token, which allows the mobile app to communicate directly with Stream. We are authenticated only to post activities for the actor `SU:john`. Since we aren't storing a corresponding object in a database, we generate an id to keep each post unique. We also pass along a message payload, which is what our followers will see.

You may be wondering what the `client.flatFeed("user")` is referring to. For this to work, we need to set up a flat feed called "user" in Stream. This is where every user's feed, which only contains their messages, will be stored.

Inside of your Stream development app create a flat feed called "user":

![](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/a688daa07c3f09d4993dac14033139a9/create-user-feed.png?auto=compress%2Cformat&ixlib=php-1.2.1)

That's all we need for Stream to store our messages. Once all of these functions return, we pop the Navigator to return to the user's profile screen, which will display our posted messages. We'll build this next.

# Step 3: Displaying Messages on our Profile

Let's first build a Flutter widget that will contain the user's messages. Here is what the screen will look like:

![](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/a77d8c46c84bcec40d1731930d9b09e2/profile.png?auto=compress%2Cformat&ixlib=php-1.2.1)

And here is the code:

https://gist.github.com/nparsons08/65f5532283c858ab93f8beb036037e9b

We're using a `FutureBuilder` to load our activities asynchronously. We won't go into this pattern here, so please refer to the [docs](https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html) or [The Boring Flutter Development Show](https://www.youtube.com/watch?v=rfagvy5xCW0&list=PLOU2XLYxmsIK0r_D-zWcmJ1plIcDNnRkK&index=4) to understand how this works. We also use a simple [`RefreshIndicator`](https://api.flutter.dev/flutter/material/RefreshIndicator-class.html) to implement pull to refresh. Putting these two built-in widgets together allows us to show a user's messages and update them when they post a new message.

The Stream specific code happens with our `_activities` variable. To get a user's activities (which contain our messages), we once again call to our `ApiService`:

https://gist.github.com/nparsons08/d8e4d7f05d8a13a74ebcd1ae56609b3d

Our `ApiService#getActivities` calls into native code to perform the work:

https://gist.github.com/nparsons08/870820175acc27f95f34ccabc902c94e

In Kotlin, we use the `CloudClient` to ask for the user's flat feed and get the last 25 messages (activities):

https://gist.github.com/nparsons08/c10d1379184a24baeb9758b3cd84474c

Next, we'll see how to follow multiple users via a timeline feed.

## User Timeline
Now that users can post messages, we'd like to follow a few and see a combined feed of all the messages for users we follow.

### Step 1: Follow a User
The first thing we need to do is view a list of users and pick a few to follow. We'll start by creating a view that shows all the users and lets a user follow a few. Here is the screen that shows all the users:

![](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/4e552d644d363964256ada3668b6cd5b/users.png?auto=compress%2Cformat&ixlib=php-1.2.1)

And here's the code that backs it:

https://gist.github.com/nparsons08/681cc2dee0cf9054aa5dce00af26e3a6

This widget follows the same pattern as the profile by using a `FutureBuilder` backed by `users`. The `users` variable is backed merely a call to our `backend` service. Since the backend is not a real implementation, we'll skip the details of how the `backend` stores and retrieves users here. Please refer to the source. In Flutter we simply make an HTTP call to retrieve the list, as seen in `ApiService#users`:

https://gist.github.com/nparsons08/6a078606778c5e03d5cdfbafd1af476e

The exciting parts are when we click a user. We show a Flutter dialog via `showDialog` which contains a single button that allows us to follow. When we press that button, we trigger `ApiService#follow`. We can see how, once again, we're leveraging native libraries to do the work of following a user. Here is the Flutter side:

https://gist.github.com/nparsons08/e742e0921c5877d51212fdd100564910

And here is the native Kotlin side using the `CloudClient`:

https://gist.github.com/nparsons08/9a163aabd63bdf8e0fd54a90b18d2f89

Here we're adding a [follow relationship](https://getstream.io/docs/#following) to another user's `user` feed to this user's `timeline` feed. All this means is anytime a user posts to their `user` feed (implemented in the first part), we'll see it on our `timeline` feed. The cool part is, we can add any number of users feeds to our `timeline` feed, and Stream will return a well-ordered list of activities.

Since we have a new feed type, we need to set this up in Stream. Just like the `user` feed, navigate to the Stream app you set up and create a flat feed group called timeline:

![](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/040eaaa05f7b1814ed095517b654ca80/create-timeline-feed.png?auto=compress%2Cformat&ixlib=php-1.2.1)

### Step 2: View Timeline

Now that we have a way to follow users, we can view our timeline. When we're done, assuming we've followed "bob" and "sara" we'll see a screen that looks like this:

![](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/634d67e6a6355184a5a588770c489939/timeline.png?auto=compress%2Cformat&ixlib=php-1.2.1)

Let's look at the code to display our timeline:

https://gist.github.com/nparsons08/fc68e0ff2aef8a4640aa08a24f2a05d0

This looks identical to our `Profile` widget using both `FutureBuilder` and `RefreshIndicator` backed by a future `_activities`. Since the `timeline` is just a feed like `user`, the code is identical to how we got our user's messages. First, we call `ApiService#getTimeline` which is simply a call to our native code:

https://gist.github.com/nparsons08/fed06a5748e851f803bec03ea8005d3b

And here's the corresponding native code:

https://gist.github.com/nparsons08/b567d8f6a3633cfc797218fe960dfe8c

And that's it! We now have a fully functioning mini social network. 

## Final Thoughts
Flutter and Stream make it straightforward to build a cross-platform mobile application leveraging activity feeds. Both come with a ton of functionality out of the box. If you're looking for an alternative to React Native, Flutter is a great choice. Calling native code is simple with platform channels, which allows us to use all the great libraries Stream has provided us.

> In addition to the tutorial above, Stream offers a comprehensive [Flutter SDK Tutorial](https://getstream.io/chat/flutter/tutorial/), so you can see how to get up and running quickly with Flutter.