できたもの
FlutterのAnimationの記事書きました。
— Yusuke Miyata (@yuskey38) February 11, 2022
【Riverpod】TabBarの選択に合わせてアニメーションするhttps://t.co/fyGB9bKW2P#flutter pic.twitter.com/ojeQgSuTWN
やったこと
1.(CustomTabBarを使うためのデータクラスを作る)
2.(CustomTabBarを作成してTabBarの見た目を変える)
3. 選択したTabのindexを取得してStateを更新し、特定のindexの場合にViewを表示する
4. Animationを追加する
1. (CustomTabBarを使うためのデータクラスを作る)
class TabItem {
const TabItem({required this.tab, required this.view});
final Tab tab;
final Widget view;
}
2. (CustomTabBarを作成してTabBarの見た目を変える)
引数に1で作成したTabItemのリストとTabを選択した際にindexを取得するための関数を渡しています。
また、以下のようにカスタマイズしてみました。
- 背景色を白
- 選択中のTabの色を青
- 非選択の色を灰色
- Indicatorの色を青
- Indicatorの幅を短く
class CustomTabBar extends StatelessWidget {
const CustomTabBar(
{Key? key, required this.tabItems, required this.onSelected})
: super(key: key);
final List<TabItem> tabItems;
final Function(int) onSelected;
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: tabItems.length,
child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
Ink(
color: Colors.white,
child: TabBar(
onTap: (index) {
onSelected(index);
},
labelColor: Colors.blue,
unselectedLabelColor: Colors.black12,
indicator: UnderlineTabIndicator(
borderSide:
const BorderSide(color: Colors.blue, width: 3.0),
insets: EdgeInsets.symmetric(
horizontal: 16.0 * tabItems.length)),
tabs: tabItems.map((e) => e.tab).toList())),
Expanded(
child: TabBarView(
physics: const NeverScrollableScrollPhysics(),
children: tabItems.map((e) => e.view).toList())),
]));
}
}
3. 選択したTabのindexを取得してStateを更新し、特定のindexの場合にViewを表示する
2で作成したCustomTabBarのonSelected
でindexを取得し、viewModel経由でstateを更新しています。
selectedIndexが0の場合はcontainerのheightを0にして表示しないようにし、その他のindexの場合はheightを100にして表示するようにしています。
class TestView extends HookConsumerWidget {
const TestView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final viewModel = ref.read(tabViewModelProvider.notifier);
final state = ref.watch(tabViewModelProvider);
return MaterialApp(
title: 'TestView',
home: Scaffold(
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: state.selectedIndex == 0 ? 0 : 100,
color: Colors.blue,
child: const Center(
child: Text(
"Tabbar with out Appbar",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
)),
Expanded(
child: CustomTabBar(
onSelected: (index) {
viewModel.select(index);
},
tabItems: const [
TabItem(
tab: Tab(text: "LEFT"),
view: Center(child: Text("LEFT"))),
TabItem(
tab: Tab(text: "RIGHT"),
view: Center(child: Text("RIGHT")))
],
))
]))));
}
}
final tabViewModelProvider =
StateNotifierProvider.autoDispose<TabViewModel, TabState>((ref) {
return TabViewModel();
});
class TabViewModel extends StateNotifier<TabState> {
TabViewModel() : super(const TabState());
void select(int index) {
state = state.copyWith(selectedIndex: index);
}
}
@freezed
class TabState with _$TabState {
const factory TabState({@Default(0) int selectedIndex}) = _TabState;
}
4. Animationを追加する
Animationを追加するにはContainer
をAnimatedContainer
に変更し、duration
を追加するだけです。
Container(
height: state.selectedIndex == 0 ? 0 : 100,
color: Colors.blue,
child: const Center(
child: Text(
"Tabbar with out Appbar",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
)),
↓
AnimatedContainer(
duration: const Duration(milliseconds: 250),
height: state.selectedIndex == 0 ? 0 : 100,
color: Colors.blue,
child: const Center(
child: Text(
"Tabbar with out Appbar",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
)),