Build a WhatsApp Clone on Android With Kotlin – Part 1
Tags: kotlin, android, chat, tutorial
Published: 2020-01-31 15:07:01

---
title: Build a WhatsApp Clone on Android With Kotlin – Part 1
published: true
description: Tutorial on how to build a WhatsApp style app using Kotlin, ViewModels, LiveData, Navigation Component and Two way data binding.
tags: kotlin, android, chat, tutorial
canonical_url: https://getstream.io/blog/build-whatsapp-clone/
cover_image: https://stream-blog-v2.imgix.net/blog/wp-content/uploads/6691bb74896406c4a17f8e3b52fe8f07/image7.png?auto=compress%2Cformat&ixlib=php-1.2.1
series: Build a WhatsApp Clone on Android With Kotlin
---

Android development has come a long way over the last few years. The [Jetpack](https://developer.android.com/jetpack) components significantly speed up development. In this tutorial, we’ll **build a functional clone of WhatsApp with [Kotlin](https://kotlinlang.org/)**. 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 WhatsApp clone Github repo](https://github.com/GetStream/WhatsApp-Clone-Android)
- [Stream’s Kotlin Chat tutorial](https://getstream.io/tutorials/android-chat/#kotlin)

The end result will look similar to this:

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

## Clone the WhatsApp Starter Repo

Start by cloning the **starter** branch of the [WhatsApp Clone Github repo](https://github.com/GetStream/WhatsApp-Clone-Android):

```bash
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](https://developer.android.com/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:

![Empty chat screen](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/eada364a1fbe9f64d54d63f16704f748/image10.png?auto=compress%2Cformat&ixlib=php-1.2.1&h=500)

## Setup the Home Screen

As a first step, we’re going to create the home screen of your app. Which will look like this:

![Example of the cloned homescreen](https://stream-blog-v2.imgix.net/blog/wp-content/uploads/d3ffce8b0bf12a74cdb6748c9d2908c9/image5.png?auto=compress%2Cformat&ixlib=php-1.2.1&h=500)

As an experienced Android developer, you’ll see a few things in this screen:

- [A Toolbar](https://developer.android.com/reference/androidx/appcompat/widget/Toolbar)
- [A Material UI Tab Layout](https://material.io/develop/android/components/tab-layout/)
- [ViewPager2](https://developer.android.com/jetpack/androidx/releases/viewpager2)
- [Material UI Floating Action Button](https://material.io/develop/android/components/floating-action-button/)
- [A ChannelList View](https://github.com/GetStream/stream-chat-android/blob/master/docs/ChannelList.md)

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](https://developer.android.com/guide/navigation/navigation-getting-started). Let’s get started.

Open up `activity_main.xml` and replace the `TextView` tag with a `NavHostFragment`:

{% gist https://gist.github.com/nparsons08/589ecaf1ae8a2dc30238000361dcf9cd %}

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`:

{% gist https://gist.github.com/nparsons08/135e36d0c9aed4438a5ae3720c20b82b %}

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:

{% gist https://gist.github.com/nparsons08/7dafc26b3bd2f621dc372ee6bd7b13cc %}

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:


```kotlin
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:

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

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:

{% gist https://gist.github.com/nparsons08/2456957262e51beba42c2ef4f15f6188 %}

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.

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

## 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:

{% gist https://gist.github.com/nparsons08/4f6e38d38a146d3375b8e324865c0951 %}

We are using the [ChannelListView](https://github.com/GetStream/stream-chat-android/blob/master/docs/ChannelList.md) 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:

{% gist https://gist.github.com/nparsons08/cd4e9b38ff3562510135d04b01c44e35 %}

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](https://getstream.io/chat/trial).

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:

{% gist https://gist.github.com/nparsons08/196d9dc001105e172910f8cff8809010 %}

Time to run your application. The resulting code should render an app that looks like this:

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

## 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:

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

This UI is a [MessageListView](https://github.com/GetStream/stream-chat-android/blob/master/docs/MessageList.md) and a custom message input. For the message input view, we’ll use [two-way databindings](https://developer.android.com/topic/libraries/data-binding/two-way). 

As a first step, open up the empty `fragment_channel.xml` and replace the content with:

{% gist https://gist.github.com/nparsons08/12ecd308ddc1ec2d145ece14899c93af %}

Next, open up the `ChannelFragment.kt` file and replace its contents with this:

```
package com.example.whatsappclone.ui.channel
```

{% gist https://gist.github.com/nparsons08/b47e86116fafe8eeb2806bb975d5abdd %}

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 

{% gist https://gist.github.com/nparsons08/efd697d05c83756c8a1c75e58d82c232 %}

If you run the app you’ll now see a channel list with a very minimalistic message input view:

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

## 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:

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

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:

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

Open the empty `view_message_input.xml` layout and update it to match this content:

{% gist https://gist.github.com/nparsons08/c674f4f66fb9aba7bfb69c611e0bd1f5 %}

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:

```kotlin
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:

{% gist https://gist.github.com/nparsons08/17934cd2f64bf3330d6724343753a2d8 %}

Next, we apply the view utility method on the take picture button:

```kotlin
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:

```kotlin
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:

{% gist https://gist.github.com/nparsons08/d54c62c1ffe060fcd8b86fb4be1b94ec %}

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:

```kotlin
//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](https://www.vogella.com/tutorials/AndroidDatabinding/article.html). 

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

## 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:

{% gist https://gist.github.com/nparsons08/b1a4cc536f3b660dd900237a48da09a2 %}

 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:

{% gist https://gist.github.com/nparsons08/a67a9d3c4e8c37912ac86b8ba7951b9b %}

As the last step, we need to tell the `ChannelList` view about the custom view holder. Open `ChannelListFragment.kt` and uncomment this line:

```kotlin
// 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](https://github.com/GetStream/WhatsApp-Clone-Android).

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

## Customizing the Stream Chat Android SDK

The Views and ViewModels provided by [stream-chat-android](https://github.com/GetStream/stream-chat-android) make it extremely simple to [add chat to your application](https://getstream.io/chat/). Depending on your use case, there are several options available to customize the UI.

1. 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:

```xml
<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"
            />
```

2. The views allow you to specify your layout, enabling you to make simple layout changes.

3. For more considerable UI changes, you can use your `ViewHolder`. In this tutorial, we used that approach for customizing the `ChannelListView`.

4. 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](https://getstream.io/chat/docs/kotlin/) 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](https://github.com/GetStream/WhatsApp-Clone-Android)
- [Stream’s Android Chat Tutorial](https://getstream.io/tutorials/android-chat/#java)
- [Navigation Component Tutorial](https://www.raywenderlich.com/6014-the-navigation-architecture-component-tutorial-getting-started)
- [Testing Tutorial](https://codelabs.developers.google.com/codelabs/advanced-android-kotlin-training-testing-basics/#0)
- [Data binding Docs](https://developer.android.com/topic/libraries/data-binding/two-way)
- [Kotlin tips for removing !!](https://android.jlelse.eu/how-to-remove-all-from-your-kotlin-code-87dc2c9767fb)

Stream also has tutorials available for [iOS Chat](https://getstream.io/tutorials/ios-chat/), [React Chat](https://getstream.io/chat/react-chat/tutorial/), and [React Native Chat](https://getstream.io/chat/react-native-chat/tutorial/) 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!