A ksp library to automatically generate navigation functions for jetpack compose.

Overview

Compose/Navigation/Generator

⚠️ This library is still under development and not considered stable!

Content

Introduction

Compose/Navigation/Generator | C/N/G is a KSP library which automatically generates navigation support for Jetpack Compose for Android. Unlike the old XML-based navigation graphs, the new Navigation for Compose does not automatically generate naviagtion functions for the destination within and between graphs. Generally, navigation for Jetpack Compose is controlled only by code, which currently has to be written from scratch by the developer for each project.

C/N/G uses Annotations and Kotlin Symbol Processing (KSP) to generate as SetupNavHost function, as well as extension functions to the NavHostController, which trigger the navigation to a declared destination.

Usage

  • All composable functions annotated with @Destination will be reachable via navigation
  • The Home-Destination, reuired by compose-navigation, can be annotated with @Home
  • Within the setup of the composable scope (usually within the ComposeActivity), call the generated SetupNavHost function
  • Use the generated extension functions navHostController.navigateTo<<Destination>>(...) to navigate to the Destinations
// Annotate a destination you want to be able to navigate to with @Destination
@Destination
@Home
@Composable
fun HomeScreen() {
    Column(modifier = Modifier.fillMaxSize()) {
        Text("Hello World")
        /*...*/
    }
}
/***** GENERATED *****/
// A setup function for compose-navigaiton is generated, using your destinations
@Composable
public fun SetupNavHost(navController: NavHostController): Unit {
  NavHost(navController = navController, startDestination = "HomeScreen")
  {
    composable("HomeScreen") {
      Log.d(TAG, "Navigating to HomeScreen") // If enabled, a logging call is inserted
      HomeScreen()
    }
  }
}

/***** GENERATED *****/
// A navigation function (extending NavHostController) is generated
public fun NavHostController.navigateToHomeScreen(): Unit {
  navigate("HomeScreen")
}
// The destination can be navigated to from every composable function, using the NavHostController
@Composable
fun SomeComposableFunction(navController: NavHostController) {
    /*...*/
    navController.navigateToHomeScreen()
}

Example: Single destination without parameters

A single destination without parameters. The function is annotated with @Destination which all navigable compose functions must be. Also, compose expects one destination (per nav-graph), to be the home destination which is the default when the nav-graph is initialized. The @Home destination must be used on a single @Destination function to inform C/N/G to treat it as the home-destination.

Source
@Destination
@Home
@Composable
fun HomeScreen() {
    Column(modifier = Modifier.fillMaxSize()) {
        Text("Hello World")
        /*...*/
    }
}

The SetupNavHost function itself is a composable function and should be called during initialization of the composeable scope. Usualy this is done during setup of the parent compose activity. The function takes a NavHostController as parameter, which can also be passed on to composables, if required.

The navHostController.navigateTo<<Destination>>() function is created for every destination and extends the existing NavHostController class. It can therefore be used from everywhere, a NavHostController is available.

Generated
// Generated
@Composable
public fun SetupNavHost(navController: NavHostController): Unit {
  NavHost(navController = navController, startDestination = "HomeScreen")
  {
    composable("HomeScreen") {
      Log.d(TAG, "Navigating to HomeScreen") // If enabled, a logging call is inserted
      HomeScreen()
    }
  }
}

private const val TAG: String = "NavHost" // If enabled, a logging tag is inserted
// Generated
public fun NavHostController.navigateToHomeScreen(): Unit {
  navigate("HomeScreen")
}
Usage
@Composable
fun SomeComposableFunction(navController: NavHostController) {
    /*...*/
    navController.navigateToHomeScreen()
}

Example: Multiple destinations without parameters

Source
@Destination
@Home
@Composable
fun HomeScreen() { 
  /*...*/
}

@Destination
@Composable
fun DetailScreen() {
  /*...*/

}
Generated
// Generated
@Composable
public fun SetupNavHost(navController: NavHostController): Unit {
  NavHost(navController = navController, startDestination = "HomeScreen")
  {
    composable("HomeScreen") {
      Log.d(TAG, "Navigating to HomeScreen")
      HomeScreen()
    }
    composable("DetailScreen") {
      Log.d(TAG, "Navigating to DetailScreen")
      DetailScreen()
    }
  }
}

private const val TAG: String = "NavHost"
// Generated
public fun NavHostController.navigateToHomeScreen(): Unit {
  navigate("HomeScreen")
}
Usage
@Composable
fun SomeComposableFunction(navController: NavHostController) {
    navController.navigateToHomeScreen()
    navController.navigateToDetailScreen()
}

