【最短理解】Flutter animationを用いてWidgetを移動させる
今回は、flutterのアニメーションの仕組みと使い方を簡潔にまとめました。
この記事を読むことで、Widgetを『好きな場所に』『好きなように』移動させる事ができるようになります。また、アニメーションをまだ実装した事がない人でも簡単に実装できるように解説もつけております。
✅今回の内容
- アニメーションの仕組み
- アニメーションを作るためのパーツ
- animationをWidgetに反映させる
- 実際に動かしてみる
- まとめ
✅メイン内容
🛠 アニメーションの仕組み
1. アニメーションはどうやって動いているのか
結論:仕組み自体はパラパラ漫画と変わらない。
Youtubeや映画などの映像・動画は、『パラパラ漫画』と同じ仕組みで出来ています。
少し違う静止画を高速で何枚も表示させる事で、まるで動いているかのように見えます。
flutterでも同じように静止画(Widget)を何枚も表示させる事で、
まるでWidgetが動いているかのように見せる事ができます。
🛠 アニメーションを作るためのパーツ
flutterでアニメーションを作るためには最低限、下記3つが必要になります。
-
AnimationController
- アニメーションの進行具合
-
Tween
- アニメーションの始点と終点の位置
-
animation
- 実際にWidgetに反映させるためのanimationそのもの
1. AnimationController
AnimationControllerは、アニメーションの進行具合を管理します。
duration
には、アニメーションに掛かる時間を設定します。今回は0.5秒に設定しています。
vsync
には、毎フレームごとに画面の更新を伝えてくれるオブジェクトを設定します。
ここのthis
はSingleTickerProviderStateMixin
になります。(後ほど説明)
AnimationController _controller = AnimationController(
duration: Duration(milliseconds: 500),
vsync: this,
);
2. Tween
Tweenで、アニメーションの始点と終点を設定します。
begin
には、開始位置を設定します。今回はAlignment(0.0, 0.0)
としています。
end
には、終了位置を設定します。今回はAlignment(1.0, 1.0)
としています。
Tween<Alignment> tween = Tween(
begin: const Alignment(0.0, 0.0),
end: const Alignment(1.0, 1.0),
);
ここで使用しているAlignment
ではx座標とy座標をそれぞれdouble型で指定します。
少し注意が必要なのは、座標の取り方です。
Container
やText
など多くのWidgetでは座標が x= 0.0,y= 0.0
であれば、
画面の左上を取ります。
このAlignment
ではx= 0.0,y= 0.0
であれば親Widgetの中央を取ります。
また、上下左右の端はそれぞれx= -1.0,y= -1.0
〜x= 1.0,y= 1.0
で
座標を指定する事ができます。
今回の場合は、開始地点が親Widgetの中央で、終了地点が親Widgetの右下です。
3. animation
animationで、実際に上記で設定したアニメーションをWidgetに反映させます。
Animation<Alignment> animation = _controller.drive(tween);
簡単にまとめると
animation = AnimationController × Tween
という事です。
🛠 animationをWidgetに反映させる
animationをWidgetに反映させる方法は2つあります。
- AnimatedBuilder
- AnimatedWidget
今回は、AnimatedBuilder
のみ解説します。
AnimatedWidget
についてはまた今度解説します。
1. AnimatedBuilder
builderの解説
animationパラメータは画面更新の通知を受けて、builderを走らせます。
画面更新の通知はAnimationControllerで受け取る事ができるので、
このanimationパラメータには先ほどのanimation変数を置いておきます。
これにより、毎フレームごとにbuilder
が走ります。
Alignの解説
alignment
パラメータにはanimation.value
を渡しています。
alignment
パラメータは、先ほど作成したanimationを反映させたいWidgetに紐付けます。
これによりAlign
Widgetが開始地点から終了地点に移動するまで画面が更新され続けます。
AnimatedBuilder(
animation: animation,
builder: (context, _) {
return Align(
alignment: animation.value,
child: Container(
width: 200,
height: 200,
color: Colors.purple[200],
),
);
},
),
これでWidgetにanimationを反映させる事ができました。🍎
🛠 実際に動かしてみる
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Alignment> animation;
bool mode_change_switch = true;
Future<void> setAnimation() async {
_controller = AnimationController(
duration: Duration(milliseconds: 500),
vsync: this,
);
Tween<Alignment> tween = Tween(
begin: const Alignment(0.0, 0.0),
end: const Alignment(1.0, 1.0),
);
animation = _controller.drive(tween);
}
void initState() {
setAnimation();
super.initState();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter app'),
),
body: Container(
width: double.infinity,
height: (MediaQuery.of(context).size.height / 2) -
AppBar().preferredSize.height,
color: Colors.amber,
child: AnimatedBuilder(
animation: animation,
builder: (context, _) {
return Align(
alignment: animation.value,
child: Container(
width: 200,
height: 200,
color: Colors.purple[200],
),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
mode_change_switch ? _controller.forward() : _controller.reverse();
mode_change_switch = !mode_change_switch;
},
backgroundColor: Colors.green,
child: Icon(
Icons.animation,
color: Colors.white,
),
),
);
}
}
画面生成時に、setAnimation
メソッドを実行してanimationを作成します。
今回は、floatingActionButton
を押して、Container
を移動させます。
一度移動した後に、元の位置に戻すためにmode_change_switch
フラグで状態を管理してます。
一応、onPressed内の処理の解説をしておきます。
mode_change_switch ? _controller.forward() : _controller.reverse();
mode_change_switch = !mode_change_switch;
最初にボタンが押された時、フラグは初期値がtrueなので、
_controller.forward()
で先ほど設定したanimationを実行します。
その後、フラグの値を逆にします。
この時、Containerは中央から右下に移動しています。
2回目にボタンが押された時は、Containerが中央に戻るように実装したかったので、
今回は_controller.reverse()
を実行してanimationを逆再生しています。
📍 SingleTickerProviderStateMixin
AnimationController
の解説で(後ほど説明)としていた箇所です。
このSingleTickerProviderStateMixin
は何かというと、
端末固有のフレームレートを教えてくれて、フレーム毎に画面更新の通知を渡してくれるクラスです。
このクラスのおかげでAnimationController
は画面の更新を受け取る事ができます。
実際に、duration
パラメータのDurationのフレームレートを計算して、1フレーム毎にAnimatedBuilder
のanimation
が通知を受け取り、Alignをリビルドしています。
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
省略
_controller = AnimationController(
duration: Duration(milliseconds: 500),
vsync: this,
);
省略
AnimatedBuilder(
animation: animation,
builder: (context, _) {
return Align(
alignment: animation.value,
child: Container(
width: 200,
height: 200,
color: Colors.purple[200],
),
);
},
),
✅まとめ
今回は、flutterでanimationを実装してみました。
移動するanimationは、
『アニメーションの進行具合』『始点と終点の位置』『Widgetに反映させるanimation』の
3つがあれば作成する事ができます。
今回紹介したWidget以外を追加する事で、
『曲線を描いたアニメーション』『回転するアニメーション』など、さまざまなアニメーションが実現可能になります。
今回はここまで
Discussion