-
Hello, I have a question about the best practice for handling Toast and Snackbar in Circuit. In the CatchUp project, I noticed the following approach in the codebase: class SettingsPresenter
@AssistedInject
constructor(
@Assisted private val screen: SettingsScreen,
@Assisted private val navigator: Navigator,
@ApplicationContext private val appContext: Context,
private val catchUpPreferences: CatchUpPreferences,
...
) : Presenter<State> {
...
@Composable
override fun present(): State {
// TODO blerg this isn't good in Circuit. Need an ActivityStarter instead on DI
val view = LocalView.current
LaunchedEffect(view) {
catchUpPreferences.reports
.drop(1) // Drop the initial true emission
.collect {
// If we change reports to false, restart
// TODO circuit-ify this
Snackbar.make(
view,
appContext.getString(AppScaffoldR.string.settings_reset),
Snackbar.LENGTH_INDEFINITE,
)
.setAction(AppScaffoldR.string.restart) { appContext.restartApp() }
.show()
}
}
val scope = rememberStableCoroutineScope()
return State(screen.showTopAppBar) { event ->
when (event) {
ClearCache -> {
scope.launch {
...
// TODO circuit-ify this
Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE)
.setAction(AppScaffoldR.string.restart) { appContext.restartApp() }
.show()
}
}
...
}
}
}
By examining the code in project, I confirmed that injecting ApplicationContext into a Presenter does not pose a problem. If handling UiEvent directly in the Presenter is not aligned with Circuit's patterns, as suggested by the Snackbar code comments, would this implementation be appropriate? In @Parcelize
data class SettingsScreen(val showTopAppBar: Boolean = true) : Screen {
...
data class State(val showTopAppBar: Boolean, val eventSink: (Event) -> Unit) : CircuitUiState
// (new) add SideEffect sealed interface
sealed interface SideEffect {
data class ShowSnackbar(val message: String) : SideEffect
}
sealed interface Event {
// TODO does this make sense or should the presenter decide?
data class NavToScreen(val screen: Screen) : Event
data object ClearCache : Event
data object InitSideEffect: Event
}
} In val snackbarHostState = remember { SnackbarHostState() }
...
// I drafted this code based on my initial thoughts, but it might not be an optimal or appropriate solution.
LaunchedEffect(state.sideEffect) {
when (val effect = state.sideEffect) {
is SideEffect.ShowSnackbar -> {
// Handle Snackbar in UI layer
scope.launch {
snackbarHostState.showSnackbar(sideEffect.message)
}
// init SideEffect State
eventSink(Event.InitSideEffect)
}
}
} In the case of Snackbar, since it requires connecting the snackbarHostState to the SnackbarHost Composable function located within the screen (UI), I devised the above method because it was not possible to call it directly from within the Presenter. In the project below, I'm using the above method, and we were able to verify that the Snackbar is being called normally. In the case of using Which approach would be the recommended way to handle Toast and Snackbar in Circuit? Should we avoid handling UIEvent directly in the Presenter?(Furthermore, Include other cases of events with Android platform dependencies) Thanks for your guidance! |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
For Snackbar, it seems we could implement it by creating a |
Beta Was this translation helpful? Give feedback.
-
I found code like ToastEffect, and it would be helpful to learn about how to use it. |
Beta Was this translation helpful? Give feedback.
-
Both UiMessage in tivi and UserMessage in Droidkaigi (Which use Rin instead of Circuit) demonstrate approaches for handling Snackbar, but these approaches seem somewhat complicated. It would be helpful to learn about simple methods, and furthermore, discuss ways to implement event handling that has Android platform dependencies." |
Beta Was this translation helpful? Give feedback.
-
https://slackhq.github.io/circuit/states-and-events/ My question was actually about I think this approach of not introducing separate channels or sharedflows aligns with Manuel Vivo's article's arguments as well. |
Beta Was this translation helpful? Give feedback.
https://slackhq.github.io/circuit/states-and-events/
My question was actually about
How to handle one-time events in Slack Circuit
, generalizing from a specific case. Based on the official documentation I shared, it seems that treating events as part of the state is Circuit's philosophy. Therefore, I'll understand that handling events through a separate state like SideEffect, as I suggested, is the appropriate approach.I think this approach of not introducing separate channels or sharedflows aligns with Manuel Vivo's article's arguments as well.