Overview

AnimatedContainer is an animated version of Container that automatically applies animations when property values change.

AnimatedContainer automatically animates between the old and new values of properties when they change using the provided curve and duration. Properties that are null are not animated. Its child and descendants are not animated. This class is useful for generating simple implicit transitions between different parameters to Container with its internal AnimationController.

See: https://api.flutter.dev/flutter/widgets/AnimatedContainer-class.html

When to use it?

  • When smoothly transitioning widget size, color, padding, etc.
  • When providing visual feedback in response to user interactions
  • When expressing state changes smoothly
  • When simple animations are needed without complex AnimationController
  • When expressing hover effects or selected states

Basic Usage

// Create a StatefulWidget to manage state
class AnimatedContainerExample extends StatefulWidget {
  createState() {
    return new AnimatedContainerExampleState();
  }
}

class AnimatedContainerExampleState extends State<AnimatedContainerExample> {
  isExpanded = false;

  build() {
    return GestureDetector({
      onTap: () => {
        this.setState(() => {
          this.isExpanded = !this.isExpanded;
        });
      },
      child: AnimatedContainer({
        duration: 300, // 300ms
        curve: Curves.easeInOut,
        width: this.isExpanded ? 200 : 100,
        height: this.isExpanded ? 200 : 100,
        color: this.isExpanded ? 'blue' : 'red',
        child: Center({
          child: Text('Tap me')
        })
      })
    });
  }
}

// Export as factory function
export default classToFunction(AnimatedContainerExample);

Props

duration (required)

Value: number

The duration over which to animate the parameters of this container (in milliseconds).

AnimatedContainer({
  duration: 500, // 0.5 seconds
  width: 100,
  height: 100
})

curve

Value: Curve (default: Curves.linear)

Defines the animation progression curve. Various built-in curves are available.

AnimatedContainer({
  duration: 300,
  curve: Curves.easeInOut, // Smooth start and end
  width: expanded ? 200 : 100
})

Available Curves:

  • Curves.linear: Constant speed
  • Curves.easeIn: Starts slowly
  • Curves.easeOut: Ends slowly
  • Curves.easeInOut: Starts and ends slowly
  • Curves.bounceIn: Bounces at the start
  • Curves.bounceOut: Bounces at the end
  • Curves.bounceInOut: Bounces at start and end
  • Curves.anticipate: Goes backward then forward
  • Curves.backIn: Pulls back then starts
  • Curves.backOut: Overshoots then returns

width

Value: number | undefined

The container’s width. Animates when changed.

AnimatedContainer({
  duration: 200,
  width: isSelected ? 150 : 100,
  height: 50
})

height

Value: number | undefined

The container’s height. Animates when changed.

AnimatedContainer({
  duration: 200,
  width: 100,
  height: isExpanded ? 200 : 100
})

color

Value: string | undefined

The container’s background color. Cannot be used with decoration.

AnimatedContainer({
  duration: 300,
  color: isActive ? 'blue' : 'gray',
  child: Text('Status indicator')
})

decoration

Value: Decoration | undefined

The decoration to paint behind the child. All BoxDecoration properties animate.

AnimatedContainer({
  duration: 400,
  decoration: BoxDecoration({
    color: isHovered ? 'lightblue' : 'white',
    borderRadius: BorderRadius.circular(isHovered ? 20 : 10),
    boxShadow: isHovered ? [
      BoxShadow({
        color: 'rgba(0, 0, 0, 0.2)',
        blurRadius: 10,
        offset: { x: 0, y: 5 }
      })
    ] : []
  })
})

margin

Value: EdgeInsets | undefined

Empty space to surround the container. Animates when changed.

AnimatedContainer({
  duration: 300,
  margin: EdgeInsets.all(isSelected ? 20 : 10),
  color: 'blue'
})

padding

Value: EdgeInsets | undefined

Empty space to inscribe inside the container. Animates when changed.

AnimatedContainer({
  duration: 300,
  padding: EdgeInsets.all(isExpanded ? 20 : 10),
  child: Text('Content')
})

