Back to blog
Compose

Creating Self-Adjusting TextFields with Jetpack Compose

2024-08-12 · 7 min read

Creating Self-Adjusting TextFields with Jetpack Compose

Hey there, fellow developers! Ever wondered how to keep text inputs readable no matter how much copy a user types? This guide walks through an auto-resizing TextField pattern powered by Jetpack Compose.

📚 Want to dig deeper into Compose animations too? Check out the official codelab .

Summary

  • Compose makes it straightforward to respond to layout constraints.
  • ParagraphIntrinsics lets us measure the rendered width of text for a given style.
  • Auto-resizing TextFields feel polished in chat views, forms, and search inputs.

The Challenge: Taming Tricky Text Inputs

Chat bubbles and long-form inputs often end up clipped or hidden behind scroll bars. We need an adaptive TextField that shrinks the font just enough to fit the available width.

The Solution: Jetpack Compose to the Rescue!

Our toolkit:

  • BoxWithConstraints exposes parent width constraints.
  • ParagraphIntrinsics measures exactly how wide our text wants to be.
  • LocalDensity keeps conversions from dp to px consistent.
  • remember prevents expensive recalculations on every recomposition.

Getting Your Hands Dirty

@Composable
fun ResizableInputField(
    text: String,
    onValueChange: (String) -> Unit,
) {
    BoxWithConstraints {
        val shrunkFontSize = calculateShrunkFontSize(text)
        BasicTextField(
            value = text,
            onValueChange = onValueChange,
            textStyle = TextStyle(fontSize = shrunkFontSize),
        )
    }
}

@Composable
private fun BoxWithConstraintsScope.calculateShrunkFontSize(text: String): TextUnit {
    var shrunkFontSize = Theme.typography.displayMedium.fontSize
    val calculateIntrinsics: @Composable () -> ParagraphIntrinsics = {
        ParagraphIntrinsics(
            text = text,
            style = Theme.typography.displayMedium.copy(fontSize = shrunkFontSize),
            density = LocalDensity.current,
            fontFamilyResolver = createFontFamilyResolver(LocalContext.current),
        )
    }

    var intrinsics = calculateIntrinsics()
    with(LocalDensity.current) {
        while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) {
            shrunkFontSize *= 0.9f
            intrinsics = calculateIntrinsics()
        }
    }

    return shrunkFontSize
}

Swap in your favorite TextField instead of BasicTextField—the resizing logic works the same way.

Why It’s Worth the Hype

Auto-resizing TextFields are your UI’s personal stylist. They keep copy legible on compact screens and prevent awkward scroll bars or truncated text in long answers.

The Grand Finale

Compose gives us the building blocks to solve layout puzzles with a few well-chosen primitives. Auto-resizing inputs are a great example of shipping polish without bolting on new dependencies. Time to make those TextFields dance! ✨

Stay Connected

Follow me on GitHub (@rezaiyan) and Twitter (@arezaiyan) for more Compose experiments, tips, and deep dives. Let’s build delightful apps together! 🚀