Movatterモバイル変換


[0]ホーム

URL:


Skip to main content

docs.flutter.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.

Learn more

Flutter 3.41 is live! Check out theFlutter 3.41 blog post!

Staggered animations

How to write a staggered animation in Flutter.

What you'll learn
  • A staggered animation consists of sequential or overlapping animations.
  • To create a staggered animation, use multipleAnimation objects.
  • OneAnimationController controls all of theAnimations.
  • EachAnimation object specifies the animation during anInterval.
  • For each property being animated, create aTween.
Terminology

Staggered animations are a straightforward concept: visual changes happen as a series of operations, rather than all at once. The animation might be purely sequential, with one change occurring after the next, or it might partially or completely overlap. It might also have gaps, where no changes occur.

This guide shows how to build a staggered animation in Flutter.

Examples

This guide explains the basic_staggered_animation example. You can also refer to a more complex example, staggered_pic_selection.

basic_staggered_animation

Shows a series of sequential and overlapping animations of a single widget. Tapping the screen begins an animation that changes opacity, size, shape, color, and padding.

staggered_pic_selection

Shows deleting an image from a list of images displayed in one of three sizes. This example uses twoanimation controllers: one for image selection/deselection, and one for image deletion. The selection/deselection animation is staggered. (To see this effect, you might need to increase thetimeDilation value.) Select one of the largest images—it shrinks as it displays a checkmark inside a blue circle. Next, select one of the smallest images—the large image expands as the checkmark disappears. Before the large image has finished expanding, the small image shrinks to display its checkmark. This staggered behavior is similar to what you might see in Google Photos.

The following video demonstrates the animation performed by basic_staggered_animation:

Watch on YouTube in a new tab: "Staggered animation example"

In the video, you see the following animation of a single widget, which begins as a bordered blue square with slightly rounded corners. The square runs through changes in the following order:

  1. Fades in
  2. Widens
  3. Becomes taller while moving upwards
  4. Transforms into a bordered circle
  5. Changes color to orange

After running forward, the animation runs in reverse.

New to Flutter?

Basic structure of a staggered animation

#
What's the point?
  • All of the animations are driven by the sameAnimationController.
  • Regardless of how long the animation lasts in real time, the controller's values must be between 0.0 and 1.0, inclusive.
  • Each animation has anInterval between 0.0 and 1.0, inclusive.
  • For each property that animates in an interval, create aTween. TheTween specifies the start and end values for that property.
  • TheTween produces anAnimation object that is managed by the controller.

The following diagram shows theIntervals used in thebasic_staggered_animation example. You might notice the following characteristics:

  • The opacity changes during the first 10% of the timeline.
  • A tiny gap occurs between the change in opacity, and the change in width.
  • Nothing animates during the last 25% of the timeline.
  • Increasing the padding makes the widget appear to rise upward.
  • Increasing the border radius to 0.5, transforms the square with rounded corners into a circle.
  • The padding and height changes occur during the same exact interval, but they don't have to.

Diagram showing the interval specified for each motion

To set up the animation:

  • Create anAnimationController that manages all of theAnimations.
  • Create aTween for each property being animated.
    • TheTween defines a range of values.
    • TheTween'sanimate method requires theparent controller, and produces anAnimation for that property.
  • Specify the interval on theAnimation'scurve property.

When the controlling animation's value changes, the new animation's value changes, triggering the UI to update.

The following code creates a tween for thewidth property. It builds aCurvedAnimation, specifying an eased curve. SeeCurves for other available pre-defined animation curves.

dart
width=Tween<double>(begin:50.0,end:150.0,).animate(CurvedAnimation(parent:controller,curve:constInterval(0.125,0.250,curve:Curves.ease,),),),

Thebegin andend values don't have to be doubles. The following code builds the tween for theborderRadius property (which controls the roundness of the square's corners), usingBorderRadius.circular().

dart
borderRadius=BorderRadiusTween(begin:BorderRadius.circular(4),end:BorderRadius.circular(75),).animate(CurvedAnimation(parent:controller,curve:constInterval(0.375,0.500,curve:Curves.ease,),),),

