Colton Idle
06/26/2021, 6:52 PMColton Idle
06/26/2021, 6:52 PMclass SignInViewState {
var email by mutableStateOf("")
var password by mutableStateOf("")
var modalError: ModalError? by mutableStateOf(null)
}
data class ModalError(
val text1: String,
val text2: String,
)
My compose screen includes this
if (viewModel.state.modalError != null) {
Dialog(onDismissRequest = { viewModel.state.modalError = null }) {
MyModal(
viewModel.state.modalError!!.text1,
viewModel.state.modalError!!.text2)
}
}
And this works-ish. My modal does not show. Then I update my modal error in my state, and the pop up shows! But then when I click outside of the dialog I get a crash. My on dismiss request tries to null out the error, but I get an NPE on viewModel.state.modalError!!.text1
rnett
06/26/2021, 7:20 PMmodalError
to a local variable and using that. Not sure about the root cause, but that should get around itColton Idle
06/26/2021, 9:31 PMif (viewModel.state.modalError != null) {
Dialog(onDismissRequest = { viewModel.state.modalError = null }) {
MyModal(
viewModel.state.modalError?.text1.orEmpty(),
viewModel.state.modalError?.text2.orEmpty())
}
}
stops the issue from happening, but now I really feel like I don't know what's going on. Can anyone identify what's going on?Albert Chang
06/27/2021, 2:37 AMCrossfade
, AninatedVisibility
, etc.
Using val error = remember { viewModel.state.modalError!! }
to solve the problem.Colton Idle
06/27/2021, 3:31 AMAlbert Chang
06/27/2021, 5:45 AMremember
that helps. It remembers the initial value so even if viewModel.state.modalError
becomes null the remembered value doesn't.Colton Idle
06/27/2021, 7:13 PMclass SignInViewState {
var email by mutableStateOf("")
var password by mutableStateOf("")
var showModal by mutableStateOf(false)
var modalError: ModalError? by mutableStateOf(null)
}
Adding a val error = remember { viewModel.state.modalError!! }
almost seems too "clever" and I'm not sure it's really self explanatory on what's actually going on.Doris Liu
06/27/2021, 8:20 PMval error = remember { viewModel.state.modalError!! }
inside the dialog content, so that the error isn't retained after the content is disposed. Or, you could retain that data in your ViewModel, and wait for the onDisposed
signal to do the cleanup. Either way works. It's a matter of personal preference.Colton Idle
06/27/2021, 9:40 PMif (viewModel.state.modalError != null) {
Dialog(onDismissRequest = { viewModel.state.modalError = null }) {
MyModal(
viewModel.state.modalError!!.text1,
viewModel.state.modalError!!.text2)
}
}
to this
if (viewModel.state.modalError != null) {
Dialog(onDismissRequest = { viewModel.state.modalError = null }) {
val error = remember { viewModel.state.modalError!! }
MyModal(error.text1, error.text2)
}
}
@Doris Liu I am not following your second option "you could retain that data in your ViewModel". How would that look?Doris Liu
06/27/2021, 10:17 PMif (viewModel.state.showError) {
Dialog(onDismissRequest = { viewModel.state.showError = false }) {
MyModal(
viewModel.state.modalError!!.text1,
viewModel.state.modalError!!.text2)
DisposableEffect(viewModel) {
// retain data until disposed
onDisposed { viewModel.state.modelError = null }
}
}
}
Doris Liu
06/27/2021, 10:19 PMif (viewModel.state.modalError != null) {
Dialog(onDismissRequest = { viewModel.state.modalError = null }) {
...
should have the Dialog removed from the tree when modalError = null
and hence the if-condition evaluates to false. Is the dialog dismissed through something other than a gesture? Could you post the full NPE stack strace?Colton Idle
06/27/2021, 11:04 PMColton Idle
06/27/2021, 11:15 PM2021-06-27 19:10:17.368 10948-10948/com.rollertoaster.app E/AndroidRuntime: FATAL EXCEPTION: main
Process: <http://com.rollertoaster.app|com.rollertoaster.app>, PID: 10948
java.lang.NullPointerException
at com.rollertoaster.app.login.SignInFragment$onCreateView$1$2$1$2.invoke(SignInFragment.kt:102)
at com.rollertoaster.app.login.SignInFragment$onCreateView$1$2$1$2.invoke(SignInFragment.kt:100)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:140)
at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2156)
at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2399)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2574)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2567)
at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(SnapshotState.kt:523)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2560)
at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:2536)
at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:613)
at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:763)
at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:102)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:446)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:415)
at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34)
at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:970)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:727)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Clicking through the link in the stacktrace puts my cursor on
viewModel.state.modalError!!.text1,
Which is basically the thing that was blowing my mind of how this got past the if statement in the first place (to Doris' point "and hence the if-condition evaluates to false")Doris Liu
06/28/2021, 2:32 AMColton Idle
06/28/2021, 5:07 AMlouiscad
06/28/2021, 6:20 AMColton Idle
06/28/2021, 2:32 PMColton Idle
06/28/2021, 2:44 PM