Deterministic rendering¶
Golden comparison is useful only when the same product state renders the same pixels. Stabilize the inputs before adding tolerance or accepting regenerated images.
Determinism checklist¶
- Application fonts are loaded from committed assets.
- Dates, clocks, IDs, ordering, and random values are fixed.
- Network, filesystem, platform channels, and persistent state are replaced.
- Images and icons come from local deterministic fixtures.
- Streams and timers are bounded and disposed.
- The selected locale, platform, direction, brightness, and contrast are explicit.
- Animations are frozen or advanced to an intentional checkpoint.
- The real application wrapper supplies deterministic DI and localization.
- The first run happens without
--update-goldens.
Fonts¶
Use loadFfGoldenFonts() in test/flutter_test_config.dart. Do not accept a
large baseline rewrite before verifying that the production font loaded. Font
fallback changes line breaks and glyph positions while leaving application
widget code untouched.
Time and asynchronous work¶
Inject a fixed clock into application code and represent server responses with local fixtures. Prefer Flutter test virtual time:
Use context.pumpUntilFound for a bounded condition. Avoid real sleeps,
uncontrolled retry loops, and background timers that survive the test.
For non-widget state, use context.pumpUntil with a descriptive predicate.
testWidgets already owns Flutter's fake-async zone, so do not start a nested
FakeAsync or discard the returned future. Use a controlled Completer in a
local fake repository when the scenario must decide when async data arrives.
Animations and shadows¶
freezeAnimations defaults to true by placing the captured application below
a disabled TickerMode. If animation is the state under test, disable freezing
and advance a fixed duration before capture.
Material shadows are rendered as deterministic boxes by default. Enable real shadows only when shadow rendering belongs to the visual contract:
configuration: const GoldenRunConfiguration(
renderShadows: true,
tolerance: GoldenTolerance(maxDiffRate: 0.0001),
),
Keep any tolerance smaller than the regression the suite must detect.
Capture timing¶
Capture only after all user-visible state is ready. A common flaky sequence is:
- a tap starts async work;
pumpAndSettle()returns because no animation remains;- an external future completes later;
- the test captures either loading or loaded state depending on timing.
Replace the external future with a controlled fixture or wait for a specific rendered condition. Do not solve this with a longer real delay.
Platform consistency¶
Golden pixels can differ across Flutter versions, host operating systems, and renderers. Keep the Flutter SDK locked in CI and choose one authoritative baseline-generation environment. Other environments can still build and run non-golden tests or publish the resulting report.
An SDK upgrade that intentionally changes rendering should be reviewed as a separate baseline operation, not mixed with unrelated UI changes.
When a screenshot changes unexpectedly¶
Compare, in order:
- PNG dimensions and capture scale;
- logical size, DPR, safe area, and platform;
- loaded fonts and text scale;
- locale, direction, brightness, contrast, and theme;
- fixture data and ordering;
- captured interaction moment;
- changed-pixel count, bounding box, and Flutter failure images.
Only after the cause is understood should the suite configuration, application code, tolerance, or baseline change.