Complete staggered animation

#

Like all interactive widgets, the complete animation consists of a widget pair: a stateless and a stateful widget.

The stateless widget specifies theTweens, defines theAnimation objects, and provides abuild() function responsible for building the animating portion of the widget tree.

The stateful widget creates the controller, plays the animation, and builds the non-animating portion of the widget tree. The animation begins when a tap is detected anywhere in the screen.

Full code for basic_staggered_animation's main.dart

Stateless widget: StaggerAnimation

#

In the stateless widget,StaggerAnimation, thebuild() function instantiates anAnimatedBuilder—a general purpose widget for building animations. TheAnimatedBuilder builds a widget and configures it using theTweens' current values. The example creates a function named_buildAnimation() (which performs the actual UI updates), and assigns it to itsbuilder property. AnimatedBuilder listens to notifications from the animation controller, marking the widget tree dirty as values change. For each tick of the animation, the values are updated, resulting in a call to_buildAnimation().

dart
classStaggerAnimationextendsStatelessWidget{StaggerAnimation({super.key,requiredthis.controller}):// Each animation defined here transforms its value during the subset// of the controller's duration defined by the animation's interval.// For example the opacity animation transforms its value during// the first 10% of the controller's duration.opacity=Tween<double>(begin:0.0,end:1.0,).animate(CurvedAnimation(parent:controller,curve:constInterval(0.0,0.100,curve:Curves.ease,),),),// ... Other tween definitions ...);finalAnimationControllercontroller;finalAnimation<double>opacity;finalAnimation<double>width;finalAnimation<double>height;finalAnimation<EdgeInsets>padding;finalAnimation<BorderRadius?>borderRadius;finalAnimation<Color?>color;// This function is called each time the controller "ticks" a new frame.// When it runs, all of the animation's values will have been// updated to reflect the controller's current value.Widget_buildAnimation(BuildContextcontext,Widget?child){returnContainer(padding:padding.value,alignment:Alignment.bottomCenter,child:Opacity(opacity:opacity.value,child:Container(width:width.value,height:height.value,decoration:BoxDecoration(color:color.value,border:Border.all(color:Colors.indigo[300]!,width:3,),borderRadius:borderRadius.value,),),),);}@overrideWidgetbuild(BuildContextcontext){returnAnimatedBuilder(builder:_buildAnimation,animation:controller,);}}

Stateful widget: StaggerDemo

#

The stateful widget,StaggerDemo, creates theAnimationController (the one who rules them all), specifying a 2000 ms duration. It plays the animation, and builds the non-animating portion of the widget tree. The animation begins when a tap is detected in the screen. The animation runs forward, then backward.

dart
classStaggerDemoextendsStatefulWidget{@overrideState<StaggerDemo>createState()=>_StaggerDemoState();}class_StaggerDemoStateextendsState<StaggerDemo>withTickerProviderStateMixin{lateAnimationController_controller;@overridevoidinitState(){super.initState();_controller=AnimationController(duration:constDuration(milliseconds:2000),vsync:this,);}// ...Boilerplate...Future<void>_playAnimation()async{try{await_controller.forward().orCancel;await_controller.reverse().orCancel;}onTickerCanceled{// The animation got canceled, probably because it was disposed of.}}@overrideWidgetbuild(BuildContextcontext){timeDilation=10.0;// 1.0 is normal animation speed.returnScaffold(appBar:AppBar(title:constText('Staggered Animation'),),body:GestureDetector(behavior:HitTestBehavior.opaque,onTap:(){_playAnimation();},child:Center(child:Container(width:300,height:300,decoration:BoxDecoration(color:Colors.black.withValues(alpha:0.1),border:Border.all(color:Colors.black.withValues(alpha:0.5),),),child:StaggerAnimation(controller:_controller.view),),),),);}}
Was this page's content helpful?

Unless stated otherwise, the documentation on this site reflects Flutter 3.38.6. Page last updated on 2025-10-28.View source orreport an issue.


[8]ページ先頭

©2009-2026 Movatter.jp