preRendering method

Future<void> preRendering(
  1. WebFBundle bundle, {
  2. Duration? timeout,
})

The aggressive mode is a step further than preloading, cutting down up to 90% of loading time for optimal performance. This mode simulates the instantaneous response of native Flutter pages but may require modifications in the existing web codes for compatibility. In this mode, all remote resources are loaded and executed similarly to the standard mode, but with an offline-like behavior. Given that JavaScript is executed in this mode, properties like clientWidth and clientHeight from the viewModule always return 0. This is because no layout or paint processes occur during preRendering. If your application depends on viewModule properties, ensure that the related code is placed within the load and DOMContentLoaded or prerendered event callbacks of the window. These callbacks are triggered once the WebF widget is mounted into the Flutter tree. Apps optimized for this mode remain compatible with both standard and preloading modes. Aggressively preloads and prerenders content from the provided WebFBundle.

This mode loads, parses, and executes content in a simulated environment before mounting. Can improve loading performance by up to 90%, but requires special handling for dimension-dependent code.

Implementation

Future<void> preRendering(WebFBundle bundle, {Duration? timeout}) async {
  if (preRenderingStatus == PreRenderingStatus.done) return;

  controllerPreRenderingCompleter = Completer();

  await controlledInitCompleter.future;

  if (preRenderingStatus != PreRenderingStatus.none) return;
  if (preloadStatus != PreloadingStatus.none) return;

  // Record prerendering phase
  _loadingState.recordPhase(LoadingState.phasePreRender, parameters: {
    'bundle': bundle.url,
    'timeout': timeout?.inMilliseconds ?? 'default',
  });

  // Update entrypoint.
  _entrypoint = bundle;
  _replaceCurrentHistory(bundle);
  view.document.initializeCookieJarForUrl(url);

  mode = WebFLoadingMode.preRendering;

  // Initialize document, window and the documentElement.
  flushUICommand(view, nullptr);

  // Set the status value for preloading.
  preRenderingStatus = PreRenderingStatus.preloading;

  // Manually initialize the root element and create renderObjects for each elements.
  view.document.documentElement!.applyStyle(view.document.documentElement!.style);

  run() async {
    bool isTimeout = false;

    try {
      Timer(timeout ?? Duration(seconds: 20), () {
        if (controllerPreRenderingCompleter.isCompleted) return;
        isTimeout = true;
        preRenderingStatus = PreRenderingStatus.fail;
        controllerPreRenderingCompleter.completeError(FlutterError('Prerendering failed with exceed timeout limits'));
      });

      // Preparing the entrypoint
      await Future.wait([_resolveEntrypoint(), module.initialize()]);

      if (isTimeout) return;

      // Stop the animation frame
      // Pause the animation timeline.
      pause();

      view.window.addEventListener(EVENT_LOAD, (event) async {
        preRenderingStatus = PreRenderingStatus.done;
      });

      if (_entrypoint!.isJavascript || _entrypoint!.isBytecode) {
        // Convert the JavaScript code into bytecode.
        if (_entrypoint!.isJavascript) {
          await _entrypoint!.preProcessing(view.contextId);
        }
      }

      if (isTimeout) return;

      preRenderingStatus = PreRenderingStatus.evaluate;

      // Evaluate the entry point, and loading the stylesheets and scripts.
      await evaluateEntrypoint();

      if (isTimeout) return;

      evaluated = true;

      view.flushPendingCommandsPerFrame();
    } catch (e, stack) {
      if (isTimeout) return;
      preRenderingStatus = PreRenderingStatus.fail;
      _handlingLoadingError(e, stack);
      controllerPreRenderingCompleter.complete();
      return;
    }

    // If there are no <script /> elements, finish this prerendering process.
    if (!view.document.scriptRunner.hasPendingScripts()) {
      controllerPreRenderingCompleter.complete();
      return;
    }
  }

  run();

  return controllerPreRenderingCompleter.future;
}