Be simple

”当たり前”が誰かのためになる

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

f:id:rozkey59:20181228134843j:plain

Flutter

はじめに

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

 

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

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

 

対象とする読者

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

 

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

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

 

BottomNavigationBarの実装

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

f:id:rozkey59:20181228164245g:plain

BottomNavigationSample

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

material.io

 

先のリンク先を見ると、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以外に指定する必要があります。

docs.flutter.io

 

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

 

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

class Home extends StatefulWidget {
@override
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;
});
}

@override
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つです。

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

 

まずは、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();

@override
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を入れる実装

f:id:rozkey59:20181228175725g:plain

BottomNavigationの中にPagerを入れたサンプル

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

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

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

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

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

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

 

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

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

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

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

@override
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を作成します。

docs.flutter.io

 

コールバックを利用するのに、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を実装するための解説を自分なりにやってみました。

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

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

 

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

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

github.com

 

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

 

参考リンク

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

 

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

github.com