State restoration
The Screen interface is Serializable. Every param of your screens will be saved and restored automatically. Because of that, it's important to know what can be passed as param: anything that can be stored inside a Bundle.
// ✔️ DO
@Parcelize
data class Post(/*...*/) : Parcelable
data class ValidScreen(
val userId: UUID, // Built-in serializable types
val post: Post // Your own parcelable and serializable types
) : Screen {
// ...
}
// 🚫 DON'T
class Post(/*...*/)
data class InvalidScreen(
val context: Context, // Built-in non-serializable types
val post: Post // Your own non-parcelable and non-serializable types
) : Screen {
// ...
}
Not only the params, but the properties will also be restored, so the same rule applies.
// ✔️ DO
class ValidScreen : Screen {
// Serializable properties
val tag = "ValidScreen"
// Lazily initialized serializable types
val randomId by lazy { UUID.randomUUID() }
// Lambdas
val callback = { /*...*/ }
}
// 🚫 DON'T
class InvalidScreen : Screen {
// Non-serializable properties
val postService = PostService()
}
If you want to inject dependencies through a DI framework, make sure it supports Compose, like Koin and Kodein.
// ✔️ DO
class ValidScreen : Screen {
@Composable
override fun Content() {
// Inject your dependencies inside composables
val postService = get<PostService>()
}
}
// 🚫 DON'T
class InvalidScreen : Screen {
// Using DI to inject non-serializable types as properties
val postService by inject<PostService>()
}

Identifying screens

The Screen interface has a key property used for saving and restoring the states for the subtree. You can override the default value to set your own key.
class HomeScreen : Screen {
override val key = "CUSTOM_KEY"
@Composable
override fun Content() {
// ...
}
}
Voyager provides a uniqueScreenKey property, useful if you don't want to manage the keys yourself.
override val key = uniqueScreenKey
You should always set your own key if the screen:
  • Is used multiple times in the same Navigator
  • Is an anonymous or local class
Copy link