FlutterでBottomNavigationBar + Pagerを実装する手法について

7 min読了の目安(約7000字TECH技術記事

はじめに

今回は、FlutterでのBottomNavigationBarの実装とBottomNavigationBar + Pagerの実装について学んだことをメモとして残しておこうと思います。

この記事に書いてあること

BottomNavigationBarの実装
BottomNavigationBarの中にPagerを入れる実装

対象とする読者

FlutterでBottomNavigationBarの実装を知りたい方
FlutterでBottomNavigationBarにPagerを入れる実装をやりたい方
Flutterをはじめたばかりの方

自分もはじめたばかりですが、誰かの助けになれば幸いです。

それではやっていきましょう。

BottomNavigationBarの実装

今回紹介するサンプルのイメージは、次のとおりです。

まずはじめに、BottomNavigationBarについてマテリアルデザインのガイドラインをみていきましょう。

先のリンク先を見ると、Bottom navigationの使用にはいくつか制約があります。

  • 単一のタスクには使用しない(設定など)
  • 3 ~ 5この要素を持つ(3つより少なかったり、5つより要素が多い場合は使用しない)
  • iOSとAndroidで若干挙動が異なる
  • アイコン下部のTextを長くしたり、折り返したりしない etc..

これらを押さえた上で、実際に作成していきましょう。

main.dartの中で、Navigatorを利用したいので、MaterialAppクラスをmainの中で指定します。

void main() {
  runApp(new MaterialApp(
    title: "BottomNavigationSample",
    home: new Home(),
  ));
}

MaterialAppクラスでは、 home、routes、onGenerateRoute、builderのいずれかのうち一つをnull以外に指定する必要があります。

今回は、homeを可変な状態を管理するWidgetのStatefulWidgetを指定します。

今回指定しているHomeクラスは、次のように定義します。

class Home extends StatefulWidget {
  
  State<StatefulWidget> createState() {
    return new HomeState();
  }
}

createStateでWidgetが開いている間の状態を管理するStateをインスタンス化します。

次にStateクラスを継承した、HomeStateを次のように新しく作成します。

class HomeState extends State<Home> {
  int currentIndex = 0;
  final List<Widget> tabs = [
    SampleTabItem("左の画面", Colors.red),
    SampleTabItem("真ん中の画面", Colors.green),
    SampleTabItem("右の画面", Colors.cyan),
  ];

  void onTabTapped(int index) {
    setState(() {
      currentIndex = index;
    });
  }

  
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("BottomNavigationSample"),
        backgroundColor: Colors.blue,
      ),
      body: tabs[currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        onTap: onTabTapped,
        currentIndex: currentIndex,
        items: [
          BottomNavigationBarItem(
            icon: new Icon(Icons.home),
            title: new Text('Home'),
          ),
          BottomNavigationBarItem(
            icon: new Icon(Icons.mail),
            title: new Text('Messages'),
          ),
          BottomNavigationBarItem(
              icon: Icon(Icons.person),
              title: Text('Profile')
          )
        ],
      ),
    );
  }
}

ここで、実装する際に抑える点は3つです。

  • Tab選択時の中身をListでもつ
  • Tabをタップした際の挙動をかく
  • 全体のレイアウトを組む

まずは、Tab選択時の中身のListについてです。

Tab選択時の中身のListでもつ

先のHomeStateにて、tabsと定義しているWidgetのリストは、下部のタブをタップした際に、各Tabの選択したものに関連するWidgetを表示した際の中身になります。

今回、自分の場合は、タイトルの文字列とカラーを指定するSampleTabItemを定義しています。

SampleTabItemの実装は次のようになります。

class SampleTabItem extends StatelessWidget {
  final String title;
  final Color color;

  const SampleTabItem(this.title, this.color) : super();

  
  Widget build(BuildContext context) {
    return new Scaffold(
      backgroundColor: this.color,
      body: new Container(
        child: new Center(
          child: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              new Text(this.title,
                  style: new TextStyle(
                      color: Colors.white,
                      fontSize: 36.0,
                      fontWeight: FontWeight.bold))
            ],
          ),
        ),
      ),
    );
  }
}

タイトルとカラーを渡すためにコンストラクタを次のように定義しています。

const SampleTabItem(this.title, this.color) : super();

