3

Helo! I been having a hard time implementing SwipeRefreshLayout on top of my WebView. It seems like pullRefresh only takes a scrollable view inside it to be able to function.

I got it working by using on my WebView: Modifier.verticalScroll(rememberScrollState())

However, the WebView content is not matching its parent height. I even used Modifier.fillMaxHeight() to fix it but it doesn't seem to work.

Any ideas on a workaround? I just transitioned from XML to Compose.

    val webClient = remember {
        object : AccompanistWebViewClient() {
            override fun onPageStarted(
                view: WebView?,
                url: String?,
                favicon: Bitmap?
            ) {
                super.onPageStarted(view, url, favicon)
                // TODO()
            }
        }
    }

    val refreshScope = rememberCoroutineScope()
    var refreshing by remember { mutableStateOf(false) }

    fun refresh() = refreshScope.launch {
        refreshing = true
        delay(1500)
        webViewNavigator.reload()
        refreshing = false
    }

    val state = rememberPullRefreshState(refreshing, ::refresh)

    Box(
        modifier = modifier
            .pullRefresh(state)
            .border(2.dp, Color.Green) // for debugging purposes
    ) {
        WebView(
            state = webViewState,
            navigator = webViewNavigator,
            onCreated = { webView ->
                webView.apply {
                    settings.javaScriptEnabled = true
                }
            },
            client = webClient,
            captureBackPresses = false,
            modifier = Modifier.fillMaxHeight()
                .verticalScroll(rememberScrollState())
        )

        PullRefreshIndicator(refreshing, state, Modifier.align(Alignment.TopCenter))
    }
Firate
  • 105
  • 5

1 Answers1

1

You can put your WebView inside a Column/Box/Row/.. and add the verticalScroll modifier there. This works for AndroidView as well as WebView from Accompanist.

For web pages that are longer than the screen it works like a charm. But for shorter pages I couldn't get them to work in fullscreen yet.

You can read about the effects of verticalScroll here: fillMaxSize modifier not working when combined with VerticalScroll in Jetpack Compose

The workarounds sadly only work for everything that is not a WebView. When implementing it you are not able to scroll anymore.

My example.

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun TestWebView() {

    val refreshScope = rememberCoroutineScope()
    var refreshing by remember { mutableStateOf(false) }
    var itemCount by remember { mutableStateOf(15) }

    fun refresh() = refreshScope.launch {
        refreshing = true
        delay(1500)
        itemCount += 5
        refreshing = false
    }

    val state = rememberPullRefreshState(refreshing, ::refresh)
    val shortUrl = "http://www.corelangs.com/html/introduction/myfirstpage.html"
    val url = "https://google.com/"

    Box(
        modifier = Modifier
            .fillMaxSize()
            .pullRefresh(state)
            .background(color = Color.Gray)
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
        ) {
            AndroidView(
                factory = { context ->
                    WebView(context).apply {
                        layoutParams = FrameLayout.LayoutParams(
                            FrameLayout.LayoutParams.MATCH_PARENT,
                            FrameLayout.LayoutParams.MATCH_PARENT
                        )
                        loadUrl(url)
                    }
                }, update = {
                    it.loadUrl(url)
                }
            )

            PullRefreshIndicator(
                refreshing = refreshing,
                state = state,
                Modifier.align(Alignment.TopCenter)
            )
        }
    }
}
IeNary
  • 21
  • 3
  • 1
    The `verticalScroll` workaround works in the sense that pull-to-refresh works; however, it breaks display of the webview itself fairly critically. As noted in the question, the viewport height of the resulting webview matches the full height of the page, so fixed-bottom elements are only shown at the bottom of the entire page (this is just one example -- any other viewport-height-relative elements won't display properly either). – Matt Bennett Mar 28 '23 at 23:10
  • I have tried using BoxWithConstraints, it worked with `verticalScroll` however it did make the WebView's screen fixed and not match its parent (as its supposed to be) :/ – Firate Mar 31 '23 at 05:46
  • I thought the mess up of the WebView is a bug so I filed one. I got an answer from google real quick. They told me its not possible to achieve it this way and closed my bugreport as "Won't fix". They recommended using the "old" way with SwipeRefreshLayout which I did and it worked. It's a bit sad that there is no other way. – IeNary Apr 01 '23 at 07:08