WebView for JetBrains Compose Multiplatform


🚨 Seeking Desktop Maintainer 🚨
Unfortunately, our current desktop platform maintainer, @DATL4G, cannot continue his role due to other commitments. We are now looking for a new team member who can take over the desktop side of the library, handling development, issue resolution, and feature enhancements. If you're a desktop developer ready to dive into open-source, we want you!
Contact: zoumingjie17@163.com
Note
This library is built using
the compose multiplatform library template.
It supports automatic package publishing, documentation, and code style checking.
This library can be considered as the Multiplatform version
of Compose WebView library.
It provides the basic WebView functionalities for JetBrains Compose Multiplatform, which supports
loading URLs, HTML, and post data. Currently, it supports the platforms of Android, iOS, and
Desktop.
For more information, visit the
documentation: https://kevinnzou.github.io/compose-webview-multiplatform/
Basic Usage
Note: Developers targeting the Desktop platform should refer
to README.desktop.md
for setup instructions first.
To use this widget, two key APIs are needed: WebView, which provides the layout, and
rememberWebViewState(url) which provides some remembered state including the URL to display.
The basic usage is as follows:
val state = rememberWebViewState("https://example.com")
WebView(state)
This will display a WebView in your Compose layout that shows the URL provided.
WebView State
This library provides a WebViewState class as a state holder to hold the state for the WebView.
It can be created using the rememberWebViewState function, which can be remembered across
Compositions.
Developers can use the WebViewState to get the loading information of the WebView, such as the
loading progress, the loading status, and the URL of the current page.
Column {
val state = rememberWebViewState("https://github.com/KevinnZou/compose-webview-multiplatform")
Text(text = "${state.pageTitle}")
val loadingState = state.loadingState
if (loadingState is LoadingState.Loading) {
LinearProgressIndicator(
progress = loadingState.progress,
modifier = Modifier.fillMaxWidth()
)
}
WebView(
state
)
}
WebView Navigator
This library provides a WebViewNavigator class to control over the navigation of a WebView from
outside the composable. E.g.for performing a back navigation in response to the user clicking the "
up" button in a TopAppBar.
It can be used to load a new URL, evaluate the JavaScript, and go back and forward in the history.
It can be created using the rememberWebViewNavigator function, which can be remembered across
Compositions.
val navigator = rememberWebViewNavigator()
@Composable
fun rememberWebViewNavigator(
coroutineScope: CoroutineScope = rememberCoroutineScope()
): WebViewNavigator = remember(coroutineScope) { WebViewNavigator(coroutineScope) }
Developers can use the WebViewNavigator to control the navigation of the WebView.
val navigator = rememberWebViewNavigator()
Column {
val state = rememberWebViewState("https://example.com")
val navigator = rememberWebViewNavigator()
TopAppBar(
title = { Text(text = "WebView Sample") },
navigationIcon = {
if (navigator.canGoBack) {
IconButton(onClick = { navigator.navigateBack() }) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription =
)
}
}
}
)
Text(text = )
loadingState = state.loadingState
(loadingState LoadingState.Loading) {
LinearProgressIndicator(
progress = loadingState.progress,
modifier = Modifier.fillMaxWidth()
)
}
WebView(
state = state,
navigator = navigator
)
}
Communication between WebView and Native
Starting from version 1.8.0, this library provides a WebViewJsBridge to allow developers to
communicate between the WebView and Native.
Developers can use the JsBridge to register a handler to handle the message from the WebView.
val jsBridge = rememberWebViewJsBridge()
LaunchedEffect(jsBridge) {
jsBridge.register(GreetJsMessageHandler())
}
The handler should implement the IJsMessageHandler interface.
Developers can use the window.kmpJsBridge.callNative to send a message to the Native.
It receives three parameters:
- methodName: the name of the handler registered in the Native.
- params: the parameters to send to the Native. It needs to be a JSON string.
- callback: the callback function to handle the response from the Native. It receives a JSON string
as the parameter. Pass null if no callback is needed.
window.kmpJsBridge.callNative = function (methodName, params, callback) {
...
}
Here is an example:
window.kmpJsBridge.callNative("Greet",JSON.stringify({message:"Hello"}),
function (data) {
document.getElementById("subtitle").innerText = data;
console.log( + data);
}
);
Note: Starting from version 1.8.6, the name of the JsBridge is configurable. Developers can
configure it in the rememberWebViewJsBridge method.
This library uses the kmpJsBridge as the default.
Request Interceptor
Starting from version 1.9.8, this library provides a RequestInterceptor to allow developers to
intercept the request and modify the request headers. It only supports the Android and iOS platform
for now.
interface RequestInterceptor {
fun onInterceptUrlRequest(
request: WebRequest,
navigator: WebViewNavigator,
): WebRequestInterceptResult
}
The onInterceptUrlRequest method will be called when the WebView sends a request.
Developers can implement the RequestInterceptor interface to define their own interceptor.
Then they can pass it to the rememberWebViewNavigator method to intercept the request.
val navigator =
rememberWebViewNavigator(
requestInterceptor =
object : RequestInterceptor {
override fun onInterceptUrlRequest(
request: WebRequest,
navigator: WebViewNavigator,
): WebRequestInterceptResult {
(request.url.contains()) {
WebRequestInterceptResult.Modify(
WebRequest(
url = ,
headers = mutableMapOf( to ),
),
)
} {
WebRequestInterceptResult.Allow
}
}
},
)
WebSettings
Starting from version 1.3.0, this library allows users to customize web settings.
Some common web settings can be shared across different platforms, such as
isJavaScriptEnabled and userAgent.
class WebSettings {
var isJavaScriptEnabled = true
var customUserAgentString: String? = null
val androidWebSettings = PlatformWebSettings.AndroidWebSettings()
val desktopWebSettings = PlatformWebSettings.DesktopWebSettings()
val iOSWebSettings = PlatformWebSettings.IOSWebSettings
}
For platform-specific settings, this library provides the PlatformWebSettings.
These settings will only be applied to the respective platforms and will not affect other platforms.
Developers can configure custom settings in the shared code in the following way:
val webViewState = rememberWebViewStateWithHTMLData(
data = html
)
DisposableEffect(Unit) {
webViewState.webSettings.apply {
isJavaScriptEnabled = true
androidWebSettings.apply {
isAlgorithmicDarkeningAllowed = true
safeBrowsingEnabled = true
}
}
onDispose { }
}
HTML
This library supports loading HTML data and HTML files.
HTML Data
Developers can load HTML data in the following way:
val html = """
<html>
<body>
<h1>Hello World</h1>
</body>
</html>
""".trimIndent()
val webViewState = rememberWebViewStateWithHTMLData(
data = html
)
HTML File
Developers can load HTML files in the following way:
val webViewState = rememberWebViewStateWithHTMLFile(
fileName = "index.html"
)
Note that the HTML file should be put in the resources/assets folder of the shared module.
It also supports external resources such as images, CSS, and JavaScript files on Android and iOS.
Desktop support is coming soon.
Handling permission requests on Android
There are 4 types of permissions that can be requested by the WebView on Android:
- RESOURCE_PROTECTED_MEDIA_ID
- RESOURCE_MIDI_SYSEX
- RESOURCE_AUDIO_CAPTURE
- RESOURCE_VIDEO_CAPTURE
RESOURCE_PROTECTED_MEDIA_ID and RESOURCE_MIDI_SYSEX are special ones, because they don't have a
native Android counterpart, so it's not possible to request a permission from the user to grant
them transitively. Therefore, you configure the WebView to grant these permissions automatically,
by setting the respective properties under AndroidWebSettings to true:
webViewState.webSettings.apply {
androidWebSettings.apply {
allowProtectedMedia = true
allowMidiSysexMessages = true
}
}
RESOURCE_AUDIO_CAPTURE and RESOURCE_VIDEO_CAPTURE are also handled internally by the WebView,
but you need to make sure to explicitly ask for these permissions in your app. If user grants them,
the WebView will be able to use them without any additional configuration.
API
The complete API of this library is as follows:
Example
A simple example would be like this:
@Composable
internal fun WebViewSample() {
MaterialTheme {
val webViewState =
rememberWebViewState("https://github.com/KevinnZou/compose-webview-multiplatform")
Column(Modifier.fillMaxSize()) {
val text = webViewState.let {
"${it.pageTitle ?: ""} ${it.loadingState} ${it.lastLoadedUrl ?: }"
}
Text(text)
WebView(
state = webViewState,
modifier = Modifier.fillMaxSize()
)
}
}
}
For a full example, please refer
to BasicWebViewSample
Download

You can add this library to your project using Gradle.
Multiplatform
To add to a multiplatform project, add the dependency to the common source-set:
repositories {
mavenCentral()
maven("https://jogamp.org/deployment/maven")
}
kotlin {
sourceSets {
commonMain {
dependencies {
api("io.github.kevinnzou:compose-webview-multiplatform:1.9.20")
}
}
}
}
Single Platform
For an Android-only project, you directly can use
my another library.
Add the dependency to app-level build.gradle.kts:
repositories {
mavenCentral()
}
dependencies {
implementation("io.github.KevinnZou:compose-webview:0.33.3")
}
Publications
The following is a list of articles/blog posts that have been published discussing this plugin:
- Web Everywhere: Introducing our Compose Multiplatform WebView Library
- What’s new in Compose Multiplatform WebView 1.5.0:Migrated to CEF Browser & Cookie Management Supported
- Loading Local HTML files in Compose Multiplatform WebView
- Bridging the Gap: Empowering JS and Native Communication in Compose Multiplatform WebView Library Version 1.8.0