hi guys, are you aware of any sort of issues regar...
# compose
n
hi guys, are you aware of any sort of issues regarding compose and xml interoperability? To be precise: I have a LazyColumn (and some other stuff) inside a ComposeView, which is inside the XML of a Fragment, which is called inside the FragmentContainerView of the Main Activity. The lazyColumn is terribly laggy. I mean, it's pretty unusable, even in release. I already specified a key and the contentType but that doesn't seem to have any effect.
r
I think it’s pretty hard to tell from just what you’ve said… nothing about that setup sounds wrong. I think you may have to share source code in order for somebody to know what might be wrong. Or more details about what might be happening in your composition.
n
this is the screen with the lazy column
Copy code
fun CartScreen(
    productWrappers: List<ProdottoWrapper>,
    totalCost: Float,
    onUIEvent: (UIEvent) -> Unit,
    loading: Boolean,
    alertDialog: AlertDialog?
) {

    val focusManager = LocalFocusManager.current
    val listSize = productWrappers.size.toString()

    Box(modifier = Modifier.fillMaxSize()) {

        Scaffold(
            bottomBar = {
                SendOrderBar(
                    totalCost = totalCost,
                    onSendClick = { onUIEvent(Submit) }
                )
            }
        ) { paddingValues ->
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .clickable(
                        onClick = { if (!loading) focusManager.clearFocus() },
                        indication = null,
                        interactionSource = remember {
                            MutableInteractionSource()
                        }
                    )
            ) {
                Column(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(paddingValues)
                        .padding(horizontal = 16.dp)
                ) {
                    Text(
                        text = stringResource(R.string.carrello),
                        style = MaterialTheme.typography.h5,
                        fontWeight = FontWeight.Bold
                    )
                    Spacer(modifier = Modifier.height(16.dp))
                    if (productWrappers.isNotEmpty()) {
                        Text(
                            text = listSize + stringResource(id = R.string.articoli_nel_carrello),
                            style = MaterialTheme.typography.subtitle1,
                            fontWeight = FontWeight.Medium
                        )
                        Spacer(modifier = Modifier.height(16.dp))
                        ProductsListHeader()
                        Spacer(modifier = Modifier.height(8.dp))
                        Divider()
                        LazyColumn(modifier = Modifier.fillMaxSize()) {
                            items(
                                items = productWrappers,
                                key = { productWrapper -> productWrapper.prodotto.id },
                                contentType = { productWrapper -> productWrapper }
                            ) { productWrapper ->
                                RevealSwipe(
                                    directions = setOf(RevealDirection.EndToStart),
                                    hiddenContentEnd = {
                                        Icon(
                                            imageVector = Icons.Default.Delete,
                                            contentDescription = null,
                                            tint = Color.White,
                                            modifier = Modifier.padding(horizontal = 25.dp)
                                        )
                                    },
                                    onBackgroundEndClick = { onUIEvent(Delete(productWrapper)) },
                                    backgroundCardEndColor = Color.Red,
                                    contentColor = Color.Black
                                ) {
                                    Box(
                                        modifier = Modifier
                                            .drawBehind {
                                                drawRect(Color.White)
                                            }
                                            .padding(vertical = 10.dp)
                                    ) {
                                        ProductRow(
                                            productWrapper = productWrapper,
                                            onUmChanged = { um -> onUIEvent(UmChanged(productWrapper, um)) },
                                            onAdd = { onUIEvent(Add(productWrapper)) },
                                            onRemove = { onUIEvent(Remove(productWrapper)) },
                                            onEditQuantity = { quantity -> onUIEvent(EditQuantity(productWrapper, quantity)) },
                                            onFocusRemoved = { onUIEvent(QuantityFocusOut(productWrapper)) }
                                        )
                                    }
                                }
                                Divider()
                            }
                        }
                    } else {
                        if (!loading) {
                            Box(modifier = Modifier.fillMaxSize()) {
                                Text(
                                    text = stringResource(R.string.carrello_nessun_articolo),
                                    style = MaterialTheme.typography.h5,
                                    fontWeight = FontWeight.Bold,
                                    textAlign = TextAlign.Center,
                                    modifier = Modifier.align(Alignment.Center)
                                )
                            }
                        }
                    }
                }
            }
        }
        if (alertDialog != null) {
            AlertDialog(
                onDismissRequest = { alertDialog.onDismiss?.invoke() },
                title = {
                    Text(
                        text = alertDialog.title,
                        fontWeight = FontWeight.Bold
                    )
                },
                text = { Text(text = alertDialog.msg) },
                buttons = {
                    Row(
                        horizontalArrangement = Arrangement.End,
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(horizontal = 8.dp)
                    ) {
                        if (alertDialog.onCancel != null) {
                            TextButton(onClick = alertDialog.onCancel::invoke) {
                                Text(
                                    text = stringResource(id = R.string.annulla_label),
                                    color = Color.Gray
                                )
                            }
                        }
                        if (alertDialog.onConfirm != null) {
                            TextButton(onClick = alertDialog.onConfirm::invoke) {
                                Text(
                                    text = stringResource(id = R.string.ok_label),
                                    color = Color.Gray
                                )
                            }
                        }
                    }
                }
            )
        }
        LoadingOverlay(isVisible = loading)
    }
}
this is the "product row" composable
Copy code
fun ProductRow(
    productWrapper: ProdottoWrapper,
    onUmChanged: (Um) -> Unit,
    onAdd: () -> Unit,
    onRemove: () -> Unit,
    onEditQuantity: (String) -> Unit,
    onFocusRemoved: () -> Unit
) {

    val product = productWrapper.prodotto
    val um = productWrapper.um
    val price = productWrapper.prezzo
    val quantity by remember(productWrapper.quantita) {
        derivedStateOf {
            productWrapper.quantita
        }
    }
    var dropdownVisibility by remember { mutableStateOf(false) }
    var dropdownWidth by remember { mutableStateOf(Size.Zero) }

    Row(
        modifier = Modifier
            .fillMaxWidth()
            .drawBehind {
                drawRect(Color.White)
            },
        horizontalArrangement = Arrangement.SpaceEvenly,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .weight(3f)
        ) {
            Text(
                text = "Cod. ${product.codice}",
                fontWeight = FontWeight.Bold,
                maxLines = 1,
                overflow = TextOverflow.Visible
            )
            Text(
                text = product.descrizione,
                maxLines = 4,
                overflow = TextOverflow.Ellipsis
            )
            Text(
                text = "Prezzo unitario ${BuildConfig.VALUTA} ${
                    Formatter.formatPrezzo(
                        LocalContext.current, productWrapper.prezzo_unitario
                    )
                }/${product.um_default.descrizione}",
                fontSize = 12.sp,
                fontWeight = FontWeight.Medium,
                color = colorResource(id = R.color.os_dark_grey),
                maxLines = 1,
                overflow = TextOverflow.Visible
            )
        }
        Column(
            modifier = Modifier
                .fillMaxSize()
                .weight(1f)
                .onGloballyPositioned { layoutCoordinates ->
                    dropdownWidth = layoutCoordinates.size.toSize()
                },
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            if (product.um.size > 1) {
                Card(
                    backgroundColor = Color.Transparent,
                    border = BorderStroke(1.dp, colorResource(id = R.color.medium_dark)),
                    elevation = 0.dp,
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(40.dp)
                        .clickable { dropdownVisibility = true },
                ) {
                    Row(
                        horizontalArrangement = Arrangement.SpaceBetween,
                        verticalAlignment = Alignment.CenterVertically,
                        modifier = Modifier.padding(all = 5.dp)
                    ) {
                        Text(text = um.descrizione)
                        Icon(
                            imageVector = Icons.Default.KeyboardArrowDown,
                            contentDescription = ""
                        )
                    }
                }
                DropdownMenu(
                    expanded = dropdownVisibility,
                    onDismissRequest = { dropdownVisibility = false },
                    modifier = Modifier.widthIn(min = with(LocalDensity.current) { dropdownWidth.width.toDp() })
                ) {
                    product.um.forEach {
                        Row(modifier = Modifier
                            .clickable {
                                onUmChanged(it)
                                dropdownVisibility = false
                            }
                            .fillMaxWidth()
                            .padding(horizontal = 10.dp, vertical = 5.dp)) {
                            Text(text = it.descrizione, overflow = TextOverflow.Ellipsis)
                        }
                    }
                }
            } else {
                Text(text = um.descrizione, fontWeight = FontWeight.Bold)
            }

        }
        Column(
            modifier = Modifier
                .fillMaxSize()
                .weight(2f)
                .padding(horizontal = 16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            QuantitySelector(
                quantity = quantity,
                onAdd =  onAdd,
                onRemove = onRemove,
                onEditQuantity = {
                    onEditQuantity(it)
                },
                onFocusRemoved = onFocusRemoved
            )
        }
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = "${BuildConfig.VALUTA} ${
                    Formatter.formatPrezzo(
                        LocalContext.current,
                        price
                    )
                }",
                fontWeight = FontWeight.Bold,
                textAlign = TextAlign.End
            )
        }
    }
}
and this is the "quantity selector"
Copy code
fun QuantitySelector(
    quantity: String,
    onAdd: () -> Unit,
    onRemove: () -> Unit,
    onEditQuantity: (String) -> Unit,
    onFocusRemoved: () -> Unit
) {

    val interactionSource = remember { MutableInteractionSource() }
    val focused = remember { mutableStateOf(false) }
    val focusManager = LocalFocusManager.current

    Card(
        backgroundColor = Color.Transparent,
        border = BorderStroke(1.dp, colorResource(id = R.color.medium_dark)),
        elevation = 0.dp,
        modifier = Modifier
            .wrapContentWidth()
            .height(40.dp)
    ) {
        Row(
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.CenterVertically
        ) {
            IconButton(onClick = onRemove, modifier = Modifier.weight(1f)) {
                Icon(
                    painter = painterResource(id = R.drawable.ic_baseline_remove_24),
                    contentDescription = null,
                    tint = colorResource(id = R.color.os_dark_grey),
                    modifier = Modifier.fillMaxSize(0.7f)
                )
            }
            BasicTextField(
                value = quantity,
                onValueChange = {
                    onEditQuantity(it)
                },
                textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
                singleLine = true,
                interactionSource = interactionSource,
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Number,
                    imeAction = ImeAction.Done
                ),
                keyboardActions = KeyboardActions(onDone = {
                    onFocusRemoved()
                    focusManager.clearFocus()
                }),
                modifier = Modifier
                    .weight(2f)
                    .fillMaxWidth()
                    .onFocusChanged {
                        if (!it.isFocused && focused.value) {
                            onFocusRemoved()
                        }
                        focused.value = it.isFocused
                    }
            ) {
                TextFieldDefaults.TextFieldDecorationBox(
                    value = quantity,
                    innerTextField = it,
                    enabled = true,
                    singleLine = true,
                    visualTransformation = VisualTransformation.None,
                    interactionSource = interactionSource,
                    contentPadding = TextFieldDefaults.textFieldWithoutLabelPadding(
                        top = 0.dp,
                        bottom = 0.dp,
                        start = 0.dp,
                        end = 0.dp
                    )
                )
            }
            IconButton(onClick = onAdd, modifier = Modifier.weight(1f)) {
                Icon(
                    imageVector = Icons.Default.Add,
                    contentDescription = null,
                    tint = colorResource(id = R.color.os_dark_grey),
                    modifier = Modifier.fillMaxSize(0.7f)
                )
            }
        }
    }
}
I noticed that when I click on a button in the Quantity Selector and trigger its action, the whole QuantitySelector recomposes.
in addition, clicking on those buttons triggers the recomposition of every single ProductRow within the lazy column