Skip to content
Boring Doc
BreakingHigh impactOfficial migration guidesince 2.5.0landed v2.3.0-13.0.pre

Replace AnimationSheetBuilder.display with collate

Framework · Official source

The `AnimationSheetBuilder.display` and `sheetSize` methods are deprecated, and should be replaced with `AnimationSheetBuilder.collate`.

Original behavior

`AnimationSheetBuilder` is a testing utility class that records frames of an animating widget, and later composes the frames into a single animation sheet for golden testing. The old way of composing involves `display` to list the images into a table-like widget, adjusting the testing surface with `sheetSize`, and capturing the table widget for comparison. A new way, `collate`, has been added that directly puts the frames together into an image for comparison, which requires less boilerplate code and outputs a smaller image without compromise in quality. APIs for the old way are thus deprecate

New behavior

The following changes have been made to the `AnimationSheetBuilder` class:

Who is affected
Applications and packages relying on the previous behaviour or signature. Code that uses AnimationSheetBuilder.display, AnimationSheetBuilder.collate, AnimationSheetBuilder.
Practical impact
Behaviour or API differences that can break builds or change runtime behaviour.
What action should I take?
Follow the official migration guide below, update affected call sites, run `dart fix --apply` where a fix is available, then run your tests.

Before and after (from the official guide)

