Skip to content

webview_sdk_advanced Pro

Self-contained, standalone WebView Advanced feature SDK. Ships its own full WebView capability plus a bidirectional JS bridge (typed handlers + Promises), request/response interceptors for auth header injection, deeplink routing from WebView back to native, and a full mini-app container (manifest registry, bridge dispatcher, lifecycle state machine, capability handlers, and MiniAppContainerPage).

Version: 2.1.0

Independent of webview_sdk

webview_sdk_advanced does not require webview_sdk to be installed first, and does not extend or build on it — generate this brick alone if your app needs the JS bridge and/or mini-app hosting. Installing both is not a supported layering model; pick the one your project needs.

Variables

VariableTypeDefaultDescription
uiKitPackageNamestringapp_ui_kitName of the UI kit package used by the mini-app container

Mini-app hosting is not gated behind a variable — it is the point of this brick. If you don't need it, install webview_sdk (the basic, independent brick) instead.

analyticsInstalled is derived automatically (not a prompted variable) from whether infrastructure/analytics_api exists in the target project — when absent, the analytics bridge capability (handler, imports, tracker param) is stripped from the generated sources and the analytics_api dependency is omitted.

Usage

Interactive

bash
archipelago generate webview_sdk_advanced

Non-interactive (CI)

bash
archipelago generate webview_sdk_advanced --config my_config.json

Generated Package

This brick generates a single, complete, standalone package — it does not patch or inject files into any other package.

features/
└── webview_advanced/
    └── lib/src/
        ├── js_handler.dart                     # Abstract JsHandler<TArgs, TResult> base
        ├── js_handler_registry.dart            # JsHandlerRegistry (first-registration-wins)
        ├── webview_request.dart                # Immutable WebViewRequest value class
        ├── webview_request_interceptor.dart    # WebViewRequestInterceptor interface
        ├── webview_advanced_sdk.dart            # WebViewAdvancedSDK contract
        ├── webview_advanced_sdk_impl.dart       # flutter_inappwebview-backed impl
        ├── webview_controller.dart
        ├── webview_inappwebview_controller.dart
        ├── webview_inappwebview_controller_advanced.dart
        ├── di/
        ├── models/
        │   ├── js_message.dart
        │   ├── webview_advanced_config.dart    # WebViewAdvancedConfig extends WebViewConfig
        │   └── ...
        └── mini_app/
            ├── bridge/
            ├── bridge_dispatcher.dart
            ├── container/
            │   ├── mini_app_container.dart
            │   └── mini_app_container_page.dart
            ├── handlers/                        # analytics_handler.dart omitted when analytics_api is absent
            ├── interceptors/
            ├── lifecycle/
            ├── manifest/
            ├── providers/
            ├── registry/
            ├── tokens/
            └── webview_bridge_binding.dart

Distinct type names

To avoid ambiguous-import collisions if both webview and webview_advanced ever end up in the same workspace (they are both kept in template_base for analyze/tests), the advanced package's FeatureSDK interface is WebViewAdvancedSDK / WebViewAdvancedSDKImpl — not WebViewSDK. Everything else (WebViewController, WebViewConfig, mini-app types, etc.) is duplicated wholesale rather than shared, per the independence design.

Mini-App Container

The mini-app container — the headline Pro feature — is generated unconditionally for running config-driven web micro-apps inside your native shell.

How it works

  1. Manifest registryMiniAppRegistry (ConfigMiniAppRegistry) loads mini-app definitions from assets/config/mini_apps.yaml via ArchipelagoConfigHelper, with an optional remote-config source layered on top (capability-guarded for net-new mini-app ids). The post-gen hook registers the asset in the host app's pubspec.yaml and writes a starter YAML file.
  2. Bridge dispatcherMiniAppBridgeDispatcher validates (bridge version, per-invoke origin, capability grant) and routes incoming JS calls to registered BridgeCapabilityHandler implementations.
  3. Lifecycle state machineMiniAppContainer tracks initializing → loading → ready → closing → closed (with an error branch) and exposes transitions as a MiniAppLifecycleEvent stream.
  4. Capability handlersLifecycleHandler (always registered), NavigationHandler, ProfileHandler, StorageHandler, PermissionsHandler, AuthHandler, and AnalyticsHandler (only generated when analytics_api is present in the target project).
  5. MiniAppContainerPage — A ready-to-use Flutter page that wires up the manifest, bridge dispatcher, and container widget.