Widget buildの中では、渡されたタイトルとカラーを指定して表示するようにレイアウトを組みます。

これをHomeStateの中でリストとしてTabの中身を持ちます。

Tabをタップした際の挙動をかく

タップした際の挙動についての関数onTabTappedを定義します。

bottom_navigation_bar.dartの中身を読むと、setStateにタブをタップした際のindexが渡ってくるので、HomeStateのなかに定義した現在のindexをもつcurrentindexに代入して選択したTabのindexを取得します。

void onTabTapped(int index) {
  setState(() {
    currentIndex = index;
  });
}

全体のレイアウトを組む

HomeStateのWidgetのbuildの中で全体のレイアウトを組みます。

骨組みを作ったら、bodyにはWidgetのリストを指定します。

ScaffoldにbottomNavigationBarを指定できるので、こちらで組みたいレイアウトを指定していきます。

BottomNavigationBarクラスでは、onTapには先ほど作成したonTabTappedを指定して、currentIndexもonTabTappedで代入したcurrentIndexを指定します。

itemsにはTabに表示したいItemをBottomNavigationBarItemで指定します。

BottomNavigationBarItem(
    icon: Icon(Icons.person),
    title: Text('Profile')
)

BottomNavigationBarItemにはiconとtitleを入れることができるので、これらを指定して入れます。

ここまでできれば、BottomNavigationBarの実装が完了です。

BottomNavigationBarの中にPagerを入れる実装

BottomNavigationBarを実装した上で、選択したタブの中にさらにPagerを入れたいことが出てくると思います。

そういう時の実装について、こちらで解説していきます。

まずはじめに、Pagerの実装についてやっていきましょう。

Pagerについて、今回はTabPagerItemと定義して自分は実装しました。

class TabPagerItem extends StatefulWidget {
  
  State<StatefulWidget> createState() {
    return TabPagerState();
  }
}

例によって、StatfulWidgetを継承したクラスを作成し、状態を保つクラスを作成します。自分はTabPagerStateを状態をもつクラスを作成しました。

TabPagerStateを次のように作成します。

class TabPagerState extends State<TabPagerItem> with SingleTickerProviderStateMixin {
  TabController controller;

  
  void initState() {
    super.initState();
    controller = new TabController(length: 3, vsync: this);
  }

  
  void dispose() {
    super.dispose();
    controller.dispose();
  }

  
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new TabBarView(
        children: <Widget>[
          new SampleTabItem("1個目", Colors.amber),
          new SampleTabItem("2個目", Colors.deepPurpleAccent),
          new SampleTabItem("3個目", Colors.orange),
        ],
        controller: controller,
      ),
    );
  }
}

pagerを作成するのに、TabBarViewを用いるので、TabBarもしくはTabBarViewを管理するTabControllerを作成します。

コールバックを利用するのに、withでSingleTickerProviderStateMixinを指定します。

initStateでControllerをインスタンス化して、Pagerのアイテム数をlengthで指定します。

今回はPagerの中身を3つにするため、3を指定しています。

disposeでControllerのdisposeを呼んで、解除することを忘れないでください。

Widget buildではPagerに表示したいレイアウトを使用します。

TabBarViewの中で、表示したいWidgetとcontrollerを指定します。

次に、このPagerをBottomNavigationBarの選択のWidgetのなかに入れます。

class HomeState extends State<Home> {
...
  final List<Widget> tabs = [
    TabPagerItem(),
    SampleTabItem("真ん中の画面", Colors.green),
    SampleTabItem("右の画面", Colors.cyan),
  ];
...
}

先に作成していた、tabsにPagerそのものを指定します。

ここまでできれば、冒頭のgifのサンプルの作成ができます。

おわりに

今回は、FlutterでBottomNavigationBarとBottomNavigationBar + Pagerを実装するための解説を自分なりにやってみました。

拙いところがあってわかりづらかったりしたら申し訳ないです。

間違っている部分があったり、説明がよくわからない部分などあれば、コメントをしていただけると嬉しいです。

また、今回のサンプルコードは次のリンクから入手できます。

クローンしてビルドすれば試すことができるのでやってみてください。

最後まで見ていただきありがとうございました。それではまたの機会に。

参考リンク

今回実装するにあたって、公式のサンプルと次のサンプルを参考にさせていただきました。

ぜひ、スターをしてこちらも試してみてください。