Jetpack Compose: How to use WebView

Introduction

Jetpack Compose has become a popular framework for developing Android applications with its modern declarative approach. Despite its flexibility, there are still some scenarios where developers need to integrate traditional Android views, such as WebView, into their Compose-based UI. This can be achieved by combining the power of Jetpack Compose with Android's AndroidView interop feature, allowing developers to embed native Android views within a Compose layout. In this article, we’ll walk through how to use a WebView inside a Jetpack Compose project and provide details on how to configure it effectively.

This Kotlin example demonstrates the use of WebView within a Jetpack Compose UI. We will break down the code into sections, explaining the setup, how to integrate WebView within Compose, and how to apply necessary settings for a smooth browsing experience. Let’s take a closer look at how this is done.

Main Structure: Setting Up Compose Scaffold

The entry point for the app is MainActivity, which inherits from AppCompatActivity. The onCreate() method is overridden to set the content using Compose’s setContent method. Inside this method, a composable function GetScaffold() is called, which serves as the main layout of the app. This layout consists of a Scaffold, a composable that provides a basic structure for a screen. The Scaffold includes a TopAppBar with a title and a content section that loads the MainContent() function.

The TopAppBar is styled with a custom background color using Color(0xFFC0E8D5), and the title text "Compose - Using WebView" is displayed. The background color for the overall content area is also customized, giving the UI a clean, modern look.

Main Content: Integrating WebView

In the MainContent() composable function, the first key element is the remember function, which stores the state of the URL the WebView will load. This state, represented by url, allows dynamic updates of the URL when the user interacts with buttons. The Box and Column composables are used to arrange the layout, where the Box takes up the majority of the screen and holds the WebView.

To integrate WebView into Jetpack Compose, the AndroidView composable is used. This allows embedding any Android view (such as WebView) into a Compose layout. The AndroidView’s factory parameter creates the WebView instance, and the applySettings() extension method is called to configure the WebView's behavior. Additionally, the update parameter of AndroidView reloads the URL whenever the url state changes, ensuring the correct page is loaded based on user interactions.

Button Controls: Switching Between URLs

The bottom section of the UI features a row of buttons that allow the user to navigate between different websites. This is done using a Row composable that contains three buttons: one for Google, one for Yahoo, and another for Example.com. Each button is associated with an onClick listener that updates the url state with the corresponding URL. Once the url state is updated, the WebView in the AndroidView automatically reloads the new URL.

The buttons are styled with padding and rounded corners using RoundedCornerShape, ensuring a visually appealing and user-friendly interface. The use of Arrangement.spacedBy(12.dp) ensures proper spacing between the buttons.

Applying WebView Settings

The applySettings() function is an extension method that configures various settings for the WebView. These settings are essential for improving user experience and compatibility with modern web standards. For example, JavaScript is enabled with settings.javaScriptEnabled = true, and caching is set up using setAppCacheEnabled(true) and setAppCachePath(), which helps optimize loading times.

Other important configurations include enabling zoom controls, setting text zoom to 100%, and ensuring that images are loaded automatically by setting loadsImagesAutomatically = true. The method also includes checks for Android version compatibility, such as enabling safe browsing for devices running Android Oreo (API level 26) and above.

Additionally, the method enables important features like DOM storage, geolocation, multiple windows, and hardware acceleration. These settings help make the WebView behave more like a fully functional browser, handling complex web pages effectively.

Summary

This Kotlin example demonstrates how to integrate a WebView into a Jetpack Compose-based UI using the AndroidView composable. By managing the URL state and applying comprehensive settings through an extension method, the WebView offers a robust browsing experience within the app. The use of buttons to switch between URLs adds interactivity, and the overall structure of the code showcases the flexibility of combining Compose with traditional Android views.

In conclusion, this approach allows developers to maintain the declarative nature of Jetpack Compose while leveraging existing Android views like WebView. Whether you're building a web-based app or simply need to display web content, this example provides a clear and effective method for achieving that in your Compose projects.


MainActivity.kt

package com.cfsuman.jetpackcompose

import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.webkit.WebSettings
import android.webkit.WebView
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedObjectState: Bundle?) {
        super.onCreate(savedObjectState)
        setContent {
            GetScaffold()
        }
    }


    @Composable
    fun GetScaffold(){
        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text(
                        text = "Compose - Using WebView"
                    )},
                    backgroundColor = Color(0xFFC0E8D5),
                )
            },
            content = {MainContent()},
            backgroundColor = Color(0xFFEDEAE0),
        )
    }


    @Composable
    fun MainContent(){
        val url = remember { mutableStateOf("https://www.google.com")}
        Box(
            modifier = Modifier.fillMaxSize(),
        ){
            Column(
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Box(
                    modifier = Modifier
                        .weight(2F)
                ) {
                    AndroidView(factory = { context ->
                        WebView(context).apply {
                            applySettings()
                            loadUrl("https://www.google.com")
                        }
                    },update = {
                        it.loadUrl(url.value)
                    })
                }

                Row(
                    modifier = Modifier
                        .padding(8.dp)
                        .clip(RoundedCornerShape(12.dp))
                        .wrapContentHeight(Alignment.CenterVertically),
                    horizontalArrangement = Arrangement.spacedBy(12.dp),
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Button(onClick = {
                        url.value = "https://www.google.com"
                    }) {
                        Text(text = "Google")
                    }
                    Button(onClick = {
                        url.value = "https://www.yahoo.com"
                    }) {
                        Text(text = "Yahoo")
                    }
                    Button(onClick = {
                        url.value = "https://example.com"

                    }) {
                        Text(text = "Example")
                    }
                }
            }
        }
    }
}


// extension method to apply web view settings
fun WebView.applySettings(){
    val layoutParams = LinearLayout.LayoutParams(
        LinearLayout.LayoutParams.MATCH_PARENT,
        LinearLayout.LayoutParams.MATCH_PARENT
    )
    // or use this
    val linearlayoutParams = FrameLayout.LayoutParams(
        FrameLayout.LayoutParams.MATCH_PARENT,
        FrameLayout.LayoutParams.MATCH_PARENT
    )
    this.layoutParams = layoutParams

    //clearCache(true)
    clearCache(true)

    // Get the web view settings instance
    val settings = settings

    // Enable java script in web view
    settings.javaScriptEnabled = true

    // Enable and setup web view cache
    settings.setAppCacheEnabled(true)
    settings.cacheMode = WebSettings.LOAD_DEFAULT
    settings.setAppCachePath(context.cacheDir.path)

    // Enable zooming in web view
    settings.setSupportZoom(false)
    settings.builtInZoomControls = true
    settings.displayZoomControls = true

    // Zoom web view text
    settings.textZoom = 100

    // Enable disable images in web view
    settings.blockNetworkImage = false
    // Whether the WebView should load image resources
    settings.loadsImagesAutomatically = true

    // More web view settings
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        settings.safeBrowsingEnabled = true  // api 26
    }

    //settings.pluginState = WebSettings.PluginState.ON
    settings.useWideViewPort = true
    settings.loadWithOverviewMode = true
    settings.javaScriptCanOpenWindowsAutomatically = true
    settings.mediaPlaybackRequiresUserGesture = false

    // More optional settings, you can enable it by yourself
    settings.domStorageEnabled = true
    settings.setSupportMultipleWindows(true)
    settings.loadWithOverviewMode = true
    settings.allowContentAccess = true
    settings.setGeolocationEnabled(true)
    settings.allowUniversalAccessFromFileURLs = true
    settings.allowFileAccess = true

    // WebView settings
    fitsSystemWindows = true
    setLayerType(View.LAYER_TYPE_HARDWARE, null)
}
More android jetpack compose tutorials