Getting Started With JetPack Compose
Tags: jepackcompsed
Published: 2021-05-26 14:28:44

In this tutorial we are going to discuss few concept in the jetpack compose library which a upcoming ui toolkit for faster development of android UI with kotlin managed by google.

What We are going to cover.
##### 1. Creating Custom Staggered Grid.
##### 2. Creating Custom Column With Composed.
##### 3. Generating ListView.
##### 4. Combining Logic to Have full UI.



# Imports
```kotlin
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.example.test2.ui.theme.Test2Theme
import com.google.accompanist.coil.rememberCoilPainter
import kotlinx.coroutines.launch
import kotlin.math.max

```


1. Creating custom Staggered Grid
```kotlin
@Composable
fun StaggeredGrid(
    modifier: Modifier = Modifier,
    rows: Int = 3,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->

        // Keep track of the width of each row
        val rowWidths = IntArray(rows) { 0 }

        // Keep track of the max height of each row
        val rowHeights = IntArray(rows) { 0 }

        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.mapIndexed { index, measurable ->
            // Measure each child
            val placeable = measurable.measure(constraints)

            // Track the width and max height of each row
            val row = index % rows
            rowWidths[row] += placeable.width
            rowHeights[row] = max(rowHeights[row], placeable.height)

            placeable
        }

        // Grid's width is the widest row
        val width = rowWidths.maxOrNull()
            ?.coerceIn(constraints.minWidth.rangeTo(constraints.maxWidth)) ?: constraints.minWidth

        // Grid's height is the sum of the tallest element of each row
        // coerced to the height constraints
        val height = rowHeights.sumBy { it }
            .coerceIn(constraints.minHeight.rangeTo(constraints.maxHeight))

        // Y of each row, based on the height accumulation of previous rows
        val rowY = IntArray(rows) { 0 }
        for (i in 1 until rows) {
            rowY[i] = rowY[i - 1] + rowHeights[i - 1]
        }

        // Set the size of the parent layout
        layout(width, height) {
            // x co-ord we have placed up to, per row
            val rowX = IntArray(rows) { 0 }

            placeables.forEachIndexed { index, placeable ->
                val row = index % rows
                placeable.placeRelative(
                    x = rowX[row],
                    y = rowY[row]
                )
                rowX[row] += placeable.width
            }
        }
    }
}
```

In order to view the implementation of what we've just created, let's create a preview

This is the column We want to insert into the staggered Grid.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z0ltb7vvqm0y9wwd6ddl.png)

The image should be in your drawable folder.

```kotlin

/* Testing custom staggered Grid. */

@Composable
fun GetImageColumn(text: String){
    val typography = MaterialTheme.typography
    MaterialTheme{
        Column (
            modifier = Modifier.padding(16.dp)
        ){
            Card(
                elevation = 10.dp

            ){
                Image(
                    painter = painterResource(R.drawable.header),
                    contentDescription = null,
                    modifier = Modifier
                        .height(180.dp)
                        .fillMaxWidth()
                        .clip(shape = RoundedCornerShape(4.dp)),
                    contentScale = ContentScale.Crop
                )
            }

            Spacer(Modifier.height(16.dp))
            Card(
                elevation = 10.dp,
                modifier = Modifier.fillMaxWidth()
            ) {
                Column (modifier = Modifier.padding(16.dp)){
                    Text(text = text, style = typography.h4)
                    Text("Nairobi Kenya", style = typography.body2)
                    Text("14 th Feb 2021", style = typography.body2)
                }
            }
        }

    }
}
```

To Create A Preview, Have the below code.
 
```kotlin
@Preview(showBackground = true)
@Composable
fun Preview1(){
    Scaffold{
        Column(modifier = Modifier
            .padding(16.dp)
            .verticalScroll(rememberScrollState()),
            content = {
                StaggeredGrid (rows = 2){
                    for (topic in topics) {
                        GetImageColumn(text = topic)
                    }
                }
            }
        )
    }

}
```
Preview Image: With frames.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u2kyf1rir1wdvqeinwa6.png)
Preview Image: Without frames.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e9pj2gpo2grkzvg4y0is.png)
 
Task One Done, Let's now move to task two.
##2. Creating Custom Column With Compose.

```kotlin
@Composable
fun GetCustomColumn(modifier: Modifier, content: @Composable () -> Unit){
    Layout(modifier = modifier, content = content){
            measurables, constraints ->

        val placebles = measurables.map { measurable -> measurable.measure(constraints) }

        var yPosition = 0

        layout(constraints.maxWidth, constraints.maxHeight){
            placebles.forEach{
                placeable -> placeable.placeRelative(x = 0, y = yPosition)
                yPosition += placeable.height
            }
        }

    }
}
```

For testing our column, we can replace all the column composables that we used in creating the preview to our custom staggered view.

