Flutter Motion Kit

Previewable Flutter animations + pitfall guide · one-click reuse from Claude Code

← Back

Expand / collapse (AnimatedSize)

implicit · difficulty 2/5 · verified Flutter 3.32 / 2026-06

A tappable header expands and collapses a body whose height is unknown. Pure implicit animation: AnimatedSize tweens to whatever intrinsic size its child reports — no manual controller, no measuring.

▶︎ Running for real (Flutter 3.32 · self-hosted, not a recording)

Code

// ✅ Recommended: pure implicit expand/collapse with AnimatedSize. No manual
// controller, no measuring — it tweens to the body's intrinsic height.
// Paste straight into DartPad (https://dartpad.dev) to run.
import 'package:flutter/material.dart';

void main() => runApp(const _App());

class _App extends StatelessWidget {
  const _App();
  @override
  Widget build(BuildContext context) => MaterialApp(
    debugShowCheckedModeBanner: false,
    theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.deepPurple),
    home: const _Demo(),
  );
}

class _Demo extends StatefulWidget {
  const _Demo();
  @override
  State<_Demo> createState() => _DemoState();
}

class _DemoState extends State<_Demo> {
  bool _expanded = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Expand / collapse')),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(24),
          child: Card(
            clipBehavior: Clip.antiAlias,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                ListTile(
                  title: const Text('What is AnimatedSize?'),
                  trailing: AnimatedRotation(
                    turns: _expanded ? 0.5 : 0,
                    duration: const Duration(milliseconds: 250),
                    child: const Icon(Icons.expand_more),
                  ),
                  onTap: () => setState(() => _expanded = !_expanded),
                ),
                // ✅ AnimatedSize tweens between zero-height and the body's
                // intrinsic height. No vsync argument — it uses the context.
                AnimatedSize(
                  duration: const Duration(milliseconds: 300),
                  curve: Curves.easeInOut,
                  alignment: Alignment.topCenter,
                  child: _expanded
                      ? const Padding(
                          padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
                          child: Text(
                            'AnimatedSize automatically animates its own size '
                            'to whatever its child reports. Because the body '
                            'here has an intrinsic height we never have to '
                            'measure or hard-code it — toggling the child is '
                            'enough to drive a smooth expand/collapse.',
                          ),
                        )
                      // Collapsed: an empty, zero-height box.
                      : const SizedBox(width: double.infinity),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

⚠️ Pitfalls (4)

AnimatedSize clips its child while resizing; if the inner child is unbounded it can overflow mid-animation.
✅ Give the body a bounded or intrinsic size (let it size to its content) so it never fights AnimatedSize's clip.
official-docs · source
Since Flutter 2.x AnimatedSize no longer takes a vsync argument; tutorials that still pass it won't compile.
✅ Drop the vsync parameter entirely — AnimatedSize gets its ticker from the build context.
official-docs · source
With no duration/curve (or a zero duration) the size snaps instantly and there is no visible animation.
✅ Supply a non-zero duration and a curve; use alignment to choose which edge stays anchored while resizing.
official-docs · source
Hand-rolling a SizeTransition forces you to know the target height up front, which fails for intrinsic content.
✅ Prefer AnimatedSize when the end size is intrinsic or unknown; reach for SizeTransition only with a fixed factor.
community-consensus

Official docs