Voyager : Compose Multiplatform Navigation and ViewModels (ScreenModel)
Designed for Jetpack Compose, Voyager simplifies navigation and viewModels (screenModel) management across platforms. In this article, we’ll explore Voyager’s core principles, features, and how it reshapes multiplatform UI development.
Setup
Voyager provides multiple dependencies for your desire, each one with a feature :
dependencies {
val voyagerVersion = "1.0.0"
// Multiplatform
// Navigator
implementation("cafe.adriel.voyager:voyager-navigator:$voyagerVersion")
// Screen Model
implementation("cafe.adriel.voyager:voyager-screenmodel:$voyagerVersion")
// BottomSheetNavigator
implementation("cafe.adriel.voyager:voyager-bottom-sheet-navigator:$voyagerVersion")
// TabNavigator
implementation("cafe.adriel.voyager:voyager-tab-navigator:$voyagerVersion")
// Transitions
implementation("cafe.adriel.voyager:voyager-transitions:$voyagerVersion")
// Android
// Koin integration
implementation("cafe.adriel.voyager:voyager-koin:$voyagerVersion")
// Hilt integration
implementation("cafe.adriel.voyager:voyager-hilt:$voyagerVersion")
// LiveData integration
implementation("cafe.adriel.voyager:voyager-livedata:$voyagerVersion")
// Desktop + Android
// Kodein integration
implementation("cafe.adriel.voyager:voyager-kodein:$voyagerVersion")
// RxJava integration
implementation("cafe.adriel.voyager:voyager-rxjava:$voyagerVersion")
}
For this tutorial we are going to use the Navigator
and ScreenModel
, so we can start adding these implementations to our version catalog file :
[versions]
voyager = "1.0.0"
[libraries]
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
voyager-screenModel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" }
Now lets import these new libraries on our compose build.gradle
file:
commonMain.dependencies {
implementation(libs.voyager)
implementation(libs.voyager.screenModel)
}
Understanding ScreenModel and ViewModel
ScreenModel
is just like a ViewModel
: designed to store and manage UI-related data in a lifecycle conscious way. It also allows data to survive configuration changes such as screen rotations. (Voyager docs)
With that in mind lets create a simple ScreenModel to fetch some data :
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
class DataScreenModel() : ScreenModel {
fun fetchData(){
screenModelScope.launch {
//fetch data code
}
}
}
Note that we are using screenModelScope
instead of viewModelScope
, but they behave similarly.
That’s it, this is a simple implementation of a screenModel
Navigating screens
First let’s wrap our main composable app on a Navigator
, and on the constructor pass the first screen that we want to display, in this case will be DataScreen
import cafe.adriel.voyager.navigator.Navigator
@Composable
fun App() {
Navigator(DataScreen())
}
Now, lets create our DataScreen
by implementing Screen
interface and overriding the content
function (this will be displayed on your screen), also on this screen we want to navigate to another screen, so in order to do this, we want to import the LocalNavigator
and call the push
function in order to push a new screen passing the required parameters
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
class DataScreen : Screen {
@Composable
override fun Content {
val navigator = LocalNavigator.currentOrThrow
// on a button click or event call this :
navigator.push(DataDetailsScreen(data))
}
}
In order to get the parameters, our second screen will be a data class
, this will be the result :
import cafe.adriel.voyager.core.screen.Screen
data class DataDetailScreen(data : Data) : Screen {
@Composable
override fun Content {
// display data on composable
// on a button click or event call this :
navigator.pop()
}
}
If you want to go back to the previous screen use the navigator.pop()
function
Transitions
If you want to add transitions to this navigator, first we need to add the transitions dependency
[versions]
voyager = "1.0.0"
[libraries]
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
commonMain.dependencies {
implementation(libs.voyager.transitions)
}
Now let’s apply the transition on our Navigator component on the App Composable :
@Composable
fun App() {
Navigator(DataScreen()) { navigator ->
SlideTransition(navigator)
}
}
In this case we are using the SlideTransition
, to see the available transitions check the documentation
Thats it! This is a simple implementation of Voyager, keep in mind that there are multiple implementations and support for koin
, hilt
, codein
, etc, this is a very well documented library so visit the documentation for further details