Does anyone encountered `java.lang.IllegalStateExc...
# compose
n
Does anyone encountered
java.lang.IllegalStateException: You must call layoutWithConstraints first
? It's weird, I can't even reproduce this error, this looks like an internal error? 🤔
j
I think you need to set fixed size
h
Can you please share the code and the compose-foundation version you are using?
It seems that somehow the internal layout was never computed until the first draw phase started.
n
yes, i'm using compose "1.6.0-alpha03"
compose compiler 1.5.1
emmm yes, but the weird thing is that this error is hard for me to reproduce, and it doesn't tell me exactly where the error occurs
h
I understand. A repro case would go a long way in an error like this. In the meantime, can you please file a bug on the official tracker?
n
yes sure !
@Halil Ozercan I just created a simple issue on issuetracker.
🙏 1
w
Hello @Halil Ozercan. I encountered the same exception today in my code. I am able to reproduce it by calling the following
ExpandableText
in a
LazyColumn
. Populate a list with 10 items or so then spam swiping up and down fast without scrolling the the entire list if you want. Hope this helps and fixed.
Copy code
@Composable
fun ExpandableText(
  text: AnnotatedString,
  modifier: Modifier = Modifier,
  minimizedMaxLines: Int = 1,
  style: TextStyle = LocalTextStyle.current,
  isExpanded: Boolean = false
) {
  var cutText by remember(text) { mutableStateOf<String?>(null) }
  var expanded by remember { mutableStateOf(isExpanded) }
  val textLayoutResultState =
    remember { mutableStateOf<TextLayoutResult?>(null) }
  val seeMoreSizeState = remember { mutableStateOf<IntSize?>(null) }
  val seeMoreOffsetState = remember { mutableStateOf<Offset?>(null) }

  // getting raw values for smart cast
  val textLayoutResult = textLayoutResultState.value
  val seeMoreSize = seeMoreSizeState.value
  val seeMoreOffset = seeMoreOffsetState.value

  LaunchedEffect(text, expanded, textLayoutResult, seeMoreSize) {
    val lastLineIndex = minimizedMaxLines - 1
    if (!expanded && textLayoutResult != null && seeMoreSize != null &&
      lastLineIndex + 1 == textLayoutResult.lineCount &&
      textLayoutResult.isLineEllipsized(lastLineIndex)
    ) {
      var lastCharIndex =
        textLayoutResult.getLineEnd(lastLineIndex, visibleEnd = true) + 1
      var charRect: Rect
      do {
        lastCharIndex -= 1
        charRect = textLayoutResult.getCursorRect(lastCharIndex)
      } while (
        charRect.left > textLayoutResult.size.width - seeMoreSize.width
      )
      seeMoreOffsetState.value =
        Offset(charRect.left, charRect.bottom - seeMoreSize.height + 8)
      cutText = text.substring(startIndex = 0, endIndex = lastCharIndex)
    }
  }

  Box(modifier) {
    val charSequence = when (cutText) {
      null -> text
      else -> {
        AnnotatedString(
          text = cutText!!,
          spanStyles = text.spanStyles
            .filter { it.start <= cutText!!.length }
            .map {
              when {
                it.end > cutText!!.length -> it.copy(end = cutText!!.length)
                else -> it
              }
            }
        )
      }
    }
    val urlAnnotations = charSequence.getStringAnnotations(
      tag = "URL",
      start = 0,
      end = charSequence.length
    )
    val maxLines = remember(expanded) {
      if (expanded) Int.MAX_VALUE else minimizedMaxLines
    }
    val onTextLayout: (TextLayoutResult) -> Unit = {
      textLayoutResultState.value = it
    }

    if (urlAnnotations.isEmpty()) {
      Text(
        text = charSequence,
        maxLines = maxLines,
        overflow = TextOverflow.Ellipsis,
        onTextLayout = onTextLayout,
        style = style
      )
    } else {
      val uriHandler = LocalUriHandler.current
      ClickableText(
        text = charSequence,
        onClick = {
          charSequence
            .getStringAnnotations(tag = "URL", start = it, end = it)
            .firstOrNull()?.let { stringAnnotation ->
              uriHandler.openUri(stringAnnotation.item)
            }
        },
        maxLines = maxLines,
        overflow = TextOverflow.Ellipsis,
        onTextLayout = onTextLayout,
        style = style
      )
    }

    if (!expanded) {
      val labelLarge = MaterialTheme.typography.bodyMedium
      val string = buildAnnotatedString {
        withStyle(labelLarge.toSpanStyle()) {
          append("... ")
        }
        withStyle(
          labelLarge.copy(
            color = MaterialTheme.colorScheme.onSurface.copy(alpha = .6f)
          ).toSpanStyle()
        ) {
          append("Read more")
        }
      }
      val density = LocalDensity.current
      Text(
        string,
        onTextLayout = { seeMoreSizeState.value = it.size },
        modifier = Modifier
          .then(
            if (seeMoreOffset != null) {
              Modifier.offset(
                x = with(density) { seeMoreOffset.x.toDp() },
                y = with(density) { seeMoreOffset.y.toDp() }
              )
            } else {
              Modifier
            }
          )
          .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
          ) {
            expanded = true
            cutText = null
          }
          .alpha(if (seeMoreOffset != null) 1f else 0f)
      )
    }
  }
}
Also, if you use Text(...) in a LazyColumn and do the same described above, you get an exception:
no paragraph
androidxComposeCompiler = "1.5.0"
,
compose-bom = "2023.08.00"
UPDATE: So I found the source of the problem (after a day of trouble), I had
material3 1.2.0-alpha05
applied:
compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
After falling back to
material3 1.1.1
, both issues disappeared @Halil Ozercan Hope this helped.
h
This specific crash was an isolated regression in foundation 1.6.0-alpha03 release and it has been fixed. Material3 1.2.0-alpha05 depends on this specific artifact so that's why it gets resolved if you fallback to any other material3 version.
👍 1
w
Thanks for the clarification!