Jaime
11/04/2021, 4:09 PMprivate val _marketStatusIsClosed = MutableStateFlow(MarketStatusUiState())
val marketStatusClosed: StateFlow<MarketStatusUiState> get() = _marketStatusIsClosed
Composable
val marketStatusState by rememberFlowWithLifecycle(tickerDetailViewModel.marketStatusClosed)
.collectAsState(MarketStatusUiState())
this is the code that is always running even though it is not visible to the user
if (!marketStatusState.isLoading) {
Log.e("BuyInSharesScreen", "$marketStatusState")
if (marketStatusState.isMarketClose) {
} else {
open another composable
}
}
the new composable (screen) is being opened many times, and it is already closing so I do the navigation
currentBackStackEntry?.arguments?.putParcelable(CREATE_ORDER_INFO, operation)
navigate(Screens.BuyStockConfirmationScreen.route)
Chris Fillmore
11/04/2021, 4:11 PMJaime
11/04/2021, 4:11 PMChris Fillmore
11/04/2021, 4:22 PMStateFlow
being updated?
If the value
is updating frequently, but your “market status” state is logically unchanged, you may want to use distinctUntilChanged()
Chris Fillmore
11/04/2021, 4:24 PMJaime
11/04/2021, 4:27 PMJaime
11/04/2021, 4:30 PMChris Fillmore
11/04/2021, 4:33 PMdistinctUntilChanged()
has no effect on a StateFlow, sorry I forgot this. But if your StateFlow is being driven by some upstream Flow
, then applying distinctUntilChanged()
upstream may help. It’s hard to get a clear picture of what’s going on in your code, without more context.Zach Klippenstein (he/him) [MOD]
11/04/2021, 4:37 PMJaime
11/04/2021, 4:42 PMButton(
modifier = Modifier
.constrainAs(sharesContinueButton) {
bottom.linkTo(<http://sharesKeyBoard.top|sharesKeyBoard.top>, margin = 20.dp)
end.linkTo(parent.end)
start.linkTo(parent.start)
}
.padding(end = 20.dp, start = 20.dp),
onClick = {
tickerDetailViewModel.marketStatus()
},
text = stringResource(id = R.string.continue_),
)
in my viewModel I have something like
fun marketStatus() {
viewModelScope.launch {
val marketStatusResult = tickerDetailMarketStatusUseCase.run(UseCase.None())
marketStatusResult collectAsSuccess {
Log.e("marketStatus", "collectAsSuccess")
_marketStatusIsClosed.value = if (it.status == MARKET_STATUS_CLOSED) {
MarketStatusUiState(
isLoading = false,
isMarketClose = false
)
} else {
MarketStatusUiState(
isLoading = false,
isMarketClose = false
)
}
} collectAsFailure {
Log.e("marketStatus", "collectAsFailure")
_marketStatusIsClosed.value = MarketStatusUiState(
isLoading = false,
isMarketClose = true
)
}
}
}
this is my variables observer in viewmodel
private val _marketStatusIsClosed = MutableStateFlow(MarketStatusUiState())
val marketStatusClosed: StateFlow<MarketStatusUiState> get() = _marketStatusIsClosed
in my composable i have this validation
if (!marketStatusState.isLoading) {
Log.e("BuyInSharesScreen", "$marketStatusState")
if (marketStatusState.isMarketClose) {
//TODO: change strings
HapiDialog(
dialog = HapiDialogModel(
content = HapiDialogContentModel(
titleId = R.string.signup_message_email_already_registered,
buttonTitleId = listOf(R.string.login),
resourceId = R.drawable.ic_dialog_error,
),
type = HapiDialogModel.HapiDialogType.MIDDLE,
resourceType = HapiDialogModel.HapiDialogResourceType.IMAGE,
quantityButtons = 1
)
)
} else {
operationDetailListener(
CreateOrderInfo(
shares = stockOptionWithoutFormat.toDouble(),
buyingPower = buyingPowerState.buyingPower,
tickerCompanyName = tickerCompanyName,
currentPrice = marketPriceState.price,
estimatedCost = estimatedCostState,
tickerCompany = ticker
)
)
}
}
go into the else and call the operationDetailListener method
navController.navigateToOperationDetailConfirm(operation)
this is my method navigateToOperationDetailConfirm
fun NavHostController.navigateToOperationDetailConfirm(operation: CreateOrderInfo) {
currentBackStackEntry?.arguments?.putParcelable(CREATE_ORDER_INFO, operation)
navigate(Screens.BuyStockConfirmationScreen.route)
}
open new screen and and the state of marketStatusState is being called several times and the new compsable is opened many timesJaime
11/04/2021, 4:42 PMJaime
11/04/2021, 4:42 PMZach Klippenstein (he/him) [MOD]
11/04/2021, 4:50 PMYou should only callTypically navigation should be done as part of an event handler, not as a result of composition.as part of a callback and not as part of your composable itself, to avoid callingnavigate()
on every recomposition.navigate()
Zach Klippenstein (he/him) [MOD]
11/04/2021, 4:51 PMJaime
11/04/2021, 5:05 PMJaime
11/04/2021, 5:08 PMZach Klippenstein (he/him) [MOD]
11/04/2021, 5:47 PMDisposableEffect
or LaunchedEffect
Jaime
11/04/2021, 5:54 PMZach Klippenstein (he/him) [MOD]
11/04/2021, 5:54 PMJaime
11/04/2021, 5:56 PMZach Klippenstein (he/him) [MOD]
11/04/2021, 5:56 PMZach Klippenstein (he/him) [MOD]
11/04/2021, 5:56 PMJaime
11/04/2021, 5:57 PMZach Klippenstein (he/him) [MOD]
11/04/2021, 5:58 PMZach Klippenstein (he/him) [MOD]
11/04/2021, 6:18 PMJaime
11/04/2021, 6:20 PMIan Lake
11/04/2021, 9:37 PMLaunchedEffect
, not using state changes as some proxy for eventsIan Lake
11/04/2021, 9:38 PMLaunchedEffect
is enough to only process that 'event' once
} else {
// Make this only happen once
LaunchedEffect(marketStatusState) {
operationDetailListener(
CreateOrderInfo(
shares = stockOptionWithoutFormat.toDouble(),
buyingPower = buyingPowerState.buyingPower,
tickerCompanyName = tickerCompanyName,
currentPrice = marketPriceState.price,
estimatedCost = estimatedCostState,
tickerCompany = ticker
)
)
}
}
Ian Lake
11/04/2021, 9:52 PMtry
(the one passed to your composable
method) has a lifecycle.currentState == Lifecycle.State.RESUMED
- when you call navigate()
, your state is synchronously moved down immediately. That doesn't change the fact that you shouldn't call startActivity
or navigate
or any other side effect as part of composition (you still need to fix that), but it does give you a way of ignoring events that happen after you've already started navigating (and crossfading/animating to the new screen)