Skip to content
Back to archipelago.banuacoder.com

Feature SDKs

How to add a WebView to a Flutter app ​

Free Reference for webview_sdk: every variable, option and package it generates. Command: archipelago generate webview_sdk

Generate the WebView SDK, set platform permissions, embed the widget and its callbacks, intercept custom URI schemes, and call JavaScript from Dart.

What you'll learn ​

  • Picking webview_sdk over webview_sdk_advanced (they're independent, pick one)
  • Generating the self-contained WebView SDK
  • Configuring platform permissions for Android and iOS
  • Embedding the WebView widget and wiring up its callbacks
  • Intercepting custom URI schemes for in-app navigation
  • Making one-way calls from Dart into the page's JavaScript context

Prerequisites ​

Step 1: Pick a Package, Basic or Advanced ​

Archipelago ships two independent WebView bricks. Install the one that matches your needs, not both:

BrickTierUse it when...
webview_sdk (this tutorial)FreeYou need URL loading, navigation controls, progress tracking, one-way JS calls, and URI scheme interception
webview_sdk_advancedProYou additionally need a bidirectional JS bridge, request interceptors, or the mini-app container

webview_sdk_advanced does not require webview_sdk to be generated first, and does not extend or build on it. It is not an upgrade path. Installing both in the same app is not a supported layering model.

Step 2: Generate the WebView SDK ​

bash
archipelago generate webview_sdk

This brick has no configuration variables. Generation is non-interactive. There is no prompt to answer and no config file to pass.

Step 3: Understand the Generated Structure ​

webview_sdk generates one self-contained package. The contract and the flutter_inappwebview implementation sit together, with no API/Impl split:

text
features/webview/
└── webview/
    └── lib/src/
        ├── webview_sdk.dart                      # WebViewSDK contract
        ├── webview_sdk_impl.dart                 # flutter_inappwebview-backed impl
        ├── webview_controller.dart               # WebViewController interface
        ├── webview_inappwebview_controller.dart  # Concrete controller
        ├── di/
        │   └── webview_global_module.dart        # WebViewGlobalModule
        └── models/
            ├── webview_config.dart               # WebViewConfig value class
            ├── webview_intercepted_url.dart      # URI intercept payload
            ├── webview_load_progress.dart
            ├── webview_load_state.dart
            └── webview_navigation_event.dart

The generator also registers WebViewGlobalModule in lib/di/injector.dart and adds the webview path dependency to your app pubspec.yaml. You write no registration code. WebViewSDK is a lazy singleton, so nothing runs at boot.

Step 4: Configure Platform Permissions ​

Android ​

Apps generated by Archipelago already declare the Internet permission in android/app/src/main/AndroidManifest.xml. For an older app, add it inside the <manifest> tag:

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

iOS ​

If you need to load plain HTTP URLs, add App Transport Security to ios/Runner/Info.plist:

xml
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

HTTPS-only apps skip this block.

Step 5: Embed the WebView Widget ​

dart
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:webview/webview.dart';

class TradePage extends StatefulWidget {
  const TradePage({required this.webViewSDK, super.key});

  final WebViewSDK webViewSDK;

  @override
  State<TradePage> createState() => _TradePageState();
}

class _TradePageState extends State<TradePage> {
  late final WebViewInAppWebViewController _controller;
  final _config = const WebViewConfig(
    initialUrl: 'https://example.com/trade',
    interceptedSchemes: ['app', 'tg'],
  );

  @override
  void initState() {
    super.initState();
    // widget.webViewSDK is passed in by the route, not read from GetIt
    _controller = widget.webViewSDK.createController(_config)
        as WebViewInAppWebViewController;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: InAppWebView(
        initialUrlRequest: URLRequest(url: WebUri(_config.initialUrl)),
        onWebViewCreated: (c) => _controller.attachNativeController(c),
        shouldOverrideUrlLoading: _controller.shouldOverrideUrlLoading,
        onProgressChanged: _controller.onProgressChanged,
        onLoadStart: _controller.onLoadStart,
        onLoadStop: _controller.onLoadStop,
        onReceivedError: _controller.onReceivedError,
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

WebViewSDK.isAvailable returns false on platforms flutter_inappwebview does not support, such as web and desktop. createController returns null in that case. Check it before casting if your app targets those platforms.

Step 6: Intercept Custom URI Schemes ​

interceptStream is a Stream<WebViewInterceptedUrl> that fires whenever the WebView navigates to a scheme listed in WebViewConfig.interceptedSchemes. No corresponding navigationStream event fires for an intercepted URL. The platform layer cancels the navigation itself.

dart
@override
void initState() {
  super.initState();
  // ... controller setup ...

  _controller.interceptStream.listen((event) {
    // event.url    — the full Uri that was intercepted
    // event.scheme — e.g. 'app' or 'tg'
    final productId = event.url.pathSegments.last;
    Navigator.pushNamed(context, '/trade/$productId');
  });
}

The web page triggers an intercept by navigating to a custom URL:

html
<a href="app://trade/BTC-IDR">Open in app</a>

Step 7: Call JavaScript from Dart ​

For one-way, fire-and-forget calls from Dart into the page's JavaScript context:

dart
await _controller.runJavaScript('window.showBanner("Order filled!")');

Need a response back from JavaScript, or bidirectional messaging? That requires the Pro-tier webview_sdk_advanced brick, which adds a full window.archipelago.* JS bridge with Promise-based responses. See WebView SDK Advanced.

Common Customizations ​

CustomizationWhere to Change
Change initial URL at runtimePass a different initialUrl in WebViewConfig
Add more intercepted schemesExtend interceptedSchemes list in WebViewConfig
Restrict which schemes can load at allOverride allowedSchemes (defaults to ['http', 'https'])
Show a loading indicatorListen to progressStream (WebViewLoadProgress, 0.0 → 1.0)
Handle navigation errorsListen to progressStream for a WebViewLoadState.error state
Clear cookies/local storageCall controller.clearCookies()

Next Steps ​