[Part 3] Jetpack Compose — Migration of Component View (Input Text) on Existing Layout

Veronica Putri Anggraini
6 min readFeb 13, 2023

In the previous article, already explained about the setup of the existing project that will be migrated. If you haven’t read the previous article, please visit the previous article first to understand more about the migration process for this project.

Part 1: Jetpack Compose — Introduction
Part 2:
Jetpack Compose — Preparing to Migrate Existing Layout to Compose
Part 3 : Jetpack Compose — Migration of Component View (Input Text) on Existing Layout
Part 4 :
Jetpack Compose — Implementing a Compose View in a ViewGroup

As discussed in the previous article, migrating projects to Compose can be done using ComposeView and AndroidView. The first to try to implement is ComposeView. However, there are 2 mechanisms that need to be considered in migrating to Compose.

  1. Migrating from the smallest components, in the form of views such as (TextView, ImageView, Button, EditText) to Widget components in compose such as (Text, Image, Button, TextField), this mechanism is commonly referred to as Bottom-up.
  2. Migrating from the largest component, which is a viewgroup such as (LinearLayout, RecyclerView) to compose components (Column, Row, LazyList), this mechanism is commonly referred to as Top-Down.

To learn about the migration process step by step, let’s start by using the Bottom-up mechanism. Previously, don’t forget to clone the starter code, which can be downloaded at the following link, and select the before-migrate branch as the starter code.

Compile the starter code then the layout view will appear which we will migrate later. It looks like below.

To make it easier for us to arrange the compose layout later by using the Bottom-up mechanism we can make the Input Text component first. If you pay attention, there are two text inputs that have a similar design. So that we can make a base widget for text input to make it more reusable and simplify writing code. For details, consider the following steps:

  1. Create a duplicate color in a kotlin file to make it easier to use when creating compose components. Create a package with the name theme, then create a Color.kt file in the theme folder, as shown below.

2. Create a duplicate color.xml in the Color.kt file as shown below.

color.xml
Color.kt

3. Then create a base widget for input text as below, but as a start, create a widget package and add the InputText.kt file to the widget package.

Input Text
widget package

As a detailed reference, each attribute (color, radius, margin, etc) of the widget that will be created can use the view in the xml file. Look at the TextInputField view code below.

TextInputField

4. In the InputText.kt file, create a composable function for the InputText compose widget named InputTextField. Composable functions are building blocks used to create user interfaces for applications built with Jetpack Compose. To create a composable function you need @Composable, but there is a shortcut that makes it easier to use just type “comp” in your kotlin file and click enter, as shown below.

5. Add some parameters to the InputTextField function so that it’s easy to customize when used later.

@Composable
fun InputTextField(
modifier: Modifier,
hint: String,
type: KeyboardType,
capitalization: KeyboardCapitalization,
color: Color
) {}

6. In the text input component there is a change in the value of the widget so we need a variable that can store changes in the value of the widget, make it in the format below.

var inputValue by remember { mutableStateOf(TextFieldValue()) }

7. There is a type of text input that must be made, namely by toggle password with hide and unhide conditions. So we need a variable that is used to store toggle status changes. Then create a variable like below.

var passwordMode by rememberSaveable { mutableStateOf(false) }

8. To create a Text Input component we can use TextField(). Add a value attribute to receive input from the user, and onValueChange to accept changes to the value of the textfield.

TextField(
value = inputValue,
onValueChange = { inputValue = it })

9. Next, add a text label with the hint parameter to the InputTextField function as shown below. This label requires text so the hint parameter is included in the text widget.

TextField(
value = inputValue,
onValueChange = { inputValue = it },
label = {
Text(
text = hint
)
})

10. Under certain conditions the textfield will be used to receive input in the form of a password with a hidden condition so it is necessary to use the visualTransformation attribute as shown below.

TextField(
value = inputValue,
onValueChange = { inputValue = it },
label = {
Text(
text = hint
)
},
visualTransformation = if (passwordMode) VisualTransformation.None else PasswordVisualTransformation())

11. To add an icon at the end of the textfield in the password input condition, add the trailingIcon attribute. Add a logical implementation that this icon only appears if the KeyboardType used is password if (type == KeyboardType.Password) {}.

TextField(
value = inputValue,
onValueChange = { inputValue = it },
label = {
Text(
text = hint
)
},
visualTransformation = if (passwordMode) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
if (type == KeyboardType.Password) {
val image =
if (passwordMode) Icons.Filled.Visibility else Icons.Filled.VisibilityOff
val desc = if (passwordMode) "Hide Password" else "Show Password"
IconButton(onClick = { passwordMode = !passwordMode }) {
Icon(imageVector = image, contentDescription = desc)

}
}
})

12. Make the textfield accept only one line of input by adding a singleLine component as shown below.

singleLine = true

13. Add padding around the input field and add the input type with the type parameter in the InputField function.

keyboardOptions = KeyboardOptions(
capitalization = capitalization, autoCorrect = true, keyboardType = type
),

14. Then make the textfield a circular rectangle by adding a shape component and a RoundedCornerShape value. Set the radius of each corner to 15 dp.

shape = RoundedCornerShape(15.dp),

15. Remove the underline in disable, focused and unfocused mode on the textfield by setting the transparent color, and setting the background color of the textfield with the color parameter in the InputTextField function.

colors = TextFieldDefaults.textFieldColors(
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
containerColor = color
)

16. To see a preview of each widget, add the code below.

@Preview
@Composable
fun PreviewWidget() {
Column() {
InputTextField(
modifier = Modifier,
hint = "Enter Username",
type = KeyboardType.Text,
capitalization = KeyboardCapitalization.None, color = White
)
InputTextField(
modifier = Modifier,
hint = "Password",
type = KeyboardType.Password,
capitalization = KeyboardCapitalization.None, color = White
)
}
}

So the whole code will be as below.

One of the widgets from the screen that will be migrated has been completed, the next process will be discussed in the next article. See you in the next article!

--

--

Veronica Putri Anggraini

Software Engineer Android @LINE Bank 🤖 Google Developer Expert for Android https://github.com/veroanggra