Skip to content

Compose Material3: PullToRefreshBox / PullToRefreshDefaults.Indicator @Composables not bound (PullToRefreshKt wrapper is empty) #1445

@jonathanpeppers

Description

@jonathanpeppers

Summary

The Material 3 pull-to-refresh state-holder surface is bound (AndroidX.Compose.Material3.PullToRefresh.IPullToRefreshState, PullToRefreshDefaults), but the @Composable PullToRefreshBox(...) function — the actual entry point users need — is not reachable from C#. The PullToRefreshKt wrapper class is bound as an empty container with zero static methods.

This makes the entire pull-to-refresh feature non-functional from bound API alone.

Library / NuGet

Verified state today (commit eb2f50b)

source/androidx.compose.material3/material3-android/PublicAPI/PublicAPI.Unshipped.txt contains the following pulltorefresh package surface:

AndroidX.Compose.Material3.PullToRefresh.IPullToRefreshState
AndroidX.Compose.Material3.PullToRefresh.IPullToRefreshState.AnimateToHidden(Kotlin.Coroutines.IContinuation! p0) -> Java.Lang.Object?
AndroidX.Compose.Material3.PullToRefresh.IPullToRefreshState.AnimateToThreshold(Kotlin.Coroutines.IContinuation! p0) -> Java.Lang.Object?
AndroidX.Compose.Material3.PullToRefresh.IPullToRefreshState.DistanceFraction.get -> float
AndroidX.Compose.Material3.PullToRefresh.IPullToRefreshState.IsAnimating.get -> bool
AndroidX.Compose.Material3.PullToRefresh.IPullToRefreshState.SnapTo(float targetValue, Kotlin.Coroutines.IContinuation! p1) -> Java.Lang.Object?
AndroidX.Compose.Material3.PullToRefresh.PullToRefreshDefaults
AndroidX.Compose.Material3.PullToRefresh.PullToRefreshDefaults.Elevation.get -> float
AndroidX.Compose.Material3.PullToRefresh.PullToRefreshDefaults.GetContainerColor(AndroidX.Compose.Runtime.IComposer? _composer, int _changed) -> long
AndroidX.Compose.Material3.PullToRefresh.PullToRefreshDefaults.GetIndicatorColor(AndroidX.Compose.Runtime.IComposer? _composer, int _changed) -> long
AndroidX.Compose.Material3.PullToRefresh.PullToRefreshDefaults.GetIndicatorContainerColor(AndroidX.Compose.Runtime.IComposer? _composer, int _changed) -> long
AndroidX.Compose.Material3.PullToRefresh.PullToRefreshDefaults.IndicatorMaxDistance.get -> float
AndroidX.Compose.Material3.PullToRefresh.PullToRefreshDefaults.LoadingIndicatorElevation.get -> float
AndroidX.Compose.Material3.PullToRefresh.PullToRefreshDefaults.PositionalThreshold.get -> float
AndroidX.Compose.Material3.PullToRefresh.PullToRefreshKt

Note how PullToRefreshKt is the last line of its block, with no static method members. The Kotlin source exposes:

@Composable
@ExperimentalMaterial3Api
fun PullToRefreshBox(
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier,
    state: PullToRefreshState = rememberPullToRefreshState(),
    contentAlignment: Alignment = Alignment.TopStart,
    indicator: @Composable BoxScope.() -> Unit = { … },
    content: @Composable BoxScope.() -> Unit,
)

@Composable @ExperimentalMaterial3Api fun rememberPullToRefreshState(): PullToRefreshState

@ExperimentalMaterial3Api
@Composable
fun PullToRefreshDefaults.Indicator(state, isRefreshing, modifier, containerColor, color, threshold)

@ExperimentalMaterial3Api
@Composable
fun PullToRefreshDefaults.LoadingIndicator(state, isRefreshing, modifier, containerColor, color, elevation)

All four of these (PullToRefreshBox, rememberPullToRefreshState, PullToRefreshDefaults.Indicator, PullToRefreshDefaults.LoadingIndicator) carry @JvmInline value class parameters (Modifier, Alignment, Color, Dp) and have hash-mangled JVM names (e.g. PullToRefreshBox-LP5b{hash}), so they are dropped by the same binder pipeline issue as the rest of material3's @Composables.

Metadata.xml today

source/androidx.compose.material3/material3-android/Transforms/Metadata.xml contains only one strip (MaterialTheme.getMotionScheme); there is no explicit <remove-node> for any of the PullToRefresh* methods. They are dropped automatically by the binder due to the inline-class mangling.

Reproduction / evidence

compose-net has not yet implemented a [ComposeBridge] for PullToRefreshBox precisely because it is blocked on the same upstream fix. The compose-net tracking issue is jonathanpeppers/compose-net#53.

A hand-written JNI bridge would be feasible (the underlying Java methods exist and are callable via androidx/compose/material3/pulltorefresh/PullToRefreshKt.PullToRefreshBox-<hash>), but that workaround should not be needed once the binder is fixed.

Suggested fix

This is a downstream effect of dotnet/java-interop#1440. When that PR ships and the binder stops dropping @JvmInline value class overloads with mangled names, every PullToRefresh* @Composable should bind automatically with no Metadata.xml changes required.

This issue is filed primarily as a tracking issue so the regression in user-facing material3 surface is visible. No action needed in the android-libraries repo itself until #1440 lands; at that point a re-binding pass should confirm the methods appear.

Cross-references

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions