Build Single Activity Apps With Jetpack Compose
Create standalone Android apps
When I started developing android apps back in 2017 I remember building out activities for every single screen or feature in the app and baking all the business logic inside these activities. The code looked so complex and messy and I would proudly show it off to my friends. I was in my 11th grade at around 15–16 y/o and I thought I was pretty smart, “No one can do what I do”, I thought to myself.
Then I learned about fragments and there was nothing I hated more about android development. What was the point? They looked just like activities but made everything harder. I couldn’t get fragment transactions working or pass some data between multiple fragments without going through a maze of errors (which taught me a lot by the way). To spare me the trouble I only used fragments when necessary and continued to use activities throughout entire apps.
In my quest to keep on upskilling and learning I discovered clean architecture and SOLID principles which I try to apply in every software I build, mobile or otherwise. The next app I built had a single activity and around 4 fragments. Turns out fragments weren’t so bad after all. I had a much better appreciation of fragments at this point and vowed to always use them. Until I picked up jetpack compose. Long story short, I hate fragments again.
If you have never used jetpack compose before you should try it. I will not go into any details on how it works or the basics. The best resources I used were coding with mitch and Google’s documentation. In this article, I go through one of many ways to build single activity apps using jetpack libraries.
The key idea is to have one single activity that serves as the main entry to and exit from your app. certain apps might require building several activities, for example, a MainActivity
and a SettingsActivity
but the fundamental idea remains the same.
Before we get started it’s important to understand the main components that make up most apps, in this context, it’s mainly one or more screens the user interacts with and a mechanism for navigating between these screens if there’s more than one. So how does this work?
Create the app’s MainActivity
Once we have our basic Activity ready we now need to set up our navigation controller which will allow us to navigate between different screens. For this example, we assume our app has only 3 possible screens, Home, Search, and Library.
The next step is to create a top level Singleton Screen Object that assigns unique routes for all possible screens in your app. For our example this Object — stored in a top level Kotlin file — looks something like this:
Add NavHost
Back in MainActivity
we now add a NavHost
with the initial route set to Screen.Home.route
because that’s our app’s default screen.
The next step is to put all top level destinations — in our case the Home, Search and Library destinations — inside the NavHost. To do this we take advantage of Kotlin’s extension functions and express each of these screens as extensions of NavGraphBuilder
.
We take advantage of Kotlin’s extension functions and express each of these screens as extensions of NavGraphBuilder
Back in MainActivity
we now put these inside the NavHost
as follows:
Of course, the app would not function like this. To actually navigate between screens you would need a Scaffold with a bottom app bar or some other means of navigation for example a button click that calls navController.navigate(route)
. The above functions allow you to create a navigation graph that defines the relationships that exist between different screens in your app.
A good practice would be to extract the navigation logic to a separate top-level Kotlin file because usually for a production application the navigation graph gets quite complicated. Another best practice would be to place our screens in separate modules altogether but that’s a topic for another day.
Thanks for stopping by. Stay sharp!