dotnet / dotnet/android-libraries
Compose: navigation-compose @Composable surface (NavHost, composable, rememberNavController, DialogHost) not bound — every Kt wrapper is empty
- Dominant language
- C#
- Stars
- 317
- Forks
- 73
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 17
Description
## Summary
`Xamarin.AndroidX.Navigation.Compose` ships with the navigator classes bound (`ComposeNavigator`, `DialogNavigator`, their `Destination` types and `*DestinationBuilder` types) but **none of the user-facing `@Composable` entry points are reachable from C#**.
Specifically, the following `*Kt` wrapper classes are bound as empty containers — class declarations exist but they have zero static methods:
- `AndroidX.Navigation.Compose.NavHostKt` — should expose `NavHost(navController, startDestination, modifier, ...) { … }`
- `AndroidX.Navigation.Compose.NavHostControllerKt` — should expose `rememberNavController(vararg navigators: Navigator<…>): NavHostController`
- `AndroidX.Navigation.Compose.NavGraphBuilderKt` — should expose `NavGraphBuilder.composable(route, arguments, deepLinks, content)` and `NavGraphBuilder.dialog(…)`
- `AndroidX.Navigation.Compose.DialogHostKt` — should expose `DialogHost(dialogNavigator)`
- `AndroidX.Navigation.Compose.NavBackStackEntryProviderKt` — should expose `NavBackStackEntry.LocalOwnersProvider(saveableStateHolder, content)`
This makes the entire navigation surface unusable without raw JNI: there is no way to build a nav graph, render a `NavHost`, or call `composable("route")` from bound API alone.
## Library / NuGet
- AAR: `androidx.navigation:navigation-compose-android`
- NuGet: `Xamarin.AndroidX.Navigation.Compose.Android` (+ facade `Xamarin.AndroidX.Navigation.Compose`)
- Source dir: [`source/androidx.navigation/navigation-compose-android/`](https://github.com/dotnet/android-libraries/tree/main/source/androidx.navigation/navigation-compose-android)
## Verified state today (commit eb2f50b)
The `PublicAPI.Unshipped.txt` is **3,479 bytes total**. Every `Kt` class binds as just:
```
AndroidX.Navigation.Compose.NavHostKt
override AndroidX.Navigation.Compose.NavHostKt.JniPeerMembers.get -> Java.Interop.JniPeerMembers!
```
Compare to what the Kotlin source actually exposes (e.g. https://developer.android.com/reference/kotlin/androidx/navigation/compose/package-summary):
| Method | Class | Status |
|---|---|---|
| `NavHost(NavHostController, String, …) { … }` | `NavHostKt` | **missing** |
| `NavHost(NavHostController, NavGraph, …)` | `NavHostKt` | **missing** |
| `rememberNavController(vararg Navigator<…>)` | `NavHostControllerKt` | **missing** |
| `NavGraphBuilder.composable(String, …)` | `NavGraphBuilderKt` | **missing** |
| `NavGraphBuilder.dialog(String, …)` | `NavGraphBuilderKt` | **missing** |
| `NavGraphBuilder.navigation(String, String, …)` | `NavGraphBuilderKt` | **missing** |
| `NavBackStackEntry.LocalOwnersProvider(…)` | `NavBackStackEntryProviderKt` | **missing** |
| `DialogHost(DialogNavigator)` | `DialogHostKt` | **missing** |
All of these have `@Composable` annotations. The empty-`*Kt`-wrapper pattern affects them even though they are **not** all hash-mangled — for example, `androidx.compose.ui.res.PainterResources_androidKt.painterResource(Int, Composer, Int): Painter` has no `@JvmInline value class` parameters yet is also dropped from its wrapper (see the companion Painter issue). The root cause is therefore unlikely to be only [dotnet/java-interop#1440](https://github.com/dotnet/java-interop/pull/1440); there appears to be a separate binder rule dropping `@Composable` statics from `*Kt` wrappers in this Compose surface area.
## Metadata.xml today
`source/androidx.navigation/navigation-compose-android/Transforms/Metadata.xml` contains only two `managedReturn` adjustments for `createDestination`. There are no `` entries that would explain why all the `@Composable` statics are dropped.
## Reproduction / evidence
`compose-net` could not bind navigation at all via the binding. The compose-net tracking issue [jonathanpeppers/compose-net#60](https://github.com/jonathanpeppers/compose-net/issues/60) confirms this gap end-to-end. (No hand-written JNI bridge has been attempted in compose-net yet — the binding shape was inspected and found empty.)
## Suggested fix
Investigate why the `@Composable` statics on `NavHostKt`, `NavHostControllerKt`, `NavGraphBuilderKt`, `NavBackStackEntryProviderKt`, and `DialogHostKt` are dropped — there is no Metadata.xml customization removing them, so the binder pipeline itself is filtering them out. Likely related to the `@Composable` annotation plus return type / receiver type metadata; possibly the same root cause as the `painterResource` drop (see the companion Painter issue).
If specific overloads must be reintroduced via `add-node`, that is acceptable as a tactical fix, but the underlying binder behaviour should be investigated since this also affects a large surface in material3 and the other Compose libraries.
## Cross-references
- compose-net tracking: [jonathanpeppers/compose-net#60](https://github.com/jonathanpeppers/compose-net/issues/60)
- Master inline-class blocker: [dotnet/java-interop#1440](https://github.com/dotnet/java-interop/pull/1440) — relevant to some overloads with inline-class parameters (e.g. `Modifier`, `Dp`) but the consistently empty `*Kt` wrappers across this binding suggest the root cause is a separate filter dropping `@Composable` statics independently of name mangling.
Contributor guide
Assessment
This issue has not been assessed yet.