くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Flutterのボタンをゲームっぽいアニメーションにする

ほぞぼそと作ってるマグロのゲーム、
ボタンの動きをゲームっぽくしたくて、
いろいろ調べたときの備忘録(*´ω`*)

maguro-sagashi.com

動きとしてはこんな感じで、ピョコンとなる感じ

やりかた

できたのはこんな感じ。このあたりを使って実装するっぽい

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class GameButtonWrapper extends HookConsumerWidget {
  const GameButtonWrapper({
    super.key,
    required this.child,
    required this.onPressed,
  });
  // ボタンのWidget
  final Widget child;
  // クリック時の処理
  final Function()? onPressed;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    TickerFuture? downTicker;
    // AnimationControllerの準備
    final animController = useAnimationController(
      duration: const Duration(milliseconds: 100),
    );
    
    // Tweenアニメーションの準備
    final pressAnim = Tween<double>(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(parent: animController, curve: Curves.easeInOut),
    );

    // RepaintBoundaryで再描画範囲を制限
    return RepaintBoundary(
      child: GestureDetector(
        // tap downしたときにアニメーションを開始
        // 実行中のTickerFutureを保持しておく
        // onPressedはまだ呼ばない
        onTapDown: (_) {
          if (onPressed == null) return;
          downTicker = animController.animateTo(1.0);
        },
        // tap upしたときに、
        // tap downのアニメーションが終了するまで待って、
        // アニメーションを逆再生し、
        // 逆再生が終わったら、onPressedを呼ぶ
        onTapUp: (_) {
          if (onPressed == null) return;
          downTicker?.whenComplete(() {
            animController.animateTo(0.0).whenCompleteOrCancel(() {
              onPressed?.call();
            });
          });
        },
        // キャンセルされたときは、resetする
        onTapCancel: () {
          if (onPressed == null) return;
          animController.reset();
        },
        // 実際のアニメーションするWidget部分
        child: AnimatedBuilder(
          animation: pressAnim,
          builder: (context, child) {
            // Transform.translateを使って、
            // 経過時間分、下にずらす
            return Transform.translate(
              offset: Offset(0.0, pressAnim.value * 10),
              child: child,
            );
          },
          child: child,
        ),
      ),
    );
  }
}

AnimatedBuilderAnimationControllerの例はあるけど、
それだけだと、いい感じにクリック動作(onPressed)が実行されない...

TickerFutureを使って、アニメーションを待つのがポイントっぽい

downTicker?.whenComplete(() {
  animController.animateTo(0.0).whenCompleteOrCancel(() {
    onPressed?.call();
  });
});

以上!! Flutterでもゲームっぽくできるようになったぞ(*´ω`*)

参考にしたサイトさま