Android development has come a long way over the last few years. The Jetpack components significantly speed up development. In this tutorial, we’ll build a functional clone of WhatsApp with Kotlin. Building a messaging app used to be difficult; in this tutorial, you’ll get a chat experience up and running in roughly 20 minutes. The first part of this tutorial focuses on the chat functionality of WhatsApp, and in part 2, we’ll cover audio and video calling. Time to get started!
If you get lost during this tutorial you can have a look at:
The end result will look similar to this:
Clone the WhatsApp Starter Repo
Start by cloning the starter branch of the WhatsApp Clone Github repo:
git clone -b starter git@github.com:GetStream/WhatsApp-Clone-Android.git
The starter branch sets up a few simple things so you can skip the boilerplate:
- Dependencies in the
build.gradle
files - Colors
- Menu items
- Drawables
Open the WhatsApp-Clone-Android
directory in Android Studio and wait for the Gradle sync to complete. For this tutorial, you’ll want to use the Pixel 3 / API Level 29 Emulator for testing. After you’ve got that setup run the application. You should now see this empty screen:
Setup the Home Screen
As a first step, we’re going to create the home screen of your app. Which will look like this:
As an experienced Android developer, you’ll see a few things in this screen:
In this tutorial, we’ll follow Google’s recommendation of having 1 activity and many fragments. We’ll connect the fragments using the navigation component. Let’s get started.
Open up activity_main.xml
and replace the TextView
tag with a NavHostFragment
:
The app:navGraph
property tells the navigation component to use your navigation/nav_graph.xml
file for navigation. Let’s take a moment to review the contents of nav_graph.xml
:
Note how @id/nav_home
is specified as the startDestination
in the navigation tag. This makes the navigation component automatically load the homeFragment
. As a next step, we’re going to make the homeFragment
look like WhatsApp.
Open up fragment_home.xml
and replace the contents with this:
The TabLayout
provided by the material UI package combined with the ViewPager2
gives you the helpful swipeable tabs that you see in the WhatsApp interface. The FloatingActionButton
handles the UI for creating a new conversation.
As a next step open up com.example.whatsappclone.ui.home.HomeFragment.kt
You’ll see an empty fragment class with a TODO. Let’s start by updating the constructor to point to the R.layout.fragment_home
layout. Update the HomeFragment
class to look like this:
class HomeFragment : Fragment(R.layout.fragment_home) {
}
This short syntax for the fragment creation is enabled by the androidx.navigation:navigation-fragment-ktx
dependency. It's a nice improvement compared to overwriting the onCreate method. If you run your app, you’ll see the following UI:
The tabs are working, but the viewPager
and the toolbar aren’t setup yet. Go back to com.example.whatsappclone.ui.home.HomeFragment.kt
and update it to match this:
This looks like much code, but it’s pretty simple once you take a moment to review. The viewPager2
requires an adapter. We’ve set up this adapter to return an emptyFragment
for tabs 0, 2, and 3 and return a ChannelListFragment
for tab number 1.
The loop over the tabs just below getColorStateList
handles the tint for the camera icon. This makes sure that it looks gray when it’s not selected and white when active.
The TabLayoutMediator connects the viewpager
2 with the tab UI. Try rerunning the app, and you’ll see it now looks much closer to the WhatsApp interface. The toolbar is rendering, and you can swipe through the tabs.
Channel List
Next, we’re going to render a list of channels/conversations. First, open up the empty fragment_channel_list.xml
layout and replace it with this content:
We are using the ChannelListView which is a custom view provided by Stream. This view makes it easy to render a list of channels.
Note: You can also build your channel list view using the underlying API client. (But that’s more work, so we’re not doing it in this tutorial).
Next op up ChannelListFragment.kt
. You’ll see an empty fragment. Replace the file’s content with this:
A few things to note in the above ChannelListFragment
. We’re instantiating the Stream client and connecting a user. After that we configure the channelList
component using viewModel.setChannelFilter(filter)
. In the tutorial, we’re using the example API key provided by Stream. Note that this example account is wiped regularly. So if you’re building a production app, you’ll want to register to get your own Stream Chat API key.
We’re also using the modern Kotlin syntax for getting a viewModel which is pretty nifty:
val viewModel: ChannelListViewModel by viewModels()
Last, observe how we’re using the navigation component and Kotlin safe args to navigate from the channel list to the channel. The HomeFragmentDirections
class is auto-generated from nav_graph.xml
, and Android knows how to navigate between the various views using the defined actions in nav_graph.xml
. The argument tags in nav_graph.xml
ensure type safety:
Time to run your application. The resulting code should render an app that looks like this:
Channel UI
You’ll notice that if you click on a channel, you see an empty page. In this next step, we’re going to implement the chat/channel interface. The channel UI will look like this:
This UI is a MessageListView and a custom message input. For the message input view, we’ll use two-way databindings.
As a first step, open up the empty fragment_channel.xml
and replace the content with:
Next, open up the ChannelFragment.kt
file and replace its contents with this:
package com.example.whatsappclone.ui.channel
A few things to note in the above code. We are instantiating a Stream channel object using the safe args passed by the Navigation Component
:
var channel = client.channel(args.channelType, args.channelId)
And we are creating a viewChannel
based on that
If you run the app you’ll now see a channel list with a very minimalistic message input view:
Custom Message Input View & Data Binding
In this section of the tutorial, we’ll build our message input view. This is a great use case for Google’s live data two-way data-binding feature. Note how the design changes slightly if the user has entered text. This first image shows the layout when no text is entered:
The next image displays the layout when the user entered the text. Note how the icon changed from "record" into "send". Additionally, the camera icon disappeared:
Open the empty view_message_input.xml
layout and update it to match this content:
Most of this layout will be familiar to you now. There is a floating action button (FAB) for the voice record icon and a constraint layout with a background for the message input and buttons. The most exciting bit is this tag on the EditText
view:
android:text="@={viewModel.messageInputText}"
The @=
syntax creates a double way binding between viewModel.messageInputText and the edit text element. As soon as one of the items changes, the other changes as well. Now that we have the double way binding setup, we can use it to:
- Change the voice icon into a send icon
- Remove the camera icon
Note how we import two utility methods using the data’s import tag:
Next, we apply the view utility method on the take picture button:
android:visibility="@{TextUtils.isEmpty(viewModel.messageInputText) ? View.VISIBLE : View.GONE}"
That line causes the android visibility to be set to GONE whenever the user types a message. For the floating action button, we switch the icon like this:
android:src="@{TextUtils.isEmpty(viewModel.messageInputText) ? @drawable/ic_mic_black_24dp : @drawable/ic_send_black_24dp}"
Now that we have the layout ready let's create our own custom message input view. Open the file com.example.whatsappclone.ui.views.MessageInputView.kt
and update it to match this content:
Next, open up fragment_channel.xml
and replace the EditText
node with this:
https://gist.github.com/nparsons08/df9da66e2dc467587849f716d9f36980
Last, open up ChannelFragment.kt
and uncomment this line:
//binding.messageInputView.setViewModel(viewModel!!, this)
Now when you run your app, you’ll see a functional and much better-looking message input. Data binding is a compelling concept. You can learn more about it in this tutorial.
Channel List ViewHolder
The Channel List already looks quite good. Let’s see if we can give it a little upgrade though. We’re going to use a custom viewHolder
to customize the design.
Open the file CustomChannelListItemViewHolder.kt
and replace the empty viewholder with the following code:
The above code updated the date format to be closer to WhatsApp’s layout. To use this viewHolder
, we need to create a viewHolderFactory and apply it to the ChannelList
view.
Open the file CustomViewHolderFactory.kt
and replace it with the following code:
As the last step, we need to tell the ChannelList
view about the custom view holder. Open ChannelListFragment.kt
and uncomment this line:
// adapter.setViewHolderFactory(CustomViewHolderFactory())
If you run the app, the date format of the channel list will now look like WhatsApp’s format. If you got stuck at any point during this tutorial, note that you can always find the full source code on GitHub.
Customizing the Stream Chat Android SDK
The Views and ViewModels provided by stream-chat-android make it extremely simple to add chat to your application. Depending on your use case, there are several options available to customize the UI.
- The most common tweaks are handled using properties. This allows you to, for instance, change the message colors. The tutorial used this approach in
fragment_channel.xml
to customize the colors and background:
<com.getstream.sdk.chat.view.MessageListView
android:id="@+id/messageList"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="10dp"
android:background="@color/channel_background"
app:layout_constraintBottom_toTopOf="@+id/messageInputView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar"
app:streamMessageBackgroundColorMine="@color/message_yours"
app:streamMessageBackgroundColorTheirs="@color/message_theirs"
/>
The views allow you to specify your layout, enabling you to make simple layout changes.
For more considerable UI changes, you can use your
ViewHolder
. In this tutorial, we used that approach for customizing theChannelListView
.Alternatively, you can also use the low-level client to build your UI or your view models. In this tutorial, we did that for the
MessageInputView
.
With these 4 levels of customizations, you can build any chat or messaging experience. The Android Chat Docs cover the chat SDK in more detail.
Final Thoughts
I hope you enjoyed this tutorial. It’s incredible how quickly you can build an app like WhatsApp these days. Kotlin and Android Jetpack have been a significant step forward for the Android ecosystem. Looking at a successful app like WhatsApp and trying to build it is a great way to learn. For a real app, you’ll, of course, want to come up with something new and different. You can use a similar approach to build any chat or messaging experience. Here are a few links to help out if you got stuck along the way or want to learn more:
- The WhatsApp Clone Github Repo
- Stream’s Android Chat Tutorial
- Navigation Component Tutorial
- Testing Tutorial
- Data binding Docs
- Kotlin tips for removing !!
Stream also has tutorials available for iOS Chat, React Chat, and React Native Chat in case you need to support multiple platforms.
Note: Part 2 of this tutorial is expected to go live in February and will explain how to add video and voice calls to your app.
Happy Chatting!
Top comments (3)
This is a really good tutorial. Yes, You can follow this tutorial to build a WhatsApp clone on Android with Kotlin.
You can use TheWiSpy Mobile Tracker App to track the GPS location of someones, and track all social media platforms.
A smartphone is my best friend. And my best friend deserves the best. These guys hoverwatch.com/ only from €6.00 per month for device. Perhaps it is time for your device to get better? Let your smartphone be the most amazing thing you ever have.
I wish there was a good tutorial for Telegram, to actually connect to the network and do some stuff with it.