Skip to content

Instantly share code, notes, and snippets.

@ardakazanci
Created May 21, 2025 16:57
Show Gist options
  • Save ardakazanci/2006cb1176275b8a1ec842638dcc6644 to your computer and use it in GitHub Desktop.
Save ardakazanci/2006cb1176275b8a1ec842638dcc6644 to your computer and use it in GitHub Desktop.
Navigation3 ViewModel Playground Gist with Jetpack Compose
@Serializable
sealed interface ScreenKey : NavKey {
@Serializable object List : ScreenKey
@Serializable data class Detail(val itemId: String) : ScreenKey
}
class DetailViewModel : ViewModel() {
var clickCount by mutableStateOf(0)
fun onClicked() { clickCount++ }
init {
Log.e("LOG", "DetailViewModel init")
}
override fun onCleared() {
super.onCleared()
Log.e("LOG", "DetailViewModel Cleared")
}
}
class BasicActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppNavigator()
}
}
}
@Composable
fun AppNavigator() {
val backStack = rememberNavBackStack(ScreenKey.List)
val navTo: (ScreenKey) -> Unit = { backStack.add(it) }
val goBack: () -> Unit = { if (backStack.size > 1) backStack.removeLastOrNull() }
NavDisplay(
backStack = backStack,
onBack = { steps -> repeat(steps) { goBack() } },
entryDecorators = listOf(
rememberSceneSetupNavEntryDecorator(),
rememberSavedStateNavEntryDecorator(),
rememberViewModelStoreNavEntryDecorator()
),
transitionSpec = {
slideInHorizontally(animationSpec = tween(300)) + fadeIn() togetherWith
slideOutHorizontally(targetOffsetX = { -it }, animationSpec = tween(300)) + fadeOut()
},
popTransitionSpec = {
slideInHorizontally(animationSpec = tween(300)) + fadeIn() togetherWith
slideOutHorizontally(animationSpec = tween(300)) + fadeOut()
},
entryProvider = entryProvider {
entry<ScreenKey.List> {
ListScreen(onItemSelected = navTo)
}
entry<ScreenKey.Detail> { key ->
val vm: DetailViewModel = viewModel()
DetailScreen(
itemId = key.itemId,
vm = vm,
onBack = goBack
)
}
}
)
}
@Composable
fun ListScreen(onItemSelected: (ScreenKey) -> Unit) {
Column(
Modifier
.padding(all = 24.dp)
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text("Item List", style = MaterialTheme.typography.headlineSmall)
listOf("A", "B", "C").forEach { id ->
Button(onClick = { onItemSelected(ScreenKey.Detail(id)) }) {
Text("Go to Detail for $id")
}
}
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun DetailScreen(
itemId: String,
vm: DetailViewModel,
onBack: () -> Unit
) {
val animatedCount by animateIntAsState(
targetValue = vm.clickCount,
animationSpec = tween(durationMillis = 300)
)
Column(
Modifier
.padding(all = 24.dp)
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Detail: $itemId", style = MaterialTheme.typography.headlineSmall)
Text("Clicks: $animatedCount", style = MaterialTheme.typography.bodyLarge)
Button(onClick = { vm.onClicked() }) {
Text("Increment")
}
Button(onClick = onBack) {
Text("Back")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment