My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more

Jetpack Compose in Android

Odhiambo Brandy's photo
Odhiambo Brandy
·May 6, 2022·

8 min read

Declarative UI Design with Jetpack Compose

Every application has a UI framework behind it. These frameworks play a huge part in how the applications are created and their performance as well. They also have different ways of operation but can be summarized into two: declarative and imperative.

Imparative UI

This is the most common paradigm. It involves having a separate prototype/model of the application’s UI. This design focuses on the how rather than the what. A good example is XML layouts in Android. We design the widgets and components which are then rendered for the user to see and interact with.

Declarative UI

This pattern is an emerging trend that allows developers to design the user interface based on the data received. This on the other hand focuses on the what. This design paradigm makes use of one programming language to create an entire application.

Example of declarative UI framework

  • Flutter
  • React native
  • SwiftUI
  • Jetpack compose.

Getting Started with jetpack Compose

Jetpack compose is a modern toolkit for building a native Android user interface. It is simple and accelerated UI development on android with less code, powerful tools, and intuitive kotlin APIs.Ui components are built based on declarative functions.

Terms Used in Compose

Composable functions

Every element on an android UI created by jetpack compose is called composable. A composable is a normal kotlin function annotated with @Composable annotation. This function does not have a return value. Its contents are added to the screen on rendering.

Layouts

Compose has three layouts to arrange its elements

  • Using a Column

The Column function lets you arrange elements vertically. The main axis of a column is vertical therefore elements are arranged vertically example verticalArrangement = Arrangement.SpaceEvenly, and alignment is done on the horizontal cross axis. example horizontalAlignment = Alignment.CenterHorizontally.

  • Using a Row

A Row function arranges items horizontally. The main axis of the row is horizontal therefore elements are arranged horizontally example horizontalArrangement = Arrangement.SpaceBetween and alignment is done on the vertical cross axis. example verticalAlignment = Alignment.CenterVertically.

  • Box

Stack elements that arrange the elements on top of each other. Modifiers are used to design the box to users' needs.

List

Listing is done by using Compose’s LazyColumn and LazyRow. These composable render only the elements that are visible on screen, so they are designed to be very efficient for long lists

A LazyColumn is a vertically scrolling list that only composes and lays out the currently visible items. It’s similar to a Recyclerview in the classic Android View system.

A LazyRow is a horizontal scrolling list.

Modifiers

This object is used to define properties for our composable. It makes use of the builder pattern to set the properties. This means that changes are applied in the order in which they were set in the modifier object. This should be a great consideration when using the Modifier class.

To learn the jetpack compose concept we'll create a user interface similar to shopping Ui.

Step 1 - Creating a new Compose project

To begin a new project, go to File > New >New project, pick Empty to compose activity from the menu, and then hit Next. Give your project a descriptive name. Kotlin is selected automatically as the language and click Finish.

Step 2 - Understanding Compose Code Structure

The main activity is the entry point to an Android app. In our project, MainActivity is launched when the user opens the app (as it's specified in the AndroidManifest.xml file). You use setContent to define your layout, instead of using an XML file as you'd do in the traditional View system, you call Composable functions within it.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BasicsCodelabTheme {
                Surface(color = MaterialTheme.colors.background) {
                    Greeting("Android")
                }
            }
        }
    }
}

This is a function annotated with @Composable.This allows other composable functions to be called within it. For example, is Greeting function is marked as @Composable. This function will produce a piece of UI hierarchy displaying the given input, String. Text is a composable function provided by the library.

@Composable
private fun Greeting(name: String) {
   Text(text = "Hello $name!")
}

To use the Android Studio preview, you just have to mark any parameterless Composable function or functions with default parameters with the @Preview annotation and build your project.

@Preview(showBackground = true, name = "Text preview")
@Composable
fun DefaultPreview() {
    BasicsCodelabTheme {
        Greeting(name = "Android")
    }
}

Step 3- Creating Arranging composable

Following the shopping UI there are some composable we are going to create an example:-

  • Image
  • Text
  • Card
  • Icon
  • Toolbar
  • Button

To arrange the composable we shall consider using the layouts described earlier that is row column and box.

Lets start by creating a tool bar composable which have the text,navigation icon and action icon.The composable are created within a function annotated with @composable.

@Composable
fun ToolBar() {
    TopAppBar(
        title = {
            Text(
                modifier = Modifier.fillMaxWidth(),
                text = "Shopping Bag",
                fontSize = 20.sp,
                fontWeight = FontWeight.Bold,
                textAlign = TextAlign.Center
            )
        },
        backgroundColor = Color.White,
        //setting navigation icon
        navigationIcon = {
            Icon(imageVector = Icons.Default.ArrowBack, contentDescription = null)
        },
         //setting action icon
        actions = {
            Icon(imageVector = Icons.Default.ShoppingCart, contentDescription = null)
        }
    )
}

Next, let's set up a card item composable with several items. Where the items are arranged in columns and rows. The items are also given specific attributes using modifiers

