ComposeNativeWebView

repository·main·Indexed 41 days ago

https://github.com/kdroidfilter/composenativewebview

A Compose Multiplatform WebView library providing a unified API for Android, iOS, Desktop (JVM), and WasmJs. It features a high-performance native desktop embedding via Nucleus Tao, a JS ↔ Kotlin bridge, and support for request interception, cookie management, and custom User-Agents. On Desktop, it supports WebKit2GTK (Linux), WKWebView (macOS), and WebView2 (Windows); on WasmJs, it utilizes an IFrame backend.

Tokens
4.1K
Snippets
14
Records
21
Agent score
51%

What's inside ComposeNativeWebView

  1. How WebViewState and WebViewNavigator work

    main

    The library uses two primary objects to control the WebView:

    1. WebViewState: Created via rememberWebViewState. It holds the observable state of the web page (like isLoading, pageTitle, and lastLoadedUrl) and configuration like webSettings (User-Agent, logging) and cookieManager.
    2. WebViewNavigator: Created via rememberWebViewNavigator. It provides imperative commands to control the browser, such as loadUrl, navigateBack, navigateForward, and evaluateJavaScript.

    You can initialize the state with a URL, inline HTML, or resource files.

    val state = rememberWebViewState(
      url = "https://example.com"
    ) {
      customUserAgentString = "MyApp/1.0"
    }
    
    val navigator = rememberWebViewNavigator()
    
    WebView(state, navigator)
  2. Desktop backend requirements (Nucleus Tao)

    main

    The Desktop (JVM) implementation requires the Nucleus Tao backend. The old Wry backend has been removed. To host the WebView on Desktop, you must use nucleusApplication with the Tao backend and include the decorated-window-tao dependency.

    Why Tao? It allows the native WebView to be embedded in the same window stack as Compose, enabling you to draw Compose UI (toolbars, dialogs, overlays) directly on top of the WebView.

    // App entry requirement
    nucleusApplication(backend = NucleusBackend.Tao) { 
        // ... 
    }
  3. Quick start with ComposeNativeWebView

    main

    To use the WebView in a Compose Multiplatform application, use rememberWebViewState to manage the URL and state, then pass it to the WebView composable.

    @Composable
    fun App() {
      val state = rememberWebViewState("https://example.com")
      WebView(state, Modifier.fillMaxSize()) {
        // Optional Compose overlay on top of the native WebView
        // (NativeView content slot on desktop; Box overlay elsewhere).
      }
    }
  4. WasmJs platform limitations

    main

    When using the WasmJs target (which uses an IFrame backend), be aware of the following limitations:

    • Navigation: Back and forward navigation is not available.
    • CORS: The IFrame will only work if the target website has appropriate CORS configurations.
    • JavaScript: evaluateJavaScript can only be executed on the same origin.
    • Cookies: Cookies can only be set if the destination of the iframe is the same as the parent destination.
  5. Use WebView in Compose Multiplatform

    main

    To display web content, use the WebView composable along with rememberWebViewState to manage the loading state and URL.

    1. Initialize the state using rememberWebViewState(url).
    2. Pass the state to the WebView composable.
    3. Apply a Modifier to control the size and layout of the web view.
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.runtime.Composable
    import androidx.compose.ui.Modifier
    import dev.nucleusframework.webview.web.WebView
    import dev.nucleusframework.webview.web.rememberWebViewState
    
    @Composable
    fun App() {
        val state = rememberWebViewState("https://sample.com")
        WebView(state = state, modifier = Modifier.fillMaxSize())
    }
  6. Configure WebView settings and cookies

    main

    You can manage browser settings and cookies through the WebViewState object.

    Settings:

    • state.webSettings.customUserAgentString: Set a custom User-Agent.
    • state.webSettings.logSeverity: Set the logging level (e.g., KLogSeverity.Debug).

    Cookies: Use the cookieManager for unified cookie management across platforms:

    • setCookie(...)
    • getCookies(url)
    • removeCookies(url)
    • removeAllCookies()
  7. Use the JS ↔ Kotlin bridge

    main

    The library provides a callback-based bridge that allows JavaScript to call native Kotlin code.

    • Injection Timing: On Desktop, the bridge is injected at document start. On Android, iOS, and WasmJs, it is injected after the page loads.
    • Customization: You can specify a custom name for the bridge using WebViewJsBridge(jsBridgeName = "myBridge").
    • Usage: In JavaScript, call the bridge using the callNative method on the injected object.

    Example (Default name kmpJsBridge):

    window.kmpJsBridge.callNative("echo", {...}, callback)
  8. Implement a RequestInterceptor

    main

    You can intercept navigator-initiated navigations (but not sub-resources) by overriding onInterceptUrlRequest. This is useful for blocking specific URLs, implementing app-driven routing, or enforcing security rules.

    override fun onInterceptUrlRequest(
      request: WebRequest,
      navigator: WebViewNavigator
    ): WebRequestInterceptResult
  9. Monitor loading progress with HtmlLoadingState

    main

    The HtmlLoadingState sealed class allows you to track the lifecycle of an HTML view's loading process:

    • Initializing: The initial state before loading begins.
    • Loading: Content is currently being fetched or processed.
    • Finished: Loading has completed. Use the isError boolean to check for success, and errorMessage to retrieve details if isError is true.
    when (state.loadingState) {
        is HtmlLoadingState.Initializing -> { /* Show placeholder */ }
        is HtmlLoadingState.Loading -> { /* Show progress bar */ }
        is HtmlLoadingState.Finished -> {
            if (state.loadingState.isError) {
                println("Error: ${state.loadingState.errorMessage}")
            } else {
                println("Load successful")
            }
        }
    }