mcpField
F
flutter-animations

flutter-animations

github.com/im5tuデザイン・UI643 回インストール6 週間前に更新
リポジトリ

flutter-animations について

flutter-animationsは、Flutter開発者向けのアニメーションスキルです。暗黙的なアニメーションから物理シミュレーションまで、一般的な表現の実装例とベストプラクティスを紹介します。

主な機能

  1. 01適切なアニメーション形式の選択
  2. 02暗黙的・明示的アニメーションの実装
  3. 03Hero画面遷移の作成
  4. 04段階的なアニメーションの編成
  5. 05物理ベースのアニメーション作成
  6. 06実装例とベストプラクティスの確認

活用シーン

  1. 01状態変化に応じたプロパティのアニメーション
  2. 02画面間の共有要素の遷移
  3. 03リストやメニュー項目の順次表示
  4. 04バネやドラッグ操作の表現

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/im5tu/claude --skill flutter-animations

インストール後、Claude Code で/flutter-animationsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/templates/
explicit_animation.dart
hero_transition.dart
implicit_animation.dart
staggered_animation.dart
references/
curves.md
explicit.md
hero.md
implicit.md
physics.md
staggered.md

詳細

Flutter アニメーション

概要

ユースケースに適した方法を使って、Flutter で滑らかで高性能なアニメーションを作成します。このスキルでは、暗黙的/明示的な方法の選択から、ヒーロー遷移や段階的アニメーションなどの複雑な効果の実装まで、アニメーションのワークフロー全体を扱います。

アニメーションの種類を選ぶ判断フロー

要件に応じて適切なアニメーションの種類を選びます。

暗黙的アニメーション - 次の場合に使用します:

  • 単一のプロパティ(色、サイズ、位置)をアニメーションさせる
  • 状態の変化によってアニメーションがトリガーされる
  • 細かい制御が必要ない

明示的アニメーション - 次の場合に使用します:

  • アニメーションのライフサイクルを完全に制御する必要がある
  • 複数のプロパティを同時にアニメーションさせる
  • アニメーションの状態変化に応じた処理が必要である
  • カスタムアニメーションやトランジションを作成する

ヒーローアニメーション - 次の場合に使用します:

  • 2 つの画面間で要素を共有する
  • 共有要素のトランジションを作成する
  • 要素がルート間を「飛んで」移動することをユーザーが期待する

段階的アニメーション - 次の場合に使用します:

  • 複数のアニメーションを順番に実行する、または重ねて実行する
  • 波紋効果や順次表示を作成する
  • リスト項目を順番にアニメーションさせる

物理ベースのアニメーション - 次の場合に使用します:

  • アニメーションに自然な物理的感覚を持たせる
  • バネのような動きやスクロールジェスチャーを表現する
  • ドラッグ可能な操作を実装する

暗黙的アニメーション

暗黙的アニメーションは、プロパティが変化したときのアニメーションを自動的に処理します。コントローラーは不要です。

よく使われる暗黙的ウィジェット

AnimatedContainer - 複数のプロパティ(サイズ、色、装飾、パディング)をアニメーションさせます:

AnimatedContainer(
  duration: const Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  width: _expanded ? 200 : 100,
  height: _expanded ? 200 : 100,
  color: _expanded ? Colors.blue : Colors.red,
  child: const FlutterLogo(),
)

AnimatedOpacity - シンプルなフェードアニメーションです:

AnimatedOpacity(
  opacity: _visible ? 1.0 : 0.0,
  duration: const Duration(milliseconds: 300),
  child: const Text('Hello'),
)

TweenAnimationBuilder - 定型コードなしでカスタムツイーンアニメーションを実装します:

TweenAnimationBuilder<double>(
  tween: Tween<double>(begin: 0, end: 1),
  duration: const Duration(seconds: 1),
  builder: (context, value, child) {
    return Opacity(
      opacity: value,
      child: Transform.scale(
        scale: value,
        child: child,
      ),
    );
  },
  child: const FlutterLogo(),
)

その他の暗黙的ウィジェット:

  • AnimatedPadding - パディングのアニメーション
  • AnimatedPositioned - 位置のアニメーション(Stack 内)
  • AnimatedAlign - 配置のアニメーション
  • AnimatedContainer - 複数のプロパティ
  • AnimatedSwitcher - ウィジェット間のクロスフェード
  • AnimatedDefaultTextStyle - テキストスタイルのアニメーション
ベストプラクティス
  • シンプルなケースでは暗黙的アニメーションを優先します
  • 自然な動きになるよう、適切なカーブを使用します(Curves クラスを参照)
  • 予測可能な動作になるよう、curve と duration を設定します
  • 必要に応じて onEnd コールバックを使用します
  • パフォーマンスを維持するため、暗黙的アニメーションのネストは避けます

明示的アニメーション

明示的アニメーションは、AnimationController によって完全な制御を実現します。

主なコンポーネント

AnimationController - アニメーションを駆動します:

late AnimationController _controller;

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

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

Tween - 開始値と終了値の間を補間します:

animation = Tween<double>(begin: 0, end: 300).animate(_controller);

CurvedAnimation - アニメーションにカーブを適用します:

animation = CurvedAnimation(
  parent: _controller,
  curve: Curves.easeInOut,
);
AnimatedWidget パターン

再利用可能なアニメーションウィジェットに最適です:

class AnimatedLogo extends AnimatedWidget {
  const AnimatedLogo({super.key, required Animation<double> animation})
    : super(listenable: animation);

  @override
  Widget build(BuildContext context) {
    final animation = listenable as Animation<double>;
    return Center(
      child: Container(
        height: animation.value,
        width: animation.value,
        child: const FlutterLogo(),
      ),
    );
  }
}
AnimatedBuilder パターン

アニメーションを含む複雑なウィジェットに最適です:

class GrowTransition extends StatelessWidget {
  const GrowTransition({
    required this.child,
    required this.animation,
    super.key,
  });

  final Widget child;
  final Animation<double> animation;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: AnimatedBuilder(
        animation: animation,
        builder: (context, child) {
          return SizedBox(
            height: animation.value,
            width: animation.value,
            child: child,
          );
        },
        child: child,
      ),
    );
  }
}
アニメーション状態の監視
animation.addStatusListener((status) {
  switch (status) {
    case AnimationStatus.completed:
      _controller.reverse();
      break;
    case AnimationStatus.dismissed:
      _controller.forward();
      break;
    default:
      break;
  }
});
複数のアニメーションを同時に実行する
class AnimatedLogo extends AnimatedWidget {
  const AnimatedLogo({super.key, required Animation<double> animation})
    : super(listenable: animation);

  static final _opacityTween = Tween<double>(begin: 0.1, end: 1);
  static final _sizeTween = Tween<double>(begin: 0, end: 300);

  @override
  Widget build(BuildContext context) {
    final animation = listenable as Animation<double>;
    return Center(
      child: Opacity(
        opacity: _opacityTween.evaluate(animation),
        child: Container(
          height: _sizeTween.evaluate(animation),
          width: _sizeTween.evaluate(animation),
          child: const FlutterLogo(),
        ),
      ),
    );
  }
}
組み込みの明示的トランジション

Flutter にはすぐに使えるトランジションが用意されています:

  • FadeTransition - フェードアニメーション
  • ScaleTransition - 拡大縮小アニメーション
  • SlideTransition - スライドアニメーション
  • SizeTransition - サイズアニメーション
  • RotationTransition - 回転アニメーション
  • PositionedTransition - 位置のアニメーション(Stack 内)

例:

FadeTransition(
  opacity: _animation,
  child: const FlutterLogo(),
)
パフォーマンスのヒント
  • ウィジェットが削除されるときにコントローラーを破棄します
  • 再ビルドを最適化するため、AnimatedBuilder を使用します
  • アニメーションリスナー内で setState() を呼び出すのは避けます(AnimatedWidget/AnimatedBuilder を使用します)
  • デバッグ中にアニメーションを遅くするには timeDilation を使用します

ヒーローアニメーション

