0

I have an app, which represent a screen with bottom bar with 4 tabs. I use compose navigation with bottom bar. When user clicks on the bar the screen appears. How can I handle the situation when user clicks on the bar button for the 2-nd time, I need to listen to this event from the current inner screen and scroll the content up or do something else.

I investigate that I should use currentBackStackEntryAsState, but I am not sure which way.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
Foenix
  • 376
  • 4
  • 18

1 Answers1

1

You can build classes shared in your compose tree with staticCompositionLocalOf.

For this purpose I've build CurrentTabClickHandler - it's API is similar to BackHandler:

@Composable
fun CurrentTabClickHandler(
    enabled: Boolean = true,
    onTabClick: suspend () -> Unit,
) {
    val currentOnTabClick by rememberUpdatedState(onTabClick)
    val currentEnabled by rememberUpdatedState(enabled)
    val coroutineScope = rememberCoroutineScope()
    val handler = remember {
        CurrentTabClickDispatcher.Handler {
            if (currentEnabled) {
                coroutineScope.launch {
                    currentOnTabClick()
                }
            }
        }
    }
    val currentTabClickDispatcher = LocalCurrentTabClickDispatcher.current
    DisposableEffect(Unit) {
        currentTabClickDispatcher.addHandler(handler)
        onDispose {
            currentTabClickDispatcher.removeHandler(handler)
        }
    }
}

class CurrentTabClickDispatcher {
    class Handler(val action: () -> Unit)

    private val handlers = mutableListOf<Handler>()

    fun addHandler(handler: Handler) {
        handlers.add(handler)
    }

    fun removeHandler(handler: Handler) {
        handlers.remove(handler)
    }

    fun currentHandler() =
        handlers.lastOrNull()
}

val LocalCurrentTabClickDispatcher = staticCompositionLocalOf {
    CurrentTabClickDispatcher()
}

So in the screen you use it like this:

CurrentTabClickHandler {
    // to your action
}    

And to perform this action add this code to BottomNavigationItem:

val currentTabClickDispatcher = LocalCurrentTabClickDispatcher.current
BottomNavigationItem(
    // ...
    selected = isCurrent,
    onClick = {
        if (isCurrent) {
            when (val handler = currentTabClickDispatcher.currentHandler()) {
                null -> {
                    // no action is specified - I pop to initial tab screen here
                }
                else -> handler.action()
            }
        } else {
            // change tab
        }
    },
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • thank you so much, could you please give an advise what to read to learn how to create such things like dispatchers – Foenix Mar 02 '23 at 11:56
  • 1
    @Foenix you're welcome. I don't read, I lear from source codes. This one is inspired by `BackHandler` – Phil Dukhov Mar 03 '23 at 00:06