androidkotlinandroid-jetpack-composepull-to-refreshandroid-jetpack-compose-material3

Material3 PullToRefreshBox prevents TopAppBar from expanding


I want to build a screen using only Material3 components and Jetpack Compose. When I combine the MediumTopAppBar with a PullToRefreshBox and the exitUntilCollapsedScrollBehavior it intercepts my scroll while the TopAppBar is not fully expanded yet. How can I prevent this?

scroll issue

MaterialTheme {
    val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
    Scaffold(
        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
        contentWindowInsets = WindowInsets.safeContent,
        topBar = { MediumTopAppBar(title = { Text("Title") }, scrollBehavior = scrollBehavior) },
    ) { padding ->

        val coroutineScope = rememberCoroutineScope()
        var isRefreshing by remember { mutableStateOf(false) }
        val state: PullToRefreshState = rememberPullToRefreshState()

        PullToRefreshBox(
            isRefreshing = isRefreshing,
            onRefresh = {
                isRefreshing = true
                coroutineScope.launch {
                    delay(3.seconds)
                    isRefreshing = false
                }
            },
            state = state,
            indicator = {
                Indicator(
                    modifier = Modifier
                        .align(Alignment.TopCenter)
                        .padding(padding),
                    isRefreshing = isRefreshing,
                    state = state,
                )
            },
        ) {
            ListItems(padding)
        }
    }
}

Solution

  • I found the problem with my code. The nestedScroll modifier should not be applied to the Scaffold, but it should be applied to the first element within the PullToRefreshBox .

    So my code now looks like this:

    MaterialTheme {
        val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
        Scaffold(
            contentWindowInsets = WindowInsets.safeContent,
            topBar = { MediumTopAppBar(title = { Text("Title") }, scrollBehavior = scrollBehavior) },
        ) { padding ->
    
            val coroutineScope = rememberCoroutineScope()
            var isRefreshing by remember { mutableStateOf(false) }
            val state: PullToRefreshState = rememberPullToRefreshState()
    
            PullToRefreshBox(
                isRefreshing = isRefreshing,
                onRefresh = {
                    isRefreshing = true
                    coroutineScope.launch {
                        delay(3.seconds)
                        isRefreshing = false
                    }
                },
                state = state,
                indicator = {
                    Indicator(
                        modifier = Modifier
                            .align(Alignment.TopCenter)
                            .padding(padding),
                        isRefreshing = isRefreshing,
                        state = state,
                    )
                },
            ) {
                Column(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
                    ListItems(padding)
                }
            }
        }
    }