What is Declarative UI in Jetpack Compose?

Meet Patadia
4 min readDec 13, 2024

--

Jetpack Compose, Google’s modern UI toolkit for Android, has revolutionized the way developers build user interfaces by embracing a declarative UI paradigm. Unlike the traditional XML-based approach, Compose allows developers to describe what the UI should look like in response to a given state, leaving the framework to manage the underlying updates. This shift simplifies UI development and enables the creation of responsive and dynamic UIs with less boilerplate.

This blog aims to provide a comprehensive guide to understanding Jetpack Compose’s declarative UI paradigm, with practical examples and insights for both new and experienced Android developers.

What is Declarative UI?

In traditional (imperative) Android development, UIs are defined using XML layouts and updated through Java or Kotlin code. This approach requires explicit instructions for updating the UI when the state changes, making it cumbersome and error-prone in complex applications.

A declarative UI paradigm, however, focuses on describing what the UI should look like at any given moment, and the framework handles the rest. You define the desired outcome, and the framework takes care of efficiently rendering and updating the UI.

Key characteristics of a declarative UI:

  1. State-driven UI updates: UI automatically updates when the underlying state changes.
  2. Stateless components: Each component is defined independently of the larger system and is rebuilt when necessary.
  3. Focus on simplicity: Developers describe the “what” instead of managing the “how.”

Imperative vs Declarative

Let’s look at an example to understand the difference between imperative and declarative programming in UI design.

Imperative Approach (XML + Java/Kotlin)

Here’s an example of a simple UI with a button that updates a TextView when clicked.

activity_main.xml

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Welcome!" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me" />
</LinearLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val textView: TextView = findViewById(R.id.textView)
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
textView.text = "Button clicked!"
}
}
}

In this approach:

  • The UI and logic are separate.
  • The developer manually updates the UI using findViewById() or binding and event listeners.

Declarative Approach (Jetpack Compose)

Here’s how the same functionality looks in Jetpack Compose:

@Composable
fun GreetingScreen() {
var message by remember { mutableStateOf("Welcome!") }

Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = message, style = MaterialTheme.typography.h5)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { message = "Button clicked!" }) {
Text("Click Me")
}
}
}

In Compose:

  • The UI is entirely defined in Kotlin code.
  • State (message) drives the UI, and Compose automatically re-renders the relevant parts when the state changes.

Core Concepts of Jetpack Compose

1. Composable Functions

At the heart of Jetpack Compose are @Composable functions. Each function represents a piece of UI.

@Composable
fun Greeting(name: String) {
Text("Hello, $name!", style = MaterialTheme.typography.h4)
}

2. State Management

Compose uses state to render UIs reactively. The remember and mutableStateOf APIs enable managing local state within composable.

@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }

Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("Count: $count", style = MaterialTheme.typography.h6)
Button(onClick = { count++ }) {
Text("Increment")
}
}
}

3. Modifiers

Modifiers allow you to style and position composable. They provide a declarative way to configure layout, appearance, and interaction.

Text(
text = "Jetpack Compose",
modifier = Modifier
.padding(16.dp)
.background(Color.LightGray)
.border(2.dp, Color.Black)
)

4. Material Design

Jetpack Compose has built-in support for Material Design, making it easy to create beautiful apps. The MaterialTheme provides consistent styling across the app.

MaterialTheme(
colors = lightColors(primary = Color.Blue),
typography = Typography(),
shapes = Shapes()
) {
GreetingScreen()
}

Complete Example

Let’s build a simple app where users can input their name, and the app displays a greeting.

MainActivity.kt

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

App Content

@Composable
fun GreetingApp() {
var name by remember { mutableStateOf("") }

Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Enter your name:", style = MaterialTheme.typography.h6)
Spacer(modifier = Modifier.height(8.dp))
TextField(
value = name,
onValueChange = { name = it },
label = { Text("Name") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
if (name.isNotEmpty()) {
Text("Hello, $name!", style = MaterialTheme.typography.h5)
}
}
}

Advantages of Jetpack Compose

  1. Faster Development: Simplifies UI creation with fewer lines of code.
  2. Reusable Components: Build modular UIs with reusable composable functions.
  3. Dynamic UIs: State-driven updates ensure your UI remains consistent with the underlying data.
  4. Integrated with Kotlin: Leverages Kotlin’s language features like lambdas, type safety, and extension functions.
  5. No XML Required: Entire UI is built programmatically, reducing the need for separate layout files.

Conclusion

Jetpack Compose represents the future of Android UI development with its declarative paradigm. It simplifies the process of building modern, reactive UIs while making the codebase cleaner and more maintainable. If you’re an Android developer, now is the perfect time to embrace Compose and take your app development skills to the next level.

Whether you’re new to Compose or transitioning from XML, the declarative paradigm will make UI development more intuitive, productive, and enjoyable.

I hope this article has provided valuable insights and assistance for your Android journey. Your support means a lot to me, so if you found this content helpful, please start following me and don’t hesitate to show some love with a hearty round of applause!👏

Your feedback fuels my passion for creating quality content. For any Android queries or just to connect, reach out on LinkedIn and Twitter.

Thanks for reading — looking forward to staying in touch!

Happy Coding!!

--

--

Meet Patadia
Meet Patadia

Written by Meet Patadia

Software Developer - Android, Java, Kotlin, MVVM, Jetpack Compose

No responses yet