alignment

Value: Alignment | undefined

Aligns the child within the container. Animates when changed.

AnimatedContainer({
  duration: 500,
  alignment: isLeft ? Alignment.centerLeft : Alignment.centerRight,
  child: Icon({ icon: Icons.star })
})

constraints

Value: Constraints | undefined

Additional constraints to apply to the child. Min/max sizes animate.

AnimatedContainer({
  duration: 300,
  constraints: Constraints({
    minWidth: 100,
    maxWidth: isExpanded ? 300 : 150,
    minHeight: 50,
    maxHeight: isExpanded ? 200 : 100
  })
})

clipped

Value: boolean (default: false)

Whether to clip children to the container’s bounds.

AnimatedContainer({
  duration: 300,
  clipped: true,
  width: 100,
  height: 100,
  child: Image({ src: 'large-image.jpg' })
})

transform

Value: Matrix4 | undefined

The transformation matrix to apply. Rotation, scaling, and translation animate.

AnimatedContainer({
  duration: 600,
  transform: isRotated 
    ? Matrix4.rotationZ(Math.PI / 4) // 45 degree rotation
    : Matrix4.identity(),
  child: Icon({ icon: Icons.refresh })
})

transformAlignment

Value: Alignment | undefined

The alignment of the origin for the transform. Used with transform.

AnimatedContainer({
  duration: 500,
  transform: Matrix4.rotationZ(angle),
  transformAlignment: Alignment.center, // Rotate around center
  child: Text('Rotating')
})

child

Value: Widget | undefined

The child widget, which is not animated.

AnimatedContainer({
  duration: 300,
  width: 100,
  height: 100,
  child: Icon({ icon: Icons.favorite })
})

onEnd

Value: () => void | undefined

A callback that is called when the animation completes.

AnimatedContainer({
  duration: 500,
  width: expanded ? 200 : 100,
  onEnd: () => {
    console.log('Animation completed');
    // Trigger next animation or state change
  }
})

Common Use Cases

State-based UI Changes

AnimatedContainer excels at smoothly transitioning between different UI states:

// Theme switching
AnimatedContainer({
  duration: 300,
  decoration: BoxDecoration({
    color: isDarkMode ? '#212121' : '#FFFFFF',
    borderRadius: BorderRadius.circular(8)
  }),
  child: content
})

// Selection states
AnimatedContainer({
  duration: 200,
  decoration: BoxDecoration({
    border: Border.all({
      color: isSelected ? 'blue' : 'transparent',
      width: isSelected ? 2 : 0
    })
  })
})

// Loading states
AnimatedContainer({
  duration: 400,
  height: isLoading ? 200 : 0,
  opacity: isLoading ? 1 : 0,
  child: LoadingIndicator()
})

Interactive Components

Create engaging interactive elements with smooth transitions:

// Expandable cards
AnimatedContainer({
  duration: 300,
  height: isExpanded ? null : 100,
  child: content
})

// Hover effects
AnimatedContainer({
  duration: 200,
  transform: Matrix4.identity()
    .scaled(isHovered ? 1.05 : 1.0),
  boxShadow: isHovered ? elevatedShadow : flatShadow
})

// Press feedback
AnimatedContainer({
  duration: 100,
  transform: Matrix4.identity()
    .scaled(isPressed ? 0.95 : 1.0)
})

Practical Examples

Example 1: Hover Effect Card

class HoverCard extends StatefulWidget {
  createState() {
    return new HoverCardState();
  }
}

class HoverCardState extends State<HoverCard> {
  isHovered = false;

