The Ultimate Guide to Writing Clean Jetpack Compose

A Step-by-Step Approach to Crafting Elegant and Maintainable Compose

Meetmistry
Simform Engineering
4 min readNov 21, 2024

--

This guide is designed to help developers embrace the best practices of Jetpack Compose, ensuring composable functions are properly named, structured, and managed.

It covers key principles such as the appropriate use of modifiers, the importance of stateless and controlled components, and how to handle state efficiently by hoisting it.

By following these guidelines, developers can build modular, maintainable, and performant UIs that fully leverage the power of Compose’s declarative nature.

Let’s explore these dos and don’ts of guidelines and practices to create optimized, well-structured Jetpack Compose applications that stand the test of time.

Naming @Composable functions

Composable functions that usually return Unit should start with an uppercase letter (PascalCase).

Ordering @Composable parameters

The order of parameters in a component could be done in 2 different ways:

  1. The Official Android recommended way
  • Required parameters.
  • `modifier: Modifier = Modifier.`
  • Optional parameters.
  • (optional) trailing @Composable lambda

2. A consistent yet modestly effective approach

  • `modifier: Modifier = Modifier.`
  • Input data.
  • UI-related parameters. (background color, style, color, shape etc.)
  • Call back lambda functions.
  • @Composable content lambda.

Why second way is preferable?

  • It sets the expectation that parameters are organized in a grouped manner, making it easy to locate and modify components.
  • We don’t need to consider whether the parameters are required or optional. We just need to add them to the respective group. For example, if you want to add BorderStroke in the above example, simply find the section where UI-related parameters are listed and add it there.
  • All the composable parameter ordering will follow a similar structure.

Emit content OR Return a value

  • @Composable functions should either emit content into the composition or return a value, but not both.
  • If a composable should offer additional control surfaces to its caller, those control surfaces or callbacks should be provided as parameters to the composable function by the caller.

Do not emit multiple pieces of content

  • A composable function should emit either 0 or 1 piece of layout, but not more than one.
  • A composable function should be cohesive, and not rely on what function it is called from.
  • This practice ensures that a composable behaves as defined for all the callers.

Composable must accept and respect a Modifier

  • Composable must accept a parameter of type Modifier. This parameter must be named “modifier” and should be the composable's first parameter. Composable must not accept multiple Modifier parameters.
  • Composable must provide their Modifier parameter to the Compose UI node they emit by passing it to the root composable they call. If the composable directly emits a Compose UI layout node, the modifier must be provided to the node.

Prefer stateless and controlled Composables

  • In this context, stateless refers to composable that retain no state of their own, but instead accept external state parameters that are owned and provided by the caller.
  • Controlled refers to the idea that the caller has full control over the state provided to the composable.

State should be Hoisted

  • To implement unidirectional flow, compose advocates for the pattern of hoisting state upwards, enabling the majority of your composable function to be stateless.
  • In practice, there are a few common things to look out for:
    – Do not pass View Models (or objects from DI) down.
    – Do not pass State<Foo> or MutableState<Bar> instances down.
  • Instead, pass down the relevant data to the function, and optional lambdas for callbacks.
  • Do not worry about a long parameter list.
  • Not all composables will be stateless; one composable will be stateful and will pass the state down to the other composables.

Use Padding provided by Scaffold

  • Always use the default padding provided by the Scaffold.

Avoid adding some modifications directly to the root component of a composable

  • Avoid applying the following modifications to the root component of composables, as the caller should manage these modifiers to ensure reusability and flexibility. This practice prevents limiting the composable’s usage to specific use cases by allowing external control over its appearance and behaviour:
    – padding()
    – fillMaxWidth()
    – fillMaxHeight()
    – fillMaxSize()
    – width()
    – height()
  • Any other modifiers that control the component’s layout from within
  • This approach allows the caller to provide their modifiers, ensuring that the composable can be adapted to various scenarios.

Jetpack Compose empowers Android UI development, but mastering its best practices is critical. By adhering to the do’s and don’ts outlined here, developers can create clean, maintainable code that avoids common pitfalls. Embracing these principles will also help prevent inefficiencies, ensuring scalable applications that leverage Compose’s full potential for building robust, optimized user interfaces with ease.

For more updates on the latest tools and technologies, follow the Simform Engineering blog.

Follow Us: Twitter | LinkedIn

--

--

Simform Engineering
Simform Engineering

Published in Simform Engineering

Our Engineering blog gives an inside look at our technologies from the perspective of our engineers.

Meetmistry
Meetmistry

Written by Meetmistry

Software Engineer @ Simform solutions #Mobile App developer #kotlin #Swift