上の画像は使っていいんですかね。。すごく壁紙?としていいなと思ったので使わせていただきました。
こんにちわ。いせきです。
色々な状態管理があり、人によって何を使うのかがバラバラですね。
僕は、最近GetXを使用して開発をすることが多いです。
ゲームのアプリを作成している段階で、ボタンにアニメーションをつけたいと思っていましたが、なかなかうまくいきませんでした。調べて、試してを繰り返した結果、実装することができたので、共有しようと思います。
GetXとは、
公式では以下のように説明されています。
GetX is an extra-light and powerful solution for Flutter. It combines high-performance state management, intelligent dependency injection, and route management quickly and practically.
詳しくは以下のサイトを参考にしてください。
開発の流れ
今回は、画面を描画する(Screen)と処理などを記載する(Controller)の2ファイルで作成しました。
基本的な書き方などは、他の方が数百倍わかりやすく記載しているので、省略します。
- lib
- component
- button_item
- home_screen
- home_screen.dart
- home_screen_controller.dart
こんなアニメーションをしたいと思います。見えづらいかもしれませんが、大きくなったり小さくなったりしています。
① AnimationControllerを使う前に必要な処理
GetXControllerの後にwith GetSingleTickerProviderStateMixin
をつけるとgetx内でもAnimationControllerに必要なTickerProvider
が使えるようになりました。
class HomeScreenController extends GetxController with GetSingleTickerProviderStateMixin {
}
②Controller内にAnimationControllerのインスタンスを作成
今回は、1秒ごとに処理を何度も行うようにしました。
repeat
を使うことで何度もアニメーションを使えるようになりました。
あと、with GetSingleTickerProviderStateMixin
を使わなければいけない理由としては、
vsync: this, のthisを入れられるようにするためです。
controller内の記載内容です。
late AnimationController animationController;
@override
void onInit() {
super.onInit();
animationController = AnimationController(
vsync: this,
duration: const Duration(
seconds: 1,
),
)..repeat(reverse: true);
}
③Screen内に記載
今回は、大きさ1.0倍から1.17倍に大きくなったり、小さくなったりするアニメーションを実装しました。
ScaleTransition(
scale: controller.animationController.drive(
Tween<double>(
begin: 1,
end: 1.17,
),
),
child: ButtonItem(
onPressed: controller.onTapLevel4,
text: 'レベル4',
),
),
ButtonItem
でComponent化していますが、無理にComponent化しなくてもOKです。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ButtonItem extends StatelessWidget {
const ButtonItem({
Key? key,
required this.onPressed,
required this.text,
}) : super(key: key);
final Function() onPressed;
final String text;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Container(
width: 280,
height: 60,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(25.0)),
boxShadow: [
BoxShadow(
color: Colors.white,
spreadRadius: 4,
blurRadius: 10,
offset: Offset(0, 3),
),
],
),
child: Center(
child: Text(
text,
style: const TextStyle(
fontSize: 25,
color: CupertinoColors.black,
fontWeight: FontWeight.bold,
),
),
),
),
style: ElevatedButton.styleFrom(
primary: Colors.grey.shade100,
onPrimary: Colors.grey.shade100,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
);
}
}
ソースコード
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'component/button_item.dart';
import 'home_screen_controller.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//ここで入れることでHomeScreenControllerが使えるようになる!
final controller = Get.put(HomeScreenController(), tag: '');
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.white,
leading: IconButton(
iconSize: 35,
icon: const Icon(
Icons.arrow_back_ios,
color: Colors.blueAccent,
),
onPressed: controller.onTapBack,
),
actions: [
IconButton(
onPressed: controller.onTapTrophyScreen,
iconSize: 40,
icon: const Icon(
Icons.flag,
color: Colors.black,
),
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ScaleTransition(
scale: controller.animationController.drive(
Tween<double>(
begin: 1,
end: 1.1,
),
),
child: ButtonItem(
onPressed: controller.onTapLevel1,
text: 'レベル1',
),
),
ScaleTransition(
scale: controller.animationController.drive(
Tween<double>(
begin: 1,
end: 1.13,
),
),
child: ButtonItem(
onPressed: controller.onTapLevel2,
text: 'レベル2',
),
),
ScaleTransition(
scale: controller.animationController.drive(
Tween<double>(
begin: 1,
end: 1.15,
),
),
child: ButtonItem(
onPressed: controller.onTapLevel3,
text: 'レベル3',
),
),
ScaleTransition(
scale: controller.animationController.drive(
Tween<double>(
begin: 1,
end: 1.17,
),
),
child: ButtonItem(
onPressed: controller.onTapLevel4,
text: 'レベル4',
),
),
ScaleTransition(
scale: controller.animationController.drive(
Tween<double>(
begin: 1,
end: 1.2,
),
),
child: ButtonItem(
onPressed: controller.onTapLevel5,
text: 'レベル5',
),
),
],
),
),
);
}
}
Preferenceの処理などは気にしないでね。。。
import 'package:flutter/animation.dart';
import 'package:get/get.dart';
import '../../preference/shared_preference.dart';
import '../challenge_screen/challenge_screen_1.dart';
import '../challenge_screen/challenge_screen_2.dart';
import '../challenge_screen/challenge_screen_5.dart';
class HomeScreenController extends GetxController
with GetSingleTickerProviderStateMixin {
var stage1 = false.obs;
var stage2 = false.obs;
var stage3 = false.obs;
var stage4 = false.obs;
var stage5 = false.obs;
late AnimationController animationController;
@override
void onInit() {
super.onInit();
sharedPreference();
animationController = AnimationController(
vsync: this,
duration: const Duration(
seconds: 1,
),
)..repeat(reverse: true);
}
void onTapBack() {
Get.back();
}
Future<void> sharedPreference() async {
stage1.value = await Preference().getBool(PreferenceKey.stage1);
stage2.value = await Preference().getBool(PreferenceKey.stage2);
stage3.value = await Preference().getBool(PreferenceKey.stage3);
stage4.value = await Preference().getBool(PreferenceKey.stage4);
stage5.value = await Preference().getBool(PreferenceKey.stage5);
}
void onTapLevel1() {
Get.to(() => const ChallengeScreen1());
}
void onTapLevel2() {
Get.to(() => const ChallengeScreen2());
}
void onTapLevel3() {
Get.to(() => const ChallengeScreen3());
}
void onTapLevel4() {
Get.to(() => const ChallengeScreen5());
}
void onTapLevel5() {
Get.to(() => const ChallengeScreen5());
}
void onTapTrophyScreen() {
Get.to(
() => TrophyScreen(
isClear1: stage1.value,
isClear2: stage2.value,
isClear3: stage3.value,
isClear4: stage4.value,
isClear5: stage5.value,
),
);
}
}
参考文献