0

I'm using a Composable which take a content @Composable in parameter, and it seems that with the final version of ConstraintLayout, there is no update.

Here is the code

@Composable
fun Example(
    modifier : Modifier = Modifier,
    content : @Composable () -> Unit
) {
     ConstraintLayout(modifier = modifier 
            .fillMaxSize()
            .background(color = Color.Blue)
     ) {
        val (title, someContent) = createRefs()

        Text(text = "a text", modifier = Modifier.constrainAs(title) {
            top.linkTo(parent.top)
            height = Dimension.wrapContent
            width = Dimension.wrapContent
        })
    
        Box(modifier = Modifier
            .constrainAs(someContent) {
                width = Dimension.fillToConstraints
                linkTo(start = parent.start, end = parent.end)
                linkTo(top = parent.top, bottom = parent.bottom, bias = 1.0f)
                height = Dimension.preferredWrapContent
            }
            .background(color = Color.Yellow)) {
            content()
        }
    }
}

The Box height is initialized with the fist element received by the composable and does not change anymore.

For example, I send to this composable a Column with [Button + result of a Webservice]. The button is displayed at startup, and then after some times I received the result of the API, it does not recompose correctly, the size of the Box stays wrapcontent with the button only!

Am I doing something wrong ? Moreover, it seems that the behaviour in a compose MotionLayout is the same (no update)

Johann
  • 27,536
  • 39
  • 165
  • 279
Tonio
  • 391
  • 3
  • 5
  • Seems like you're doing everything correctly, I suggest you [report](https://issuetracker.google.com/issues/new?component=323867) it. – Phil Dukhov Feb 12 '22 at 04:43

1 Answers1

0

A recomposition only occurs on a composable when at least one of the parameter values change. Because you are set the content parameter to a function, the parameter never changes.

The content parameter is pointing to a function reference and that reference never changes during recomposition.

If you want your composable to recompose with the updated data, you need to add a mutable state variable inside your composable that your viewmodel updates, or pass in a parameter that will change when you have received new data.

Here's an example of updating a parameter value using a state variable in a view model. Updating the state variable recomposes your composable and provides it with the updated variable (name):

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        startActivity(intent)

        setContent {
            val vm: MyViewModel = viewModel()
            vm.loadData()

            Example(name = vm.name.value) { name ->
                Text("Name: $name")
            }

        }
    }
}

class MyViewModel: ViewModel() {
    val name = mutableStateOf("")

    fun loadData() {
        viewModelScope.launch {
            delay(2000)
            name.value = "Joe Cool"
        }
    }
}

@Composable
fun Example(
modifier : Modifier = Modifier,
name: String,
content : @Composable (name: String) -> Unit
) {
    ConstraintLayout(modifier = modifier
        .fillMaxSize()
        .background(color = Color.Blue)
    ) {
        val (title, someContent) = createRefs()

        Text(text = "a text", modifier = Modifier.constrainAs(title) {
            top.linkTo(parent.top)
            height = Dimension.wrapContent
            width = Dimension.wrapContent
        })

        Box(modifier = Modifier
            .constrainAs(someContent) {
                width = Dimension.fillToConstraints
                linkTo(start = parent.start, end = parent.end)
                linkTo(top = parent.top, bottom = parent.bottom, bias = 1.0f)
                height = Dimension.preferredWrapContent
            }
            .background(color = Color.Yellow)) {
            content(name)
        }
    }
}


Johann
  • 27,536
  • 39
  • 165
  • 279
  • HJi Johann, thanks for the answear. Can you show an example for " pass in a parameter that will change when you have received new data." – Tonio Feb 14 '22 at 08:08
  • Ok thank you for your update. For my case I have replace string by Any? (because this template will be used everywhere). I does not seems to work. For example, by defaut the screen is empty and wait for the webservice, and after received the result of the API it stays wrapContent to the "empty" start content, so a height of 0.dp :( In your example, why the reference of content should be changed ? – Tonio Feb 14 '22 at 10:22