ubuntudroid
11/03/2023, 10:01 AMAndroidViewModel
? I’m trying attachEventHandler()
, but still only the previous handler is called.
Background: I’m building with Compose and keep the ViewModel around longer than the screen, and I pass a SnackbarHostState
to the EventHandler to display snackbars. That also means that I need to regularly update the SnackbarHostState to match the currently used one and thus also the EventHandler.Casey Brooks
11/03/2023, 2:09 PMrememberCoroutineScope()
to only run the EventHandler when that UI is shown, for exampleubuntudroid
11/03/2023, 2:58 PMSnackbarHostState
.
An alternative, that just came to my mind, would be to pass the SnackbarHostState not via constructor, but via a public property and then use a LaunchedEffect
to update the property whenever the composition changes. 🤔 One might run into subtle timing race conditions that way though, especially when trying to send stuff to the snackbar shortly before or during a recomposition.SnackbarHostState
or a Router
to the event handler? Or is that too tight coupling?Casey Brooks
11/03/2023, 3:24 PM@Composable
fun Content(router: Router<Screen>, vm: AndroidViewModel<
ExampleContract.Inputs,
ExampleContract.Events,
ExampleContract.State,
>) {
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(vm, router, snackbarHostState) {
vm.attachEventHandler(this, ExampleEventHandler(router, snackbarHostState))
}
val uiState by vm.observeStates().collectAsState()
Content(uiState, snackbarHostState) { vm.trySend(it) }
}
ubuntudroid
11/03/2023, 3:49 PMattachEventHandler()
doesn’t really replace the event handler if there already is one in place. If I set a breakpoint into the event handler it always seems to be the “old” one with access to the “old” SnackbarHostState
.Casey Brooks
11/03/2023, 4:04 PMattachEventHandler()
just collects a channel as a Flow within the CoroutineScope, and when the coroutineScope gets cancelled the EventHandler goes away.
The issues with both this and the debugger issue are making me think that something in your setup is keeping CoroutineScopes active longer than they should be.ubuntudroid
11/03/2023, 4:18 PMCasey Brooks
11/03/2023, 4:22 PMContent
composable function). But you want the EventHandler to run only within that Content function’s scope, so you need to create a new EventHandler and attach it to the VM on a coroutineScope that is not the same as the VM’s, or derived from it.
In the snippet, that shorter-lived CoroutineScope comes from a LaunchedEffect
, which should collect and process Events only as long as the Content
function is in the Composition. Once you leave that screen, the VM doesn’t necessarily get cleared (since it was passed in from above and isn’t scoped to the screen), but the EventHandler does get cleared since it is scoped to the screen.ubuntudroid
11/03/2023, 4:30 PMCasey Brooks
11/03/2023, 4:32 PMBasicViewModel
. You can’t swap the EventHandler on a BasicViewModel
(though maybe one should be able to. might be something for me to look into).
Essentially, BallastViewModelImpl
is the actual Ballast VM you should be embedding. All the other VM types provided by Ballast (AndroidViewModel, BasicViewModel, etc) just wrap thatubuntudroid
11/03/2023, 4:34 PMAndroidViewModel
because in contrast to BasicViewModel
it had the attachEventHandler()
function. I’ll check whether BallastViewModelImpl
can help.