Hi there, I'm trying to maintain the navigation an...
# compose
m
Hi there, I'm trying to maintain the navigation and scrollstate of a WebView but haven't manage to make it work so far. I decided to hoist the webView and webViewClient to the parent composable but when switching tab, the webview doesn't seem to come back to its previous state. Any idea what would cause such problem ?
f
m
My bad 🙂
Copy code
@Composable
fun ParentComposable() {
    var webView by rememberSaveable { mutableStateOf<WebView?>(null) }
    var webViewClient by rememberSaveable { mutableStateOf<WebViewClient?>(null) }

    Scaffold(
        ...
    ) {
        ChildComposable(
            urlToRender = AppConfig.url,
            webView = webView,
            onWebViewInit = onWebViewInit = { webView = it },
            webViewClient = webViewClient,
            onWebViewClientInit = { webViewClient = it },
        )
    }
}


@Composable
fun ChildComposable(urlToRender: String, webView: WebView?, onWebViewInit: (WebView) -> Unit, webViewClient: WebViewClient?, onWebViewClientInit: (WebViewClient) -> Unit) {
    val client = object : WebViewClient() {
        override fun onPageFinished(view: WebView, url: String) {
            loadJs()
        }
    }
    AndroidView({ context ->
        WebView(context).apply{
            layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            settings.domStorageEnabled = true
            settings.javaScriptEnabled = true
        }.also { onWebViewInit(it) }
    })
    DisposableEffect(webView, urlToRender) {
        onWebViewClientInit(client)
        webView?.webViewClient = webViewClient!!
        webView?.loadUrl(urlToRender)
        onDispose {
            webView?.stopLoading()
        }
    }
}
🙏 1
j
AFAIK WebView doesn’t save/restore scroll position even with the “old” View system. Only way I could ever get around this is to somehow keep the instance of the WebView object alive (i.e. don’t let it be garbage collected and then recreated).
m
Thanks @julioromano. How would you recommend to do so ? I've done some test storing the webView in a Singleton but it doesn't seem to work.
Copy code
if (TestSingleton.webView === null) {
        AndroidView({ context ->
            WebView(context).apply{
                layoutParams = ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT
                )
                settings.domStorageEnabled = true
                settings.javaScriptEnabled = true
                webViewClient = client
            }.also { TestSingleton.webView = it }
        })
    }

    DisposableEffect(TestSingleton.webView, urlToRender) {
        TestSingleton.webView?.loadUrl(urlToRender)
        onDispose {
            TestSingleton.webView?.stopLoading()
        }
    }
j
I’ve done something similar with the View system in the past but not with Compose. I used to keep the WebView instance inside a ViewModel. However your code looks incomplete: what if
(TestSingleton.webView != null)
? In that case when the AndroidView is created you should pass in the WebView instance previously saved in the singleton.
m
Great, I'll check that out, thanks a lot 🙂
z
FWIW I filed an feature request to make this easier to do if you wanna star it: https://issuetracker.google.com/208439888
👌 1