Best Website for Jetpack Compose App Development

Android Jetpack Compose

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

WebView in Jetpack Compose

WebView in Jetpack Compose - Responsive Blogger Template
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 Bihar