J. Hegg
J. Hegg

Reputation: 2123

Jetpack Compose WebView Handling Back Navigation And Go To Previous Page

I am using Jetpack Compose and have a WebView wrapped in a AndroidView composable that looks like the following:

AndroidView(modifier = modifier, factory = { context ->
        WebView(context).apply {
            layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            webViewClient = WebViewClient()
            settings.javaScriptEnabled = true
        }
    }, update = { webView -> webView.loadUrl(url) })

In the legacy way, we could add a OnBackPressedDispatcher to the Activity to intercept the back press and navigate inside the WebView by accessing it via viewBinding for example with functions of the WebView like goBack() and to check if you can go back with canGoBack().

But how can we achieve the same with this Jetpack Compose approach?

Upvotes: 9

Views: 14591

Answers (3)

Jay Sharma
Jay Sharma

Reputation: 11

You can handle the back press using WebViewClient's doUpdateVisitedHistory function.

var backEnabled by remember { mutableStateOf(false) }
var webView: WebView? by remember { mutableStateOf(null) }

AndroidView(
modifier = Modifier,
factory = {
    context ->
    WebView(context).apply {
        layoutParams = ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )
        settings.javaScriptEnabled = true
        webViewClient = WebViewClient() {
            override fun doUpdateVisitedHistory(
                view: WebView?,
                url: String?,
                isReload: Boolean
            ) {
                backEnabled = view?.canGoBack() ?: false
            }
        }
        loadUrl(url)
        webView = this
    }
}, update = {
    webView = it
})

BackHandler(enabled = backEnabled) {
    webView?.goBack()
}

Upvotes: 1

nhcodes
nhcodes

Reputation: 2004

You can also take a look at Accompanist, which is "a group of libraries that aim to supplement Jetpack Compose with features that are commonly required by developers but not yet available."

Here's an example:

val state = rememberWebViewState("https://example.com")

WebView(
    state
)

"By default the WebView will capture back presses/swipes when relevant and navigate the WebView back."

Upvotes: 1

Scott Cooper
Scott Cooper

Reputation: 3099

There doesn't seem to be anything wrong with assigning the WebView to an external var so that's what I've done here.

var backEnabled by remember { mutableStateOf(false) }
var webView: WebView? = null
AndroidView(
    modifier = modifier,
    factory = { context ->
        WebView(context).apply {
            layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            webViewClient = object : WebViewClient() {
                override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
                    backEnabled = view.canGoBack()
                }
            }
            settings.javaScriptEnabled = true

            loadUrl(url)
            webView = this
        }
    }, update = {
        webView = it
    })

BackHandler(enabled = backEnabled) {
    webView?.goBack()
}

The WebViewClient listens for onPageStarted which checks if the WebView can navigate backwards and then updates backEnabled. This causes a recomposition which toggles the BackHandler on and off.

I've also moved loadUrl out of the update function and into factory because update is called every time there's a recomposition whereas factory is only called the once. This may or may not be relevant based on your implementation.

Upvotes: 31

Related Questions