Arnold
Arnold

Reputation: 131

WebView nested scroll doesn't work in Android Jetpack Compose

WebView is not scrolling inside Compose. I need to put the WebView inside BottomSheet using the Compose. The problem that WebView is not scrolling even if we use e.g. NestedWebView, or NestedScrollWebView. If I put WebView inside NestedScrollView it still doesn't react on scroll.

BottomSheetScaffold(
        sheetContent = {
            AndroidView(factory = {
                NestedWebView(it).apply {
                        layoutParams = ViewGroup.LayoutParams(
                            ViewGroup.LayoutParams.MATCH_PARENT,
                            ViewGroup.LayoutParams.MATCH_PARENT
                        )
                        settings.domStorageEnabled = true
                        settings.javaScriptEnabled = true
                        settings.useWideViewPort = true
                        webViewClient = WebViewClient()
                        //loadUrl("https://contest.rippl.club/")
                        loadUrl("https://codeflarelimited.com")
                    }
                })

        }) {
    }

One of the workaround is to use verticalScroll and set the webview height as WRAP_CONTENT:

 val scrollState = rememberScrollState()
 AndroidView(modifier = Modifier.verticalScroll(scrollState), factory = {
            WebView(it).apply {
                     layoutParams = ViewGroup.LayoutParams(
                          ViewGroup.LayoutParams.MATCH_PARENT,
                          ViewGroup.LayoutParams.WRAP_CONTENT
                          ---//---

but there are a lot of sites that doesnt work with wrap_content for e.g. because of inner scrolling like this site https://contest.rippl.club/. This site doesn't work with that workaround. If we set the webview height as screen height that it still doesnt work, because the verticalScroll works as ScrollView, so it will just scroll until this height.

I've also checked this doc https://developer.android.com/jetpack/compose/gestures#parent-compose-child-view, but nothing works for webview case.

Upvotes: 9

Views: 7226

Answers (5)

Pitos
Pitos

Reputation: 867

I'm using Scaffold where in the content I add a Column with verticalScroll(rememberScrollState()). I tested few sites and works in my case.

Scaffold(
    topBar = {
        // Your Top bar with e.g url, navigation etc
    },
    content = { pv ->
        Column(
            modifier = Modifier
                .padding(pv)
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
        ) {
            AndroidView(factory = {
                WebView(it).apply {
                    layoutParams = ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT
                    )
                    webViewClient = WebViewClient()
                    settings.javaScriptEnabled = true
                    settings.safeBrowsingEnabled = true
                    loadUrl(url)
                }
            }, update = {
                it.loadUrl(url)
            })
        }
    }
)

Upvotes: 0

Bogdan Stolyarov
Bogdan Stolyarov

Reputation: 126

This answer helped: https://stackoverflow.com/a/70195667/4256193

It works for me:

Surface(
    modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())
) {
    WebView(
        state = state,
        modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
        onCreated = { }
    )
}

Upvotes: 5

Jürgen Obernolte
Jürgen Obernolte

Reputation: 359

Here is a small sample code, which at least makes a WebView scrollable:

private class MyWebView(context: Context) : WebView(context) {
    val verticalScrollRange: Int get() = computeVerticalScrollRange() - height
}

@Composable
fun WebViewCompose(url: String, modifier: Modifier = Modifier, onCreated: (WebView) -> Unit = {}) {
    val context = LocalContext.current
    val webView: MyWebView = remember(context) {
        MyWebView(context).also(onCreated)
    }
    DisposableEffect(webView) {
        onDispose {
            webView.stopLoading()
            webView.destroy()
        }
    }
    val scrollabeState = rememberScrollableState { delta ->
        val scrollY = webView.scrollY
        val consume = (scrollY - delta).coerceIn(0f, webView.verticalScrollRange.toFloat())
        webView.scrollTo(0, consume.roundToInt())
        (scrollY - webView.scrollY).toFloat()
    }
    AndroidView(
        factory = { webView },
        modifier = modifier
            .scrollable(scrollabeState, Orientation.Vertical)
    ) { webView2 ->
        webView2.loadUrl(url)
    }
}

Upvotes: 5

Dwane13
Dwane13

Reputation: 408

There is the ease way to work around this issue. Ive got exact same case as you. All you need to do is implement pointerInput somehow like this:

AndroidView(
        modifier = Modifier
            .fillMaxHeight(0.9f)
            .pointerInput(Unit) {
                detectVerticalDragGestures { _, dragAmount ->
                    val initialDrag = currentVerticalDrag.value + dragAmount
                    val calculatedDrag = when {
                        initialDrag > 0 -> {
                            0
                        }
                        abs(initialDrag) > measuredHeight.value -> {
                            measuredHeight.value
                        }
                        else -> {
                            initialDrag.roundToInt()
                        }
                    }
                    currentVerticalDrag.value = calculatedDrag
                }
            },
        factory = {
            WebView(it).apply {
                settings.javaScriptEnabled = true
                addJavascriptInterface(jsInterface, "HTMLOUT")
                webViewClient = FaqWebViewClient(
                    onPageLoadingStart = {
                        isPageLoading.value = true
                    },
                    onPageLoadComplete = {
                        isPageLoading.value = false
                    }
                )
                loadUrl(url)
            }
        }, update = { webView ->
            webView.scrollTo(0, abs(currentVerticalDrag.value))
        }
    )

Upvotes: 0

user496854
user496854

Reputation: 6810

You need to use a NestedScrollConnection object, and pass it to a nestedSCroll modifier on the outer view . Here's a basic example

Upvotes: 2

Related Questions