Jetpack Compose — Let’s Make An Application

Pradyot Prakash
9 min readOct 16, 2023

--

What are we going to make?

Below are the screenshots of the application which we will try to make using Jetpack Compose.

A simple exchange rate application made using Jetpack Compose

A video of the application will be more helpful to understand

Exchange Rate — Video

What are we using to achieve the above application?

So we will be using some libraries to achieve the above

  1. Jetpack compose — For UI
  2. Retrofit — To connect to an exchange rate service
  3. Hilt — For dependency injection, don’t want to do the dirty work by my own
  4. Room — To store the response locally, so that the number of calls to the service is less and also if network is not available we are still able to use the application (to a certain extent)
  5. Logger — For logging
  6. Testing libraries — To add few test cases to see if everything is working fine

So, which service we will be using to get the exchange rates. There are many available, the one which we will be using is

It provides a monthly usages of 1000 requests, it should be enough to complete the application, hopefully.

How are going to make the application?

Let’s first see how our build.gradle.kts looks like

app/build.gradle.kts

Now most of the code in the above snippet is something I assume you would be familiar with, but what is updateTranslations task for?

As the name suggests, this is use for updating the application translations file. Let’s see the ./script/generate_translation_file.py file.

generate_translation_file.py

If you don’t know Python basic, then the gist of this code is to fetch the JSON content from ./app/src/main/assets/localization_en.json and create the TR.kt file, by using the key as a constant variable which can be used to get the value and show it on the UI.

This script is helpful in maintaining the localisation of the application. Check the localization folder to understand how it works.

And in the gradle you can see we have added the task to the prebuild, which runs the python script before the build is done and updates the translation file of Kotlin.

Why do we need the script?Well we don’t. But in some ways it makes our life easier after spending 2–3 hours. So why not? And also it’s fun writing script.

Now our gradle is done. So let’s move to the next step of deciding how our project structure will look like.

Project Structure

It looks something like below

Packages/Layers

Basic details of all the packages are given below, you will get the idea of how each layer communicate with each other.

Project Structure Diagram

di is only for dependency injection.

This structure basically removes the business logic from UI, and also hides the place from where the data is fetched from. So in future if you want to change the service or make some changes to the logic of local storage then not every layer will be affected.

There are many great architecture patterns to go with, in my opinion which is easier and understandable to you, as a developer, you should go with that. Architecture is all about making developers life easy.

Now let’s look at each layer one by one, in such a way that in the end we will have our application up and running.

Let’s start with data and device layer

Layers structure

Data

This layer is basically for communicating with our remote service and make the requests and get the response

Let’s create our retrofit client first, we will be needing it to make the requests

ApiClient.kt

Few things here to note,

  1. What is ChuckerInterceptor? — This is basically to check the requests/response details. You can check the https://github.com/ChuckerTeam/chucker link to know more about it.
  2. What is DebugInterceptor? — This interceptor is used to redirect the request from actual service to a local JSON files. DebugInterceptor is helpful while debugging the application and also not affecting our monthly usage. And also helpful in many scenarios where the actual service is not ready and this idea can be used to make the UI and also handles all the other edge cases without making any changes to the actual service. Check the class to know how it is implemented. It can be done in a more proper way, this is the most simplest way I can think of.
  3. What is ApiKeyInterceptor? — This is only used to add the API key given by the service to each requests header. Rather than adding it to the each request this intercepter will automatically add it before making the requests to the service.

Hope the explanation was enough to understand the ApiClient, any doubt let me know in the comments will be happy to help. This will be used by our service classes which will define our requests.

Now after the client we will be needing to create the service classes. In this particular application we will be needing only one service class, and the implementation is as below

ExchangeService.kt

ExchangeService.kt has only 2 functions, first to get all the currencies and other one to get the exchange rates for the source currency.

Now we need to add it to the DI module, so that when we need it in the repositories to make the requests then it can be injected.

Services.kt

Now with the above setup, we will be able to connect to https://api.apilayer.com/currency_data/ service and make the /list and /live requests.

We have completed our data layer for the remote, let’s create the device layer which will help us in storing the response in local storage and fetch whenever needed.

Device

Before jumping to Room Database, few things you need to know which you can get from https://developer.android.com/training/data-storage/room/. Basic knowledge will be needed before hand.

We need 2 entities for room

Entities — AllCurrencies & ExchangeRate

This will define our tables.

Now we need the DAO interfaces for making our queries for each table

DAO — AllCurrenciesDao & ExchangeRatesDao

Both the DAO interfaces have only three usage

  1. Get all details
  2. Insert new values
  3. Delete entries

Let’s create our database, which will connect both the entities and DAO

AppDatabase.kt

This is just the basic structure of any Room database. But hold on what are these TypeConverters. These are basically for room to know on how to store the Map<K, T>.

Now we have completed our Device layer as well. So the application can connect to remote and store the details in local storage as well.

Let’s see how we are going to create our domain layer which will connect to Data and Device layer.

Domain

Let’s see our repository class

ExchangeRepository.kt

ExchangeRepository.kt class is what works as a source of truth. It gets the details from data or device layer without letting other layers the source.

Check the response folder in core layer to know how the common response class is created to handle the loading, success, error and idle state.

Now the ExchangeUsecase.kt is something which becomes easy to create after all the handling is done by the repositories

ExchangeUsecase.kt

And yes we will be using flows to update the app layer on the latest state. This will help us in updating the loading state and also know when the data is received.

Looks like we have created our data, device and domain layers. Lets see the app layer which will show the UI and also make the requests.

App

I will be taking only one screen, the home screen, and showing the implementation. Rest all are likewise and also less complicated.

Forgot to mention, we will be using ViewModel to handle the business logic and also update the UI.

First let’s check the HomeViewModel.kt class

  1. Declaring the state variables
HomeViewModel.kt

2. Getting all the currencies

HomeViewModel.kt

3. Getting all the live exchange values

HomeViewModel.kt

Now these are main calls to the domain layer.

Now whenever a user input a number in the test field, we also update the exchange values. Let’s see how we are doing it

HomeViewModel.kt

So this is what makes the whole HomeViewModel.kt class. There are other methods as well but those are basically to update the UI and other small stuff, which you can understand by going through the class.

Now let’s see how we will be making our HomeView

We will be dividing our UI into smaller composables which makes it easier to tests

AmountEntryComposable
Result of above code

We also need the exchange rate composable to show the rate and also update it based on the text field changes

ExchangeRateComposable
Result of above code

Now we have our smaller composables, lets combine it into the screen

HomeView

Now, when everything is combined — HomeView + HomeViewModel then we get

Final Result

There are other parts of the UI and business logic as well, make sure to go through the application and code as well to understand the whole project.

It’s not a big project but helpful to start with something.

So in the end we completed our ExchangeRate application

Exchange Rate — Video
NOTE

In this project you can see that I have hardcoded the API_KEY into
the project.

For this demo project this is okay, but it's not a good practice. Since the
key is being deleted from the service so it is not useful.

Make sure you follow guidelines on how to store or get the API_KEYS or
sensitive information.

Same goes for storing data in local storage.

There are many things we should consider or do while making the application, and this project doesn’t show all of them.

Take this as a demo application and make your own better versions of it.

Get the full source code from

If you have any questions or suggestions please provide it in the comments, will try to answer or implement it.

Follow for getting the notification when new articles are published.

Follow me on Twitter and GitHub.

--

--

No responses yet