Skip to content

Instantly share code, notes, and snippets.

@hristogochev
Created April 29, 2024 00:43
Show Gist options
  • Save hristogochev/dff5d9d5c782726f0ea7260f22337d0d to your computer and use it in GitHub Desktop.
Save hristogochev/dff5d9d5c782726f0ea7260f22337d0d to your computer and use it in GitHub Desktop.
Fix for Voyager TabNavigator not disposing of nested navigators
object FirstTab : Tab {
override var navigator: Navigator? = null
override val options: TabOptions
@Composable
get() = TODO("Not yet implemented")
@Composable
override fun Content() {
Navigator(FirstTabFirstScreen()) {
LaunchedEffect(it) {
navigator = it
}
FadeTransition(it)
}
}
}
class FirstTabFirstScreen : Screen {
@Composable
override fun Content(
) {
// Content
}
}
class SampleTabsScreen : Screen {
private val tabs = listOf<Tab>()
@Composable
override fun Content() {
TabNavigator(
tab = tabs.first(),
tabDisposable = { navigator ->
TabDisposable(navigator, tabs)
},
) {
CurrentTab()
}
}
}
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.Navigator
@Composable
public fun CurrentTab() {
val tabNavigator = LocalTabNavigator.current
val currentTab = tabNavigator.current
tabNavigator.saveableState("currentTab") {
currentTab.Content()
}
}
public data class TabOptions(
val index: UShort,
val title: String,
val icon: Painter? = null
)
public interface Tab : Screen {
public var navigator: Navigator?
public val options: TabOptions
@Composable get
}
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
import cafe.adriel.voyager.core.lifecycle.DisposableEffectIgnoringConfiguration
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior
import cafe.adriel.voyager.navigator.compositionUniqueId
public typealias TabNavigatorContent = @Composable (tabNavigator: TabNavigator) -> Unit
public val LocalTabNavigator: ProvidableCompositionLocal<TabNavigator> =
staticCompositionLocalOf { error("TabNavigator not initialized") }
@OptIn(InternalVoyagerApi::class)
@Composable
public fun TabNavigator(
tab: Tab,
disposeNestedNavigators: Boolean = false,
tabDisposable: (@Composable (TabNavigator) -> Unit)? = null,
key: String = compositionUniqueId(),
content: TabNavigatorContent = { CurrentTab() }
) {
Navigator(
screen = tab,
disposeBehavior = NavigatorDisposeBehavior(
disposeNestedNavigators = disposeNestedNavigators,
disposeSteps = false
),
onBackPressed = null,
key = key
) { navigator ->
val tabNavigator = remember(navigator) {
TabNavigator(navigator)
}
tabDisposable?.invoke(tabNavigator)
CompositionLocalProvider(LocalTabNavigator provides tabNavigator) {
content(tabNavigator)
}
}
}
@OptIn(InternalVoyagerApi::class)
@Composable
public fun TabDisposable(navigator: TabNavigator, tabs: List<Tab>) {
DisposableEffectIgnoringConfiguration(Unit) {
onDispose {
tabs.forEach { tab ->
navigator.navigator.dispose(tab)
tab.navigator?.let { tabNavigator ->
tabNavigator.items.forEach {
tabNavigator.dispose(it)
}
}
}
}
}
}
public class TabNavigator internal constructor(
internal val navigator: Navigator
) {
public var current: Tab
get() = navigator.lastItem as Tab
set(tab) = navigator.replaceAll(tab)
@Composable
public fun saveableState(
key: String,
tab: Tab = current,
content: @Composable () -> Unit
) {
navigator.saveableState(key, tab, content = content)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment