Flutter暗黙的アニメーション

暗黙的アニメーションは一番簡単で、一番よく使います

目次

暗黙的アニメーション Widget 一覧

  • AnimatedContainer・・・サイズ、色、位置、角丸など
  • AnimatedOpacity・・・透明度(フェードイン/アウト)
  • AnimatedAlign・・・位置
  • AnimatedPositioned・・・Stack内での位置
  • AnimatedPadding・・・パディング
  • AnimatedDefaultTextStyle・・・テキストスタイル
  • AnimatedPhysicalModel・・・影、立体感
  • AnimatedCrossFade・・・2つのWidgetを切り替え
  • AnimatedSwitcher・・・Widgetの入れ替え
  • AnimatedScale・・・拡大・縮小
  • AnimatedRotation・・・回転

ポイント: 名前が Animated〇〇 = 自動アニメーション! 😊

実践例1: AnimatedContainer (超重要!)

サイズと色が変わるボタン

import 'package:flutter/material.dart';

class AnimatedButtonDemo extends StatefulWidget {
  @override
  _AnimatedButtonDemoState createState() => _AnimatedButtonDemoState();
}

class _AnimatedButtonDemoState extends State<AnimatedButtonDemo> {
  bool _isExpanded = false;
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('AnimatedContainer')),
      body: Center(
        child: GestureDetector(
          onTap: () {
            setState(() {
              _isExpanded = !_isExpanded;  // 状態を切り替え
            });
          },
          child: AnimatedContainer(
            // ========================================
            // ここが自動でアニメーションする!
            // ========================================
            width: _isExpanded ? 200 : 100,   // 幅が変わる
            height: _isExpanded ? 200 : 100,  // 高さが変わる
            color: _isExpanded ? Colors.blue : Colors.red,  // 色が変わる
            
            // アニメーションの設定
            duration: Duration(milliseconds: 300),  // 0.3秒かけて変化
            curve: Curves.easeInOut,  // 変化の仕方(イージング)
            
            child: Center(
              child: Text(
                'タップ!',
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

たったこれだけ! ✨
動き:

タップ → サイズが大きくなる + 赤→青
もう一度タップ → サイズが小さくなる + 青→赤

AnimatedContainer でできること全部!

AnimatedContainer(
  // サイズ
  width: _isExpanded ? 200 : 100,
  height: _isExpanded ? 200 : 100,
  
  // 色
  color: _isExpanded ? Colors.blue : Colors.red,
  
  // 角丸
  decoration: BoxDecoration(
    color: _isExpanded ? Colors.blue : Colors.red,
    borderRadius: BorderRadius.circular(_isExpanded ? 50 : 10),
    boxShadow: _isExpanded
        ? [BoxShadow(color: Colors.black26, blurRadius: 10)]
        : [],
  ),
  
  // パディング
  padding: EdgeInsets.all(_isExpanded ? 40 : 20),
  
  // マージン
  margin: EdgeInsets.all(_isExpanded ? 20 : 50),
  
  // 位置(Alignment)
  alignment: _isExpanded ? Alignment.center : Alignment.topLeft,
  
  // トランスフォーム(回転・拡大など)
  transform: _isExpanded
      ? Matrix4.rotationZ(0.1)
      : Matrix4.rotationZ(0),
  
  // アニメーション設定
  duration: Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  
  child: Text('Hello'),
)

ほぼ全部アニメーションできる!

実践例2: AnimatedOpacity (フェード)

じわ〜っと消える・現れる

class FadeDemo extends StatefulWidget {
  @override
  _FadeDemoState createState() => _FadeDemoState();
}

class _FadeDemoState extends State<FadeDemo> {
  bool _isVisible = true;
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Fade Animation')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // ========================================
            // 透明度がアニメーション
            // ========================================
            AnimatedOpacity(
              opacity: _isVisible ? 1.0 : 0.0,  // 1.0=表示, 0.0=透明
              duration: Duration(milliseconds: 500),
              child: Container(
                width: 200,
                height: 200,
                color: Colors.blue,
                child: Center(
                  child: Text(
                    'Hello',
                    style: TextStyle(color: Colors.white, fontSize: 30),
                  ),
                ),
              ),
            ),
            SizedBox(height: 40),
            
            ElevatedButton(
              onPressed: () {
                setState(() {
                  _isVisible = !_isVisible;
                });
              },
              child: Text(_isVisible ? '消す' : '表示'),
            ),
          ],
        ),
      ),
    );
  }
}