dart+1−8
Before
1 testWidgets('Indeterminate CircularProgressIndicator', (WidgetTester tester) async {
2 final AnimationSheetBuilder animationSheet = AnimationSheetBuilder(frameSize: const Size(40, 40));
3  
4 await tester.pumpFrames(animationSheet.record(
5 const Directionality(
6 textDirection: TextDirection.ltr,
7 child: Padding(
8 padding: EdgeInsets.all(4),
9 child: CircularProgressIndicator(),
10 ),
11 ),
12 ), const Duration(seconds: 2));
13  
14− // The code starting here needs migration.
15− 
16− tester.binding.setSurfaceSize(animationSheet.sheetSize());
17− 
18− final Widget display = await animationSheet.display();
19− await tester.pumpWidget(display);
20− 
21 await expectLater(
22− find.byWidget(display),
23 matchesGoldenFile('material.circular_progress_indicator.indeterminate.png'),
24 );
25 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/42767
After
1 testWidgets('Indeterminate CircularProgressIndicator', (WidgetTester tester) async {
2 final AnimationSheetBuilder animationSheet = AnimationSheetBuilder(frameSize: const Size(40, 40));
3  
4 await tester.pumpFrames(animationSheet.record(
5 const Directionality(
6 textDirection: TextDirection.ltr,
7 child: Padding(
8 padding: EdgeInsets.all(4),
9 child: CircularProgressIndicator(),
10 ),
11 ),
12 ), const Duration(seconds: 2));
13  
 
 
 
 
 
 
 
14 await expectLater(
15+ animationSheet.collate(20),
16 matchesGoldenFile('material.circular_progress_indicator.indeterminate.png'),
17 );
18 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/42767
Raw before / after
testWidgets('Indeterminate CircularProgressIndicator', (WidgetTester tester) async {
    final AnimationSheetBuilder animationSheet = AnimationSheetBuilder(frameSize: const Size(40, 40));

    await tester.pumpFrames(animationSheet.record(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: Padding(
          padding: EdgeInsets.all(4),
          child: CircularProgressIndicator(),
        ),
      ),
    ), const Duration(seconds: 2));

    // The code starting here needs migration.

    tester.binding.setSurfaceSize(animationSheet.sheetSize());

    final Widget display = await animationSheet.display();
    await tester.pumpWidget(display);

    await expectLater(
      find.byWidget(display),
      matchesGoldenFile('material.circular_progress_indicator.indeterminate.png'),
    );
  }, skip: isBrowser); // https://github.com/flutter/flutter/issues/42767
testWidgets('Indeterminate CircularProgressIndicator', (WidgetTester tester) async {
    final AnimationSheetBuilder animationSheet = AnimationSheetBuilder(frameSize: const Size(40, 40));

    await tester.pumpFrames(animationSheet.record(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: Padding(
          padding: EdgeInsets.all(4),
          child: CircularProgressIndicator(),
        ),
      ),
    ), const Duration(seconds: 2));

    await expectLater(
      animationSheet.collate(20),
      matchesGoldenFile('material.circular_progress_indicator.indeterminate.png'),
    );
  }, skip: isBrowser); // https://github.com/flutter/flutter/issues/42767
Migration checklist0/3
Official migration guide (excerpt, © The Flutter Authors, CC BY 3.0)

To migrate to the new API, change the process of setting surface size and displaying the widget into [AnimationSheetBuilder.collate][].

Derive cells per row

The collate requires an explicit cellsPerRow argument, which is the number of frames per row in the output image. It can be manually counted, or calculated as follows:

  • Find the width of frame, specified when constructing AnimationSheetBuilder. For example, in the following snippet it's 80:
final AnimationSheetBuilder animationSheet = AnimationSheetBuilder(frameSize: const Size(80, 30));
  • Find the width of surface size, specified when setting the surface size; the default is 800. For example, in the following snippet it's 600:
tester.binding.setSurfaceSize(animationSheet.sheetSize(600));
  • The frames per row should be the result of the two numbers divided, rounded down. For example, 600 / 80 = 7 (rounded down), therefore
animationSheet.collate(7)

Migrate code

Code before migration:

  testWidgets('Indeterminate CircularProgressIndicator', (WidgetTester tester) async {
    final AnimationSheetBuilder animationSheet = AnimationSheetBuilder(frameSize: const Size(40, 40));

    await tester.pumpFrames(animationSheet.record(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: Padding(
          padding: EdgeInsets.all(4),
          child: CircularProgressIndicator(),
        ),
      ),
    ), const Duration(seconds: 2));

    // The code starting here needs migration.

    tester.binding.setSurfaceSize(animationSheet.sheetSize());

    final Widget display = await animationSheet.display();
    await tester.pumpWidget(display);

    await expectLater(
      find.byWidget(display),
      matchesGoldenFile('material.circular_progress_indicator.indeterminate.png'),
    );
  }, skip: isBrowser); // https://github.com/flutter/flutter/issues/42767

Code after migration (cellsPerRow is 20, derived from 800 / 40):

  testWidgets('Indeterminate CircularProgressIndicator', (WidgetTester tester) async {
    final AnimationSheetBuilder animationSheet = AnimationSheetBuilder(frameSize: const Size(40, 40));

    await tester.pumpFrames(animationSheet.record(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: Padding(
          padding: EdgeInsets.all(4),
          child: CircularProgressIndicator(),
        ),
      ),
    ), const Duration(seconds: 2));

    await expectLater(
      animationSheet.collate(20),
      matchesGoldenFile('material.circular_progress_indicator.indeterminate.png'),
    );
  }, skip: isBrowser); // https://github.com/flutter/flutter/issues/42767

It's expected that related golden test reference images are invalidated, which should all be updated. The new images should be identical to the old ones except 1/3 in scale.

Migration assistantAI-generated · grounded in cited sources
AI explanations are not configured
Set AI_PROVIDER (for example to openai-compatible with a local Ollama or LM Studio endpoint) to enable grounded explanations. Everything else on this page works without an AI provider.
Official migration guide·Verified classification (95%)

Listed in the official breaking-changes index (docs.flutter.dev/release/breaking-changes).

An official announcement or migration guide documents an intended behaviour or API change. A verified source change (git diff) shows code changed but does not by itself prove a public API is breaking. AI interpretations are labelled and never used to classify a change.

Source commit
b3045439b7c0
Source last updated
Oct 21, 2026
Last synchronized
Oct 21, 2026
Publication status
published
Publication history
  • Oct 21, 2026, 8:31 PM: new → published (ingestion) — Backed by an official breaking-change/migration guide.
AI explanationAI-generated · grounded in cited sources
AI explanations are not configured
Set AI_PROVIDER (for example to openai-compatible with a local Ollama or LM Studio endpoint) to enable grounded explanations. Everything else on this page works without an AI provider.
Was this useful?
Replace AnimationSheetBuilder.display with collate · Boring Doc