Example: Multiple destinations with parameters

C/N/G supports nullable- and non-nullable parameters as navigation arguments.

If a destination only useses non-nullable parameters, a "non-nullable" navigation path will be generated:

val navigationPath = "Destination/{arg1}/{arg2}/{arg3}" // ...

If one or more of the arguments are nullable, a "nullable" navigation path will be generated:

val navigationPath = "Destination?arg1&arg2&arg3" // ...
Source
@Destination
@Home
@Composable
fun HomeScreen() { 
  /*...*/
}

@Destination
@Composable
fun DetailScreen(name: String, age: Int) {
  /*...*/
}

@Destination
@Composable
fun UltraDetailScreen(name: String, age: Int, height: Double? = 1.90) {
  /*...*/
}
Generated
// Generated
@Composable
public fun SetupNavHost(navController: NavHostController): Unit {
  NavHost(navController = navController, startDestination = "HomeScreen")
  {
    // Simple, no-argument destination
    composable("HomeScreen") {
      Log.d(TAG, "Navigating to HomeScreen")
      HomeScreen()
    }
    // Destination with exclusivly non-nullable arguments
    composable("DetailScreen/argName/argAge", arguments = listOf(
      // Type and properties of navArgs is automatically determined
      navArgument("argName"){
        nullable = false 
        type = NavType.fromArgType("String")
      },
      navArgument("argAge"){
        nullable = false 
        type = NavType.fromArgType("Int")   
      },
    )) { backStackEntry ->
      // Read arguments from backstack
      val argName = backStackEntry.arguments?.getString("argName")  
      val argAge = backStackEntry.arguments?.getInt("argAge")       

      // Non-null is required for such parameters
      requireNotNull(argName)   
      requireNotNull(argAge)    

      Log.d(TAG, "Navigating to DetailScreen")

      // Destination is called with provided parameters
      DetailScreen(name=argName, age=argAge) 
    }
    // Destination with nullable and non-nullable arguments
    composable("UltraDetailScreen?argName={name}&argAge={age}&argHeight={height}", arguments = listOf(
      // Type and properties of navArgs is automatically determined
      navArgument("argName"){
        nullable = false
        type = NavType.fromArgType("String")
      },
      navArgument("argAge"){
        nullable = false
        type = NavType.fromArgType("Int")
      },
      navArgument("argHeight"){
        nullable = true
        type = NavType.fromArgType("Double")
      },
    )) { backStackEntry ->
        // Read arguments from backstack
      val argName = backStackEntry.arguments?.getString("argName")
      val argAge = backStackEntry.arguments?.getInt("argAge")
      val argHeight = backStackEntry.arguments?.getDouble("argHeight")

      // Non-null is required for such parameters
      requireNotNull(argName) 
      requireNotNull(argAge)  

      Log.d(TAG, "Navigating to UltraDetailScreen")

      // Destination is called with provided parameters
      UltraDetailScreen(name=argName, age=argAge, height=argHeight)
    }
  }
}

private const val TAG: String = "NavHost"
// Generated
public fun NavHostController.navigateToHomeScreen(): Unit {
  navigate("HomeScreen")
}

// Generated
public fun NavHostController.navigateToDetailScreen(name: String, age: Int): Unit {
  navigate("DetailScreen/$name/$age")
}

// Generated
public fun NavHostController.navigateToUltraDetailScreen(name: String,  age: Int,  height: Double?): Unit {
  navigate("UltraDetailScreen?arg_name=$name&arg_age=$age&arg_height=$height")
}
Usage
@Composable
fun SomeComposableFunction(navController: NavHostController) {
    navController.navigateToHomeScreen()
    navController.navigateToDetailScreen(name = "Steffen", age = 27)
    navController.navigateToUltraDetailScreen(name = "Steffen", age = 27, height = null)
}

Features

  • Generates the SetupNavHost function
  • Generates navigation functions for each destination
  • Parses and wraps all parameters in the appropriate navigation paths
  • more coming soon!

Setup

TBD...

Bugs

Lots...

Used Libraries

You might also like...
Kotlin Symbol Processing (KSP) sample project

Kotlin Symbol Processing (KSP) Sample Project Sample annotation processor created with Kotlin Symbol Processing (KSP) API. The repository supplements

Mocking for Kotlin/Native and Kotlin Multiplatform using the Kotlin Symbol Processing API (KSP)

