Skip to content

Instantly share code, notes, and snippets.

@bmc08gt
Last active February 16, 2024 10:58
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bmc08gt/e890e63124846e02f6afb38ee27e9f9c to your computer and use it in GitHub Desktop.
Save bmc08gt/e890e63124846e02f6afb38ee27e9f9c to your computer and use it in GitHub Desktop.
Autosizing Text in Jetpack Compose
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.TextUnit
sealed class AutoSizeConstraint(open val min: TextUnit = TextUnit.Unspecified) {
data class Width(override val min: TextUnit = TextUnit.Unspecified): AutoSizeConstraint(min)
data class Height(override val min: TextUnit = TextUnit.Unspecified): AutoSizeConstraint(min)
}
@Composable
fun AutoSizeText(
text: AnnotatedString,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
style: TextStyle = LocalTextStyle.current,
constraint: AutoSizeConstraint = AutoSizeConstraint.Width(),
) {
var textStyle by remember { mutableStateOf(style) }
var readyToDraw by remember { mutableStateOf(false) }
Text(
modifier = modifier.drawWithContent {
if (readyToDraw) drawContent()
},
text = text,
color = color,
fontSize = fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
style = style,
onTextLayout = { result ->
fun constrain() {
val reducedSize = textStyle.fontSize * 0.9f
if (constraint.min != TextUnit.Unspecified && reducedSize <= constraint.min) {
textStyle = textStyle.copy(fontSize = constraint.min)
readyToDraw = true
} else {
textStyle = textStyle.copy(fontSize = textStyle.fontSize * 0.9f)
}
}
when (constraint) {
is AutoSizeConstraint.Height -> {
if (result.didOverflowHeight) {
constrain()
} else {
readyToDraw = true
}
}
is AutoSizeConstraint.Width -> {
if (result.didOverflowWidth) {
constrain()
} else {
readyToDraw = true
}
}
}
}
)
}
@Composable
fun AutoSizeText(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
style: TextStyle = LocalTextStyle.current,
constraint: AutoSizeConstraint = AutoSizeConstraint.Width(),
) {
AutoSizeText(
modifier = modifier,
text = AnnotatedString(text),
color = color,
fontSize = fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
style = style,
constraint = constraint
)
}
@HolenZhou
Copy link

Thank you very much!
There is a small error in the code that needs to be corrected.
Text(..., style = textStyle,...)

@zhangchf
Copy link

Great, Thanks! 👍

@VitalyPeryatin
Copy link

Great, thank you!

@inidamleader
Copy link

inidamleader commented Dec 30, 2023

This is an improved version:

  1. Best performance: Utilizes a dichotomous binary search algorithm to quickly find the optimal text size without unnecessary iterations.
  2. Alignment support: Supports 6 possible alignment values through the Alignment interface.
  3. Material Design 3 support.
  4. Font scaling support: Changing the font scale by the user does not affect the visual rendering result.
  5. Multiline Support with maxLines Parameter.

Tested on an app with 900k+ downloads.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment