Skip to Content

Core Concepts

Understanding these core concepts will help you effectively use WebF in your Flutter applications.

The Managed WebF Widget

The WebF widget is the entry point for displaying WebF apps in Flutter. Always use WebF.fromControllerName instead of the default constructor for proper lifecycle management:

WebF.fromControllerName( controllerName: 'home', // Reference to managed controller initialRoute: '/', // Initial URL path loadingWidget: CircularProgressIndicator(), onControllerCreated: (controller) { print('Controller ready: $controller'); }, onBuildSuccess: () { print('UI rendered successfully'); }, )

Why use fromControllerName?

  • Automatic lifecycle management
  • Seamless controller recreation after disposal
  • Memory-efficient controller sharing
  • Support for preloading and pre-rendering

Accessing the WebFController

The WebFController provides programmatic control over the WebF instance. Access it in several ways:

1. Via onControllerCreated callback

WebFController? _controller; WebF.fromControllerName( controllerName: 'home', onControllerCreated: (controller) { _controller = controller; // Configure hybrid history delegate controller.hybridHistory.delegate = MyCustomHybridHistoryDelegate(); }, )

2. Via WebFControllerManager

// In an async context final controller = await WebFControllerManager.instance.getController('home'); if (controller != null) { controller.evaluateJavaScript('alert("Hello from Dart!")'); }

3. In setup callback during initialization

WebFControllerManager.instance.addWithPrerendering( name: 'home', createController: () => WebFController(), bundle: WebFBundle.fromUrl('https://example.com/'), setup: (controller) { // Controller is guaranteed to be available here controller.httpLoggerOptions = HttpLoggerOptions( requestHeader: true, requestBody: true, ); }, );

Common Controller Operations

// Navigation controller.hybridHistory.pushState(stateData, '/new-route'); controller.hybridHistory.back(); // JavaScript execution controller.evaluateJavaScript('console.log("Hello!")'); // Theme control controller.darkModeOverride = true; // Loading state controller.loadingState.onLargestContentfulPaint((event) { print('LCP at ${event.elapsed.inMilliseconds}ms'); });

Lifecycle of a Managed Controller

Controllers go through distinct lifecycle states:

[Created] → [Initialized] → [Attached] → [Detached] → [Disposed]

States Explained

  1. Created: Controller instance exists but not initialized
  2. Initialized: Bridge to JavaScript engine established, ready to load content
  3. Attached: Actively rendering in Flutter widget tree
  4. Detached: Paused rendering but state preserved in memory
  5. Disposed: Fully destroyed, all resources released

Lifecycle Hooks

WebFControllerManager.instance.initialize( WebFControllerManagerConfig( onControllerDetached: (name, controller) { // Called when controller stops rendering but stays in memory // Good for pausing animations, stopping timers print('$name detached'); }, onControllerDisposed: (name, controller) { // Called when controller is fully destroyed // Release any external resources held by this controller print('$name disposed'); }, ), ); // Per-controller lifecycle WebFController( onControllerInit: (controller) async { // Called once after bridge initialization print('Controller initialized'); }, )

Automatic Lifecycle Management

The manager automatically handles:

  • Attachment: When a WebF widget appears on screen
  • Detachment: When removed from widget tree (but kept in memory if under limit)
  • Disposal: When memory limits are reached (LRU strategy)

Manual Control

// Manually dispose a controller await WebFControllerManager.instance.disposeController('home'); // Dispose all controllers await WebFControllerManager.instance.disposeAll();

Performance: Understanding Loading Modes

WebF supports three loading modes to optimize performance:

WebFControllerManager.instance.addWithPrerendering( name: 'home', createController: () => WebFController(), bundle: WebFBundle.fromUrl('https://example.com/'), );
  • Fully loads, executes, and renders WebF app in background
  • Instant display when widget attached
  • Best user experience with minimal perceived load time
  • Recommended for most screens in your app

2. Preloading (Optional)

WebFControllerManager.instance.addWithPreload( name: 'settings', createController: () => WebFController(), bundle: WebFBundle.fromUrl('https://example.com/'), );
  • HTML/JS downloaded and parsed in background
  • JavaScript execution deferred until widget attached
  • Memory usage: <10 MB per controller
  • Use when you need a separate JavaScript context with lower memory footprint

3. Normal Loading (Special Cases)

WebF.fromControllerName( controllerName: 'rarely_used', bundle: WebFBundle.fromUrl('https://example.com/'), // No special mode specified )
  • Controller created when widget is first built
  • WebF app loaded on-demand
  • Lowest memory usage
  • Use only for rarely-accessed screens or when memory is extremely constrained

Performance Comparison

ModeInitial Load TimeMemory UsageUser ExperienceWhen to Use
Pre-renderNear instant10-30 MBBestMost controllers (recommended default)
Preload~60% of normal<10 MBGoodSeparate JS contexts with lower memory
Normal100% (baseline)MinimalSlowerRarely-used screens only

Best Practices

  • Pre-render your controller - WebF is designed for long-lived contexts where a single controller manages an entire app with multiple routes
  • Most apps need only one controller that hosts all screens and routes in a single JavaScript context
  • Use WebF’s routing frameworks (@openwebf/react-router, @openwebf/vue-router) within your WebF app instead of creating multiple controllers
  • If you need multiple separate JavaScript contexts (e.g., two independent in-app apps), pre-render multiple controllers - one for each context
  • Preloading is useful when you need a separate JS context but want to reduce memory usage
  • Monitor with onLCP and onFCP callbacks to measure improvements

Next Steps