Mahdi
10/16/2020, 2:53 AMobserveAsState
It is very odd that after update liveData compose ui update is not happening, but on some events ( click on items) I can see that model is updated! Is it better way to have handle items state outside compose? ThanksZach Klippenstein (he/him) [MOD]
10/16/2020, 2:57 AMMahdi
10/16/2020, 2:59 AM@Composable
fun acquiredCouponScreen(viewModel: AcquiredCouponViewModel) {
val items by viewModel.acquiredCoupons.observeAsState(initial = emptyList())
val lastIndex = items.lastIndex
val state = viewModel.networkState
when (state.value) {
NetworkState.INITIAL -> viewModel.getCouponFromStart()
}
And this is inside my viewModel:
private val _acquiredCoupons = MutableLiveData<List<AcquiredCoupon>>(listOf())
val acquiredCoupons: LiveData<List<AcquiredCoupon>> = _acquiredCoupons
fun doExpand(coupon : AcquiredCoupon){
val index = _acquiredCoupons.value?.indexOf(coupon) ?: -1
val newItems = _acquiredCoupons.value?.toMutableList().also {
it?.get(index)?.isExpanded = true
}
_acquiredCoupons.value = newItems
Timber.i("Mahdi $index")
}
doExpand will calls from compose UI.Zach Klippenstein (he/him) [MOD]
10/16/2020, 3:05 AMacquiredCouponScreen
seems like it should recompose, but you’re also not using items
or lastIndex
for anything, so i’m not sure what you’re expecting to update.Mahdi
10/16/2020, 3:06 AM@Composable
fun acquiredCouponScreen(viewModel: AcquiredCouponViewModel) {
val items by viewModel.acquiredCoupons.observeAsState(initial = emptyList())
val lastIndex = items.lastIndex
val state = viewModel.networkState
when (state.value) {
NetworkState.INITIAL -> viewModel.getCouponFromStart()
}
ConstraintLayout {
val (list, progress) = createRefs()
if (state.value == NetworkState.LOADING || state.value == NetworkState.INITIAL) {
CircularProgressIndicator(
color = Color.Black,
modifier = Modifier.fillMaxWidth().height(32.dp).width(32.dp)
.constrainAs(progress) {
top.linkTo(<http://parent.top|parent.top>)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
)
}
LazyColumnForIndexed(
items = items,
contentPadding = PaddingValues(top = 8.dp),
modifier = Modifier.constrainAs(list) {
top.linkTo(<http://parent.top|parent.top>)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
) { i, coupon ->
if (lastIndex == i) {
onActive {
viewModel.getAcquiredCoupons(i + 1)
}
}
Timber.i(coupon.campaignUrl)
if (coupon.isExpanded) {
couponExpandedItem(
coupon = coupon,
{ viewModel.doCollapse(it) }, { _, _ -> }
)
} else {
couponCollapsedItem(
coupon = coupon
) {
viewModel.doExpand(it)
}
}
}
}
}
Zach Klippenstein (he/him) [MOD]
10/16/2020, 3:09 AMLazyColumn*
has bugs where it doesn’t update – if you replace your `LazyColumn*`s with regular `ScrollableColumn`s, does it work?viewModel.networkState
a State
as well?getCouponFromStart
is going to get called on every recomposition here, is that intentional?
when (state.value) {
NetworkState.INITIAL -> viewModel.getCouponFromStart()
}
Mahdi
10/16/2020, 3:11 AMprivate val _networkState = MutableStateFlow(NetworkState.INITIAL)
val networkState: StateFlow<NetworkState> = _networkState
For handle SUCCESS ,ERROR, LOADING state.Ian Lake
10/16/2020, 3:12 AMvalue
on a StateFlow
isn't enough to recompose (it is just getting a snapshot value). You have to collectAsState()
and value
on thatZach Klippenstein (he/him) [MOD]
10/16/2020, 3:19 AMgetCouponFromStart
call, I would probably advise against putting imperative logic that is unrelated to the UI in the composable at all. That’s something your view model, or something in a layer above it, should be responsible for. If your UI needs to send an event to signal it’s being shown for the first time, it should just send the event and the higher layer can decide whether it needs to actually do anything to handle the event depending what state it’s in.
E.g.
onActive {
viewModel.notifyDisplayed()
}
Mahdi
10/16/2020, 3:25 AMZach Klippenstein (he/him) [MOD]
10/16/2020, 3:28 AMMahdi
10/16/2020, 3:30 AM