Jetpack Compose: TabRow custon indicator

Introduction

Jetpack Compose, Google's modern toolkit for building native Android UIs, allows developers to create complex interfaces with minimal code. One of its strengths is the ease of customizing elements, such as tabs and indicators. In this article, we'll explore an Android Kotlin example that demonstrates how to create a TabRow with a custom tab indicator using Jetpack Compose. This example highlights how to customize the appearance and behavior of tabs, offering a sleek and visually appealing design for users.

This tutorial will break down the provided example code to explain how various Compose elements work together. By the end, you'll understand how to implement and customize a tab layout using Jetpack Compose's declarative UI approach.

MainActivity Structure

The example begins with the MainActivity, which extends ComponentActivity. This activity serves as the entry point for the Android application. In the onCreate method, the setContent function is invoked, setting the Composable content of the activity. The GetScaffold function, a Composable, is passed as the content. This is where the overall layout of the app is defined using Jetpack Compose's Scaffold component, which is ideal for laying out UI components like top bars, tab rows, and content areas.

The Scaffold in the GetScaffold function includes a TopAppBar with a title and a custom background color, setting the stage for the app's interface. The main content of the app, provided by the MainContent function, is placed inside the scaffold, with a different background color to distinguish between the content and the app bar. This separation ensures a clear and organized UI structure.

TabRow with Custom Indicator

The core feature of the example is the TabRow, which is used to create a horizontal row of tabs. The MainContent function contains the logic for handling the tabs and their custom indicator. First, a mutable state variable, selectedIndex, is initialized using remember { mutableStateOf(0) }, which tracks the currently selected tab. The list of tab labels is stored in the tabs list.

The TabRow component is responsible for displaying the tabs. It accepts several parameters, including the selectedTabIndex (which highlights the selected tab), backgroundColor, and contentColor. The most notable customization is the indicator parameter, which is where the custom tab indicator is defined. Instead of using the default indicator, a Spacer is used to create a 2.5 dp tall bar beneath the selected tab. This bar is positioned using the tabIndicatorOffset modifier, ensuring it moves with the selected tab. The custom color of the indicator is set to Color(0xFFE30022), giving it a striking red appearance.

Handling Tab Selections

The tabs are dynamically created using a loop over the tabs list, with the forEachIndexed function. For each tab, a Tab Composable is used, which handles the appearance and behavior of individual tabs. The selected parameter determines if the current tab is selected, and the onClick event updates the selectedIndex state when a tab is clicked.

Each tab displays its label using the Text Composable, which is padded for better visual alignment. When a tab is clicked, the selectedIndex is updated, which in turn triggers a recomposition of the TabRow and its indicator, reflecting the new selected tab.

Displaying Tab Content

Below the TabRow, a Box is used to display the content of the selected tab. The Box is centered within its parent container using wrapContentSize(Alignment.Center). Inside the Box, a Text Composable is used to show the label of the selected tab, with a larger font size of 30 sp for emphasis. This content dynamically updates based on the selected tab, offering a simple but effective way to display different content for each tab.

Summary

This example demonstrates how to create a visually customized TabRow in Jetpack Compose, with a focus on implementing a custom tab indicator. The code showcases the declarative nature of Compose, where UI elements are defined in Kotlin code, and updates are automatically handled through state changes.

By using Jetpack Compose's rich set of UI components and modifiers, it's easy to build flexible and customizable user interfaces. The custom tab indicator and dynamic tab content highlight how Compose enables developers to create polished and interactive UIs with minimal effort. Whether you're building a simple tabbed layout or more complex navigation, this approach provides a solid foundation for future customization.


MainActivity.kt

package com.cfsuman.jetpackcompose

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.TabRowDefaults.tabIndicatorOffset
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp


class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {GetScaffold()}
    }


    @Composable
    fun GetScaffold() {
        Scaffold(
            topBar = {TopAppBar(
                title = {Text(text = "Compose - TabRow Custom Indicator")},
                backgroundColor = Color(0xFFA9B2C3),
            )},
            content = { MainContent() },
            backgroundColor = Color(0xFFEDEAE0)
        )
    }


    @Composable
    fun MainContent() {
        var selectedIndex by remember { mutableStateOf(0)}
        val tabs = listOf("Tab A","Tab B","Tab C","Tab D")

        Column(Modifier.fillMaxSize()) {
            TabRow(
                selectedTabIndex = selectedIndex,
                backgroundColor = Color(0xFF7BB661),
                contentColor = Color(0xFFFEFEFA),
                indicator = {
                    Spacer(
                        Modifier
                            .tabIndicatorOffset(it[selectedIndex])
                            .height(2.5.dp)
                            .background(Color(0xFFE30022))
                    )
                }
            ) {
                tabs.forEachIndexed { index, tab ->
                    Tab(
                        selected = selectedIndex == index,
                        onClick = { selectedIndex = index},
                    ) {
                        Text(
                            text = tab,
                            modifier = Modifier.padding(12.dp)
                        )
                    }
                }
            }

            Box(Modifier.fillMaxSize().wrapContentSize(Alignment.Center)) {
                Text(
                    text = tabs[selectedIndex],
                    style = TextStyle(fontSize = 30.sp)
                )
            }
        }
    }
}
More android jetpack compose tutorials