🗂️

[Flutter] 選択したタブだけ目立たせる

2022/11/27に公開約7,800字

タブの選択で、「選択したタブだけ、目立たせたい!」こんな実装したいと思ったことありませんか?
ええあるはず、この記事では初心者エンジニアが本業でつまずいた「タブ選択に起きる挙動」について解決していきます。
では早速やっていきましょう!

不正解な例

タブを選択してもテキストに変化がない挙動

全体コード

class TabPage extends StatefulWidget {
  const TabPage({super.key});

  
  _TabPageState createState() => _TabPageState();
}

class _TabPageState extends State<TabPage> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('選択したタブだけ目立たせる'),
      ),
      body: AttentionTabBar(
        tabs: <Tab>[
          Tab(
            child: Text(
              'タブ1',
              style: TextStyle(color: Colors.black, fontSize: 10),
            ),
          ),
          Tab(
            child: Text(
              'タブ2',
              style: TextStyle(color: Colors.black, fontSize: 10),
            ),
          ),
        ],
        tabviews: const [
          TabPage1(),
          TabPage2(),
        ],
      ),
    );
  }
}

class AttentionTabBar extends StatelessWidget {
  const AttentionTabBar({
    Key? key,
    required this.tabs,
    required this.tabviews,
    this.onTap,
    this.controller,
  }) : super(key: key);
  final List<Tab> tabs;
  final List<Widget> tabviews;
  final Function(int)? onTap;
  final TabController? controller;

  
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: tabs.length,
        child: Scaffold(
            appBar: TabBar(
              controller: controller,
              tabs: tabs,
              onTap: onTap,
            ),
            body: TabBarView(controller: controller, children: tabviews)));
  }
}

完成系

タブを選択したらテキストに変化が起こる挙動

全体コード

class TabPage extends StatefulWidget {
  TabPage({this.index = 0});
  final int index;
  
  _TabPageState createState() => _TabPageState();
}

class _TabPageState extends State<TabPage> with SingleTickerProviderStateMixin {
  late TabController _tabController;

  
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('選択したタブだけ目立たせる'),
      ),
      body: AttentionTabBar(
        controller: _tabController,
        onTap: (tab) {
          setState(() {});
        },
        tabs: <Tab>[
          Tab(
            child: Text(
              'タブ1',
              style: TextStyle(
                  color: _tabController?.index == 0
                      ? const Color(0xFF3D4A50)
                      : const Color(0xFFB3B3B3),
                  fontSize: 10),
            ),
          ),
          Tab(
            child: Text(
              'タブ2',
              style: TextStyle(
                  color: _tabController?.index == 1
                      ? const Color(0xFF3D4A50)
                      : const Color(0xFFB3B3B3),
                  fontSize: 10),
            ),
          ),
        ],
        tabviews: const [
          TabPage1(),
          TabPage2(),
        ],
      ),
    );
  }
}

class AttentionTabBar extends StatelessWidget {
  const AttentionTabBar({
    Key? key,
    required this.tabs,
    required this.tabviews,
    this.onTap,
    this.controller,
  }) : super(key: key);
  final List<Tab> tabs;
  final List<Widget> tabviews;
  final Function(int)? onTap;
  final TabController? controller;

  
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: tabs.length,
        child: Scaffold(
            appBar: TabBar(
              controller: controller,
              tabs: tabs,
              onTap: onTap,
            ),
            body: TabBarView(controller: controller, children: tabviews)));
  }
}

解説

クラス引数にindexを用意します。これは表示中のタブ取得するのに使用します。

class TabPage extends StatefulWidget {
  TabPage({this.index = 0}); //引数にindexを用意
  final int index;
  
  _TabPageState createState() => _TabPageState();
}

タブを表示させるためにTabControllerクラスを用意します。

