0
0
Fluttermobile~20 mins

Staggered animations in Flutter - Mini App: Build & Ship

Choose your learning style9 modes available
Build: Staggered Animation Demo
This screen shows a box that changes its size, color, and position one after another in a smooth staggered animation.
Target UI
┌───────────────────────────────┐
│ Staggered Animation Demo       │
│                               │
│       [Animated Box]           │
│                               │
│   [Start Animation Button]    │
└───────────────────────────────┘
Display a square box in the center of the screen.
Add a button labeled 'Start Animation' below the box.
When the button is pressed, animate the box with staggered effects:
1. Move the box horizontally from left to right.
2. Change the box color from blue to red.
3. Increase the box size from 100x100 to 200x200 pixels.
Each animation should start after the previous one finishes.
Use Flutter's AnimationController and Tween animations.
Ensure the animations run smoothly and sequentially.
Starter Code
Flutter
import 'package:flutter/material.dart';

class StaggeredAnimationDemo extends StatefulWidget {
  @override
  _StaggeredAnimationDemoState createState() => _StaggeredAnimationDemoState();
}

class _StaggeredAnimationDemoState extends State<StaggeredAnimationDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 6),
      vsync: this,
    );
    // TODO: Define animations here
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _startAnimation() {
    // TODO: Start the staggered animation sequence
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Staggered Animation Demo')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            // TODO: Animated box widget
            const SizedBox(height: 40),
            ElevatedButton(
              onPressed: _startAnimation,
              child: const Text('Start Animation'),
            ),
          ],
        ),
      ),
    );
  }
}
Task 1
Task 2
Task 3
Task 4
Solution
Flutter
import 'package:flutter/material.dart';

class StaggeredAnimationDemo extends StatefulWidget {
  @override
  _StaggeredAnimationDemoState createState() => _StaggeredAnimationDemoState();
}

class _StaggeredAnimationDemoState extends State<StaggeredAnimationDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _positionAnimation;
  late Animation<Color?> _colorAnimation;
  late Animation<double> _sizeAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 6),
      vsync: this,
    );

    _positionAnimation = Tween<double>(begin: -100, end: 100).animate(
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(0.0, 0.33, curve: Curves.easeInOut),
      ),
    );

    _colorAnimation = ColorTween(begin: Colors.blue, end: Colors.red).animate(
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(0.34, 0.66, curve: Curves.easeInOut),
      ),
    );

    _sizeAnimation = Tween<double>(begin: 100, end: 200).animate(
      CurvedAnimation(
        parent: _controller,
        curve: const Interval(0.67, 1.0, curve: Curves.easeInOut),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _startAnimation() {
    _controller.reset();
    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Staggered Animation Demo')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            AnimatedBuilder(
              animation: _controller,
              builder: (context, child) {
                return Transform.translate(
                  offset: Offset(_positionAnimation.value, 0),
                  child: Container(
                    width: _sizeAnimation.value,
                    height: _sizeAnimation.value,
                    color: _colorAnimation.value,
                  ),
                );
              },
            ),
            const SizedBox(height: 40),
            ElevatedButton(
              onPressed: _startAnimation,
              child: const Text('Start Animation'),
            ),
          ],
        ),
      ),
    );
  }
}

We use a single AnimationController with a 6-second duration to control all animations.

Three animations are created with Tween and ColorTween, each assigned a part of the total duration using Interval:

  • Position: Moves the box horizontally from -100 to 100 pixels in the first third (0.0 to 0.33).
  • Color: Changes from blue to red in the middle third (0.34 to 0.66).
  • Size: Grows from 100 to 200 pixels in the last third (0.67 to 1.0).

The AnimatedBuilder rebuilds the box whenever the controller ticks, applying the current animation values for position, color, and size.

The _startAnimation method resets and starts the controller to run the staggered animations in sequence.

Final Result
Completed Screen
┌───────────────────────────────┐
│ Staggered Animation Demo       │
│                               │
│       [■]                     │
│                               │
│   [Start Animation Button]    │
└───────────────────────────────┘
When the user taps 'Start Animation', the box moves smoothly from left to right.
After moving, the box color changes from blue to red.
Finally, the box grows bigger from 100x100 to 200x200 pixels.
All animations happen one after another in a smooth sequence.
Stretch Goal
Add a reset button that returns the box to its original position, color, and size instantly.
💡 Hint
Use _controller.reset() and setState to update the UI immediately.