Guarantee your WebViews are appropriate with Android 16, as Android 16 removes the power to opt-out of drawing your app edge-to-edge. The best way you deal with insets for WebViews relies on whether or not or not your app owns the net content material.
This publish assumes fundamental data of dealing with insets and is relevant in case your WebViews are drawing beneath system bars or show cutouts. See the Compose and Views documentation, and the Inset dealing with suggestions weblog for steerage on dealing with insets.
In case you’re searching for steerage on the right way to make your webpage edge-to-edge on Chrome, see the Chrome on Android edge-to-edge migration information as a substitute.
This publish covers the next subjects:
- Tips on how to deal with WebViews in case your app doesn’t personal the net content material
- Tips on how to deal with WebViews in case your app owns the net content material
- Dealing with IME insets in WebViews
WebViews displaying exterior content material can not simply draw edge-to-edge and will as a substitute be inset to keep away from the system bars and show cutout. The implementation is completely different relying on when you’re utilizing Compose or Views, however usually follows these steps:
- Wrap the WebView in a container and apply insets as padding on that container.
- Set the app’s background coloration in a best-effort try and match the webpage.
See the next code samples.
Apply insets to a WebView in a Compose app
To make sure a WebView in Compose avoids overlapping with system bars, show cutout and keyboard, apply Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
to its wrapper (e.g. AndroidView
). This offers the mandatory padding to maintain the WebView content material throughout the secure space of the display screen.
@Composable
enjoyable WebViewInCompose() {
AndroidView(
modifier = Modifier.windowInsetsPadding(WindowInsets.safeDrawing),
manufacturing unit = { context ->
WebView(context).apply {
// Configure WebView choices right here
}
}
)
}
Then, replace your app’s background so it matches the WebView’s background as a lot as potential.
Apply insets to a WebView in a Views app
To make sure a WebView in Views avoids overlapping the system bars, show cutout and keyboard, wrap the WebView in a container like a FrameLayout
and do one of many following:
- Set
android:fitsSystemWindows="true"
- Use
ViewCompat.setOnApplyWindowInsetsListener()
and apply systemBars, displayCutout, and ime insets on the container. See the Views documentation for a code pattern utilizing the listener.
Lastly, replace android:windowBackground
in your themes.xml
so it matches the WebView’s background if potential.
In case your app owns the net content material, both pad the WebView’s mum or dad as described above; or, use JavaScript to inject padding into the net web page to attract the web page edge-to-edge as described under.
To inject padding into the net web page, first add to your HTML.
Second, outline CSS variables for prime, proper, backside and left secure space insets, because the env(safe-area-inset-*)
variables return 0px on Android on the time of scripting this weblog.
/* CSS */
physique {
padding-top: var(--safe-area-inset-top);
padding-right: var(--safe-area-inset-right);
padding-bottom: var(--safe-area-inset-bottom);
padding-left: var(--safe-area-inset-left);
}
As a substitute of making use of insets on the WebView’s container, use JavaScript to move the insets to your CSS variables to pad the webpage. The implementation is completely different relying on when you’re utilizing Compose or Views, however it usually follows these steps:
- Retrieve the highest, proper, backside and left system bar, show cutout and IME insets as uncooked pixel values.
- Convert the uncooked pixel values to density impartial pixels.
- Inject the density impartial pixels into the CSS variables as CSS pixels. When the web site renders on the Android system, the WebView converts the CSS pixels again to density impartial pixels. See Help completely different screens in internet apps for extra info.
- In Compose, recompose when the software program keyboard expands or collapses if relevant.
- In Views, dispatch insets when the webpage first masses.
See the next code samples.
Utilizing Compose and JavaScript to inject insets into internet pages
A code pattern displaying the right way to use JavaScript to inject insets into webpages in a Compose app.
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.basis.format.WindowInsets
import androidx.compose.basis.format.ime
import androidx.compose.basis.format.safeDrawing
import androidx.compose.ui.unit.Dp
...@Composable
enjoyable MainScreen() {
Field(modifier = Modifier.fillMaxSize()) {
// Retrieve insets as uncooked pixels
val insets = WindowInsets.safeDrawing
WebViewInCompose(
initialUrl = "file:///android_asset/instance.html",
insets = insets
)
}
}
@SuppressLint("SetJavaScriptEnabled")
@Composable
enjoyable WebViewInCompose(
initialUrl: String,
insets: WindowInsets,
density: Density = LocalDensity.present,
layoutDirection: LayoutDirection = LocalLayoutDirection.present,
myWebViewClient: CustomWebViewClient = bear in mind { CustomWebViewClient(
insets, density, layoutDirection
) }
) {
// Do not apply insets to the container
AndroidView(
manufacturing unit = { context ->
WebView(context).apply {
webViewClient = myWebViewClient
settings.javaScriptEnabled = true
loadUrl(initialUrl)
}
}, replace = { view ->
// Updates webpage when software program keyboard expands or collapses.
// In case your webpage would not have an enter discipline that opens the
// software program keyboard, take away this line.
applySafeAreaInsetsToWebView(insets, density, layoutDirection, view)
}
)
}
class CustomWebViewClient(
non-public val insets: WindowInsets,
non-public val density: Density,
non-public val layoutDirection: LayoutDirection
) : WebViewClient(){
override enjoyable onPageFinished(view: WebView?, url: String?) {
tremendous.onPageFinished(view, url)
// Inject insets into the webpage as soon as the web page has absolutely loaded
applySafeAreaInsetsToWebView(insets, density, layoutDirection, view)
}
}
non-public enjoyable applySafeAreaInsetsToWebView(
insets: WindowInsets,
density: Density,
layoutDirection: LayoutDirection,
webView: WebView?){
// Convert uncooked pixels to density impartial pixels
val prime = insets.getTop(density).toDp(density).worth
val proper = insets.getRight(density, layoutDirection).toDp(density).worth
val backside = insets.getBottom(density).toDp(density).worth
val left = insets.getLeft(density, layoutDirection).toDp(density).worth
val safeAreaJs = """
doc.documentElement.fashion.setProperty('--safe-area-inset-top', '${prime}px');
doc.documentElement.fashion.setProperty('--safe-area-inset-right', '${proper}px');
doc.documentElement.fashion.setProperty('--safe-area-inset-bottom', '${backside}px');
doc.documentElement.fashion.setProperty('--safe-area-inset-left', '${left}px');
"""
// Inject the density impartial pixels into the CSS variables as CSS pixels
webView?.evaluateJavascript(safeAreaJs, null)
}
non-public enjoyable Int.toDp(density: Density): Dp = with(density) { this@toDp.toDp() }
Utilizing Views and JavaScript to inject insets into internet pages
A code pattern displaying the right way to use JavaScript to inject insets into webpages in a Views app.
import androidx.core.util.TypedValueCompat.pxToDp
import
androidx.core.view.WindowInsetsCompat.Sort.displayCutout
import androidx.core.view.WindowInsetsCompat.Sort.ime
import androidx.core.view.WindowInsetsCompat.Sort.systemBars
...override enjoyable onCreate(savedInstanceState: Bundle?) {
...
val myWebView: WebView = findViewById(R.id.webView)
myWebView.settings.javaScriptEnabled = true
myWebView.loadUrl("file:///android_asset/instance.html")
ViewCompat.setOnApplyWindowInsetsListener(myWebView) { _, windowInsets ->
// Retrieve insets as uncooked pixels
val safeDrawingInsets = windowInsets.getInsets(
systemBars() or displayCutout() or ime()
)
val displayMetrics = myWebView.context.assets.displayMetrics
// Convert uncooked pixels to density impartial pixels
val prime = pxToDp(safeDrawingInsets.prime.toFloat(), displayMetrics)
val proper = pxToDp(safeDrawingInsets.proper.toFloat(), displayMetrics)
val backside = pxToDp(safeDrawingInsets.backside.toFloat(), displayMetrics)
val left = pxToDp(safeDrawingInsets.left.toFloat(), displayMetrics)
val safeAreaJs = """
doc.documentElement.fashion.setProperty('--safe-area-inset-top', '${prime}px');
doc.documentElement.fashion.setProperty('--safe-area-inset-right', '${proper}px');
doc.documentElement.fashion.setProperty('--safe-area-inset-bottom', '${backside}px');
doc.documentElement.fashion.setProperty('--safe-area-inset-left', '${left}px');
"""
// Inject the density impartial pixels into the CSS variables as CSS pixels
myWebView.evaluateJavascript(safeAreaJs, null)
windowInsets
}
myWebView.webViewClient = object : WebViewClient() {
override enjoyable onPageFinished(view: WebView, url: String) {
tremendous.onPageFinished(myWebView, url)
// dispatch insets as a result of insets aren't utilized when the webpage first masses.
view.requestApplyInsets()
}
}
}
Be aware: If the injected insets look too giant, it may be as a result of your web site’s Viewport initial-scale is greater than 1. On this case, divide the highest, proper, backside, and left variables by the size.
To make sure your internet web page appropriately adjusts when the on-screen keyboard (IME) seems, you should additionally account for IME insets. The offered Compose and Views code instance already contains this by retrieving WindowInsets.safeDrawing() in Compose or WindowInsetsCompat.Sort.ime() in Views:
// Getting IME insets from the above Compose code pattern
// safeDrawing contains ime, systemBars, and displayCutout
WindowInsets.safeDrawing// Getting IME insets from the above Views code pattern
val safeDrawingInsets = windowInsets.getInsets(
systemBars() or displayCutout() or ime()
)
Omitting IME insets will result in points the place the webpage content material is obscured by the keyboard. For instance, when the person faucets an HTML textual content enter and the IME expands, the webpage gained’t resize, stopping the person from seeing content material hidden behind the keyboard. By together with IME insets, the webpage dynamically adjusts its format when the IME is displayed, making certain all content material stays accessible to the person.
In abstract, making certain WebViews are appropriate with an edge-to-edge show requires completely different approaches relying on whether or not the app owns the net content material. For exterior content material, wrap the WebView in a container and apply insets as padding. For owned content material, both pad the container or inject JavaScript to deal with insets throughout the webpage.