Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call evaluateJavascript, outside of webview creation, in jetpack compose?

I have business logic outside of composable creation, which needs to be able to feed data to webview -> javascript. However, the webview shouldn't be saved, as stated here:

Note: Constructing the view in the AndroidView viewBlock is the best practice. Do not hold or remember a direct view reference outside AndroidView. https://developer.android.com/jetpack/compose/interop/interop-apis

How can I call webview's evaluateJavascript() outside of the composable creating the webview?

This is the code (lateinit as an example of what doesn't work):

@SuppressLint("SetJavaScriptEnabled")
@Composable
fun WebPageScreen(urlToRender: String, someViewModel: SomeViewModel) {
    AndroidView(factory = {
        WebView(it).apply {
            layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            webViewClient = WebViewClient()
            settings.javaScriptEnabled = true

            addJavascriptInterface(someViewModel.createWebAppInterface(context), "Android")
            loadUrl(urlToRender)

            someViewModel.webViewObj = this //<<--- won't work like this.
    }, update = {
        it.loadUrl(urlToRender)
    })
}


class SomeViewModel(application: Application): AndroidViewModel(application) {
    lateinit var webViewObj: WebView  // <<--"This field leaks a context object"
    fun callJs() {
        webViewObj.evaluateJavascript("updateSomething(123)",null)
    }

    fun createWebAppInterface(mContext: Context) : WebAppInterface {
        return WebAppInterface(mContext)
    }
    
    inner class WebAppInterface(private val mContext: Context) {
        @JavascriptInterface
        fun webViewCallJs() {
            callJs()
        }
    }
}

Here the idea is, that callJs() could be called both from webView (e.g. a HTML UI button), and from within business logic (SomeViewModel). How can we achieve this when webview cannot be remembered, but we should be able call its function from outside?

like image 300
duckduck Avatar asked Jan 22 '26 08:01

duckduck


1 Answers

In the accompanist-webview library, they've used a local webview state like below:

var webView by remember { mutableStateOf<WebView?>(null) }

AndroidView(
        factory = { context ->
            WebView(context).apply {
                ...
            }.also { webView = it }
        },

    ) {
        ...
    }

You can then use webView.post{ webview.evaluateJavaScript() } to execute JS functions

like image 131
original_anu Avatar answered Jan 24 '26 20:01

original_anu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!