  build() {
    return GestureDetector({
      onMouseEnter: () => this.setState(() => { this.isHovered = true; }),
      onMouseLeave: () => this.setState(() => { this.isHovered = false; }),
      child: AnimatedContainer({
        duration: 200,
        curve: Curves.easeOut,
        width: 200,
        height: this.isHovered ? 250 : 200,
        decoration: BoxDecoration({
          color: 'white',
          borderRadius: BorderRadius.circular(12),
          boxShadow: [
            BoxShadow({
              color: this.isHovered ? 'rgba(0, 0, 0, 0.15)' : 'rgba(0, 0, 0, 0.05)',
              blurRadius: this.isHovered ? 20 : 10,
              offset: { x: 0, y: this.isHovered ? 10 : 5 }
            })
          ]
        }),
        child: Padding({
          padding: EdgeInsets.all(16),
          child: Column({
            children: [
              Icon({ 
                icon: Icons.shopping_cart,
                size: 48,
                color: this.isHovered ? 'blue' : 'gray'
              }),
              SizedBox({ height: 12 }),
              Text('Product Card')
            ]
          })
        })
      })
    });
  }
}

Example 2: Expandable Panel

class ExpandablePanel extends StatefulWidget {
  createState() {
    return new ExpandablePanelState();
  }
}

class ExpandablePanelState extends State<ExpandablePanel> {
  isExpanded = false;

  build() {
    return AnimatedContainer({
      duration: 300,
      curve: Curves.easeInOut,
      width: 300,
      height: this.isExpanded ? 400 : 80,
      decoration: BoxDecoration({
        color: 'white',
        borderRadius: BorderRadius.circular(8),
        border: Border.all({ color: '#E0E0E0' })
      }),
      child: Column({
        children: [
          GestureDetector({
            onTap: () => this.setState(() => { this.isExpanded = !this.isExpanded; }),
            child: Container({
              height: 80,
              padding: EdgeInsets.symmetric({ horizontal: 16 }),
              child: Row({
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text('Panel Title'),
                  AnimatedContainer({
                    duration: 300,
                    transform: Matrix4.rotationZ(this.isExpanded ? Math.PI : 0),
                    transformAlignment: Alignment.center,
                    child: Icon({ icon: Icons.expand_more })
                  })
                ]
              })
            })
          }),
          if (this.isExpanded) Expanded({
            child: Padding({
              padding: EdgeInsets.all(16),
              child: Text('Expanded content goes here.')
            })
          })
        ]
      })
    });
  }
}

Example 3: Progress Indicator

const ProgressIndicator = ({ progress }: { progress: number }) => {
  return Container({
    width: 300,
    height: 20,
    decoration: BoxDecoration({
      color: '#E0E0E0',
      borderRadius: BorderRadius.circular(10)
    }),
    child: Stack({
      children: [
        AnimatedContainer({
          duration: 500,
          curve: Curves.easeOut,
          width: 300 * progress,
          height: 20,
          decoration: BoxDecoration({
            color: progress < 0.5 ? 'orange' : 'green',
            borderRadius: BorderRadius.circular(10)
          })
        }),
        Center({
          child: Text(`${Math.round(progress * 100)}%`, {
            style: TextStyle({ color: 'white', fontWeight: 'bold' })
          })
        })
      ]
    })
  });
};

Example 4: Selectable Chip

const SelectableChip = ({ label, selected, onTap }) => {
  return GestureDetector({
    onTap: onTap,
    child: AnimatedContainer({
      duration: 200,
      curve: Curves.easeOut,
      padding: EdgeInsets.symmetric({ horizontal: 16, vertical: 8 }),
      decoration: BoxDecoration({
        color: selected ? 'blue' : 'transparent',
        border: Border.all({ 
          color: selected ? 'blue' : 'gray',
          width: selected ? 2 : 1
        }),
        borderRadius: BorderRadius.circular(20)
      }),
      child: Text(label, {
        style: TextStyle({
          color: selected ? 'white' : 'black',
          fontWeight: selected ? 'bold' : 'normal'
        })
      })
    })
  });
};

Example 5: Loading Skeleton

class LoadingSkeleton extends StatefulWidget {
  createState() {
    return new LoadingSkeletonState();
  }
}

class LoadingSkeletonState extends State<LoadingSkeleton> {
  animationPhase = 0;
  Timer? timer;

  initState() {
    super.initState();
    this.timer = Timer.periodic(Duration({ milliseconds: 800 }), () => {
      this.setState(() => {
        this.animationPhase = (this.animationPhase + 1) % 3;
      });
    });
  }

