LoginSignup
2
0

More than 1 year has passed since last update.

【Riverpod】TabBarの選択に合わせてアニメーションする

Last updated at Posted at 2022-02-11

できたもの

やったこと

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を追加するにはContainerAnimatedContainerに変更し、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),
      ),
    )),
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0