@Composable
fun CartItem(
    // declaring card item modifier
    modifier: Modifier = Modifier
) {
    //wraping composables in a card
    Card(
        modifier = modifier
            .height(130.dp)
            .padding(5.dp),
        shape = RoundedCornerShape(15.dp),
        elevation = 5.dp
    )
    {
        //using row layout to arrange the elements
        Row(
            modifier = modifier.fillMaxWidth()
        ) {
            Image(
                modifier = Modifier
                    .fillMaxHeight()
                    .fillMaxWidth(0.25f)
                    .clip(shape = RoundedCornerShape(15.dp)),
                contentScale = ContentScale.Crop,
                painter = painterResource(R.drawable.pic),
                contentDescription = null
            )
            Spacer(modifier = Modifier.width(8.dp))
            Column(
                verticalArrangement = Arrangement.SpaceEvenly,
                modifier = Modifier
                    .fillMaxHeight()
                    .fillMaxWidth(0.9f),

                ) {
                Text(
                    text = "Cotton Dress",
                    fontSize = 16.sp,
                    fontWeight = FontWeight.SemiBold
                )
                Text(
                    text = "$250.00",
                    fontSize = 16.sp,
                    fontWeight = FontWeight.Bold
                )

                Row(
                    horizontalArrangement = Arrangement.SpaceEvenly
                ) {
                    Icon(
                        imageVector = Icons.Default.AddCircle,
                        contentDescription = null
                    )
                    Spacer(modifier = Modifier.width(8.dp))
                    Text(
                        text = "1",
                        fontSize = 16.sp,
                    )
                    Spacer(modifier = Modifier.width(8.dp))
                    Icon(
                        imageVector = Icons.Default.AddCircle,
                        contentDescription = null
                    )
                }
            }
            // using column layout to arrange the elements
            Column(
                modifier = Modifier
                    .fillMaxHeight()
                    .fillMaxWidth(),
                verticalArrangement = Arrangement.SpaceEvenly,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(text = "B", fontSize = 16.sp)
                Icon(imageVector = Icons.Default.Delete, contentDescription = null)
            }

        }
    }

Next, let's create a card with text and buttons arranged horizontally using a Row layout and giving it relevant spaces and properties.

@Composable
fun PromoCard(
    modifier: Modifier = Modifier
) {
    Card(
        //setting card properties
        shape = RoundedCornerShape(12.dp),
        backgroundColor = Color.LightGray,
        modifier = modifier
            .fillMaxWidth()
            .height(55.dp)
    ) {
        // arranging elements in row 
        Row(
            modifier = Modifier
                .fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                modifier = Modifier
                    .fillMaxWidth(0.4f)
                    .padding(start = 8.dp),
                text = "Promo Code",
                fontSize = 16.sp,
                textAlign = TextAlign.Justify,
            )
            Button(
                onClick = { /*TODO*/ },
                modifier = Modifier
                    .fillMaxHeight()
                    .fillMaxWidth(0.6f),
                shape = RoundedCornerShape(12.dp),
                colors = ButtonDefaults.buttonColors(Orange)
            ) {
                Text(text = "Apply", color = Color.White)
            }
        }
    }
}

The card items initially created the need to be listed. To do listing we can use LazyColumn or LazyRow for our case the items need to be listed vertically and therefore we shall be using LazyColumn and giving our lazy column with the item and specifying the count.

@Composable
fun CartList() {
    LazyColumn {
        items(3) {
            CartItem(modifier = Modifier.fillMaxWidth())
        }
    }
}

Adding the remaining elements of the UI

@Composable
fun OtherDetails() {
    Column(
        modifier = Modifier.fillMaxWidth().padding(top = 16.dp),
        verticalArrangement = Arrangement.SpaceEvenly,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceEvenly,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                modifier = Modifier
                    .padding(start = 8.dp),
                text = "Sub Total  -------------------------------------------",
                fontSize = 16.sp,
                color = Color.Gray,
                textAlign = TextAlign.Justify
            )
            Spacer(modifier = Modifier.width(8.dp))
            Text(
                modifier = Modifier
                    .padding(end = 16.dp),
                text = "$250.00",
                fontSize = 16.sp,
                fontWeight = FontWeight.Bold
            )
        }
        Spacer(modifier = Modifier.padding(16.dp))
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceEvenly,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                modifier = Modifier
                    .padding(start = 8.dp),
                text = "Shipping  -------------------------------------------",
                fontSize = 16.sp,
                color = Color.Gray,
                textAlign = TextAlign.Justify
            )
            Spacer(modifier = Modifier.width(8.dp))
            Text(
                modifier = Modifier
                    .padding(end = 16.dp),
                text = "$4.00",
                fontSize = 16.sp,
                fontWeight = FontWeight.Bold
            )
        }
         // composable to add space
        Spacer(modifier = Modifier.padding(16.dp))
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceEvenly,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                modifier = Modifier
                    .padding(start = 8.dp),
                text = "Bag Total  -------------------------------------------",
                fontSize = 16.sp,
                color = Color.Gray,
                textAlign = TextAlign.Justify
            )
             // composable to add space
            Spacer(modifier = Modifier.width(8.dp))
            Text(
                modifier = Modifier
                    .padding(end = 16.dp),
                text = "$250.00",
                fontSize = 16.sp,
                fontWeight = FontWeight.Bold,
                color = Orange
            )
        }
        // composable to add space
        Spacer(modifier = Modifier.padding(16.dp))
        Button(
            onClick = { /*TODO*/ },
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
            shape = RoundedCornerShape(12.dp),
            colors = ButtonDefaults.buttonColors(Orange)
        ) {
            Text(text = "Proceed to checkout", color = Color.White, fontSize = 16.sp)
        }
    }
}

Finally, let us preview our UI, the UI can be either previewed in the dark or light mode. All the composable created are grouped within a column to give it a relevant layout on the screen.


@Preview(name = "Light Mode",showBackground = true,
)
@Preview(
    uiMode = Configuration.UI_MODE_NIGHT_YES,
    name = "Dark Mode"
)
@Composable
fun DefaultPreview() {
    ShoppingTheme {
        Column {
            ToolBar()
            CartList()
            PromoCard(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp)
            )
            OtherDetails()

        }
    }
}

Conclusion

In this article, we have studied what is jetpack compose, the terms used in jetpack compose, and how to use jetpack compose in android. you can also learn more about composing side effects

Get the full code implementation on this GitHub Repository

Futher Reading