  dispose() {
    this.timer?.cancel();
    super.dispose();
  }

  build() {
    return AnimatedContainer({
      duration: 800,
      curve: Curves.easeInOut,
      width: 300,
      height: 100,
      decoration: BoxDecoration({
        gradient: LinearGradient({
          colors: ['#E0E0E0', '#F5F5F5', '#E0E0E0'],
          stops: [0, 0.5, 1],
          begin: Alignment.centerLeft.add(Alignment(-1 + this.animationPhase * 0.5, 0)),
          end: Alignment.centerRight.add(Alignment(-1 + this.animationPhase * 0.5, 0))
        }),
        borderRadius: BorderRadius.circular(8)
      })
    });
  }
}

Example 6: Multi-step Onboarding Screen

class OnboardingScreen extends StatefulWidget {
  createState() {
    return new OnboardingScreenState();
  }
}

class OnboardingScreenState extends State<OnboardingScreen> {
  currentStep = 0;
  
  steps = [
    { color: '#3F51B5', icon: Icons.rocket_launch, title: 'Get Started', description: 'Build amazing apps with Flitter' },
    { color: '#00BCD4', icon: Icons.palette, title: 'Design', description: 'Create beautiful UIs easily' },
    { color: '#4CAF50', icon: Icons.speed, title: 'Performance', description: 'Fast and smooth animations' },
    { color: '#FF5722', icon: Icons.done_all, title: 'Complete', description: 'Your first app is ready!' }
  ];

  build() {
    const step = this.steps[this.currentStep];
    
    return Container({
      width: 400,
      height: 600,
      child: Column({
        children: [
          // Animated background
          AnimatedContainer({
            duration: 600,
            curve: Curves.easeInOut,
            width: 400,
            height: 400,
            decoration: BoxDecoration({
              gradient: RadialGradient({
                colors: [step.color, this.darkenColor(step.color)],
                radius: 1.5,
              }),
            }),
            child: Center({
              child: AnimatedContainer({
                duration: 400,
                curve: Curves.bounceOut,
                transform: Matrix4.identity()
                  .scaled(this.currentStep === 3 ? 1.2 : 1.0)
                  .rotateZ(this.currentStep * Math.PI / 8),
                transformAlignment: Alignment.center,
                child: Icon({
                  icon: step.icon,
                  size: 100,
                  color: 'white',
                }),
              }),
            }),
          }),
          
          // Text area
          Expanded({
            child: Container({
              padding: EdgeInsets.all(24),
              child: Column({
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Column({
                    children: [
                      AnimatedContainer({
                        duration: 300,
                        height: 40,
                        child: Text(step.title, {
                          style: TextStyle({
                            fontSize: 28,
                            fontWeight: 'bold',
                            color: step.color,
                          }),
                        }),
                      }),
                      SizedBox({ height: 12 }),
                      AnimatedContainer({
                        duration: 400,
                        curve: Curves.easeOut,
                        padding: EdgeInsets.symmetric({ horizontal: 20 }),
                        child: Text(step.description, {
                          style: TextStyle({
                            fontSize: 16,
                            color: '#666',
                            textAlign: 'center',
                          }),
                        }),
                      }),
                    ],
                  }),
                  
                  // Progress indicators and buttons
                  Column({
                    children: [
                      // Progress dots
                      Row({
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: this.steps.map((_, index) => {
                          return Container({
                            margin: EdgeInsets.symmetric({ horizontal: 4 }),
                            child: AnimatedContainer({
                              duration: 300,
                              width: index === this.currentStep ? 24 : 8,
                              height: 8,
                              decoration: BoxDecoration({
                                color: index === this.currentStep ? step.color : '#E0E0E0',
                                borderRadius: BorderRadius.circular(4),
                              }),
                            }),
                          });
                        }),
                      }),
                      
                      SizedBox({ height: 24 }),
                      
                      // Navigation buttons
                      Row({
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          AnimatedContainer({
                            duration: 200,
                            opacity: this.currentStep > 0 ? 1 : 0,
                            child: TextButton({
                              onPressed: () => {
                                if (this.currentStep > 0) {
                                  this.setState(() => {
                                    this.currentStep--;
                                  });
                                }
                              },
                              child: Text('Previous'),
                            }),
                          }),
                          
                          GestureDetector({
                            onTap: () => {
                              if (this.currentStep < this.steps.length - 1) {
                                this.setState(() => {
                                  this.currentStep++;
                                });
                              }
                            },
                            child: AnimatedContainer({
                              duration: 300,
                              padding: EdgeInsets.symmetric({ horizontal: 32, vertical: 12 }),
                              decoration: BoxDecoration({
                                color: step.color,
                                borderRadius: BorderRadius.circular(24),
                                boxShadow: [
                                  BoxShadow({
                                    color: `${step.color}40`,
                                    blurRadius: 12,
                                    offset: Offset({ x: 0, y: 4 }),
                                  }),
                                ],
                              }),
                              child: Text(
                                this.currentStep === this.steps.length - 1 ? 'Get Started' : 'Next',
                                { style: TextStyle({ color: 'white', fontWeight: 'bold' }) }
                              ),
                            }),
                          }),
                        ],
                      }),
                    ],
                  }),
                ],
              }),
            }),
          }),
        ],
      }),
    });
  }
  
  darkenColor(color: string): string {
    // Simple helper to darken colors
    const colors: { [key: string]: string } = {
      '#3F51B5': '#303F9F',
      '#00BCD4': '#0097A7',
      '#4CAF50': '#388E3C',
      '#FF5722': '#D84315',
    };
    return colors[color] || color;
  }
}

Example 7: Responsive Grid Item

class ResponsiveGridItem extends StatefulWidget {
  item: { id: string; title: string; color: string; icon: any };
  
  constructor({ item }: { item: any }) {
    super();
    this.item = item;
  }
  
  createState() {
    return new ResponsiveGridItemState();
  }
}

class ResponsiveGridItemState extends State<ResponsiveGridItem> {
  isPressed = false;
  isHovered = false;
  
  build() {
    return GestureDetector({
      onTapDown: () => this.setState(() => { this.isPressed = true; }),
      onTapUp: () => this.setState(() => { this.isPressed = false; }),
      onTapCancel: () => this.setState(() => { this.isPressed = false; }),
      onMouseEnter: () => this.setState(() => { this.isHovered = true; }),
      onMouseLeave: () => this.setState(() => { this.isHovered = false; }),
      
      child: AnimatedContainer({
        duration: 200,
        curve: Curves.easeOut,
        margin: EdgeInsets.all(this.isPressed ? 12 : 8),
        transform: Matrix4.identity()
          .scaled(this.isPressed ? 0.95 : (this.isHovered ? 1.05 : 1.0)),
        transformAlignment: Alignment.center,
        decoration: BoxDecoration({
          color: this.widget.item.color,
          borderRadius: BorderRadius.circular(this.isHovered ? 20 : 12),
          boxShadow: [
            BoxShadow({
              color: this.isPressed 
                ? 'rgba(0,0,0,0.1)' 
                : (this.isHovered ? 'rgba(0,0,0,0.3)' : 'rgba(0,0,0,0.15)'),
              blurRadius: this.isPressed ? 5 : (this.isHovered ? 20 : 10),
              offset: Offset({ 
                x: 0, 
                y: this.isPressed ? 2 : (this.isHovered ? 8 : 4) 
              }),
            }),
          ],
        }),
        child: Stack({
          children: [
            // Background pattern
            Positioned({
              right: -20,
              bottom: -20,
              child: AnimatedContainer({
                duration: 400,
                curve: Curves.easeOut,
                width: 100,
                height: 100,
                transform: Matrix4.identity()
                  .rotateZ(this.isHovered ? Math.PI / 6 : 0),
                transformAlignment: Alignment.center,
                decoration: BoxDecoration({
                  color: 'rgba(255,255,255,0.1)',
                  borderRadius: BorderRadius.circular(20),
                }),
              }),
            }),
            
            // Content
            Padding({
              padding: EdgeInsets.all(20),
              child: Column({
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  AnimatedContainer({
                    duration: 300,
                    padding: EdgeInsets.all(this.isHovered ? 12 : 8),
                    decoration: BoxDecoration({
                      color: 'rgba(255,255,255,0.2)',
                      borderRadius: BorderRadius.circular(this.isHovered ? 16 : 12),
                    }),
                    child: Icon({
                      icon: this.widget.item.icon,
                      size: 32,
                      color: 'white',
                    }),
                  }),
                  
                  Spacer(),
                  
                  AnimatedContainer({
                    duration: 200,
                    transform: Matrix4.identity()
                      .translate(this.isHovered ? -4 : 0, 0),
                    child: Text(this.widget.item.title, {
                      style: TextStyle({
                        color: 'white',
                        fontSize: 18,
                        fontWeight: 'bold',
                      }),
                    }),
                  }),
                  
                  AnimatedContainer({
                    duration: 300,
                    height: this.isHovered ? 20 : 0,
                    child: this.isHovered ? Text('Learn more →', {
                      style: TextStyle({
                        color: 'rgba(255,255,255,0.8)',
                        fontSize: 14,
                      }),
                    }) : null,
                  }),
                ],
              }),
            }),
          ],
        }),
      }),
    });
  }
}