使い道:

ローディング完了後に表示
エラーメッセージを徐々に表示
要素をフェードアウトで削除

実践例3: AnimatedAlign (位置移動)

左右に動く

class SlideDemo extends StatefulWidget {
  @override
  _SlideDemoState createState() => _SlideDemoState();
}

class _SlideDemoState extends State<SlideDemo> {
  bool _isLeft = true;
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Slide Animation')),
      body: Column(
        children: [
          Expanded(
            child: AnimatedAlign(
              alignment: _isLeft ? Alignment.centerLeft : Alignment.centerRight,
              duration: Duration(milliseconds: 500),
              curve: Curves.easeInOut,
              child: Container(
                width: 100,
                height: 100,
                color: Colors.red,
                child: Center(child: Text('🚗', style: TextStyle(fontSize: 40))),
              ),
            ),
          ),
          
          ElevatedButton(
            onPressed: () {
              setState(() {
                _isLeft = !_isLeft;
              });
            },
            child: Text('移動'),
          ),
          SizedBox(height: 20),
        ],
      ),
    );
  }
}

動き: 車が左右にスライド!

実践例4: AnimatedSwitcher (切り替え)

カウンターの数字が滑らかに変わる

class CounterDemo extends StatefulWidget {
  @override
  _CounterDemoState createState() => _CounterDemoState();
}

class _CounterDemoState extends State<CounterDemo> {
  int _counter = 0;
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Counter Animation')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // ========================================
            // 数字が変わるときにアニメーション
            // ========================================
            AnimatedSwitcher(
              duration: Duration(milliseconds: 300),
              transitionBuilder: (child, animation) {
                return ScaleTransition(
                  scale: animation,
                  child: child,
                );
              },
              child: Text(
                '$_counter',
                key: ValueKey<int>(_counter),  // keyが重要!
                style: TextStyle(fontSize: 80, fontWeight: FontWeight.bold),
              ),
            ),
            SizedBox(height: 40),
            
            ElevatedButton(
              onPressed: () {
                setState(() {
                  _counter++;
                });
              },
              child: Text('増やす'),
            ),
          ],
        ),
      ),
    );
  }
}

動き: 数字がポップアップで変わる! ✨

アニメーションの設定

AnimatedContainer(
  duration: Duration(milliseconds: 300),  // 0.3秒
  // duration: Duration(seconds: 1),       // 1秒
  // duration: Duration(milliseconds: 100), // 0.1秒(速い)
)

目安:

100-200ms: 速い、クイックな反応
300ms: 標準、ちょうど良い ⭐
500-1000ms: ゆっくり、目立つ

curve (変化の仕方)

AnimatedContainer(
  curve: Curves.easeInOut,  // なめらか
)

主なカーブ:

linear 一定速度・・・・・・・・・シンプル
easeIn ゆっくり→速く・・・・・・表示開始
easeOut 速く→ゆっくり・・・・・・表示終了
easeInOut ゆっくり→速く→ゆっくり・・・万能! ⭐
bounceIn バウンド・・・・・・楽しい
elasticOut びよーん・・・・・・目立つ

おすすめ: Curves.easeInOut が一番自然! 😊


まとめ: 暗黙的アニメーション

Widget用途難易度
AnimatedContainer万能! サイズ・色・位置
AnimatedOpacityフェードイン/アウト
AnimatedAlign位置移動
AnimatedSwitcher切り替え⭐⭐


一言まとめ:
暗黙的アニメーションは超簡単!
AnimatedContainer でサイズ・色・位置が自動アニメーション。
AnimatedOpacity でフェード。
duration は 300ms、
curve は easeInOut が標準。
setState で値を変えるだけで自動的に滑らかに変化! 🎨✨

目次