```kotlin
@Preview(showBackground = true)
@Composable
fun Preview1(){
    Scaffold{
        /* New  GetCustomColumn we created*/
        GetCustomColumn(modifier = Modifier
            .padding(16.dp)
            .verticalScroll(rememberScrollState()),
            content = {
                StaggeredGrid (rows = 2){
                    for (topic in topics) {
                        GetImageColumn(text = topic)
                    }
                }
            }
        )
    }

}
```
Some Preview.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mj8aw2je37lx5j82v05h.png)
 
Now let's move to the next task.
## 3. Generating ListView.

here is our list.
```kotlin
val topics = listOf(
    "Arts & Crafts", "Beauty", "Books", "Business", "Comics", "Culinary",
    "Design", "Fashion", "Film", "History", "Maths", "Music", "People", "Philosophy",
    "Religion", "Social sciences", "Technology", "TV", "Writing"
)
```


List Code.
```kotlin
@Composable
fun SimpleList(){
    val listSize = 100
    // We save the scrolling position with this state
    val scrollState = rememberLazyListState()
    // We save the coroutine scope where our animated scroll will be executed
    val coroutineScope = rememberCoroutineScope()

    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceEvenly
    ) {
        Button(
            onClick = {
                coroutineScope.launch {
                    scrollState.animateScrollToItem(0)
                }
            }
        ) {
            Text(text = "Top")
        }
        Button(
            onClick = {
                coroutineScope.launch {
                    scrollState.animateScrollToItem(listSize - 1)
                }
            }
        ) {
            Text(text = "Bottom")
        }
        
    }
    LazyColumn(state = scrollState) {
        items(listSize){
            PhotographerCard(numberCount = it)
        }
    }

}


@Preview(showBackground = true)
@Composable
fun LayoutsCodeLabPreview(){
    Test2Theme{
        SimpleList()
    }

}



@Composable
fun PhotographerCard(modifier: Modifier = Modifier , numberCount: Number){

    Card(elevation = 10.dp, ) {
        Row(
            modifier
                .padding(6.dp)
                .clip(RoundedCornerShape(4.dp))
                .background(MaterialTheme.colors.surface)
                .clickable(onClick = { /*TODO Implement click */ })
                .padding(16.dp)
                .fillMaxWidth()

        ){
            Surface(
                modifier = Modifier.size(50.dp),
                shape = CircleShape,
                color = MaterialTheme.colors.onSurface.copy(alpha = 0.2f)

            ) {
                Image(
                    painter = rememberCoilPainter(
                        request = "https://developer.android.com/images/brand/Android_Robot.png"
                    ),
                    contentDescription = "Android Logo",
                    modifier = Modifier
                        .size(50.dp)
                        .padding(8.dp)
                )
//                Icon(Icons.Filled.PersonOutline, modifier = Modifier.padding(8.dp), contentDescription = null)
            }

                Column(
                    modifier
                        .padding(start = 8.dp)
                        .align(Alignment.CenterVertically)
                ) {
                    Text("List View. ", fontWeight = FontWeight.Bold)
                    CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
                        Text(text = "$numberCount sec.", style = MaterialTheme.typography.body2)
                    }

                }


        }
    }

}
```
Preview
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5i0yemnugp7t6dvdt4na.png)

Lastly Let's combine everything to have some simple UI in action.
```kotlin
/* UI  play */
@Preview(showBackground = true)
@Composable
fun LayoutsCodeLab(){
    Scaffold (
        topBar = {
            TopAppBar(
                title = {
                    Text(text = "Design With Composed", style = MaterialTheme.typography.h5)
                },
                actions = {
                    IconButton(onClick = { /*TODO*/ }) {
                        Icon(Icons.Filled.Favorite, contentDescription = null)
                    }
                }
            )
        }
    ){

        innerPadding -> BodyContent(
        Modifier
            .padding(innerPadding)
            .padding(8.dp))
        
    }
}
@Composable
fun BodyContent(modifier: Modifier = Modifier){
    Spacer(Modifier.height(10.dp))
    GetCustomColumn(modifier = modifier) {
        Row(modifier = modifier
            .fillMaxWidth()
            .padding(16.dp)
            .size(200.dp)
            .horizontalScroll(rememberScrollState()),
            content = {
                StaggeredGrid {
                    for (topic in topics) {
                        Chip(modifier = Modifier.padding(8.dp), text = topic)
                    }
                }
            }
        )
        PreviewListView1()
    }

}
@Composable
fun PreviewListView1(){
    Scaffold{
        /* New  GetCustomColumn we created*/
        // We save the scrolling position with this state
        val scrollState = rememberLazyListState()

        LazyColumn(state = scrollState) {
            for (topic in topics) {
                items(topics.size) {
                    GetImageColumn(text = topic)
                }
            }
        }
    }

}
```
Preview.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3w8iljc6i0tf4r3tiwtx.png)
 

{% user kchienja %}
{% twitter 1397560507238952963 %}
{% github kahdichienja/kotlin_test1 %}
[![Kah Di Chienja's DEV Community Profile](https://d2fltix0v2e0sb.cloudfront.net/dev-badge.svg)](https://dev.to/kchienja)