Skip to content

Commit a3209ee

Browse files
committed
Add Kotlin Multiplatform support docs
1 parent 6928f51 commit a3209ee

5 files changed

Lines changed: 96 additions & 2 deletions

File tree

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,24 @@ navgraph {
253253
The full guide, including multi module baselines and CI integration, lives in the
254254
**[Nav Baseline documentation](https://skydoves.github.io/compose-nav-graph/gradle-plugin/baseline/)**.
255255

256+
## Kotlin Multiplatform Support
257+
258+
compose-nav-graph works on **Kotlin Multiplatform** out of the box: the annotations live in `commonMain` (published
259+
for `android`, `jvm`, `iosArm64`/`iosSimulatorArm64`/`iosX64`, `js`, and `wasmJs`), and the Gradle plugin detects your
260+
module shape and wires the right KSP pass automatically:
261+
262+
- **KMP + Android**: the graph is extracted from the Android compilation, and every screen still gets a device free
263+
thumbnail. A shared module has no Android resources of its own, so the renderer reuses the consuming Android app's
264+
merged Compose Multiplatform resources automatically. Complex Compose Multiplatform screens that Layoutlib can't
265+
draw fall back to the Robolectric backend (`renderBackend` defaults to `AUTO`).
266+
- **KMP without Android** (iOS/JS/wasm only): extraction runs on the common metadata pass and produces a structure
267+
only graph: nodes, typed arguments, and transitions, without thumbnails.
268+
269+
See it on a real app: [`samples/sample-kotlinconf/`](samples/sample-kotlinconf) applies the plugin to JetBrains'
270+
Compose Multiplatform **KotlinConf app** and renders its complete graph (26 screens, 36 transitions, every thumbnail
271+
included). The full guide lives in the
272+
**[Kotlin Multiplatform documentation](https://skydoves.github.io/compose-nav-graph/kotlin-multiplatform/)**.
273+
256274
## Documentation
257275

258276
The full documentation covers the annotations, the `navgraph { }` DSL, the `.nav` baseline, the exports, and the IDE

docs/kotlin-multiplatform.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Kotlin Multiplatform
2+
3+
compose-nav-graph supports **Kotlin Multiplatform** out of the box. The annotations are a KMP library that lives in
4+
your `commonMain` source set (published for `android`, `jvm`, `iosArm64` / `iosSimulatorArm64` / `iosX64`, `js`, and
5+
`wasmJs`), and the Gradle plugin detects your module's shape and wires the right KSP pass for you. There is no
6+
KMP specific configuration: you apply the same two plugins as on a plain Android module.
7+
8+
```kotlin
9+
// shared/build.gradle.kts
10+
plugins {
11+
id("com.google.devtools.ksp") version "<matching your Kotlin version>"
12+
id("com.github.skydoves.navgraph") version "0.1.0"
13+
}
14+
```
15+
16+
The plugin adds `compose-nav-graph-annotations` to `commonMain` automatically, so `@NavDestination`, `@NavEdge`,
17+
`@NavPreview`, and `@NavGraphRoot` are usable from shared code right away.
18+
19+
## How module detection works
20+
21+
The plugin recognizes three module shapes and picks the extraction pass accordingly:
22+
23+
| Module shape | Extraction pass | Thumbnails |
24+
|---|---|---|
25+
| Plain Android | the debug variant's KSP pass | Yes |
26+
| KMP **with** an Android target | the Android compilation's KSP pass (`kspAndroidMain` on the new `androidLibrary {}` DSL, the legacy per variant pass otherwise) | Yes |
27+
| KMP **without** an Android target (iOS/JS/wasm only) | the common metadata KSP pass | Structure only |
28+
29+
Detection happens automatically, including the new `com.android.kotlin.multiplatform.library` (`androidLibrary {}`)
30+
DSL, where the module namespace is also resolved for you.
31+
32+
## Thumbnails for shared Compose Multiplatform screens
33+
34+
Rendering needs Android, but a KMP shared module has **no Android resources of its own**. The plugin solves this by
35+
reusing the **consuming Android app's** linked resources: when it finds an Android app that depends on your shared
36+
module, the renderer runs against that app's fully merged Compose Multiplatform resources (the app's own, the shared
37+
module's, and every transitive dependency's). Your `composeResources` images, fonts, and strings all show up in the
38+
thumbnails.
39+
40+
Two render backends cooperate (see [`renderBackend`](gradle-plugin/configuration.md#renderbackend)):
41+
42+
- **Layoutlib** (device free, fast): the same engine Android Studio uses for `@Preview`. Most screens render here.
43+
- **Robolectric** (full Android runtime): complex Compose Multiplatform screens that Layoutlib can't draw fall back
44+
here automatically when `renderBackend` is `AUTO` (the default).
45+
46+
!!! tip "Keep previews render friendly"
47+
48+
The renderer instantiates your `@NavPreview` composables without a running app, so previews should stay
49+
**stateless**: render plain UI with mock data, and avoid view models, real network or media players, and other
50+
runtime only machinery inside preview bodies. This is the same rule that makes Android Studio previews reliable.
51+
52+
## Structure only graphs
53+
54+
A KMP module with no Android target (an iOS/JS/wasm only project) still gets a full navigation graph: nodes, typed
55+
arguments, start destination, and transitions, extracted from the common metadata pass. Only the thumbnails are
56+
skipped, and the IDE plugin and exports render thumbnail-less nodes gracefully. You can also opt into the same
57+
behavior anywhere with `navgraph { renderThumbnails.set(false) }` when you only care about the graph's shape.
58+
59+
## Multi module aggregation
60+
61+
Cross module aggregation works the same as on Android: each module (shared KMP or Android) emits its own
62+
`nav-graph.json`, and the app module merges its own graph with every dependency's into one picture. See
63+
[Configuration](gradle-plugin/configuration.md#aggregate) for the `aggregate` switch.
64+
65+
## A real world example
66+
67+
[`samples/sample-kotlinconf/`](https://github.com/skydoves/compose-nav-graph/tree/main/samples/sample-kotlinconf)
68+
applies the plugin to JetBrains' **KotlinConf app**, a production grade Compose Multiplatform project (Android, iOS,
69+
desktop, and web targets, multi module). The shared `:app:shared` module declares the two plugins, screens are
70+
annotated in `commonMain`, and the result is the complete conference app flow: **26 screens, 36 transitions, and a
71+
rendered thumbnail for every screen**, including screens built entirely from Compose Multiplatform resources.
72+
73+
The committed export of that graph is in
74+
[`nav-results/`](https://github.com/skydoves/compose-nav-graph/tree/main/nav-results), if you want to see the output
75+
without building anything.

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ nav:
2121
- 'NavGraph Graph': ide-plugin/graph.md
2222
- 'Preview Gallery': ide-plugin/preview-gallery.md
2323
- 'Settings': ide-plugin/settings.md
24+
- 'Kotlin Multiplatform': kotlin-multiplatform.md
2425
- 'More Plugins': more-plugins.md
2526

2627
# Repository

samples/sample-kotlinconf/app/shared/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ plugins {
1616
alias(libs.plugins.hotswan.compiler)
1717
alias(libs.plugins.metro)
1818
id("com.google.devtools.ksp") version "2.3.9"
19-
id("com.github.skydoves.navgraph") version "0.1.0-SNAPSHOT"
19+
id("com.github.skydoves.navgraph") version "0.1.0"
2020
}
2121

2222
kotlin {

samples/sample-nowinandroid/gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ kotlinxSerializationJson = "1.8.0"
5353
ksp = "2.3.4"
5454
ktlint = "1.4.0"
5555
hotswan = "1.3.4"
56-
navgraph = "0.1.0-SNAPSHOT"
56+
navgraph = "0.1.0"
5757
okhttp = "4.12.0"
5858
protobuf = "4.29.2"
5959
protobufPlugin = "0.9.6"

0 commit comments

Comments
 (0)