class _TabPageState extends State<TabPage> {
  late TabController _tabController;

TabBarViewの部分が横スクロールさせる準備

TabBarViewの部分が横スクロールさせるにはSingleTickerProviderStateMixinを使用します。
正直私はあまり理解しておりませんが、「Tabbarを横スクロールするのに必要なもの、おまじない」だと、考えて使用しております(笑)
ちなみにSingleTickerProviderStateMixinを使用しないとエラーが表示されるので、Tabbarを使用する際は必須みたいですね
https://api.flutter.dev/flutter/material/TabController-class.html

class _TabPageState extends State<TabPage> with SingleTickerProviderStateMixin {
  late TabController _tabController;

TabBar数を配置

変数 _tabControllerTabControllerクラスを代入させ、引数にTabBar数(length: 2,)を記述します。その後ろにある(vsync: this)は上記で使用した、SingleTickerProviderStateMixinとセットで必要な引数になります。


  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
  }

独自のTabbarを用意

独自のTabbarを用意します。今回は AttentionTabBar と言う名前でTabbarを作ります。
AttentionTabBarの引数には上記でプロパティとして作成した _tabController 配置します。
onTapの中身の setState は必須です。
(setStateがない状態でTabを切り替えても、画面を再描画してくれません。)

 body: AttentionTabBar(
        controller: _tabController,
        onTap: (tab) {
          setState(() {});
        },

AttentionTabBar

class AttentionTabBar extends StatelessWidget {
  const AttentionTabBar({
    Key? key,
    required this.tabs,
    required this.tabviews,
    this.onTap,
    this.controller,
  }) : super(key: key);
  final List<Tab> tabs;
  final List<Widget> tabviews;
  final Function(int)? onTap;
  final TabController? controller;

  
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: tabs.length,
        child: Scaffold(
            appBar: TabBar(
              controller: controller,
              tabs: tabs,
              onTap: onTap,
            ),
            body: TabBarView(controller: controller, children: tabviews)));
  }

Tabを設置

bodyプロパティの中に tabs プロパティを用意します。
さらに <Tab> クラスを用意し、その中に表示させたい Tab を配置します。

 tabs: <Tab>[
          Tab(
            child: Text(
              'タブ1',
              style: TextStyle(
                  color: _tabController.index == 0
                      ? const Color(0xFF3D4A50)
                      : const Color(0xFFB3B3B3),
                  fontSize: 10),
            ),
          ),
          Tab(
            child: Text(
              'タブ2',
              style: TextStyle(
                  color: _tabController.index == 1
                      ? const Color(0xFF3D4A50)
                      : const Color(0xFFB3B3B3),
                  fontSize: 10),
            ),
          ),
        ],
        tabviews: const [
          TabPage1(),
          TabPage2(),
        ],
      ),

三項演算子(Tabの色を切り替える)

ようやくここで本記事のお目当ての箇所の解説です。
三項演算子を使用して、Tabの色を切り替えます。
「選択している _tabController0 番目の tab が一致していたら」
? const Color(0xFF3D4A50)(濃いグレー) を表示。
そうでない場合、
: const Color(0xFFB3B3B3),(薄いグレー) 表示。

Tab(
            child: Text(
              'タブ1',
              style: TextStyle(
                  color: _tabController.index == 0
                      ? const Color(0xFF3D4A50)
                      : const Color(0xFFB3B3B3),
                  fontSize: 10),
            ),
          ),

お世話になった記事

https://qiita.com/Dreamwalker/items/cc19bb4f8b7068ae0fd3
https://note.com/hatchoutschool/n/n391e40e78912

終わりに

いかがでしょうか?
今回は三項演算子を使用して、Tabを切り替え際、色を変化させる挙動について解説しました。
これはアプリ制作なら必要不可欠なギミックになるのですが、記事では見かけなかったので書いてみまいした。

余談ですが、
私はFlutterエンジニア集団が集まるコミュニティに所属しています。
勉強会など熱心に取り組んでるので興味がある方は是非!もちろんプログラミング初心者でモバイルアプリエンジニアを目指している方も大歓迎です。
それではまた
https://flutteruniv.com/

Discussion

ログインするとコメントできます