ヒーローアニメーションは、画面間の共有要素トランジションを作成します。

基本的なヒーローアニメーション

遷移元の画面:

Hero(
  tag: 'hero-image',
  child: Image.asset('images/logo.png'),
)

遷移先の画面:

Hero(
  tag: 'hero-image',  // Same tag!
  child: Image.asset('images/logo.png'),
)
完全な例
class PhotoHero extends StatelessWidget {
  const PhotoHero({
    super.key,
    required this.photo,
    this.onTap,
    required this.width,
  });

  final String photo;
  final VoidCallback? onTap;
  final double width;

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: width,
      child: Hero(
        tag: photo,
        child: Material(
          color: Colors.transparent,
          child: InkWell(
            onTap: onTap,
            child: Image.asset(photo, fit: BoxFit.contain),
          ),
        ),
      ),
    );
  }
}

画面間の移動:

Navigator.of(context).push(
  MaterialPageRoute<void>(
    builder: (context) {
      return Scaffold(
        appBar: AppBar(title: const Text('Detail')),
        body: Center(
          child: PhotoHero(
            photo: 'images/logo.png',
            width: 300.0,
            onTap: () => Navigator.of(context).pop(),
          ),
        ),
      );
    },
  ),
);
放射状ヒーローアニメーション

トランジション中に円から長方形へ変形します:

class RadialExpansion extends StatelessWidget {
  const RadialExpansion({
    super.key,
    required this.maxRadius,
    this.child,
  }) : clipRectSize = 2.0 * (maxRadius / math.sqrt2);

  final double maxRadius;
  final double clipRectSize;
  final Widget? child;

  @override
  Widget build(BuildContext context) {
    return ClipOval(
      child: Center(
        child: SizedBox(
          width: clipRectSize,
          height: clipRectSize,
          child: ClipRect(child: child),
        ),
      ),
    );
  }
}

中心を基準とした補間には MaterialRectCenterArcTween と組み合わせて使用します:

static RectTween _createRectTween(Rect? begin, Rect? end) {
  return MaterialRectCenterArcTween(begin: begin, end: end);
}
Hero のベストプラクティス
  • 一意で一貫性のあるタグを使用します(多くの場合、データオブジェクト自体を指定します)
  • ルート間で Hero ウィジェットツリーの構成を似せます
  • 「ポップ」効果を出すには、画像を色を透明にした Material でラップします
  • トランジションをデバッグするには timeDilation を使用します
  • 必要に応じて Hero アニメーションを無効にするために HeroMode を検討します

スタッガーアニメーション

タイミングを変えて複数のアニメーションを実行します。

基本的なスタッガーアニメーション

すべてのアニメーションで同じコントローラーを使用します。

class StaggerAnimation extends StatelessWidget {
  StaggerAnimation({super.key, required this.controller})
    : opacity = Tween<double>(begin: 0.0, end: 1.0).animate(
        CurvedAnimation(
          parent: controller,
          curve: const Interval(0.0, 0.100, curve: Curves.ease),
        ),
      ),
      width = Tween<double>(begin: 50.0, end: 150.0).animate(
        CurvedAnimation(
          parent: controller,
          curve: const Interval(0.125, 0.250, curve: Curves.ease),
        ),
      );

  final AnimationController controller;
  final Animation<double> opacity;
  final Animation<double> width;

