•over 1 year ago
Flutter is the latest cross-platform UI toolkit (provided by Google) for building Android, iOS, and even desktop apps that is gaining popularity amongst developers. Stream Chat, on the other hand, is an enterprise-grade chat solution that offers extensive APIs and SDKs to power chat on multiple platforms. When they come together, magic happens... 🔮
If you'd simply like to see the code for the final project, check out the GitHub repo.
To proceed with this tutorial, you'll need the following:
- A Stream account. You can create one here if you don’t have one.
- Node.js and npm
- Dart and the Flutter SDK
- Android Studio or Visual Studio Code
With all these in check, feel free to proceed!
Creating a Stream App
The first thing we will do is create a new app in your Stream Dashboard.
If you’re already on the Stream home page, the Dashboard button is at the top-right corner:
If you've just created a new account, you might notice that an app was already created for you; you can use this one or create another. To create an app from your dashboard, use the Create App button:
Once you click the button, you will see a dialog to get the details of your app. You can populate it with the following information:
Note that if you're creating your app for production, rather than just as a demo for this tutorial, you should select the "Production" option.
After creating your app, you will see it listed on your dashboard:
Copy out the KEY and SECRET values, as you will need them in subsequent sections of this tutorial.
Setting Up the Backend
Next up, we will build the backend app using Node.js. Go ahead and create a directory named
nodejs-backend. If you’re using a Unix machine, you can do so by running this command:
Installing the Backend Packages
cd into the
nodejs-backend directory we just created and run this command:
This command will generate a
package.json file in the
nodejs-backend directory. The
package.json file is the heart of any Node.js project; this file will contain important data about the project.
Next, we can install all of the packages we will make use of. While still, in the
nodejs-backend directory, run this command:
With this command, we are installing three packages:
express: Express.js is a Node.js framework that makes it easier to build Node.js apps.
body-parser: This is a middleware used to extract the body of an incoming request.
The Heart and Soul of the Backend
Next, create a new
index.js file in the
nodejs-backend directory. Inside the
index.js file, add this snippet:
Make sure to replace "your_API_KEY" and "your_API_SECRET" (in the code above) with the credentials from your Stream app dashboard.
From the snippet above, the server has one endpoint (
/token), which takes the
userId and generates a token for the user (
Ideally, for a production app, you will ensure that whoever is providing the userId is properly authenticated against your database.
Starting Up the Backend
After that, run the server using this command:
This will serve the app on port
4000. Next, we will use Ngrok to provide a secure endpoint for the local server:
After running that, you should see something like this:
Now, copy out the secure URL (https://0a83ad73.ngrok.io, in my case) for use later in our app development.
Building the Flutter App
Now we’re ready to build the Flutter app! In this tutorial, the screenshots will be that of Android Studio. However, you can also use VS Code to build your app.
Start by creating a new Flutter application by selecting the Flutter Application option and clicking Next:
After that, we'll need to fill in the application details:
Here, you'll need to enter the Project name, Project location, and Description. You'll also have to reference the Flutter SDK path; if you properly installed the Flutter SDK, the system should automatically detect that for you.
Finally, for the project creation wizard, you need to enter a Package name and choose to enable the Platform channel languages:
Then, just click Finish, and Android Studio will bootstrap the project for you!
Now that the project is ready, the next step is to add packages that we will use in the app.
pubspec.yaml file and add the following packages in the
pubspec.yaml file should now look like this:
Here, we added two packages:
stream_chat_flutter: This package is Stream’s Flutter package, which will give us access to chat capabilities.
http: We need this package so we can make API calls.
After adding the packages, click the Packages get action in the upper right corner of the window:
This will download the packages we just added!
Building the Login, Users List, and Chat Screen
Our app will have three screens: the Login screen, the Users List screen, and the Chat screen.
The Login Screen
Let's start with the Login screen. Create a new file called
login_page.dart and add the following code:
We start by importing the files we will access throughout the course of building this screen. After that, we create a stateful widget called "
LoginPage"; as the name "stateful widget" implies, this widget will have a state called "
We then go on to create the
_LoginPageState class; in this class, we start by declaring the variables we'll use throughout the class:
userIDController: the controller of the
_scaffoldKey: will serve as the
_client: object that is an instance of the
Clientclass provided by the Stream Flutter SDK.
Be sure to replace STREAM_API_KEY with the key from your Stream app and YOUR_NGROK_BASE_URL with the secure Ngrok URL, which you got earlier on.
In this class, we also implement the layout of the Login screen, via the
build() method. The layout contains a
TextFormField to accept the user’s id and a button to trigger the log in action.
Here is what we've just built:
Apart from the
build() method, the
_LoginPageState class has two other methods:
dispose(): In this method, we dispose of the
_userIDControllerobject when it is no longer in use. This is a good practice to keep the memory allocation of your app in check.
_loginUser(): In this method, we first validate the user id to ensure the user did not submit empty text. In the case that no text was entered, the method stops and displays an error message to the user. If the user has typed something, we make an API call to the
/tokenendpoint to get a token for the user.
After we get the token, we call the
setUser method of the
Client class. This method notifies the Stream SDK of the current user in the session. Once everything is successful, the user will be directed to the
UsersListPage. This page does not exist yet; let's create it!
The Users List Screen
The next thing we will do is build the Users List page. Create a new file called
users_list_page.dart and add the following code:
Similar to the login screen, we declare the imports and then create a stateful widget called
UsersListPage. After that, we create the
_UsersListPageState. In the
_UsersListPageState, we declare two variables:
_usersList: to keep data of users the logged-in user can chat with.
_loadingData: to know when the app is fetching any sort of data.
As usual, we've built the layout in the
build() method. The layout will have three different displays depending on the outcome of fetching users: if data is still fetching, there will be a loading indicator displayed on the screen; if the fetch is complete and the
usersList is still empty, it will show an error message; and if the
_usersList has data, it shows a list view of the users.
We reference the
_fetchUsers() method in the
initState() of the class. This is so that the method is called as soon as the user opens this page.
_fetchUsers() method, we first set the value of
true, using the
setState function. Using the
setState() function signifies that the state of the page has changed, and so the
build() method is rendered again to reflect the new changes.
After that, we get the
client object (passed down from the parent widget) and query for users. Once this is successful, we filter the users to omit the logged-in user, and then we assign the result to the
_usersList object. We also set
When the app displays a list of users, there is a click listener added to take action based on which user is selected. The
_navigateToChannel() method is then called when a list item is selected.
In the method, we are creating a channel between the logged-in user and the user that was selected on the list. Note that if this channel has been created before (the logged-in user has previously chatted with the selected user), the Stream API will ensure that only one channel for the members we specified exists. If everything is successful, the app will open the
The Chat Screen
To finish setting up the app screens, create another file called
channel_page.dart and add the following code:
In this page, we are making use of Stream’s built-in widgets. They include:
ChannelHeader: This widget is an
AppBarwidget that will display the channel name and display the profile picture of the logged-in user.
MessageListView: This widget will display the messages in real time.
MessageInput: This widget will enable the user to send messages (Not only text messages, but photos, videos, etc).
This is one of the advantages of using Stream. They provide inbuilt widgets you can easily make use of to help you build chats in record time!
To complete the Chat screen setup, replace the
main.dart file with the following:
Here, we’re making the
LoginPage the first screen to be displayed.
At this point, the app is good to go!
In this tutorial, you have learned how to build a one on one chat using Flutter and the most powerful chat API out there - Stream. There is still so much that can be done with Stream’s API! Feel free to explore the official docs.
Thanks for reading, and Happy Coding!