Mockative Mocking for Kotlin/Native and Kotlin Multiplatform using the Kotlin Symbol Processing API (KSP). Installation Mockative uses KSP to generate

glide's ksp compiler ,use kotlin symbol processor
glide's ksp compiler ,use kotlin symbol processor

glide-ksp glide's ksp compiler ,use kotlin symbol processor requirements library version kotlin = 1.6.10 ksp 1.6.10-1.0.2 usage add jitpack repositor

Automatically filled the declared non-null field is missing or null with default value.

[TOC] Moshi-kotlin-nullsafe 中文版 1. moshi-kotlin moshi-kotlin support kotlin type safe check. When parsing json, fields declared as non-null types may

Automatically generates UI demos which allow users to call any function with any parameters
Automatically generates UI demos which allow users to call any function with any parameters

Automatically generates UI demos which allow users to call any function (including composable ones) with any parameters. Useful for building demo screens in playground apps of various design systems.

Attend HoYoLAB Check-in events automatically

Croissant Attend HoYoLAB Check-in events automatically https://play.google.com/store/apps/details?id=com.joeloewi.croissant Stacks Room Database Hilt

Checks for app updates and automatically updates the current app if the new one in local storage have a different version

Silent Android App Update Sample This sample shows how to update Android app silently without user confirmation with a device owner app. It works on A

A project aiming to generate KMP declarations from several library versions

kotlin-ketchup A project aiming to generate KMP declarations from several library versions LICENSE Apache 2.0. See ./LICENSE in this repository See th

A Kotlin library used to analyse discrete Markov chains, in order to generate plausible sequences

Markov Markov is a Kotlin library used to analyse discrete Markov chains, in order to generate plausible sequences. Using This project is still under

Comments
  • CI/CD

    CI/CD

    • [x] Run Tests on Processor
    • [x] Use correct package information
    • [ ] Use SemVer?
    • [x] Bundle Processor to lib
    • [x] Deploy Processor to GHPR
    • [x] Improve gradle caching within ci
    opened by SteffenEckardt 0
Owner
Steffen Eckardt
Steffen Eckardt
An annotation processor library that automatically creates Hilt's `@Binds` functions and modules.

HiltBinder An annotation processor library that automatically creates Hilt's @Binds functions and modules. If you think this library is useful, please

SangMin Park 5 Sep 19, 2022
Kotlin compiler plugin for converting suspend functions to platform-compatible functions

Kotlin suspend transform compiler plugin Summary Kotlin compiler plugin for generating platform-compatible functions for suspend functions. JVM class

ForteScarlet 5 Oct 12, 2022
Ksp-di-library - Small library for DI in KMM apps

DI-KSP Small library for DI in KMM apps. Uses KSP for processing DI annotations:

Anna Zharkova 3 Feb 6, 2022
KSP-based library that generates lists from your annotation usages

ListGen, Generate Lists From Functions That Have @Listed Annotations! Welcome to ListGen! ListGen is a KSP-based library that can generate lists (and

Adib Faramarzi 24 Dec 6, 2022
BindsAdapter is an Android library to help you create and maintain Adapter class easier via ksp( Kotlin Symbol Processing).

BindsAdapter BindsAdapter is an Android library to help you create and maintain Adapter class easier via ksp( Kotlin Symbol Processing). Installation

Jintin 5 Jul 30, 2022
android webview loader using ksp

KSPWebViewLoader ?? @WebViewBuilder Annotation can be automating your webview settings. (WIP) How to use @WebViewBuilder( url = "https://www.googl

sehee Jeong 8 Apr 8, 2022
Exploring Kotlin Symbol Processing - KSP. This is just an experiment.

KSP example Exploring Kotlin Symbol Processing - KSP. This is just an experiment. Project contains 2 modules Processing Example Processing module is t

Merab Tato Kutalia 12 Aug 23, 2022
KSP annotation processor for Toothpick

toothpick-ksp KSP annotation processor for Toothpick. All credits go to olivierperez/ksp for the initial work on a KSP processor. Bear in mind this is

null 0 Oct 19, 2021
A collection of code generators powered by ksp.

AutoKsp A collection of code generators powered by ksp. status: working in progress Projects AutoGradlePlugin - Generate gradle plugin properties file

shenghaiyang 0 Nov 8, 2021
KSP extension for the kotlin-maven-plugin

kotlin-maven-symbol-processing Extension for the kotlin-maven-plugin to support Kotlin Symbol Processing (KSP). Usage To use this extension, add the d

Dyescape 19 Dec 18, 2022