Crash on ReadMoreText with IntrinsicSize
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 54/100
Línea de trabajo
Start in the CoreReadMoreText implementation and trace how BoxWithConstraints supplies the width used to calculate collapsed text. The proposal identifies replacing it with Box, recording width from BasicText's onTextLayout, and recalculating collapsed text; check the foundation, material, and material3 tests, including intrinsic-height measurement and width changes. Done means intrinsic measurement no longer crashes and the collapsed text updates correctly without public API changes.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Problem
ReadMoreText crashes when its parent asks for intrinsic measurements, for example Row(Modifier.height(IntrinsicSize.Max)). This applies to BasicReadMoreText and to the material and material3 wrappers.
java.lang.IllegalStateException: Asking for intrinsic measurements of SubcomposeLayout layouts is not supported. This includes components that are built on top of SubcomposeLayout, such as lazy lists, BoxWithConstraints, TabRow, etc. To mitigate this:
Since 1.7.0 (#93), CoreReadMoreText wraps BasicText in BoxWithConstraints to read constraints.maxWidth for TextMeasurer. BoxWithConstraints is built on SubcomposeLayout, which does not support intrinsic measurements.
A common case is a horizontal carousel whose cards all match the tallest one:
Row(
modifier = Modifier
.horizontalScroll(rememberScrollState())
.height(IntrinsicSize.Max),
) {
posts.forEach { post ->
Column(Modifier.width(292.dp).fillMaxHeight()) {
ReadMoreText(
text = post.body,
expanded = false,
readMoreText = "more",
readMoreMaxLines = 3,
)
}
}
}
Proposal
BoxWithConstraints is only used for constraints.maxWidth. BasicText already reports the same value through onTextLayout (TextLayoutResult.layoutInput.constraints), and onTextLayout is called only for the real layout, not for intrinsic measurements.
- Replace
BoxWithConstraintswithBox. - Keep
maxWidthin a state and update it fromonTextLayout. - Compute the collapsed text with
remember(maxWidth, ...)instead ofLaunchedEffect.ReadMoreState.applyCollapsedText()becomescalculateCollapsedText(), which returns the collapsed text. Its body stays the same.
The collapsed text is still measured from the original text with TextMeasurer. Only the width source changes. No public API changes.
val textMeasurer = rememberTextMeasurer()
- val state = remember { ReadMoreState() }
+ var maxWidth by remember { mutableIntStateOf(UnknownWidth) }
+ val collapsedText = remember(
+ textMeasurer,
+ maxWidth,
+ overflowText,
+ readMoreTextWithStyle,
+ style,
+ readMoreStyle,
+ text,
+ readMoreMaxLines,
+ softWrap,
+ inlineContent,
+ ) {
+ if (maxWidth == UnknownWidth) {
+ AnnotatedString("")
+ } else {
+ calculateCollapsedText(
+ textMeasurer = textMeasurer,
+ constraints = Constraints(maxWidth = maxWidth),
+ overflowText = overflowText,
+ readMoreTextWithStyle = readMoreTextWithStyle,
+ style = style,
+ readMoreStyle = readMoreStyle,
+ text = text,
+ readMoreMaxLines = readMoreMaxLines,
+ softWrap = softWrap,
+ inlineContent = inlineContent,
+ )
+ }
+ }
+ val isCollapsible = collapsedText.isNotEmpty()
...
- BoxWithConstraints(
+ Box(
modifier = modifier
.then(toggleableModifier)
.padding(contentPadding),
) {
BasicText(
text = currentText,
modifier = Modifier,
style = style,
- onTextLayout = onTextLayout,
+ onTextLayout = { result ->
+ maxWidth = result.layoutInput.constraints.maxWidth
+ onTextLayout(result)
+ },
overflow = TextOverflow.Ellipsis,
softWrap = softWrap,
maxLines = if (expanded) Int.MAX_VALUE else readMoreMaxLines,
inlineContent = inlineContent,
)
-
- val constraints = Constraints(maxWidth = constraints.maxWidth)
- LaunchedEffect(
- ...
- ) {
- state.applyCollapsedText(...)
- }
}
Checked with a local prototype
verifyRoborazziDebugpasses forreadmore-foundation,readmore-materialandreadmore-material3(168 cases) with no image changes.- In
Row(Modifier.height(IntrinsicSize.Max))it no longer crashes, and the last line ends with… more. - When the width changes from 300dp to 200dp and then to 320dp, the collapsed text is recalculated each time.
- Frame timing is the same as now. The first frame shows the original text with an ellipsis, and the next frame shows the collapsed text with "more".
Alternatives considered
- Capturing the constraints in
Modifier.layout {}: its measure block also runs for intrinsic queries, so the state would be written during intrinsic passes.onTextLayoutruns only for the real layout. - A custom
Layoutthat measures withTextMeasurerand draws withdrawText(): the collapsed text would be ready in the first frame, but it would lose whatBasicTextprovides, such asLinkAnnotationforToggleArea.More, semantics andinlineContent.
- Lenguaje dominante
- Kotlin
- Estrellas
- 209
- Forks
- 14
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de webtoon/ReadMoreTextView
-
Support Kotlin MultiplatformAbierto
Dificultad 5/5 Más de una semana Aptitud para principiantes 20/100
webtoon/ReadMoreTextView#57 ·
Todos los issues de webtoon/ReadMoreTextView
Issues similares
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
"Check mod" buttonAbiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
yairm210/Unciv#15803 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
enhancement triaged
Dificultad 1/5 Menos de una hora Aptitud para principiantes 63/100
Los mantenedores suelen responder en 1 día
-
bug follow-up
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
bug 🐞 CI/CD ⚒️
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
valkey-io/valkey-glide#7370 ·
Los mantenedores suelen responder en 1 día