[Flutter] 三(卍^o^)卍←こいつで学ぶアニメーション

# これは何？
### [[CSSアニメーション]三(卍^o^)卍←こいつのこれ→卍を回したい](https://qiita.com/mame_hashbill/items/98118f4e7721a1ac30c9)

- mame_hashbill氏の記事を読み，「ぼくもこれやりたい！」ってなったため
- Flutterのアニメーション復習したかったため
- DartPadでFlutterが動かせるようになっていたのでリンクで共有するやつをやりたかったため

以上の理由でやりました．
怒られたら消します．

# まずはこれを見てくれ

https://dartpad.dev/experimental/embed-new-flutter.html?id=a2c99090c3567cb969537b21922a4935

スマホでも動くけど，横画面にするかPCで見たほうが良いです．
右上のRunボタンを押すと，アイツが動き出すはずです．
<img width="1440" alt="スクリーンショット 2019-09-10 13.49.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154601/2a04e354-89d5-e6a3-3a1c-cb62f5d58ec2.png">

# 実装
Flutterのアニメーションにはいくつか実装の方法があるんですが，今回は細かくアニメーションを制御できる最も基本的な書き方のうち2種類を使います．
2つとも同じような方法ですが，実際に書いてみて比べたくなったのでやってみました．

## 普通にAnimationを直に使う場合
[公式チュートリアル](https://flutter.dev/docs/development/ui/animations/tutorial) 
これの通り．ﾄﾞｩﾙﾙﾙ自体の移動に使ってます．

```dart
  @override
  void initState() {
    super.initState();

    //省略
    
    _bodyController = AnimationController(
      duration: const Duration(milliseconds: 2500),
      vsync: this,
    );
    _bodyAnimation = Tween<double>(begin: 0, end: 600).animate(_bodyController)
      ..addListener(() => setState((){}));
    _bodyController.repeat();
    
  }
  
  @override
  Widget build(BuildContext context) {
    return Transform.translate(
      offset: Offset(_bodyAnimation.value, 0),
      child: _buildCrazy(context),
    );
  }
```

## `AnimatedBuilder` を使う場合
[公式ドキュメント](https://api.flutter.dev/flutter/widgets/AnimatedBuilder-class.html)
これの通りです．腕の回転ですね．

```dart
@override
  void initState() {
    super.initState();
    _armController = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    )..repeat();
    
    //省略
  }

Widget _buildCrazy(BuildContext context) {
    return AnimatedBuilder(
      animation: _armController,
      builder: (context, child) {
        return Row(
          children: <Widget>[
            const Text('三('),
            Transform.rotate(
              angle: _armController.value * 2.0 * math.pi,
              origin: Offset.zero,
              child: Text('卍'),
            ),
            const Text('^o^)'),
            Transform.rotate(
              angle: _armController.value * 2.0 * math.pi,
              origin: Offset.zero,
              child: Text('卍'),
            ),
            const Text('ﾄﾞｩﾙﾙﾙﾙﾙﾙﾙﾙ'),
          ],
        );
      },
    );
  }
```

（アニメーションタイプが回転でrepeatメソッド使うのもまんまだったのでびっくり）

# ２つの実装，どっち使えばいいの？
ここでは簡単な実装だったので，どっちでもいいじゃんって感じになってるんですけど，一応使い分けポイントとしては

- `AnimatedBuilder` の場合， `animation` にコントローラーを直接突っ込める
    - つまり `Animation` の宣言をしなくてもアニメーションできて手間が減る

（箇条書きしたけど一つしか出なかった）
というのがパッと見わかります．

## 罠
しかし上記の使い分けには罠があります．
確かに `AnimatedBuilder` にコントローラーを突っ込めば楽ができますが，この場合アニメーションの種類は0.0 ~ 1.0のTweenアニメーションで固定になります．またイージングカーブもLinear固定になります．

でも， `AnimatedBuilder` のプロパティ名って `animation` ですよね？

そう，普通に `Animation` が突っ込めます．

```dart
  AnimationController _armController;
  Animation<double> _armAnimation;

  @override
  void initState() {
    super.initState();
    _armController = AnimationController(
      duration: const Duration(milliseconds: 500),
      vsync: this,
    )..repeat();
    
    _armAnimation = Tween<double>(begin: 0, end: 2.0 * math.pi).animate(_armController);
  }

  @override
  Widget build(BuildContext context) {
    return _buildCrazy(context);
  }

  Widget _buildCrazy(BuildContext context) {
    return AnimatedBuilder(
      animation: _armAnimation,
      builder: (context, child) {
        return Row(
          children: <Widget>[
            const Text('三('),
            Transform.rotate(
              angle: _armAnimation.value,
              origin: Offset.zero,
              child: Text('卍'),
            ),
            const Text('^o^)'),
            Transform.rotate(
              angle: _armAnimation.value,
              origin: Offset.zero,
              child: Text('卍'),
            ),
            const Text('ﾄﾞｩﾙﾙﾙﾙﾙﾙﾙﾙ'),
          ],
        );
      },
    );
  }
```
腕のアニメーションだけ抜き出して，書き換えてみました．
これで細かいアニメーションの設定もできるし，更にリスナーを登録する必要がないので無駄にsetState書かなくてスッキリですね😋

# 結局の所
Animatedなんちゃら系の一番ラクなWidgetを使わずにアニメーションを実装する場合，基本的にAnimatedBuilderで組むのがよさそうです．
[公式チュートリアル](https://flutter.dev/docs/development/ui/animations/tutorial) の **Monitoring the progress of the animation** でやっているような場合分けも `addStatusListener` するだけで済みます．

結果的に書く量ってそこまで変わらない気がするので，どちらの方法でやってもあまり変わらない……？ もし明確な違いがあったらコメントで教えて下さい．