CODEX

Exploring Jetpack Compose — Build a Simple Countdown Timer App

Inuwa Ibrahim
Mar 8 · 5 min read

Recently the Android Team announced the release of Jetpack Compose beta—

“Android’s modern toolkit for building native UI. It simplifies and accelerates UI development on Android. Quickly bring your app to life with less code, powerful tools, and intuitive Kotlin APIs.”

Basically, Jetpack Compose uses a “Declarative” approach to build UI by providing Declarative API’s.

In simple terms - with Jetpack Compose, there will be no need for creating “XML” layouts to build your UI.

I will show you how to get started with JetPack Compose by building a simple Countdown Timer App, that countdown from 60 seconds (1 hr) to 0.

The final app will look like this:

Here is a video showing how it works

Okay lets start “COMPOSING” 🎼

STEPS

  1. Set up Android Studio
  2. Add Dependencies
  3. Create Activities, Classes, Files

1. SET UP ANDROID STUDIO

  • At the time of writing this post, Jetpack Compose is currently in beta, and all features are not fully stable. It is recommended to download Latest Release of Android Studio Canary to use with Jetpack Compose. Download here — https://developer.android.com/studio/preview
  • Install
  • After Installing, Create A New Project, In the Select a Project Template window, select Empty Compose Activity and click Next.
  • In the Configure your project window, do the following:
  • Set the Name, Package name, and Save location as you normally would.
  • Note that, in the Language dropdown menu, Kotlin is the only available option because Jetpack Compose works only with classes written in Kotlin.
  • In the Minimum API level dropdown menu, select API level 21 or higher.
  • Click Finish
  • Create 5 new Packages under project package name:
    Name them: helper, ui (Most times this is automatically created for you), utils, view, viewmodel

2. ADD DEPENDENCIES

  • Go to Build.gradle (Module:App)
  • Include the following dependencies along with the ones that came by default
  • Your full Build.gradle (Module:App) dependency block should look like this:

3. CREATE ACTIVITIES, CLASSES AND FILES

Helper and Utility Classes

  • Under helper package, right click and select New Kotlin File/Class
  • Click and name the class — SingleLiveEvent
  • This class is a subclass of MutableLiveData with a single Observer Observing it at a time, hence it is aware of view’s lifecycle, and emits data once when required
  • Full code below
  • Great, Under utils package, Create A new Kotlin — Object, name it Utililty
  • The Utility Object contains:
    - Time Constant — Which is 1 hr converted to milliseconds — 60000L
    - Function for formatting time, converting value to minutes and seconds which is displayed in UI.
  • Full code below:

ViewModel Class

  • Under viewmodel package, create a new class called MainViewModel
  • The MainViewModel class extends viewModel, it contains codes designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
  • It also contains Logic such as:
    - Function for handling Countdown — Pause, Stop, Start
    - Saving state of values of progress, time etc
  • Full Code for MainViewModel below:

View — Components and Activity

Components

  • Under view package, create a sub package called components
  • The components package lies all UI related views such as count down indicator, button and confetti view after countdown finishes
  • Right click components package and create a new kotlin file, name it CountDownIndicator
  • This file contains the UI for the:
    - Circular Progress Bar Indicator
    - Circular Progress Bar Background
  • Full code for CountDownIndicator below:
  • Under the components package again, create a new file called — CountDownButton
  • This view contains UI code for the button displayed on the app — With a text reading “Start” or “Stop” depending on the state of the timer
  • Full code for CountDownButton below
  • Lastly, under components package, create a new file called — ShowCelebrationView
  • This view simply contains “confetti” shown when the count down elapses. This was achieved with the help of an external Library — https://github.com/DanielMartinus/Konfetti
  • Full code for ShowCelebrationView
  • Notice how we use AndroidView() to reference of traditional custom android Views/UI. This is useful Compose interoperability. You could also use AndroidViewBinding() for binding xml layouts while using in a composable.
    More — https://developer.android.com/jetpack/compose/interop
  • Now, under view package, create a new file called — CountDownScreen
  • This file groups all the components we just created to a single file as a composable, so we can finally use it in our activity.
  • Full code for CountDownScreen below
  • Note just like coroutines, a composable function can only be called within a composable function, that is why we were able to call those functions inside this class which is annotated with @Composable

Activity

  • Now open your MainActivity.kt file and add these after super.OnCreate
  • MyApp is a composable function that calls the CountDownScreen composable function which displays UI related Items
  • Full Code For MainActivity below:

AND WE ARE DONE! 👏

Can you believe we just built an android app without any XML layout.

This project was built as part of an entry for #AndroidDevChallenge week 2 for jetPack Compose.

Full Code

https://github.com/ibrajix/TimerJetpackCompose

Learning Resources:

My take on JetPack Compose

PROS

  • Easy to build layouts which can be extremely flexible and reusable
  • Maintainable code — Easier to test — Components Based UI.
  • Less lines of codes
  • No need for Adapters for recyclerView
  • Easier to create animations and complex shapes when compared to traditional XML.

CONS

  • Unstable Preview Layout which just doesn't work most of the time
  • Limited resources to learn, — When stuck, its so hard to find a solution quickly
  • Changes to inbuilt API’S e.g getting a context in a composable was:
    val context = ContextAmbient.current,
    its now:
    val context = LocalContext.current.
    Also RoundedCornerShape() class now requires top, bottom, (start/end) values as parameters which was not so before. Something like — shape = RoundedCornerShape(25.dp), throws an error — “ Type mismatch. Required: CornerSize Found:Dp”

See — https://stackoverflow.com/questions/66496662/jetpack-compose-has-cutcornershape-api-now-changed/66496849#comment117588166_66496849

VERDICT

Jetpack Compose is great and most of the cons I listed will surely be worked on/improved when it is on stable release.
Obviously, not ready for “production apps” (For me!)

Thanks for reading! 🙌

Reach Me

Twitter
https://twitter.com/ibrajix/

Instagram
https://www.instagram.com/designjix/

Proud to geek out.

Sign up for Geek Culture Hits

By Geek Culture

Subscribe to receive top 10 most read stories of Geek Culture — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store