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:
AndroidViewis 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
CircularProgressIndicatorthat disappears once theWebViewClientsignalsonPageFinished.