// Usage example
GridView.count({
  crossAxisCount: 3,
  crossAxisSpacing: 16,
  mainAxisSpacing: 16,
  padding: EdgeInsets.all(16),
  children: [
    { id: '1', title: 'Dashboard', color: '#3F51B5', icon: Icons.dashboard },
    { id: '2', title: 'Analytics', color: '#00BCD4', icon: Icons.analytics },
    { id: '3', title: 'Reports', color: '#4CAF50', icon: Icons.description },
    { id: '4', title: 'Settings', color: '#FF5722', icon: Icons.settings },
    { id: '5', title: 'Users', color: '#9C27B0', icon: Icons.people },
    { id: '6', title: 'Notifications', color: '#FF9800', icon: Icons.notifications },
  ].map(item => ResponsiveGridItem({ item })),
});

Example 8: Floating Action Menu

class FloatingActionMenu extends StatefulWidget {
  createState() {
    return new FloatingActionMenuState();
  }
}

class FloatingActionMenuState extends State<FloatingActionMenu> {
  isOpen = false;
  
  menuItems = [
    { icon: Icons.photo_camera, label: 'Photo', color: '#4CAF50' },
    { icon: Icons.videocam, label: 'Video', color: '#2196F3' },
    { icon: Icons.mic, label: 'Audio', color: '#FF5722' },
    { icon: Icons.attach_file, label: 'File', color: '#FF9800' },
  ];
  
