Jetpack Compose - Part 2

In the previous article we learnt about what is jetpack compose, it's uses, how it is different from traditional XML approach and a simple example to get the look and feel of compose.

In this article, let us learn about how to add and arrange different elements in the layout, styling them using material design principles and how to enhance the look of your UI using animations.

Arrange different elements inside the layout.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
           DisplayMessage(Messages("Person1","Welcome to Jetpack Compose Learning"))
        }
    }
}

data class Messages(val nameOfPerson: String, val messageText: String)


@Composable
fun DisplayMessage(msg: Messages){
    Row{
        Image(
            painter = painterResource(id = R.drawable.family_daughter),
            contentDescription = "Profile pic",
            modifier = Modifier.size(50.dp)
                .clip(CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))
        Column{
            Text(text =msg.nameOfPerson)
            Spacer(modifier = Modifier.height(4.dp))
            Text(text = msg.messageText)
        }
    }
}

@Preview
@Composable
fun PreviewMessage(){
    DisplayMessage(msg = Messages("Person1","Welcome to Jetpack Compose"))

}

In the above pic, you can see that 2 text elements and one image element are arranged in a layout. Text elements are arranged one below the other by using Column function with a space of 4dp height in between. One image element is placed horizontally next to the text elements. Hence Row function is used to separate the text elements and image element. Here also a space of 8dp width is given between the image and text elements. Image height is set to 50dp.

Compose Modifiers are used to change the composable's size, appearance,etc. It can be used to improve layout style and appearance.

Use of material design with compose.

Material design is built on Color, Typography and Shape. We will use it to enhance the look of our screen.

@Composable
fun DisplayMessage(msg: Messages){
    Row{
        Image(
            painter = painterResource(id = R.drawable.family_daughter),
            contentDescription = "Profile pic",
            modifier = Modifier
                .size(40.dp)
                .clip(CircleShape)
                .background(color= Color.Gray)
                .border(1.5.dp,MaterialTheme.colorScheme.primary, CircleShape)
        )
        Spacer(modifier = Modifier.width(8.dp))
        Column{
            Text(text =msg.nameOfPerson,
                color=MaterialTheme.colorScheme.primary,
                style=MaterialTheme.typography.titleSmall)
            Spacer(modifier = Modifier.height(4.dp))
            Surface(shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp){
                Text(text = msg.messageText,
                    color=MaterialTheme.colorScheme.secondary,
                    style =MaterialTheme.typography.bodyMedium)
            }

        }
    }
}

Adding Lists and Animations

First create list of messages using Lazy Column. I used the Sample data provided in developer.android.com.

The animation we are going to add here is that - when a message is clicked, it gets expanded if it has more than 1 lines and the background color gets changed.

First declare a variable isExpanded to track the state of the UI. remember and mutableStateOf functions are used for this. And clickable modifier should be used to handle click events.

 var isExpanded by remember{ mutableStateOf(false) }

  Column(modifier =  Modifier.clickable{isExpanded = !isExpanded})

To expand the message text add maxLines property as shown below.

  Text(text = msg.messageText,
                    modifier = Modifier.padding(all = 4.dp),
                    maxLines = if(isExpanded) Int.MAX_VALUE else 1,
                    style =MaterialTheme.typography.bodyMedium)

To animate the background color modification after the click, use animateColorAsState function and to animate the container size smoothly use animateContentSize.

//To update the surface color
        val surfaceColor by animateColorAsState(
            if (isExpanded) MaterialTheme.colorScheme.primary else
             MaterialTheme.colorScheme.surface
        )
  Surface(shape = MaterialTheme.shapes.medium,
                shadowElevation = 1.dp,
                color=surfaceColor,
                modifier = Modifier.animateContentSize().padding(1.dp)){
                Text(text = msg.messageText,
                    modifier = Modifier.padding(all = 4.dp),
                    maxLines = if(isExpanded) Int.MAX_VALUE else 1,
                    style =MaterialTheme.typography.bodyMedium)
            }

Final output looks something like this.