Joost Klitsie
07/19/2020, 8:47 AMmy name is joost
m name ist jooyt
which is very weird 😄MutableStateFlow<ViewState>
, where the userName is just a String in the ViewState and it is updated as such:
viewState.value = viewState.value.copy(
userName = userName,
isButtonEnabled = !userName.isBlank() && !viewState.value.password.isNullOrBlank()
)
Adam Powell
07/19/2020, 2:48 PMDispatchers.Main
isn't the intended end state but at worst it should be incurring a single frame of latency before changes are reflected in the UI, not affecting correctness. Can you post some more of the related code you're using to update the StateFlow?Joost Klitsie
07/19/2020, 2:50 PMAdam Powell
07/19/2020, 2:58 PMJoost Klitsie
07/19/2020, 2:59 PMAdam Powell
07/19/2020, 2:59 PMJoost Klitsie
07/19/2020, 2:59 PMAdam Powell
07/19/2020, 3:00 PMJoost Klitsie
07/19/2020, 3:01 PMAdam Powell
07/19/2020, 3:01 PMJoost Klitsie
07/19/2020, 3:01 PMOutlinedTextField(
value = viewState.userName ?: "",
onValueChange = viewModel::updateUserName,
modifier = Modifier.fillMaxWidth(),
label = { Text("User name") }
)
override val viewState = MutableStateFlow(LoginViewState())
override fun updateUserName(userName: String) {
viewState.update {
copy(
userName = userName,
isButtonEnabled = viewState.value.password.isNotNullOrBlack() && userName.isNotNullOrBlack()
)
}
}
data class LoginViewState(
override var userName: String? = null,
override var userNameError: String? = null,
override var password: String? = null,
override var passwordError: String? = null,
override var isLoading: Boolean = false,
override var isButtonEnabled: Boolean = false
): LoginContract.ViewState
fun <T> MutableStateFlow<T>.update(
block: T.() -> T
) {
value = value.run(block)
}
@Composable
fun LoginInputComponent(props: LoginContract.Props) {
val viewModel by instance<LoginContract.Props, LoginContract.ViewModel>(props)
LoginInput(viewModel.viewState.collectAsState(Dispatchers.Main.immediate).value, viewModel)
}
@Composable
fun LoginInput(viewState: LoginContract.ViewState, viewModel: LoginContract.ViewModel) {
//... OutlinedTextField
Adam Powell
07/19/2020, 3:07 PMJoost Klitsie
07/19/2020, 3:07 PMoverride fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val stateFlow = MutableStateFlow(LoginViewState())
val update = { userName: String ->
stateFlow.value = stateFlow.value.copy(userName = userName)
}
setContent {
val viewState = stateFlow.collectAsState().value
OutlinedTextField(
value = viewState.userName ?: "",
onValueChange = update,
modifier = Modifier.fillMaxWidth(),
label = { Text("User name") }
)
}
}
update
will run too late (as a handler.post)Adam Powell
07/19/2020, 3:09 PMJoost Klitsie
07/19/2020, 3:09 PMval viewState = stateFlow.collectAsState(Dispatchers.Main.immediate).value
Adam Powell
07/19/2020, 3:10 PMJoost Klitsie
07/19/2020, 3:12 PM@Composable
fun <T : R, R> Flow<T>.collectAsState(
initial: R,
context: CoroutineContext = Dispatchers.Main
): State<R> {
val state = state { initial }
onPreCommit(this, context) {
val job = CoroutineScope(context).launch {
collect {
FrameManager.framed {
state.value = it
}
}
}
onDispose { job.cancel() }
}
return state
}
Adam Powell
07/19/2020, 3:14 PMJoost Klitsie
07/19/2020, 3:15 PMAdam Powell
07/19/2020, 3:16 PMJoost Klitsie
07/19/2020, 3:16 PMAdam Powell
07/19/2020, 3:16 PMJoost Klitsie
07/19/2020, 3:17 PMAdam Powell
07/19/2020, 3:38 PMAndroidUiDispatcher
is in dev14 can you try AndroidUiDispatcher.Main
as the context for that collect operation?Joost Klitsie
07/19/2020, 3:40 PMAdam Powell
07/19/2020, 3:40 PMJoost Klitsie
07/19/2020, 3:41 PMAdam Powell
07/19/2020, 3:43 PMlaunchInComposition
, which is probably what the collect extensions should be updated to use. (The former didn't exist when the collect extensions were first written)Joost Klitsie
07/19/2020, 3:46 PMAdam Powell
07/19/2020, 3:47 PMJoost Klitsie
07/19/2020, 4:00 PMAdam Powell
07/19/2020, 4:24 PMJoost Klitsie
07/19/2020, 4:26 PMAdam Powell
07/19/2020, 4:26 PMJoost Klitsie
07/19/2020, 4:27 PM