runPreviewHarness function

void runPreviewHarness(
  1. List<PreviewEntry> entries, {
  2. List<PreviewCanvas> canvases = const [],
  3. FutureOr<void> setup()?,
})

Runs every preview in a package as a widget test, and answers what each one reported.

The same widgets, the same errors, a different engine. Each entry is mounted under CatalogGuest and its annotation's wrapper, exactly as the embedder guest's entrypoint mounts it, and errors are collected into GuestErrors — the same buffer, the same dedup key, the same counts. What changes is only that the frame is drawn by a test binding under FakeAsync rather than by a real engine in real time, which is what takes a catalog-wide render from minutes to seconds: a demo that animates for ever costs a few microseconds of fake clock instead of a three-second wait.

Two lanes off one generated file, told apart by whether a test runner is already declaring:

  • Declarer.current == null — nobody is. This was launched as a bare program in a flutter_tester the tool spawned, so it declares into its own Declarer, registers ext.flutterware.previews.audit and waits to be asked. This is the lane whose fonts are real: the tool omits --use-test-fonts and --disable-asset-fonts, which flutter test passes unconditionally.
  • Declarer.current != null — flutter test is running this file as an ordinary test. Each entry is declared as an ordinary testWidgets that fails when the entry reports anything. Convenient, shardable, and measuring unstyled text in approximate Roboto — real bytes under the platform-default family names, which is near enough for an overflow verdict to mean something and not near enough for a pixel-exact one.

setup is the package's declared previewSetup — see PreviewsPackage.setup — run once in either lane, after the binding exists and before any entry builds.

Implementation

void runPreviewHarness(
  List<PreviewEntry> entries, {
  List<PreviewCanvas> canvases = const [],
  FutureOr<void> Function()? setup,
}) {
  if (Declarer.current != null) {
    // The driven lane loads fonts before it declares anything; this one has
    // nowhere earlier to do it. A scenario folder has a
    // `flutter_test_config.dart` to hang that on and a generated preview
    // harness has no folder of its own, so the declaration carries it — and
    // the project's own setup, for the same reason.
    //
    // Without this the catalog is measured in the fallback font, which is wrong
    // in the one way nothing catches: it still renders, and reports the
    // difference as `RenderFlex overflowed by 3.5 pixels`.
    //
    // The defaults too, and only in this branch: `--use-test-fonts` boxes the
    // families nobody loads bytes for, and the families most of a catalog
    // names none of are exactly those. `runScenarios` does the same two lines
    // for the scenario half of this lane; see [loadDefaultScenarioFonts] for
    // why the driven lane below must not.
    setUpAll(() async {
      await loadScenarioFonts();
      await loadDefaultScenarioFonts();
      await setup?.call();
    });
    _declare(entries, canvases, collect: null);
    return;
  }
  // Guarded for the reason the scenario harness is: a failing entry can leak an
  // async error after its test completes, and unguarded that reaches
  // `tester_main.cc`'s unhandled handler, which kills the process. A shared
  // harness dying because one preview failed is the one outcome this may never
  // have.
  unawaited(
    runZonedGuarded(() => _serve(entries, canvases, setup), (error, stack) {
      stderr.writeln('[previews] uncaught: $error\n$stack');
    }),
  );
}