flutter_animateでお手軽アニメーション実装!✨
アニメーションを実装することで、アプリの印象ってガラリと変わりますよね!🎨
Flutterを採用してるアプリだとPoCやレビューに向けての短期間開発は結構あるかと思います。
限られた開発期間の中でも、手軽にアニメーションを実装してアプリの品質を上げられたら理想的です!
ただAnimationControllerやTweenなど組み合わせて実装するとなると、特にFlutter経験が浅い場合は理解までに意外と時間がかかるかと思います。
そういう時は flutter_animate を使ってお手軽アニメーション実装しましょう!
flutter_animateとは?🤔
シンプルで直感的!いろんな種類のアニメーションが実装できるパッケージです。
Flutter Favoritesにも選定されていて、公式YouTube動画のPackage Of the Weekでも紹介されています。
基本的な実装🌱
公式でもサンプルコードが載っていますが、実際に触れてみたのでこの記事でもいくつか紹介します!
基本的にアニメーションさせたいウィジェットに対してAnimateで囲むことでアニメーションを適用させることができます。
Animate(
effects: [FadeEffect(), ScaleEffect()],
child: Text("Hello World!"),
)
また、より簡単に実装できるよう、flutter_animateをインストールするとすべてのウィジェットにanimate
という拡張関数が利用できるようになります。
こっちのがより直感的なので以降のコードもこちらで実装しています!
rotate 🔄
Icon(Icons.public)
.animate(
onPlay: (controller) => controller.repeat(),
)
.rotate();
このコードではIcons.publicを繰り返し回転させるようアニメーションさせています。
animate
関数はいくつかプロパティを設定でき、ここではonPlay
で繰り返しアニメーションするよう実装しています。
shake 📳
Stack(
children: [
const Icon(Icons.notifications_outlined)
.animate(
onPlay: (controller) => controller.repeat(),
)
.shake(duration: 500.ms, delay: 1.seconds),
const Positioned(
right: 2,
top: 2,
child: DecoratedBox(
decoration: BoxDecoration(color: Colors.white, shape: BoxShape.circle),
child: Padding(
padding: EdgeInsets.all(1.0),
child: DecoratedBox(
decoration: BoxDecoration(color: Colors.red, shape: BoxShape.circle),
child: SizedBox(width: 8, height: 8),
),
),
),
),
],
);
Icons.notifications_outlinedを揺らすようアニメーションさせてます。
各アニメーションエフェクトの関数ではduration
, delay
が用意されており、
duration
ではアニメーション再生時間を、delay
ではアニメーション開始までの遅延時間を設定できます。
ここでは1秒間置きに、0.5秒間アニメーションさせています。
fadeIn/fadeOut 🌗
const Text('Fade In')
.animate(
onPlay: (controller) => controller.repeat(),
)
.fadeIn(duration: 2.seconds)
.then(delay: 1.seconds)
.fadeOut(duration: 2.seconds);
フェードインとフェードアウトを1秒置きに繰り返しアニメーションさせています。
このようにthen
関数を使って複数アニメーションを組み合わせることも可能です。
scale 🔍
const Text("Scale")
.animate(
onPlay: (controller) => controller.repeat(),
)
.scale(
begin: const Offset(1.0, 1.0),
end: const Offset(1.5, 1.5),
duration: 2.seconds,
delay: 1.seconds,
curve: Curves.easeInOut,
)
.then(delay: 1.seconds)
.scale(
begin: const Offset(1.5, 1.5),
end: const Offset(1.0, 1.0),
duration: 2.seconds,
curve: Curves.easeInOut,
);
テキストを拡大するようアニメーションさせています。
begin
でアニメーション開始時位置、end
でアニメーション終了時位置を設定できます。
curve
ではアニメーション進行具合を設定しています。Curves.easeInOut
を設定することで、アニメーションの開始と終了はゆっくりと、中盤では素早く再生させています。
操作イベントによるアニメーション実装☝️
ここまでのアニメーションは自動再生していました。
操作イベントでもアニメーションを制御できます!
タップ時アニメーション👆
var isFavorite = false;
void toggleFavorite() {
setState(() {
isFavorite = !isFavorite;
});
}
GestureDetector(
onTap: toggleFavorite,
child: Icon(
isFavorite ? Icons.star : Icons.star_outline,
),
)
.animate(
target: isFavorite ? 1.0 : 0.0,
autoPlay: false,
)
.scale(
begin: const Offset(1.0, 1.0),
end: const Offset(1.3, 1.3),
curve: Curves.easeInOut,
)
.then()
.scale(
begin: const Offset(1.3, 1.3),
end: const Offset(1.0, 1.0),
curve: Curves.easeInOut,
);
autoPlay
をfalseで設定することで自動アニメーションをオフにしています。
アイコンタップ時にisFavorite
の状態を変更し、target
の値を切り替えることでアニメーションさせています。
1.0ではbegin
→end
の順でアニメーションされ、0.0ではend
→begin
の順でアニメーションされることを利用してタップ時に再生、逆再生をするようにしています。
スクロール時再生📜
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
class SampleScroll extends StatefulWidget {
const SampleScroll({super.key});
State<SampleScroll> createState() => _SampleScrollState();
}
class _SampleScrollState extends State<SampleScroll> {
late ScrollController controller;
void initState() {
super.initState();
controller = ScrollController();
}
void dispose() {
controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView.builder(
controller: controller,
itemCount: 50,
itemBuilder: (context, index) {
return const ListTile(
leading: CircleAvatar(child: FlutterLogo()),
title: Text('SubTitle', style: TextStyle(fontSize: 24)),
subtitle: Text('Description', style: TextStyle(fontSize: 18)),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => controller.animateTo(
controller.position.minScrollExtent,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
),
child: const Icon(Icons.arrow_upward),
)
.animate(
autoPlay: false,
adapter: ScrollAdapter(controller, end: 150),
)
.scale()
.animate(
autoPlay: false,
adapter: ScrollAdapter(controller, begin: 3000),
)
.fadeOut(),
);
}
}
ここではスクロールに応じてFloatingActionButton
をアニメーションさせています。
ScrollAdapter
にScrollController
を設定することで簡単にスクロールに合わせてアニメーションさせることができます。
begin
とend
を設定することでスクロール位置に応じてアニメーションの開始と終了を制御できます。
開発時のTips&アクセシビリティ🛠️
開発時はホットリロードでアニメーションが再生されるよう、restartOnHotReload
をオンに設定しておくと便利です!
Animate.restartOnHotReload = true;
また少し話が逸れますが、今後アクセシビリティを高めていきたい場合はアニメーション再生時間も気をつけなければなりません!
どれほどのアクセシビリティ基準に対応するかにもよりますが、例えばWCAGの達成基準2.1では繰り返し再生するアニメーションは5秒以内に停止することとしています。
この場合、下記のようにonPlay
で5秒後には停止するようanimate
を実装しておくと良いかと思います。
const Icon(Icons.notifications_outlined).animate(
onPlay: (controller) {
controller.repeat();
Future.delayed(5.seconds, () {
controller.stop();
});
},
).shake(duration: 500.ms, delay: 1.seconds),
まとめ🎉
flutter_animateを使用することで、シンプルで効果的なアニメーション実装ができました!
最後に上記で紹介したアニメーションを使って簡単な画面を実装したサンプルリポジトリもアップロードしましたので、少しでもご参考になれば幸いです。
アニメーションを駆使してユーザーに親しみやすいアプリを提供できるよう、これからもFlutterライフを楽しんでいければと思います!💪
Discussion