  build() {
    return Container({
      width: 200,
      height: 300,
      child: Stack({
        alignment: Alignment.bottomRight,
        children: [
          // Menu items
          ...this.menuItems.map((item, index) => {
            const angle = (Math.PI / 2) * (index / (this.menuItems.length - 1));
            const distance = 80;
            
            return AnimatedContainer({
              duration: 300 + index * 50,
              curve: this.isOpen ? Curves.easeOut : Curves.easeIn,
              transform: Matrix4.identity()
                .translate(
                  this.isOpen ? -Math.cos(angle) * distance : 0,
                  this.isOpen ? -Math.sin(angle) * distance : 0,
                ),
              child: AnimatedContainer({
                duration: 200,
                opacity: this.isOpen ? 1 : 0,
                transform: Matrix4.identity()
                  .scaled(this.isOpen ? 1 : 0.5),
                transformAlignment: Alignment.center,
                child: Row({
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    if (this.isOpen) Container({
                      padding: EdgeInsets.symmetric({ horizontal: 8, vertical: 4 }),
                      decoration: BoxDecoration({
                        color: 'white',
                        borderRadius: BorderRadius.circular(4),
                        boxShadow: [
                          BoxShadow({
                            color: 'rgba(0,0,0,0.1)',
                            blurRadius: 4,
                            offset: Offset({ x: 0, y: 2 }),
                          }),
                        ],
                      }),
                      child: Text(item.label, {
                        style: TextStyle({ fontSize: 12 }),
                      }),
                    }),
                    SizedBox({ width: 8 }),
                    Container({
                      width: 48,
                      height: 48,
                      decoration: BoxDecoration({
                        color: item.color,
                        shape: BoxShape.circle,
                        boxShadow: [
                          BoxShadow({
                            color: `${item.color}40`,
                            blurRadius: 8,
                            offset: Offset({ x: 0, y: 4 }),
                          }),
                        ],
                      }),
                      child: Center({
                        child: Icon({
                          icon: item.icon,
                          color: 'white',
                          size: 24,
                        }),
                      }),
                    }),
                  ],
                }),
              }),
            });
          }),
          
          // Main FAB
          GestureDetector({
            onTap: () => {
              this.setState(() => {
                this.isOpen = !this.isOpen;
              });
            },
            child: AnimatedContainer({
              duration: 300,
              width: 56,
              height: 56,
              transform: Matrix4.identity()
                .rotateZ(this.isOpen ? Math.PI / 4 : 0),
              transformAlignment: Alignment.center,
              decoration: BoxDecoration({
                color: this.isOpen ? '#F44336' : '#2196F3',
                shape: BoxShape.circle,
                boxShadow: [
                  BoxShadow({
                    color: this.isOpen ? 'rgba(244,67,54,0.4)' : 'rgba(33,150,243,0.4)',
                    blurRadius: 12,
                    offset: Offset({ x: 0, y: 6 }),
                  }),
                ],
              }),
              child: Center({
                child: Icon({
                  icon: Icons.add,
                  color: 'white',
                  size: 28,
                }),
              }),
            }),
          }),
        ],
      }),
    });
  }
}

Performance Best Practices

1. Optimize Animated Properties

Avoid animating too many properties simultaneously:

// ❌ Bad: Animating many properties at once
AnimatedContainer({
  duration: 300,
  width: isExpanded ? 300 : 100,
  height: isExpanded ? 300 : 100,
  margin: EdgeInsets.all(isExpanded ? 20 : 10),
  padding: EdgeInsets.all(isExpanded ? 30 : 15),
  decoration: BoxDecoration({
    color: isExpanded ? 'blue' : 'red',
    borderRadius: BorderRadius.circular(isExpanded ? 20 : 10),
    boxShadow: isExpanded ? [...largeShadows] : [...smallShadows]
  }),
  transform: Matrix4.rotationZ(isExpanded ? Math.PI : 0)
})

// ✅ Good: Animate only necessary properties
AnimatedContainer({
  duration: 300,
  width: isExpanded ? 300 : 100,
  height: isExpanded ? 300 : 100,
  decoration: BoxDecoration({
    color: isExpanded ? 'blue' : 'red',
    borderRadius: BorderRadius.circular(10)
  })
})

2. Use Appropriate Curves

Choose curves that match your animation intent:

// Quick interactions
AnimatedContainer({
  duration: 200,
  curve: Curves.easeOut,  // Fast start, smooth end
  ...
})

// Smooth transitions
AnimatedContainer({
  duration: 400,
  curve: Curves.easeInOut,  // Smooth start and end
  ...
})

// Playful animations
AnimatedContainer({
  duration: 600,
  curve: Curves.bounceOut,  // Bounce effect
  ...
})

3. Avoid Frequent State Changes

Debounce rapid state changes to prevent animation interruptions:

class DebouncedAnimation extends StatefulWidget {
  createState() => new DebouncedAnimationState();
}

class DebouncedAnimationState extends State<DebouncedAnimation> {
  Timer? debounceTimer;
  bool isExpanded = false;