Mini-app config file

The post-gen hook creates assets/config/mini_apps.yaml in your app:

yaml
mini_apps:
  - id: rewards
    entry_url: https://rewards.example.com/
    allowed_origins:
      - https://rewards.example.com
    bridge_origins:
      - https://rewards.example.com
    capabilities: [auth, profile, navigation, analytics]
    display:
      mode: fullscreen
      show_header: true
      title: "Rewards"

Token provider

Implement MiniAppTokenProvider to mint short-lived, scoped tokens for mini-app bridge sessions (see MiniAppScopedToken):

dart
class MyTokenProvider implements MiniAppTokenProvider {
  @override
  Future<MiniAppScopedToken> mintScopedToken(
    String miniAppId,
    List<String> scopes,
  ) async => MiniAppScopedToken(
    token: await tokenService.exchangeForMiniApp(miniAppId, scopes),
    expiresIn: 300,
    scopes: scopes,
    miniAppId: miniAppId,
  );
}

Token Exchange 2.1.0+

Each mini-app can independently opt into one of two auth.getToken strategies via token_strategy in its mini_apps.yaml entry:

yaml
mini_apps:
  - id: rewards
    # ...existing manifest fields...
    token_strategy: exchange        # default: interceptor (MVP)
    exchange_endpoint: /api/mini-apps/token-exchange   # optional override
  • token_strategy: interceptor (default) — unchanged MVP behavior: auth.getToken returns whatever your MiniAppAuthProvider.getToken supplies as-is, and MiniAppAuthInterceptor injects that same token on bridge-origin requests.
  • token_strategy: exchangeauth.getToken calls a backend token-exchange endpoint via ExchangeMiniAppTokenProvider (ships with this brick, implements MiniAppTokenProvider) to mint a short-lived, audience-bound token scoped to that mini-app. Wire your instance into MiniAppContainer.tokenProvider / MiniAppContainerPage.tokenProvider.

ExchangeMiniAppTokenProvider handles scope narrowing against the manifest's granted capabilities, in-memory caching until expires_in - 30s, single-flight de-duplication, expires_in clamping to 300s, and 401-refresh-retry-once session handling. You supply four seams: a TokenExchangeHttpClient (this package has no HTTP dependency of its own — bring your own client), a host session token supplier, a host session refresh hook, and a manifest lookup function.

Full backend contract (request/response shape, error-code table, client behavior) is documented in the Archipelago-ac3d spec: docs/plans/2026-07-10-miniapp-token-exchange-spec.md.

Integration

1. Implement Handlers

dart
class GetTokenHandler implements JsHandler<void, String> {
  @override
  String get handlerName => 'getToken';

  @override
  void parseArgs(List<dynamic> args) {}

  @override
  Future<String> handle(void args) async =>
      await tokenService.getAccessToken();

  @override
  Map<String, dynamic> serializeResult(String result) => {'token': result};
}

2. Create Config

dart
final config = WebViewAdvancedConfig(
  initialUrl: 'https://app.example.com',
  trustedOrigins: WebViewTrustPolicy.origins(['https://app.example.com']),
  interceptors: [AuthInterceptor(tokenProvider)],
  jsHandlers: [GetTokenHandler()],
);

3. Create Controller

dart
final controller = getIt<WebViewAdvancedSDK>().createController(config);

4. Call from JavaScript

js
const token = await window.archipelago.getToken();

5. Post Native Messages to JS

dart
await controller.postMessageToJs('userLoggedIn', {'userId': '123'});
js
window.archipelago.onNativeMessage = function(name, payload) {
  if (name === 'userLoggedIn') console.log('User:', payload.userId);
};

Auth Interceptor Example

dart
class AuthInterceptor implements WebViewRequestInterceptor {
  const AuthInterceptor(this._tokenProvider);
  final TokenProvider _tokenProvider;

  @override
  Future<WebViewRequest?> intercept(WebViewRequest request) async {
    final token = await _tokenProvider.getAccessToken();
    return request.copyWith(
      headers: {...request.headers, 'Authorization': 'Bearer $token'},
    );
  }
}

Provider Capability

Featureflutter_inappwebview
JS bridgeFull (Promise-based, per-handler channels)
Header injectionAll requests
File uploadFull (onCreateFileChooser)
File downloadFull

Built by Banua Coder