dotnet / dotnet/android-libraries

Compose: navigation-compose @Composable surface (NavHost, composable, rememberNavController, DialogHost) not bound — every Kt wrapper is empty

Open
#1,444 0 comments 1 reaction 2 assignees Claimed by @jonathanpeppers View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.