  handleChange(bool value) {
    this.debounceTimer?.cancel();
    this.debounceTimer = Timer(Duration({ milliseconds: 100 }), () => {
      this.setState(() => {
        this.isExpanded = value;
      });
    });
  }

  build() {
    return AnimatedContainer({
      duration: 300,
      width: this.isExpanded ? 200 : 100,
      height: 100,
      color: this.isExpanded ? 'blue' : 'gray'
    });
  }
}

4. Consider Using AnimatedBuilder for Complex Animations

For complex animations that require more control:

class ComplexAnimation extends StatefulWidget {
  createState() => new ComplexAnimationState();
}

class ComplexAnimationState extends State<ComplexAnimation> 
    with SingleTickerProviderStateMixin {
  late AnimationController controller;
  late Animation<double> scaleAnimation;
  late Animation<double> rotationAnimation;

  initState() {
    super.initState();
    this.controller = AnimationController({
      duration: Duration({ seconds: 2 }),
      vsync: this
    });
    
    this.scaleAnimation = Tween<double>({
      begin: 1.0,
      end: 1.5
    }).animate(CurvedAnimation({
      parent: this.controller,
      curve: Curves.easeInOut
    }));
    
    this.rotationAnimation = Tween<double>({
      begin: 0,
      end: 2 * Math.PI
    }).animate(this.controller);
  }

  build() {
    return AnimatedBuilder({
      animation: this.controller,
      builder: (context, child) => {
        return Transform.scale({
          scale: this.scaleAnimation.value,
          child: Transform.rotate({
            angle: this.rotationAnimation.value,
            child: Container({
              width: 100,
              height: 100,
              color: 'blue'
            })
          })
        });
      }
    });
  }
}

Important Notes

  • duration is a required property. You must specify the animation duration.
  • color and decoration cannot be used together.
  • Child widgets are not animated. To animate children, use separate AnimatedWidgets.
  • Animating too many properties simultaneously can affect performance.
  • When state changes rapidly, the current animation is interrupted and a new one starts.
  • transform does not change the layout size. Use width/height for animations that need to occupy space.
  • Complex decoration animations can impact performance, so use them judiciously.

Troubleshooting

Animation Not Smooth

If animations appear jerky:

// ❌ Problem: Too short duration
AnimatedContainer({
  duration: 50,  // Too fast
  width: expanded ? 300 : 100
})

// ✅ Solution: Use appropriate duration
AnimatedContainer({
  duration: 300,  // Smooth transition
  width: expanded ? 300 : 100
})

Animation Not Triggering

Ensure state changes are properly managed:

// ❌ Problem: Direct mutation
class MyWidget extends StatefulWidget {
  createState() => new MyWidgetState();
}

class MyWidgetState extends State<MyWidget> {
  expanded = false;
  
  build() {
    // Wrong: Direct mutation without setState
    this.expanded = true;  // No re-render
  }
}

// ✅ Solution: Use setState
class MyWidget extends StatefulWidget {
  createState() => new MyWidgetState();
}

class MyWidgetState extends State<MyWidget> {
  expanded = false;
  
  build() {
    return GestureDetector({
      onTap: () => {
        this.setState(() => {
          this.expanded = true;  // Triggers re-render
        });
      },
      child: AnimatedContainer({
        duration: 300,
        width: this.expanded ? 200 : 100,
        // ...
      })
    });
  }
}

Conflicting Properties

Watch for property conflicts:

// ❌ Problem: color and decoration conflict
AnimatedContainer({
  color: 'blue',
  decoration: BoxDecoration({ color: 'red' })  // Error!
})

// ✅ Solution: Use decoration only
AnimatedContainer({
  decoration: BoxDecoration({ color: 'blue' })
})
  • Container: Basic container without animation
  • AnimatedPadding: Widget that only animates padding
  • AnimatedAlign: Widget that only animates alignment
  • AnimatedOpacity: Widget that only animates opacity
  • AnimatedPositioned: Widget that animates position within a Stack
  • AnimatedPhysicalModel: Widget that animates shadows and elevation
  • AnimatedDefaultTextStyle: Widget that animates text styles