Bringing our UI into the future

Basic Form Validation in Android with Jetpack Compose

Brandon Wever
DataDrivenInvestor
Published in
4 min readSep 13, 2021

--

It has been nearly 2 years since I posted my tutorial on Basic Form Validation with LiveData and Data Binding.

In the time since I wrote that post, the Android Team has released a new UI toolkit called Jetpack Compose. Jetpack Compose is a completely new way of writing Android UI Code that, in my opinion, makes writing Android fun again. The last time I felt I was having this much fun writing Android code was when I first started writing Kotlin code instead of Java.

I thought it would be a good idea to come back to one of my old posts and update it for this new world. Let’s take a look at how we can ditch Data Binding and XML and still achieve a simple form in Jetpack Compose.

Composing the View

Previously the layout I used to demonstrate this form was a simple LinearLayout with an EditText and a Button. Let’s go ahead and make that with Jetpack Compose:

Our new (old) layout in Jetpack Compose! (No more XML! 🎉)

The Column here handles laying out the child views in the same way that the vertical LinearLayout did in the old XML layout. The OutlinedTextField takes the place of EditText and Button is well, a Button. There are a couple of modifiers added, just to get the style right, but this is just a simple example, and hopefully, the code speaks for itself.

😕 Nothing is happening!

When we run this above code, our view looks alright, but the TextField is unresponsive! This is due to the design of Jetpack Compose. There are plenty of articles and tutorials out there about why this is the case, so I won’t spend the time to explain the why here, just how we get our TextField to change when the user types something.

Wiring State Updates

TextField needs a value property and an onValueChanged property, we could declare this local to the MyCoolForm composable function, but it is recommended to "hoist state" up to a higher level. This means our form doesn't really care about what its values are or what happens when they change. This is a good thing for reusability as this form component we created could be used in different places in our application.

We can lift our state just like TextField by declaring an emailText parameter and an onEmailChanged parameter. Our composable function now looks like this:

the default values are added as a convenience for Previews

Now we need something that can handle those parameters and luckily we’ve got the perfect thing.

Dusting off the ViewModel

Since our form is trying to accomplish the same logic as the form from my post in 2019, we can reuse that ViewModel. If you want to see how we created the ViewModel with unit tests please see the article. Here is the ViewModel:

I’ve added Hilt here, mainly for my convenience, but it is really easy to use in a small test app like this. Read the setup guide here: Hilt Quick Start Guide

In our Activity (or Fragment, or even another Composable) we can now handle the Form’s state updates with the LiveData defined in the ViewModel.

In the Activity, we are doing the following:

  1. Observing the LiveData emailAddress as state, this will be scoped to the Activity's Lifecycle.
  2. Passing in the emailState value to the emailText field. This will set the text shown in the TextField
  3. Deciding what happens when we get an update to the TextField. Right now it is just posting the new value to the LiveData directly, but you could have the ViewModel perform more logic on it here as well.
We’re getting text updates! But that save button still needs to be enabled…

We’re not quite done yet. The save button is not being enabled when a value is entered. We need to observe a new piece of state and update the enabled property on that button in the Form's composable. Luckily, that business logic lives in the ViewModel, so we just need to wire it up.

In the MyCoolForm composable function, you can see that the change is very similar to how the TextField is being updated. We just pass in whether the form is or is not valid, based on some logic our view does not need to care about, and just pass that information along to the enabled property of the button. Now let's head to the activity and finish wiring it up.

Our form is now complete when comparing to the form from 2 years ago. This time we were able to write the whole feature entirely in Kotlin.

All finished.

Wrapping Up

We were able to convert a simple screen that used Data Binding and LiveData into one that uses Jetpack Compose and the same LiveData and ViewModel. Hopefully, this shows that using the tools in the Jetpack Libraries you can move to use Jetpack Compose without causing much trouble in your business logic. Jetpack Compose is the biggest step forward in writing Android Apps since the introduction of Kotlin and I am personally happy any time I get to write some UI in Compose. I hope you’ve learned something new from my post and please let me know in the comments if you have any questions or corrections.

Thanks for reading.

All of the code written for this post is available here

--

--