Creating a Form using Jetpack Compose and Material Design 3 | by Waseef Akhtar | Sep, 2023
5. Next, we’re going to create a text field for the medication name, by creating a new text label as well as a text field where user can enter the medication name.
We’re also going to store the medication name as a value when the user enters it. For that we’ll create a new variable as rememberSaveable
so that the value is stored as long as the screen’s lifecycle and it is not lost in case the screen rotates, or a foldable phone is unfolded/folded.
So we’ll create the variable first thing at the top of the body of AddMedicationScreen()
function.
And then add label and the text field, like so:
Since the TextField is a part of Material Design 3 which is an experimental API as we write, we need to add an OptIn annotation at the top of our AddMedicationScreen function too.
So our code now looks something like this:
Upon running, our app looks like this:
6. Next up, getting number of dosage per day and the recurrence of medication, like so:
To make things look appealing, since dosage per day only requires at max a 2 digit number, it won’t take a lot of horizontal space, so we’re going to add Dosage per day and Recurrence fields on the same row.
In dosage per day, we’re going to introduce a validation so that dosage per day doesn’t exceed 2 digits and shows an error in case the user violates it.
For that, we’re going to create a new rememberSaveable
boolean variable check named isMaxDoseError
and update its value as the value in the text field change.
Upon error, we show a text underneath the Dosage textfield.
We’ll also make sure the keyboard only shows numbers as input values when dosage per day is shown.
As for Recurrence, since Recurrence will only have limited option, for instance, Daily, Weekly, Monthly. We can go for a dropdown menu with those options so it’s easy for users to select between them.
For that, we’re going to create an enum class called Recurrence, so it’s easy to keep it consistent across the app.
Let’s do that first so we can call upon the enum values when creating the input fields.
a) Create a new Kotlin class named, RecurrenceUtil.kt
.
b) Inside the class, create the enum class, and then a list where all the all the enum entries, like so:
Once done, we can continue with the AddMedication screen.
6.1. First thing, create the rememberSaveable
variables for both Dosage and Recurrence, giving them some default values.
6.2. Add a isMaxDoseError
rememberSaveable
variable.
6.4. Add a row to keep the fields in a single row, and then a column for each element (dosage and recurrence) since we always have two elements for each field: a label text on top of the input field.
With everything now explained, here’s how the new changes in the code will now look:
For the RecurrenceDropDownList, we implement another composable function underneath AddMedicationScreen
composable, and it should look like this:
Note: At the time of writing this, with the latest Material Design 3, this is how the APIs are designed, but since there’s been a lot of API changes lately, this could affect how things are defined.
For instance, I noticed that my dropdown menu stopped working after updating material design version, and that was because they added a new requirement for it where one needed to add modifier = Modifier.menuAnchor()
for dropdown menu to work.
In case some other behavioral changes or build issue occur in your case, just make sure you go through the material design docs for Jetpack Compose or search on StackOverflow for latest solutions.
7. Next up, we need an end date from the user when the medication needs to be stopped so that they’re no longer be reminded about it.
To do this, we plan to show a Calendar / DatePickerDialog where user can select a future date as an end date for the medication.
As of creating this, I could not find a Calendar element from the Jetpack Compose / Material Design library.
So in our case, we’re going to use the DatePickerDialog from the Android SDK.
Then, we’re going to create a TextField composable and attach it to the DatePickerDialog in such a way that it listens to the interaction changes inside the DatePickerDialog i.e. we’re going to make use of a compose function called MutableInteractionSource
.
Apart from that, it should be very similar to the rest of our form elements.
Let’s see how it looks in code.
Upon running the app after this, we’re going to have the element working something like this:
7. Next up, we’re going to let user select at what times of day they need to be reminded about the medication, i.e. Morning, Afternoon, Evening, Night.
For this, we’re going explore a new Material component called FilterChip, whose purpose will be that the user selects one or more of them, like a checklist.
Since we have a limited options here, as well, we know what to do about it — i.e. create an enum with those entries.
So,
7.1. Create a new TimesOfDay.kt
file.
7.2. Create a new enum, like so:
We’re also going to keep a check on how many FilterChips can be selected at once, i.e. we should not allow the user to select more FilterChips than the number of dosage per day.
And in case user selects, we show a Toast error message explaining to the user.
For this validation, we need to keep a check of the number of selection of the FilterChips when the state of the FilterChip is changed.
So we need to create common helper methods, handleSelection
and a showMaxSelectionToast
that we can use for each of the FilterChip we’re going to add.
For each of the FilterChips, we’re also going to add a rememberSaveable
variables like we have done for all the previous input fields before.
One final thing.
We’d also like the FilterChips to be evenly divided and not take too much or too little space on the screen. So we’re going to keep two FilterChips per row.
Let’s see how it works in code. 👇
With these common helper functions that we use with each of our FilterChips.
And with this error function that shows a message when max selection is exceeded.
Upon running the app at this stage, you’ll have it looking something like this:
And we’re almost done with all our form elements.
The only element remaining, a button.
The purpose of which will be that the user will press it in order to:
- Check that none of the mandatory input fields are empty or have an invalid value.
- Show an error message in case one of the value is invalid.
- Carry all the values to either the next screen or store locally.
Since #3 is out of the scope of this blog post and we’ve already crossed the 2000th word mark on this blog post, we’re not going to go further into #3 and rather just look at how the validation will be performed by the button.
Let’s see how it looks in code.
With this helper function that checks validation for every input field.
Upon running the app, this is how it is going to look and function:
And voilà! we’re done creating our Add Medication form using Jetpack Compose and Material Design 3.
To recap, here are all the components that we explored in this blog post:
- TextField
- TextField with input type as Number only.
- ExposedDropdownMenuBox
- DatePickerDialog
- FilterChip
- Button
Since this form is a part of my work-in-progress side project called, you can have a look at its source code to see how it’s implemented with a more broader scope on where form values go next and how they’re saved to Room Database and retrieved back from it.