Introduction
In the world of Android development, Jetpack Compose has quickly become a favored tool for building modern, responsive UIs. It simplifies the UI code by offering declarative syntax and allowing developers to focus more on building a seamless user experience. However, integrating features that interact with the native Android components, like WebView, can still be a bit challenging for developers who are more accustomed to the imperative approach. In this example, we will explore how to display a WebView with a progress indicator using Jetpack Compose. This tutorial walks through a simple app that displays the page load progress as a percentage when using WebView in Jetpack Compose.
This example uses a combination of Jetpack Compose for UI and Android's native WebView to load and display web pages. We will break down the code to understand how the scaffold structure works in Compose, how to integrate native Android components (WebView), and how to show the loading progress dynamically using a circular progress bar.
MainActivity Overview
The MainActivity
class inherits from AppCompatActivity
, and its onCreate
method calls setContent
, which is a Jetpack Compose function to define the app’s UI content. The UI is defined using composable functions that construct and render the elements of the application. The function GetScaffold()
is the main entry point for the composable structure, and it encapsulates the app’s toolbar and the web content area.
Scaffold and TopAppBar
The Scaffold
composable provides a structure with slots for different UI elements such as a TopAppBar
and a content area. Here, the TopAppBar
is used to display a simple title "Compose - WebView Progress Percentage." This bar has a background color set to a light greenish shade using the Color(0xFFC0E8D5)
hex code. The Scaffold
function also sets a background color for the entire screen and passes MainContent()
to its content slot, which holds the main part of the user interface.
MainContent and State Management
The MainContent()
composable is where the core logic of the WebView and progress indicator lives. Several key state variables are defined using the remember
and mutableStateOf
functions. These variables help track the URL to be loaded (url
), the visibility of the progress indicator (visibility
), and the current progress value (progress
). These states are reactive and are updated as the WebView loads pages.
The UI elements in MainContent()
are arranged using Jetpack Compose's Box
and Column
layouts. The Row
layout is used to align the buttons and the progress indicator horizontally. There are two buttons labeled "MSN" and "Yahoo," which, when clicked, change the url
state, triggering the WebView to load the corresponding page.
WebView and Progress Handling
The WebView is integrated into Jetpack Compose through the AndroidView
composable, which allows you to use native Android views within a Compose layout. Inside AndroidView
, a WebView
is created and configured. The JavaScript support is enabled via settings.javaScriptEnabled = true
, allowing modern websites to function properly.
To handle the web page loading process, two listeners are implemented. The WebViewClient
is responsible for detecting when a page starts and finishes loading. When a page starts, the onPageStarted
callback is triggered, setting the visibility of the progress indicator to true
. When the page finishes, onPageFinished
sets the visibility back to false
.
The WebChromeClient
is used to track the actual page load progress. The onProgressChanged
callback monitors the progress, updating the progress
state, which is displayed to the user as a percentage. As the progress changes, the UI automatically re-renders to reflect the new value in both the circular progress indicator and the percentage text next to it.
Displaying Progress to the User
The progress indicator consists of a CircularProgressIndicator
composable, which shows a spinning circle, and a Text
composable displaying the percentage value. These elements only become visible when visibility.value
is true
, which happens during page loading. The percentage value is rounded using roundToInt()
to avoid floating-point values, offering a more user-friendly display.
Summary
This Jetpack Compose example demonstrates how to seamlessly integrate Android's native WebView within a modern declarative UI framework. The app is built around state management in Compose to handle URL changes, display a circular progress indicator, and show the current loading percentage. By using AndroidView
, developers can bridge the gap between native Android components and Jetpack Compose, enhancing flexibility.
In summary, this project offers a straightforward solution for developers looking to combine Jetpack Compose's power with native views, providing a polished and responsive UI. It serves as a great starting point for anyone aiming to integrate complex, interactive components like WebView in a Compose-based Android app.
package com.cfsuman.jetpackcompose
import android.graphics.Bitmap
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.webkit.WebViewClient
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.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import kotlin.math.roundToInt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedObjectState: Bundle?) {
super.onCreate(savedObjectState)
setContent {
GetScaffold()
}
}
@Composable
fun GetScaffold(){
Scaffold(
topBar = {
TopAppBar(
title = { Text(
text = "Compose - WebView Progress Percentage"
)},
backgroundColor = Color(0xFFC0E8D5),
)
},
content = {MainContent()},
backgroundColor = Color(0xFFEDEAE0),
)
}
@Composable
fun MainContent(){
val url = remember { mutableStateOf("https://www.msn.com")}
val visibility = remember { mutableStateOf(false)}
val progress = remember { mutableStateOf(0.0F)}
Box(
modifier = Modifier.fillMaxSize(),
){
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
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.msn.com"
}) {
Text(text = "MSN")
}
Button(onClick = {
url.value = "https://www.yahoo.com"
}) {
Text(text = "Yahoo")
}
}
if (visibility.value){
CircularProgressIndicator(
color = Color(0xFFE30022)
)
Text(
text = "${progress.value.roundToInt()}%",
fontWeight = FontWeight.Bold
)
}
}
Box(
modifier = Modifier
.weight(2F)
) {
AndroidView(factory = { context ->
WebView(context).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
settings.javaScriptEnabled = true
webViewClient = object: WebViewClient(){
override fun onPageStarted(
view: WebView, url: String,
favicon: Bitmap?) {
visibility.value = true
}
override fun onPageFinished(
view: WebView, url: String) {
visibility.value = false
}
}
// Set web view chrome client
webChromeClient = object: WebChromeClient(){
override fun onProgressChanged(
view: WebView, newProgress: Int) {
progress.value = newProgress.toFloat()
}
}
loadUrl(url.value)
}
},update = {
it.loadUrl(url.value)
})
}
}
}
}
}
- jetpack compose - Room add remove update
- jetpack compose - How to use WebView
- jetpack compose - WebView ProgressIndicator
- jetpack compose - Backdrop scaffold
- jetpack compose - Background brush
- jetpack compose - Pass onClick event to function
- jetpack compose - How to use TabRow
- jetpack compose - Get screen orientation
- jetpack compose - Get screen width height in dp
- jetpack compose - How to change SystemBars color
- jetpack compose - How to hide status bar
- jetpack compose ktor - How to get api data
- jetpack compose - Get text from url
- jetpack compose - Kotlinx serialization lenient parsing
- jetpack compose - Kotlinx serialization ignore unknown keys