【Flutter】BottomNavigationBarでスクロール以外の遷移する際のアニメーションをさせてみる
やりたいこと
いわゆるフッターで画面遷移を行う際にパッと画面が切り替わるのが味気ないのでアニメーションをさせました。
調べるとPageViewにしてpositionをずらして横方向のスクロールをさせて遷移する。という方法やよく出てきたのですが、その場合一番左のメニューから一番右のメニューに遷移した場合、他の画面が表示されてしまうので微妙と思っています。
他の遷移アニメーションがないかを探索してみました。
パッと切り替わるパターン

PageViewでpositionをずらして横方向のスクロールをして遷移させるパターン

こんな感じでできた
フェード

縦方向

横方向

公式のpackageであるanimationsの
PageTransitionSwitcherとSharedAxisTransitionやFadeThroughTransitionの組み合わせで遷移アニメーションを作りました。
一旦、コード一式を貼り付けます。
final menus = [
(icon: Icons.settings, label: 'RED', color: Colors.red),
(icon: Icons.account_circle, label: 'BLUE', color: Colors.blue),
(icon: Icons.show_chart, label: 'YELLOW', color: Colors.yellow),
(icon: Icons.calendar_month, label: 'GREEN', color: Colors.green),
];
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<StatefulWidget> createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
final views = [
ColoredView(key: GlobalKey(), menus[0].color, menus[0].label),
ColoredView(key: GlobalKey(), menus[1].color, menus[1].label),
ColoredView(key: GlobalKey(), menus[2].color, menus[2].label),
ColoredView(key: GlobalKey(), menus[3].color, menus[3].label),
];
var selectedMenuIndex = 0;
@override
Widget build(context) {
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavBarApp'),
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
),
body: PageTransitionSwitcher(
transitionBuilder: (child, primaryAnimation, secondaryAnimation) {
return FadeThroughTransition(
animation: primaryAnimation,
secondaryAnimation: secondaryAnimation,
child: child,
);
},
child: views[selectedMenuIndex],
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: selectedMenuIndex,
items: menus
.map((e) =>
BottomNavigationBarItem(icon: Icon(e.icon), label: e.label))
.toList(),
onTap: (value) => setState(() => selectedMenuIndex = value),
type: BottomNavigationBarType.fixed,
),
);
}
}
ScaffoldのbodyにPageTransitionSwitcherをセットし、transitionBuilderにFadeThroughTransitionをセットすればフェードアニメーションで遷移します。
また、SharedAxisTransitionをセットすれば、縦方向や横方向のアニメーションで遷移します。
躓いたこと
この記事を書くために分かりやすく背景を単色にして同じレイアウトのViewを以下の様に作成していました。
class ColoredView extends StatelessWidget {
final Color color;
final String label;
const ColoredView(this.color, this.label, {super.key});
@override
Widget build(BuildContext context) {
return ColoredBox(
color: color,
child: Center(
child: Text(label),
),
);
}
}
final views = [
ColoredView(menus[0].color, menus[0].label),
ColoredView(menus[1].color, menus[1].label),
ColoredView(menus[2].color, menus[2].label),
ColoredView(menus[3].color, menus[3].label),
];
ただ、上記のコードだとPageTransitionSwitcherを使ってもアニメーションされませんでした。
どうやら同じクラスで中身だけ違うようにすると、PageTransitionSwitcherは同じ画面として扱ってしまうようでした。
(例えばSettingView(設定画面)やAccountView(アカウント画面)のように別のクラスを配列にセットする場合はkeyをセットせずともアニメーションが発生しました。)
そのため、ColoredViewにKeyをセットすることで、ちゃんとアニメーションをするようになりました。
final views = [
ColoredView(key: GlobalKey(), menus[0].color, menus[0].label),
ColoredView(key: GlobalKey(), menus[1].color, menus[1].label),
ColoredView(key: GlobalKey(), menus[2].color, menus[2].label),
ColoredView(key: GlobalKey(), menus[3].color, menus[3].label),
];
Discussion