Skip to content

Latest commit

 

History

History
263 lines (199 loc) · 9.14 KB

README.md

File metadata and controls

263 lines (199 loc) · 9.14 KB

solivagant 🔆

Compose Multiplatform Navigation - Pragmatic, type safety navigation for Compose Multiplatform. Based on Freeletics Navigation.

  • Integrate with Jetpack Compose and Compose Multiplatform seamlessly.

  • Integrate with kmp-viewmodel library seamlessly

    • Stack entry scoped ViewModel, exists as long as the stack entry is on the navigation stack, including the configuration changes on Android.
    • Supports SavedStateHandle, used to save and restore data over configuration changes or process death on Android.
  • Type safety navigation, easy to pass data between destinations.

  • Supports Multi-Backstacks, this is most commonly used in apps that use bottom navigation to separate the back stack of each tab. See Freeletics Navigation - Multiple back stacks.

  • Supports Lifecycle events, similar to AndroidX Lifecycle library.

Note: This library is still in alpha, so the API may change in the future.

Credits

maven-central codecov GitHub license Kotlin version KotlinX Coroutines version Compose Multiplatform version Hits

badge badge badge badge badge badge badge badge badge badge badge badge

Liked some of my work? Buy me a coffee (or more likely a beer)

Buy Me A Coffee

Docs

Installation

allprojects {
  repositories {
    [...]
    mavenCentral()
  }
}
implementation("io.github.hoc081098:solivagant-navigation:0.0.1-alpha01")

Getting started

  • The concept is similar to Freeletics Navigation library, so you can read the Freeletics Navigation to understand the concept.

1. Create NavRoots, NavRoutes

@Immutable
@Parcelize
data object StartScreenRoute : NavRoute, NavRoot

@Immutable
@Parcelize
data object SearchProductScreenRoute : NavRoute

2. Create NavDestinations along with Composables and ViewModels

@JvmField
val StartScreenDestination: NavDestination =
  ScreenDestination<StartScreenRoute> { StartScreen() }

@Composable
internal fun StartScreen(
  modifier: Modifier = Modifier,
  viewModel: StartViewModel = koinKmpViewModel(),
) {
  // UI Composable
}

class StartViewModel(
  // used to trigger navigation actions from outside the view layer (e.g. from a ViewModel).
  // Usually, it is singleton object, or the host Activity retained scope.
  private val navigator: NavEventNavigator,
) : ViewModel() {
  internal fun navigateToProductsScreen() = navigator.navigateTo(ProductsScreenRoute)
  internal fun navigateToSearchProductScreen() = navigator.navigateTo(SearchProductScreenRoute)
}
@JvmField
val SearchProductScreenDestination: NavDestination =
  ScreenDestination<SearchProductScreenRoute> { SearchProductsScreen() }

@Composable
fun SearchProductsScreen(
  modifier: Modifier = Modifier,
  viewModel: SearchProductsViewModel = koinKmpViewModel<SearchProductsViewModel>(),
) {
  // UI Composable
}

class SearchProductsViewModel(
  private val searchProducts: SearchProducts,
  private val savedStateHandle: SavedStateHandle,
  // used to trigger navigation actions from outside the view layer (e.g. from a ViewModel).
  // Usually, it is singleton object, or the host Activity retained scope.
  private val navigator: NavEventNavigator,
) : ViewModel() {
  fun navigateToProductDetail(id: Int) {
    navigator.navigateTo(ProductDetailScreenRoute(id))
  }
}

3. Setup

@Stable
private val AllDestinations: ImmutableSet<NavDestination> = persistentSetOf(
  StartScreenDestination,
  SearchProductScreenDestination,
  // and more ...
)

@Composable
fun MyAwesomeApp(
  // used to trigger navigation actions from outside the view layer (e.g. from a ViewModel).
  // Usually, it is singleton object, or the host Activity retained scope.
  navigator: NavEventNavigator,
  modifier: Modifier = Modifier,
) {
  var currentRoute: BaseRoute? by remember { mutableStateOf(null) }

  NavHost(
    modifier = modifier,
    // route to the screen that should be shown initially
    startRoute = StartScreenRoute,
    // should contain all destinations that can be navigated to
    destinations = AllDestinations,
    navEventNavigator = navigator,
    destinationChangedCallback = { currentRoute = it },
  )
}
class MainActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle) {
    super.onCreate()

    // navigator can be retrieved from the DI container, such as Koin, Dagger Hilt, etc.

    setContent {
      MyAwesomeApp(
        navigator = navigator
      )
    }
  }
}

4. Use NavEventNavigator in ViewModels

// navigate to the destination that the given route leads to
navigator.navigateTo(DetailScreenRoute("some-id"))
// navigate up in the hierarchy
navigator.navigateUp()
// navigate to the previous destination in the backstack
navigator.navigateBack()
// navigate back to the destination belonging to the referenced route and remove all destinations
// in between from the back stack, depending on inclusive the destination
navigator.navigateBackTo<MainScreenRoute>(inclusive = false)

Samples

  • Samples sample: a complete sample using Compose Multiplatform (Android, Desktop, iOS)
    • solivagant-navigation for navigation in Compose Multiplatform.
    • kmp-viewmodel to share ViewModel and SavedStateHandle.
    • Koin DI.

Roadmap

  • Add more tests
  • Support transition when navigating

License

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/