この記事は
主にアニメーションの話になります。
環境はFlutter 1.17.1になります。
全体コード
loading.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Loader extends StatefulWidget {
final Color color;
final Duration duration;
Loader({
this.color = Colors.black,
this.duration = const Duration(milliseconds: 1000),
});
@override
_LoaderState createState() => _LoaderState();
}
class _LoaderState extends State<Loader> with SingleTickerProviderStateMixin {
Animation<double> animation_1;
Animation<double> animation_2;
Animation<double> animation_3;
Animation<double> animation_4;
Animation<double> animation_5;
Animation<double> animation_6;
Animation<double> animation_7;
Animation<double> animation_8;
Animation<double> animation_9;
Animation<double> animation_10;
AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: widget.duration, vsync: this);
animation_1 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.0, 0.70, curve: Curves.linear),
),
);
animation_2 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.1, 0.80, curve: Curves.linear),
),
);
animation_3 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.2, 0.90, curve: Curves.linear),
),
);
animation_4 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.3, 1.00, curve: Curves.linear),
),
);
animation_5 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.4, 1.10, curve: Curves.linear),
),
);
animation_6 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.5, 1.20, curve: Curves.linear),
),
);
animation_7 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.6, 1.30, curve: Curves.linear),
),
);
animation_8 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.7, 1.40, curve: Curves.linear),
),
);
animation_9 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.8, 1.50, curve: Curves.linear),
),
);
animation_10 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.9, 1.60, curve: Curves.linear),
),
);
controller.addListener(() {
setState(() {
});
});
controller.repeat();
}
@override
Widget build(BuildContext context) {
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Opacity(
opacity: (animation_1.value <= 0.4
? 2.5 * animation_1.value
: (animation_1.value > 0.40 && animation_1.value <= 0.60)
? 1.0
: 2.5 - (2.5 * animation_1.value)),
child: Padding(
padding: const EdgeInsets.only(right: 5.0),
child: _Item(
text: 'L',
color: widget.color,
),
),
),
Opacity(
opacity: (animation_2.value <= 0.4
? 2.5 * animation_2.value
: (animation_2.value > 0.40 && animation_2.value <= 0.60)
? 1.0
: 2.5 - (2.5 * animation_2.value)),
child: Padding(
padding: const EdgeInsets.only(right: 5.0),
child: _Item(
text: 'o',
color: widget.color,
),
),
),
Opacity(
opacity: (animation_3.value <= 0.4
? 2.5 * animation_3.value
: (animation_3.value > 0.40 && animation_3.value <= 0.60)
? 1.0
: 2.5 - (2.5 * animation_3.value)),
child: Padding(
padding: const EdgeInsets.only(right: 5.0),
child: _Item(
text: 'a',
color: widget.color,
),
),
),
Opacity(
opacity: (animation_1.value <= 0.4
? 2.5 * animation_1.value
: (animation_1.value > 0.40 && animation_1.value <= 0.60)
? 1.0
: 2.5 - (2.5 * animation_1.value)),
child: Padding(
padding: const EdgeInsets.only(right: 5.0),
child: _Item(
text: 'd',
color: widget.color,
),
),
),
Opacity(
opacity: (animation_2.value <= 0.4
? 2.5 * animation_2.value
: (animation_2.value > 0.40 && animation_2.value <= 0.60)
? 1.0
: 2.5 - (2.5 * animation_2.value)),
child: Padding(
padding: const EdgeInsets.only(right: 5.0),
child: _Item(
text: 'i',
color: widget.color,
),
),
),
Opacity(
opacity: (animation_3.value <= 0.4
? 2.5 * animation_3.value
: (animation_3.value > 0.40 && animation_3.value <= 0.60)
? 1.0
: 2.5 - (2.5 * animation_3.value)),
child: Padding(
padding: const EdgeInsets.only(right: 5.0),
child: _Item(
text: 'n',
color: widget.color,
),
),
),
Opacity(
opacity: (animation_1.value <= 0.4
? 2.5 * animation_1.value
: (animation_1.value > 0.40 && animation_1.value <= 0.60)
? 1.0
: 2.5 - (2.5 * animation_1.value)),
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: _Item(
text: 'g',
color: widget.color,
),
),
),
Opacity(
opacity: (animation_2.value <= 0.4
? 2.5 * animation_2.value
: (animation_2.value > 0.40 && animation_2.value <= 0.60)
? 1.0
: 2.5 - (2.5 * animation_2.value)),
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: _Dot(
radius: 5.0,
color: widget.color,
),
),
),
Opacity(
opacity: (animation_3.value <= 0.4
? 2.5 * animation_3.value
: (animation_3.value > 0.40 && animation_3.value <= 0.60)
? 1.0
: 2.5 - (2.5 * animation_3.value)),
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: _Dot(
radius: 5.0,
color: widget.color,
),
),
),
Opacity(
opacity: (animation_1.value <= 0.4
? 2.5 * animation_1.value
: (animation_1.value > 0.40 && animation_1.value <= 0.60)
? 1.0
: 2.5 - (2.5 * animation_1.value)),
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: _Dot(
radius: 5.0,
color: widget.color,
),
),
),
],
),
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
class _Item extends StatelessWidget {
final String text;
final Color color;
_Item({
this.text,
this.color
});
@override
Widget build(BuildContext context) {
return Center(
child: Transform.rotate(
angle: 0.0,
child: Container(
width: 20,
height: 20,
child: Text(
text
),
),
),
);
}
}
class _Dot extends StatelessWidget {
final double radius;
final Color color;
_Dot({this.radius, this.color});
@override
Widget build(BuildContext context) {
return Center(
child: Transform.rotate(
angle: 0.0,
child: Container(
width: radius,
height: radius,
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
),
),
);
}
}
適所解説
Flutterでアニメーションを実装するにあたって押さえておかないといけない項目が4つあります。
Animation
Animationクラスはアニメーションの状態を保持します。ここの変更に応じて画面を書き換えるようにすることでアニメーションを実装していきます。
今回のコードで言うと
loading.dart
Animation<double> animation_1;
Animation<double> animation_2;
Animation<double> animation_3;
Animation<double> animation_4;
Animation<double> animation_5;
Animation<double> animation_6;
Animation<double> animation_7;
Animation<double> animation_8;
Animation<double> animation_9;
Animation<double> animation_10;
ここが該当します。
Animationクラスにanimation_xを保持させ、次に解説するAnimationControllerで扱います。
AnimationController
AnimationControllerでは実際にアニメーションを制御します。また、与えられた条件に従いAnimationクラスの状態を変更する部分です。
今回のコードでは
loading.dart
controller = AnimationController(
duration: widget.duration, vsync: this);
animation_1 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.0, 0.70, curve: Curves.linear),
),
);
animation_2 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.1, 0.80, curve: Curves.linear),
),
);
animation_3 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.2, 0.90, curve: Curves.linear),
),
);
animation_4 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.3, 1.00, curve: Curves.linear),
),
);
animation_5 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.4, 1.10, curve: Curves.linear),
),
);
animation_6 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.5, 1.20, curve: Curves.linear),
),
);
animation_7 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.6, 1.30, curve: Curves.linear),
),
);
animation_8 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.7, 1.40, curve: Curves.linear),
),
);
animation_9 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.8, 1.50, curve: Curves.linear),
),
);
animation_10 = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Interval(0.9, 1.60, curve: Curves.linear),
),
);
controller.repeat();
}
ここの部分が該当します。
controllerに次に解説するTweenで条件を定義していきます。
Tween
Tweenでは数値の間隔を定義します。ここの数値に合わせてアニメーションの動きを表します。
今回のロード画面で言うと、各文字がアニメーションする範囲の開始と終了をここで決めています。
addListener
Animationは全てのリスナーに変更を通知します。controllerへTwennで状態を定義した後、setStateを呼び出すことによって実際に状態を描画します。
loading.dart
controller.addListener(() {
setState(() {
});
});
基本的にこの4点を押さえていればアニメーションを組むことは可能だと思います。
アニメーションを組んでみた個人的所感
基本の4点にプラスアルファを付け足せばアニメーションはチョチョイのちょいです。
すごく簡単にアニメーションが組めるので皆さんもいろいろ調べてみてください。