You are currently viewing [Android] Show WebView as Dialog using Jetpack Compose

[Android] Show WebView as Dialog using Jetpack Compose

As mobile developers, we often need to display external content—like Privacy Policies, Terms of Service, or third-party login pages—without forcing the user to leave the app. In the world of Jetpack Compose, the most elegant way to handle this is by wrapping a WebView inside a Dialog.

In this guide, we’ll build a reusable WebView Dialog and, more importantly, solve the notorious ERR_CACHE_MISS error that often trips up developers.

1. The Implementation

Since Jetpack Compose doesn’t have a native “ComposableView,” we use the AndroidView interop to host the traditional android.webkit.WebView.

The Reusable Dialog Component

This component creates a full-screen-ish dialog with a close button and a configured WebView.

@Composable
fun WebViewDialog(
    url: String,
    onDismiss: () -> Unit
) {
    Dialog(
        onDismissRequest = onDismiss,
        properties = DialogProperties(usePlatformDefaultWidth = false) 
    ) {
        Surface(
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp),
            shape = RoundedCornerShape(12.dp),
            color = MaterialTheme.colorScheme.surface
        ) {
            Column {
                // Header Area
                IconButton(
                    onClick = onDismiss,
                    modifier = Modifier.align(Alignment.End)
                ) {
                    Icon(Icons.Default.Close, contentDescription = "Close")
                }

                // The WebView Interop
                AndroidView(
                    modifier = Modifier.fillMaxSize(),
                    factory = { context ->
                        WebView(context).apply {
                            layoutParams = ViewGroup.LayoutParams(
                                ViewGroup.LayoutParams.MATCH_PARENT,
                                ViewGroup.LayoutParams.MATCH_PARENT
                            )
                            webViewClient = WebViewClient()
                            
                            // Essential Settings
                            settings.javaScriptEnabled = true
                            settings.domStorageEnabled = true
                            settings.cacheMode = WebSettings.LOAD_DEFAULT
                            
                            loadUrl(url)
                        }
                    },
                    update = { webView ->
                        webView.loadUrl(url)
                    }
                )
            }
        }
    }
}

2. Handling the “ERR_CACHE_MISS” Error

If you’ve tried loading a site like Google and saw a white screen with net::ERR_CACHE_MISS, don’t panic. This is rarely a coding error and usually a configuration issue.

Step A: The Manifest Check

The most common cause is missing permissions. Ensure these are in your AndroidManifest.xml outside the <application> tag:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Step B: Cleartext Traffic

If you are trying to load an http site (rather than https), Android will block it by default. Add this to your <application> tag:

<application
    android:usesCleartextTraffic="true"
    ... >

3. Triggering the Dialog

In your main screen, manage the dialog state with a simple boolean:

@Composable
fun MainContent() {
    var showWebView by remember { mutableStateOf(false) }

    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        Button(onClick = { showWebView = true }) {
            Text("View Documentation")
        }
    }

    if (showWebView) {
        WebViewDialog(
            url = "https://google.com",
            onDismiss = { showWebView = false }
        )
    }
}

Key Takeaways

  • Interop is Key: AndroidView is your bridge for components not yet native to Compose.
  • Permissions First: Always verify your Manifest when network errors occur.
  • User Experience: Use DialogProperties(usePlatformDefaultWidth = false) to give your WebView enough room to be readable.

Pro Tip: For a more polished feel, consider adding a CircularProgressIndicator that disappears once the WebViewClient signals onPageFinished.