Blog Infos
Author
Published
Topics
Published

Harmonizing Modifiers in Jetpack Compose

 

Introduction

In the world of Jetpack Compose, where designing reusable and customizable UI components is essential, there’s a tale of two approaches to modifiers for me. One was born out of necessity when our design system was still finding its footing, and the other, a more flexible approach, emerged as the design system matured. In this article, we’ll explore the journey from discouraging modifier modifications to embracing the idea of applying modifiers to the provided Modifier parameter. 🚀

The Backstory: “Avoid Applying Modifiers to the Provided Modifier”

In a previous article titled “Avoid Applying Modifiers to the Provided Modifier,” I highlighted the challenges we faced in the early days of our design system. The primary issue was the overuse of modifiers in our composable functions. For instance, a simple “primary button” composable would receive a Modifier as a parameter. However, every developer had a unique vision for the button’s appearance. Some wanted it taller, others preferred it in blue, and this endless cycle of modifications made it incredibly challenging to manage and customize our UI components effectively. 🧩

The Revelation of Flexibility

Fast forward to the present day, and our design system is much more robust. However, I continued to champion my belief that some modifier modifications can be helpful, especially when it comes to enhancing usability. One of my colleagues, Amin, shared a different perspective. He argued that not all modifier modifications are troublesome. In fact, some, like those related to click behavior, can enhance usability and reduce complexity. 🧐

The Clarification

However, adhering to the principle of “Avoid Applying Modifiers to the Provided Modifier” doesn’t mean that everything should be defined within the caller composable. The input modifier is meant to influence the layout of the component, and component-specific details should be determined within the component itself.

For example, to display a button on the screen, it might be presented with some padding or in a specific location, such as at the bottom of a box. These layout-related tasks are the responsibilities of the parent composable.

Conversely, the radius of the button, its color, or the text it displays are part of the button component’s specific functionalities. This doesn’t mean delegating everything to the component. 📦

@Composable
fun SimpleButton(
    text: String,
    type: ButtonType,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Box(modifier = modifier) {
        BaseButton(
            text = text,
            modifier = Modifier
                .clip(RoundedCornerShape(8.dp))
                .background(type.backgroundColor)
                .clickable { onClick() },
        )
    }
}

enum class ButtonType {
    Primary,
    Danger,
    Neutral,
    Success
}

As you see, I’ve defined some parameters to modify the button design and behavior, but the SimpleButton component is responsible for applying the modifier based on its specific requirements. This approach enhances the component’s clarity and reusability for developers. It eliminates the need to repeatedly consider both parent and component modifiers every time the button is used, saving valuable development time. Additionally, it fosters a common UI language between designers and developers, promoting efficient collaboration and streamlined workflows.

Applying Modifiers to Modifiers

This shift in perspective didn’t introduce a revolutionary concept but clarified existing principles. For example, consider the “SimpleButton” composable, which now allows click functionality to be directly applied to the provided modifier. This approach enhances code clarity, eliminates ambiguity for developers, and ultimately enhances the usability of our UI components. 🛠️

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Migrating to Jetpack Compose – an interop love story

Most of you are familiar with Jetpack Compose and its benefits. If you’re able to start anew and create a Compose-only app, you’re on the right track. But this talk might not be for you…
Watch Video

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer for Jetpack Compose
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engin ...
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer f ...
Google

Jobs

@Composable
fun SimpleButton(
    text: String,
    type: ButtonType,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    BaseButton(
        text = text,
        modifier = modifier
            .clip(RoundedCornerShape(8.dp))
            .background(type.backgroundColor)
            .clickable { onClick() }
    )
}

I could accept just a single modifier and everyone calling SimpleButton needed to apply clickable modifier on the passing modifier.

Why the “onClick” Parameter Shines

💡 Clear and Specific: The “onClick” parameter unambiguously defines the composable’s purpose, sparing developers from delving deeply into the modifier.

💡 Reduced Complexity: Separating the click functionality from the modifier streamlines development, as developers no longer need to recall which modifier component handles click behavior.

💡 Enhanced Usability: By abstracting click behavior into a parameter, developers can confidently reuse the composable, knowing it will work consistently across various use cases.

The Benefits of This Approach

Harmonizing Modifiers allows for greater flexibility, code readability, and an improved developer experience. With clear previews and a focus on simplicity, developers can create UI components that are both powerful and user-friendly. This approach not only enhances the developer experience but also ensures that our UI components remain adaptable to various use cases. 🌟

Exceptions to the Rule

While the new approach promotes flexibility, there are still situations where certain modifiers should be handled by the parent function. For example, a component may need margin relative to the screen, a task best handled by the parent function. By recognizing these exceptions, we maintain a balance between customization and simplicity. ⚖️

A Note to Designers

This shift in the handling of modifiers isn’t limited to developers alone. Designers working on platforms like Figma also play a pivotal role. They can contribute significantly to upholding principles and assisting developers in their journey. For instance, aspects designated as the responsibility of the parent modifier can also be adhered to by designers. For instance, ensuring that margins for a component in relation to its surroundings don’t belong to the component itself. Instead, each component is best when it is as self-contained as possible in its simplest form.

This collaborative approach between designers and developers ensures that the principles of harmonizing modifiers are carried through from the design phase to implementation, resulting in a smoother and more efficient workflow.

Conclusion: A Balance Struck

In the evolving world of Jetpack Compose, the approach to modifiers has shifted from discouraging modifications to embracing them within reason. The key lies in creating composable functions that strike a balance between customization and ease of use. With clear previews and a focus on simplicity, developers can create UI components that are both powerful and user-friendly. 🌈

 

This article was previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog

How to animate BottomSheet content using Jetpack Compose

Early this year I started a new pet project for listening to random radio…
READ MORE
blog
Yes! You heard it right. We’ll try to understand the complete OTP (one time…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu