Best Premium Templates For App and Software Downloading Site. Made By HIVE-Store

Android App Development

Stay ahead with the latest tools, trends, and best practices in Android development

WebView in Jetpack Compose

WebView in Jetpack Compose - Coding Bihar
WebView in Jetpack Compose: A Beginner-Friendly Tutorial

WebView in Jetpack Compose: A Beginner-Friendly Tutorial

If you're building an Android app with Jetpack Compose and want to display a website inside your app, you might be asking: "Where is the WebView in Compose?" Jetpack Compose doesn’t offer a native WebView yet — but don't worry. In this tutorial, you'll learn how to integrate the traditional WebView in your Compose app using AndroidView.

✅ What You’ll Learn

  • How to add WebView in Jetpack Compose
  • Enable JavaScript support
  • Support file upload from web pages
  • Handle back button navigation

👉Step 1: Set Up Your Project

Create a new project in Android Studio using the Empty Compose Activity template. Then open your AndroidManifest.xml and add this permission:

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

This lets your app access websites from the internet.

👉Step 2: Add WebView Using AndroidView

Jetpack Compose provides a helper called AndroidView to use classic Android views like WebView.

@Composable
fun SimpleWebView(urlToRender: String) {
    AndroidView(factory = { context ->
        WebView(context).apply {
            layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            webViewClient = WebViewClient() // open links inside app
            loadUrl(urlToRender)
        }
    })
}

  • AndroidView: Allows using classic views like WebView inside Compose.
  • WebViewClient: Ensures links open inside the WebView, not in a browser.
  • loadUrl: Loads the web page URL.

Use it in your MainActivity.kt like this:

setContent {
    MaterialTheme {
        WebViewScreen(url = "https://developer.android.com/")
    }
}

⚙️ Step 3: Enable JavaScript

Most websites today need JavaScript. So don’t forget to enable it:

WebView(context).apply {
    settings.javaScriptEnabled = true  // ✅ Enable JavaScript
    webViewClient = WebViewClient()
    loadUrl(urlToRender)
}

👉Step 4: File Upload Support

If your webpage has a file input, use this Composable with ActivityResultLauncher:

var filePathCallback: ValueCallback>? = null
val filePickerLauncher = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.GetContent()
) { uri ->
    uri?.let {
        filePathCallback?.onReceiveValue(arrayOf(it))
        filePathCallback = null
    }
}

AndroidView(factory = { context ->
    WebView(context).apply {
        settings.javaScriptEnabled = true
        webViewClient = WebViewClient()

        webChromeClient = object : WebChromeClient() {
            override fun onShowFileChooser(
                view: WebView?,
                filePathCallbackNew: ValueCallback>?,
                fileChooserParams: FileChooserParams?
            ): Boolean {
                filePathCallback = filePathCallbackNew
                filePickerLauncher.launch("*/*")
                return true
            }
        }

        loadUrl(urlToRender)
    }
})
  • onShowFileChooser: Called when <input type="file" /> is triggered.
  • filePickerLauncher: Opens system file picker and returns the file to the WebView.

👉Step 5: Handle Back Button

Let’s use BackHandler to make the system back button work like a browser’s back button:

val webView = remember { WebView(LocalContext.current) }

BackHandler(enabled = webView.canGoBack()) {
    webView.goBack()
}
  • BackHandler: A Compose utility that overrides the system back button.
  • webView.canGoBack(): Checks if the user has navigated inside WebView.
  • webView.goBack(): Goes to the previous page.

✅ Final Tips

  • Use HTTPS URLs only
  • Enable JavaScript only when necessary
  • Don’t store passwords or auto-fill in sensitive WebViews

👇Full working WebView code in Jetpack Compose with:

✅ WebView inside Jetpack Compose using AndroidView
✅ JavaScript support
✅ File upload support
✅ Back button navigation
✅ Loading indicator and error handling

MainActivity

package com.example.codingcompose

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import com.example.codingcompose.ui.theme.CodingComposeTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            CodingComposeTheme {
                /*Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        name = "Android",
                        modifier = Modifier.padding(innerPadding)
                    )
                }*/
                WebViewScreen(url = "https://developer.android.com/")
            }
        }
    }
}

WebViewScreen

package com.example.codingcompose

import android.annotation.SuppressLint
import android.net.Uri
import android.view.ViewGroup
import android.webkit.ValueCallback
import android.webkit.WebChromeClient
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView

@SuppressLint("SetJavaScriptEnabled")
@Composable
fun WebViewScreen(url: String) {
    val context = LocalContext.current
    val webView = remember { WebView(context) }
    var isLoading by remember { mutableStateOf(true) }
    var hasError by remember { mutableStateOf(false) }
    var filePathCallback: ValueCallback>? by remember { mutableStateOf(null) }

    val filePickerLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent()
    ) { uri ->
        filePathCallback?.onReceiveValue(uri?.let { arrayOf(it) })
        filePathCallback = null
    }

    Box(modifier = Modifier.fillMaxSize()
        .systemBarsPadding()) {
        AndroidView(
            factory = {
                webView.apply {

                    layoutParams = ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT
                    )

                    settings.domStorageEnabled = true
                    settings.javaScriptEnabled = true

                    webViewClient = object : WebViewClient() {
                        override fun onPageFinished(view: WebView?, url: String?) {
                            isLoading = false
                            hasError = false
                        }

                        override fun onReceivedError(
                            view: WebView?,
                            request: WebResourceRequest?,
                            error: WebResourceError?
                        ) {
                            hasError = true
                        }
                    }

                    webChromeClient = object : WebChromeClient() {
                        override fun onShowFileChooser(
                            view: WebView?,
                            filePathCallbackNew: ValueCallback>?,
                            fileChooserParams: FileChooserParams?
                        ): Boolean {
                            filePathCallback = filePathCallbackNew
                            filePickerLauncher.launch("*/*")
                            return true
                        }
                    }

                    loadUrl(url)
                }
            },
            update = { it.loadUrl(url) },
            modifier = Modifier.fillMaxSize()
        )

        // Loading Spinner
        if (isLoading) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(MaterialTheme.colorScheme.background.copy(alpha = 0.6f)),
                contentAlignment = Alignment.Center
            ) {
                CircularProgressIndicator()
            }
        }

        // Error Message
        if (hasError) {
            Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                Text("⚠️ Failed to load the page.", color = MaterialTheme.colorScheme.error)
            }
        }
    }

    // Back button handler
    BackHandler {
        if (webView.canGoBack()) {
            webView.goBack()
        } else {
            // Exit the app if no more history
            (context as? ComponentActivity)?.finish()
        }
    }
}
WebView in Jetpack Compose Screenshot

📌 Summary

Jetpack Compose doesn’t yet have a built-in WebView, but you can easily integrate it with AndroidView. You’ve now learned how to:

  • Add WebView to Compose
  • Enable JavaScript
  • Support file inputs
  • Implement back button support

This makes your app powerful enough to load and interact with modern websites — directly from your Compose UI!

Special Message

Welcome to Coding