{"id":13605623,"url":"https://github.com/FunkyMuse/foSho","last_synced_at":"2025-04-12T05:34:00.406Z","repository":{"id":208274352,"uuid":"713433647","full_name":"FunkyMuse/foSho","owner":"FunkyMuse","description":"Type-safe navigation library for Jetpack Compose on Android","archived":true,"fork":false,"pushed_at":"2024-09-09T21:25:53.000Z","size":1163,"stargazers_count":24,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-09-10T02:11:22.305Z","etag":null,"topics":["android-library","compose","compose-navigation","navigation-android","navigation-component"],"latest_commit_sha":null,"homepage":"http://funkymuse.dev/foSho/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FunkyMuse.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-02T14:10:10.000Z","updated_at":"2024-09-09T21:26:07.000Z","dependencies_parsed_at":"2023-11-22T23:23:42.766Z","dependency_job_id":"4975c8c6-81be-49ef-bdfa-624923ae7cfd","html_url":"https://github.com/FunkyMuse/foSho","commit_stats":null,"previous_names":["funkymuse/fosho"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FunkyMuse%2FfoSho","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FunkyMuse%2FfoSho/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FunkyMuse%2FfoSho/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FunkyMuse%2FfoSho/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FunkyMuse","download_url":"https://codeload.github.com/FunkyMuse/foSho/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223497967,"owners_count":17155229,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android-library","compose","compose-navigation","navigation-android","navigation-component"],"created_at":"2024-08-01T19:01:00.887Z","updated_at":"2024-11-07T10:30:58.623Z","avatar_url":"https://github.com/FunkyMuse.png","language":"Kotlin","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"readme":"## Successfully ran in two projects with over +1M installs, archived in favor of https://developer.android.com/guide/navigation/design/type-safety\n\u003cp align=\"center\"\u003e \n   \u003cimg src=\"https://raw.githubusercontent.com/FunkyMuse/foSho/main/foSho.png\"/\u003e \n\u003c/p\u003e\n\n\n[![](https://jitpack.io/v/FunkyMuse/foSho.svg)](https://jitpack.io/#FunkyMuse/foSho)\n\n### \"Opinionated\" Android type safe navigation for Compose's Navigation Component\n\nA KSP library that helps you generate type safe code with minimal effort and provides out of the box solutions to bundle everything together and scale your app in easy manner.\n\n### Main features\n- Typesafe navigation arguments (special thanks for parsing the arguments to [compose destinations](https://github.com/raamcosta/compose-destinations))\n- Simple setup with minimal code\n- ViewModel type safe arguments with injection support when using Hilt\n- Screen \"level\" type safe arguments\n- Callback arguments\n- Bottom sheet support through [Accompanist Material](https://github.com/google/accompanist/tree/main/navigation-material)\n- Nested navigation support out of the box\n- Transitions enter/exit to and from screens\n- Deep links\n- Graph aggregator factory\n- Custom navigator to open screens easily\n- Global navigation\n- Multi (out of the box) and single module support\n\nEverything you can do with the Official Jetpack Compose Navigation but in a type safe way.\n\n### Setup\nThe library is available through [JitPack](https://jitpack.io/).\n\n1. Add JitPack to your project's settings.gradle\n```kotlin\ndependencyResolutionManagement {\n    ...\n    repositories {\n        ...\n        maven { setUrl(\"https://jitpack.io\") }\n    }\n}\n```\n\n2. Add the dependency in the build.gradle\n```toml\n[versions]\nfoSho = \u003cversion\u003e\nksp = \u003cversion\u003e\n\n[libraries]\nfoSho-android = { module = \"com.github.FunkyMuse.foSho:navigator-android\", version.ref = \"foSho\" }\nfoSho-codegen = { module = \"com.github.FunkyMuse.foSho:navigator-codegen\", version.ref = \"foSho\" }\n\n[plugins]\nksp = { id = \"com.google.devtools.ksp\", version.ref = \"ksp\" }\n```\n\nInside the project build.gradle make sure to add the [KSP](https://github.com/google/ksp) plugin\n```kotlin\nplugins {\n    ...\n    alias(libs.plugins.ksp).apply(false)\n}\n```\nInside the `:app` module\n```kotlin\nplugins {\n    ....\n    alias(libs.plugins.ksp)\n}\n```\n\n```kotlin\ndependencies {\n    implementation(libs.foSho.android)\n    ksp(libs.foSho.codegen)\n}\n```\n\nIf you intend to use `Hilt/Dagger/Anvil` with `foSho` and want your generated ViewModel arguments injectable,\ninside `:app` module set `foSho.injectViewModelArguments` to \"true\", default is \"false\"\n```kotlin\nksp {\n    arg(\"foSho.injectViewModelArguments\", \"true\")\n}\n```\n\n\u003cdetails open\u003e\n  \u003csummary\u003eSingle module\u003c/summary\u003e\n\nInside the `:app` module make sure to add the KSP argument, the library is multi module by default.\n\n```kotlin\nksp {\n    arg(\"foSho.singleModule\", \"true\")\n}\n```\n\n\u003c/details\u003e\n\n3. Make magic happen\n\nThere are three things you need to write code for and it's pretty natural\n1. Graph\n2. Destination (Argument, Callback Argument)\n3. Content\n\nAll you need to do is click build or just run `./gradlew kspDebugKotlin` as a faster way to get the generated code.\n\n- `Graph`: Starting point is a `@Graph`, each `Graph` has a `startingDestination` and other `destinations`, also a `Graph` can be a `rootGraph` (only one throughout your app).\n- `Destination`: every destination that's part of `startingDestination` and `destinations` is marked with `@Destination` and you implement one of the three UI types `Screen`, `Dialog` for which you can control the [properties](https://developer.android.com/reference/kotlin/androidx/compose/ui/window/DialogProperties#DialogProperties(kotlin.Boolean,kotlin.Boolean,androidx.compose.ui.window.SecureFlagPolicy,kotlin.Boolean,kotlin.Boolean)) and `BottomSheet` for each one you can additionally control whether to generate view model arguments or nav stack entry arguments, `val generateViewModelArguments: Boolean = false`,\n  `val generateScreenEntryArguments: Boolean = false`, you can annotate a destination with `@Argument` and `@CallbackArgument` in order to control to/from arguments.\n- `Content`: everytime you click build a `Destination` implementation is generated for you, it's your responsibility to implement it and annotate that object with a `@Content`\nIn code this will look like this\n\n```kotlin\n@Graph(\n    startingDestination = Home::class,\n    destinations = [HomeDetails::class],\n    rootGraph = true\n)\ninternal object HomeGraph\n\n@Destination\n@Argument(\n    name = \"hideBottomNav\",\n    argumentType = ArgumentType.BOOLEAN,\n    defaultValue = DefaultBooleanValueFalse::class\n)\ninternal object Home : Screen\n\n@Destination(generateViewModelArguments = true, generateScreenEntryArguments = false)\n@Argument(name = \"cardId\", argumentType = ArgumentType.INT)\n@CallbackArgument(name = \"clickedShare\", argumentType = ArgumentType.BOOLEAN)\ninternal object HomeDetails : Screen {\n    override val deepLinksList: List\u003cDeepLinkContract\u003e\n        get() = listOf(\n            DeepLinkContract(\n                action = Intent.ACTION_VIEW,\n                uriPattern = \"custom:url/{cardId}\"\n            )\n        )\n}\n\n@Content\ninternal object HomeContent : HomeDestination {\n  @Composable\n  override fun AnimatedContentScope.Content() {\n    val argumentsFromHomeDetailsDestination = HomeDetailsCallbackArguments.rememberHomeDetailsCallbackArguments()\n    argumentsFromHomeDetailsDestination.OnSingleBooleanCallbackArgument(\n      key = HomeDetailsCallbackArguments.CLICKED_SHARE,\n      onValue = {\n        if (it == true){\n          //user has clicked share\n        }\n      }\n    )\n    HomeScreen(onClick = {\n      Navigator.navigateSingleTop(HomeDetailsDestination.openHomeDetails(cardId = 42))\n    })\n  }\n}\n\n@Content\ninternal object HomeDetailsContent : HomeDetailsDestination {\n  @Composable\n  override fun AnimatedContentScope.Content() {\n\n  }\n}\n```\n\nA `GraphFactory` is generated for you which you can use with an extension function `addGraphs` to have ease of use like \n```kotlin\naddGraphs(\n  navigationGraphs = GraphFactory.graphs\n)\n```\nyou can checkout [this line](https://github.com/FunkyMuse/foSho/blob/365221e73e7aacdefd48b9fe84e9db0bec6c57c6/app/src/main/java/dev/funkymuse/fosho/sample/MainActivity.kt#L123).\n\nA `Navigator` is there for you to collect the navigation events and also to send navigation events, for a single module setup, you can check out the [sample](https://github.com/FunkyMuse/foSho/tree/main/app).\n\n\u003cdetails open\u003e\n  \u003csummary\u003eMulti module\u003c/summary\u003e\n\nYou would need to create one umbrella module, usually named \"navigator\" or however you see fit where you would write the code\nfor the Graphs\n\n```kotlin\n//:navigator module\n//UserAccountGraph.kt\n@Graph(\n  startingDestination = UserAccountDetails::class,\n  destinations = [\n    EditAccountDetails::class,\n    DeleteAccount::class,\n    ChangePassword::class,\n  ]\n)\ninternal object UserAccountGraph\n\n@Destination\ninternal object UserAccountDetails : Screen\n\n@Destination\ninternal object ChangePassword : BottomSheet\n\n@Destination\ninternal object DeleteAccount : Dialog\n\n@Destination(generateScreenEntryArguments = true)\n@Argument(name = \"email\", argumentType = ArgumentType.STRING)\n@CallbackArgument(name = \"isAccountUpdated\", argumentType = ArgumentType.BOOLEAN)\ninternal object EditAccountDetails : Screen\n\n//HomeGraph.kt\n@Graph(startingDestination = Home::class, rootGraph = true)\ninternal object HomeGraph\n\n@Destination\n@Argument(name = \"hideBottomNav\", argumentType = ArgumentType.BOOLEAN, defaultValue = DefaultBooleanValueFalse::class)\ninternal object Home : Screen\n```\n\nYour `:navigator` module acts as the only point where you have the navigation codegen code and nothing else, here you can control the `arg` whether to generate injectable view model arguments through \n```kotlin\nksp {\n    arg(\"foSho.injectViewModelArguments\", \"true\")\n}\n```\n\nThen inside your feature module\n\n```kotlin\n// :feature:user_details\n@Content\ninternal object UserAccountDetailsContent : UserAccountDetailsDestination {\n    @Composable\n    override fun AnimatedContentScope.Content() {\n        \n    }\n}\n```\nand also make sure to add the `:feature:user_details` in your `:app` module so that it can be aggregated into the `GraphFactory`.\n\n\u003c/details\u003e\n\n- `Screen` has [AnimatedContentScope](https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedContentScope) as a receiver\n- `Dialog` doesn't have any receiver\n- `BottomSheet` has a [ColumnScope](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/ColumnScope) as a receiver\n\n#### Important for Kotlin \u003c 1.8.0\n\nWhen using Kotlin version older than 1.8.0, you need to make sure the IDE looks at the generated folder.\nSee KSP related [issue](https://github.com/google/ksp/issues/37).\n\nHow to do it depends on the AGP version you are using in this case:\n\n\u003e **Warning**: In both cases, add this inside `android` block and replacing `applicationVariants` with `libraryVariants` if the module is not an application one (i.e, it uses `'com.android.library'` plugin).\n\n\u003cdetails\u003e\u003csummary\u003eSince AGP (Android Gradle Plugin) version 7.4.0\u003c/summary\u003e  \n\n\n* groovy - build.gradle(:module-name)\n\n```gradle\napplicationVariants.all { variant -\u003e\n    variant.addJavaSourceFoldersToModel(\n            new File(buildDir, \"generated/ksp/${variant.name}/kotlin\")\n    )\n}\n```\n\n\n* kotlin - build.gradle.kts(:module-name)\n\n```gradle\napplicationVariants.all {\n    addJavaSourceFoldersToModel(\n        File(buildDir, \"generated/ksp/$name/kotlin\")\n    )\n}\n```\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\u003csummary\u003eFor AGP (Android Gradle Plugin) version older than 7.4.0\u003c/summary\u003e  \n\n* groovy - build.gradle(:module-name)\n\n```gradle\napplicationVariants.all { variant -\u003e\n    kotlin.sourceSets {\n        getByName(variant.name) {\n            kotlin.srcDir(\"build/generated/ksp/${variant.name}/kotlin\")\n        }\n    }\n}\n```\n\n* kotlin - build.gradle.kts(:module-name)\n\n```gradle\napplicationVariants.all {\n    kotlin.sourceSets {\n        getByName(name) {\n            kotlin.srcDir(\"build/generated/ksp/$name/kotlin\")\n        }\n    }\n}\n```\n\n\u003c/details\u003e\n\n\n## Found this repository useful? ❤️\n\nSupport it by joining [stargazers](https://github.com/FunkyMuse/foSho/stargazers) for this repository. 🌠\n\nAnd [follow me](https://github.com/FunkyMuse) or check out my [blog](https://funkymuse.dev/) for my next creations! ⭐\n\n## Contributing\nPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.\n\n## License\n[Apache 2.0](https://choosealicense.com/licenses/apache-2.0/)\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFunkyMuse%2FfoSho","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FFunkyMuse%2FfoSho","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FFunkyMuse%2FfoSho/lists"}