I'm having trouble understanding why my value I'm observing as state is properly passed into a Composable lambda, but does not trigger that composable to recompose.
Here's my setup.
@Composable
fun Screen(
showBottomSheet: (@Composable () -> Unit) -> Unit,
viewModel: MyViewModel = hiltViewModel()
) {
val someValue = viewModel.someValue.observeAsState().value
MyController(
onShowBottomSheetClick = {
showBottomSheet {
Log.d("DEBUG", "value updated: $someValue")
BottomSheetLayout(
someValue = someValue,
onUpdateClick = { newValue -> viewModel.setValue(newValue) }
)
}
}
)
}
@Composable
fun MyController(
onShowBottomSheetClick: () -> Unit
) {
Text(
text = "show BottomSheet",
modifier = Modifier.clickable { onShowBottomSheetClick() }
)
}
@Composable
fun BottomSheetLayout(
someValue: Int,
onUpdateClick: (Int) -> Unit
) {
Text(
text = "Some value: $someValue",
modifier = Modifier.clickable { onUpdateClick(someValue + 1) }
)
}
@HiltViewModel
class MyViewModel @Inject constructor(): ViewModel() {
private val _someValue: Int? = MutableLiveData(null)
val someValue: LiveData<Int?> = _someValue
fun setValue(newValue: Int) {
_someValue.value = newValue
}
}
When I run this and see output, the log doesn't show updated someValue
.
value updated: 0
value updated: 0
value updated: 0
However, if I add a new state value to the lambda itself, it work fine.
// Screen
// ..
showBottomSheet {
val currentValue = viewModel.someValue.observeAsState().value // <-- Note the difference
Log.d("DEBUG", "value updated: $currentValue")
BottomSheetLayout(
someValue = currentValue,
onUpdateClick = { newValue -> viewModel.setValue(newValue) }
)
}
// ..
When I run this and see output, the log shows properly updated value.
value updated: 0
value updated: 1
value updated: 2
Why is this happening? Is there any way I can pass the first state value to the showBottomSheet
composable lambda?
Thank you in advance.
I haven't tried your code but this looks like an effect of scoped recomposition as in this question. In that question even without remember mutableState value is updated because outer scope wasn't recomposed. In your case since you are passing value instead of state that is not updated because of not triggering recomposition.
Why does mutableStateOf without remember work sometimes?
@Composable
fun Screen(
showBottomSheet: (@Composable () -> Unit) -> Unit,
viewModel: MyViewModel = hiltViewModel()
) {
val someValue = viewModel.someValue.observeAsState().value
MyController(
onShowBottomSheetClick = {
showBottomSheet {
Log.d("DEBUG", "value updated: $someValue")
BottomSheetLayout(
someValue = someValue,
onUpdateClick = { newValue -> viewModel.setValue(newValue) }
)
}
}
)
}
In this code only showBottomSheet
is recomposed and because of that someValue which is a value is not updated since there is no recomposition in MyController
scope.
With this code, i assume showBottomSheet
is a Composable since you are able to call @Composable function Flow.collectAsState you are recomposing everything inside and that's why it's recomposed with lates value of viewModel.someValue
showBottomSheet {
val currentValue = viewModel.someValue.observeAsState().value // <-- Note the difference
Log.d("DEBUG", "value updated: $currentValue")
BottomSheetLayout(
someValue = currentValue,
onUpdateClick = { newValue -> viewModel.setValue(newValue) }
)
}