  Widget _buildAnimation(BuildContext context, Widget? child) {
    return Container(
      alignment: Alignment.bottomCenter,
      child: Opacity(
        opacity: opacity.value,
        child: Container(
          width: width.value,
          height: 150,
          color: Colors.blue,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: controller,
      builder: _buildAnimation,
    );
  }
}
Interval ベースのタイミング

各アニメーションには、0.0 から 1.0 の範囲の Interval を設定します。

animation = Tween<double>(begin: 0, end: 300).animate(
  CurvedAnimation(
    parent: controller,
    curve: const Interval(
      0.25,  // Start at 25% of controller duration
      0.50,  // End at 50% of controller duration
      curve: Curves.ease,
    ),
  ),
);
よく使用する Tween
borderRadius = BorderRadiusTween(
  begin: BorderRadius.circular(4),
  end: BorderRadius.circular(75),
).animate(
  CurvedAnimation(
    parent: controller,
    curve: const Interval(0.375, 0.500, curve: Curves.ease),
  ),
);
スタッガーメニューアニメーション
class _MenuState extends State<Menu> with SingleTickerProviderStateMixin {
  static const _initialDelayTime = Duration(milliseconds: 50);
  static const _itemSlideTime = Duration(milliseconds: 250);
  static const _staggerTime = Duration(milliseconds: 50);
  static const _buttonDelayTime = Duration(milliseconds: 150);
  static const _buttonTime = Duration(milliseconds: 500);

  final _animationDuration =
      _initialDelayTime +
      (_staggerTime * _menuTitles.length) +
      _buttonDelayTime +
      _buttonTime;

  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: _animationDuration,
      vsync: this,
    );
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}
スタッガーのベストプラクティス
  • アニメーションの開始時刻をずらすには Interval を使用します
  • コントローラーの再生時間がすべての Interval をカバーするようにします
  • Interval 内で自然な動きになるようにカーブを使用します
  • タイミングをデバッグするには timeDilation を検討します
  • 波紋効果を出すため、遅延を段階的に増やしながらメニュー項目をスタッガー表示します

物理ベースのアニメーション

物理シミュレーションを使用して、自然な動きのアニメーションを作成します。

フリングアニメーション
_controller.fling(
  velocity: 2.0,  // Units per second
);
カスタム物理シミュレーション
_controller.animateWith(
  SpringSimulation(
    spring: const SpringDescription(
      mass: 1,
      stiffness: 100,
      damping: 10,
    ),
    start: 0.0,
    end: 1.0,
    velocity: 0.0,
  ),
);
よく使用する物理シミュレーション
  • SpringSimulation - バネの物理特性
  • BouncingScrollSimulation - バウンドするスクロール
  • ClampingScrollSimulation - バウンドしないスクロール
  • GravitySimulation - 重力ベース

ベストプラクティス

推奨事項
  • ウィジェットの破棄時に AnimationController を破棄します
  • リスナー内で setState() を使う代わりに、AnimatedBuilder/AnimatedWidget を使用します
  • 自然な動きになる適切なカーブを選択します
  • アニメーションのデバッグには timeDilation を使用します
  • パフォーマンスを考慮します(アニメーションのビルド内で負荷の高いウィジェットを避けます)
  • さまざまなデバイスでアニメーションをテストします
  • 直感的な操作感を得るため、逆方向のアニメーションにも対応します
禁止事項
  • AnimationController の破棄を忘れないでください(メモリリークにつながります)
  • AnimatedBuilder で十分な場合に、アニメーションリスナー内で setState() を使用しないでください
  • アニメーションが即座に完了すると思い込まないでください(AnimationStatus を処理してください)
  • アニメーションを過剰に使用しないでください(ユーザーの注意をそらすことがあります)
  • 「ぎくしゃく」したアニメーションを作成しないでください(滑らかなカーブを使用してください)
  • アクセシビリティを無視しないでください(disableAnimations の設定を尊重してください)

リソース

references/

implicit.md - 暗黙的なアニメーションウィジェットの完全なリファレンスと、例およびベストプラクティスです。

explicit.md - 明示的なアニメーション、AnimationController、およびパターンを詳しく解説します。

hero.md - 標準および放射状トランジションを含む Hero アニメーションのガイドです。

staggered.md - スタッガーアニメーションのパターンとタイミング戦略です。

physics.md - 物理ベースのアニメーションとシミュレーションです。

curves.md - Curves クラスと適切なカーブの選び方に関するリファレンスです。

assets/templates/

一般的なアニメーションパターンのテンプレートコードです。

  • implicit_animation.dart - 暗黙的なアニメーションの例
  • explicit_animation.dart - 明示的なアニメーションのセットアップ
  • hero_transition.dart - Hero アニメーションのボイラープレート
  • staggered_animation.dart - スタッガーアニメーションのテンプレート

コメント