[Widget]点滅するアニメーションを追加する

動作環境: Flutter 2.8.1 Dart 2.15.1
LINEでこのページを共有するTwitterでこのページを共有するこのページをはてなブックマークに追加

Widgetの背景色を繰り返し点滅させるアニメーションを導入してみましょう。
これはAnimationControllerを使い、Transition系のWidgetを追加することで実現可能です。

まず、コード全体を掲載します。

lib/main.dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
final DecorationTween _decorationTween = DecorationTween(
begin: const BoxDecoration(
color: Colors.blue,
),
end: BoxDecoration(
color: Colors.blue.shade100,
),
);
@override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 3000),
)..repeat(reverse: true);
super.initState();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('サンプル'),
),
body: Center(
child: DecoratedBoxTransition(
decoration: _decorationTween.animate(_animationController),
child: const SizedBox(
width: 200,
height: 200,
child: Center(
child: Text('demo'),
),
),
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.play_arrow),
onPressed: () {
print('aa');
},
),
),
);
}
}

アニメーションの状態を保持する必要があるため、今回はStatefulWidgetを使います。
最初はアニメーションに必要な変数たちを準備します。
以下のコードを見てください。

lib/main.dart
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
final DecorationTween _decorationTween = DecorationTween(
begin: const BoxDecoration(
color: Colors.blue,
),
end: BoxDecoration(
color: Colors.blue.shade100,
),
);

まず、12行目でクラスの定義を開始しますが、SingleTickerProviderStateMixinを組み込んでいます。
これによりこのクラスの中でアニメーションを取り扱うことが可能になります。

そしてAnimationControllerを状態として保持できるように_animationController変数を用意しています。

15行目から始まるDecorationTweenは今回のアニメーションの内容です。
begin:にアニメーション開始時の状態を設定します。逆にend:は終了時の状態ですね。
今回は青色から薄い青に変化していくように設定しました。

そして、_animationControllerを初期化する処理が必要です。
以下のコードを見てください。

lib/main.dart
@override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 3000),
)..repeat(reverse: true);
super.initState();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}

initState_animationControllerの初期化処理をしています。

duration:では再生スピードを指定しますが、今回は3秒にしています。
先ほど15行目で指定したアニメーションを3秒の時間をかけて再生することになります。
begin:の状態からend:の状態に3秒間で変化していきます。

29行目でrepeat(reverse: true)としています。
これにより、アニメーションは繰り返し再生されるようになります。
reverse: trueにすると、繰り返し方が変わり、再生と逆再生を交互に行うようになります。
今回は背景色をシームレスに点滅させたいため、reverse: trueにしています。

35行目はおまじないですね。インスタンスが破棄されるときに_animationControllerも破棄しています。

最後にアニメーションを仕掛けるところを指定します。
以下のコードを見てください。

lib/main.dart
body: Center(
child: DecoratedBoxTransition(
decoration: _decorationTween.animate(_animationController),
child: const SizedBox(
width: 200,
height: 200,
child: Center(
child: Text('demo'),
),
),
),
),

アニメーションを追加する対象は50行目のSizedBox以下になります。
対象を48行目のようにDecorateBoxTransitionで囲みます。
このとき、49行目のように15行目で定義したアニメーションを紐つけてあげることで、指定したアニメーションが適用されます。
あとはビルドをすると以下のコードのように点滅のアニメーションが再生されるでしょう。

*「Run」を押すと実行できます。

さて、今はアプリが起動した時点でアニメーションが再生されました。
ユーザーが任意の操作をしたタイミングで点滅のアニメーションを追加するにはどうしたらいいでしょうか?

まず、以下のようにinitStateで実行していたrepeat(reverse: true)を取り除きます。

_animationController = AnimationController(
  vsync: this,
  duration: const Duration(milliseconds: 3000),
);

これで勝手にアニメーションが再生されることはなくなりました。
次にfloatingActionButtonを押した時にアニメーションが再生されるようにしてみます。

floatingActionButton: FloatingActionButton(
  child: Icon(_animationController.isAnimating
      ? Icons.pause
      : Icons.play_arrow),
  onPressed: () {
    setState(() {
      if (_animationController.isAnimating) {
        _animationController.stop();
      } else {
        _animationController.repeat(reverse: true);
      }
    });
  },
),

onPressed:のところにアニメーションの制御を追加しています。
_animationController.isAnimatingtrueのときはアニメーション再生中です。
そのときはアニメーションをストップするようにしました。

falseのとき(初回含む)は逆にアニメーションを再生しないといけません。
ここで、先ほど25行目からはじまるinitStateで取り除いたrepeat(reverse: true)を追加すればOKです。

こうすれば任意のタイミングで繰り返しのアニメーションを有効化・無効化が可能になります。
以下はボタンを追加した場合のサンプルです。

*「Run」を押すと実行できます。