15. Flutter のBottomNavigationBarでUITabbarControllerぽく作る
今回はFlutterでBottomNavigatonBarの拡張を行いたいと思います。 またBottomNavigatonBarの基本的な使い方は以前に学習しました。
こちらは画面の切り替えができておらず、本当にアイコンの選択/未選択とウィジェットの更新だけの実装でした。 そこで今回はアイコンだけでなく、画面切り替えの部分も学習していきます。 ルーティングを使いますので、ルーティングについて学習します。
ルーティングとは
ルーティングとは画面遷移をpathで表現する方法です。
StatelessWidget
にはルーティングを設定するroutes
というプロパティが存在します。
さらにアプリ起動時にどのウィジェットを表示させるかを指定するためのinitialRoute
のプロパティも存在します。
使い方としては次のような書き方です。
MaterialApp( title: [タイトル], initialRoute: '/', routes: { '/': (context) => MainPage(), /// MainPageウィジェットに飛ばす }, );
これをもとにしてBottomNavigationBarのアイコンをタップしたときの画面遷移を実装しようと思ったのですが 使い方が間違っていたみたいです。 前日ですが、無限ループが発生してアプリが起動できませんでした。
今回は2回目として別の方法で実装しました。
アプローチの方法
イメージとしては次の画像のようになります。
メインとなるウィジェット(今回のサンプルコードではMainPage と命名しました)を用意します。
メインウィジェットでScaffoldのbodyにIndexedStack
を使います。
これでindexに応じたウィジェットのStackが出来上がります。
あとはBottomNavigationBarのonTap
とcurrentIndex
にて選択されたindexを再代入します。
これで動的にBottomNavigationBarのアイコンをタップしたことで画面が変わったかのような実装を実現することができます。
サンプルコード
では早速、このアプローチをもとにしたサンプルコードを書いていきます。
今回の登場人物は
の5つのクラスです。
main.dart
import 'package:flutter/material.dart'; import 'package:practice_app/main_page.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { final title = 'BottomNavigationBar複数画面のサンプルコード'; @override Widget build(BuildContext context) { return new MaterialApp( debugShowCheckedModeBanner: true, title: 'BottomNavigationBar App', theme: new ThemeData( primarySwatch: Colors.blue, primaryColor: const Color(0xff2196f3), accentColor: const Color(0xff2196f3), canvasColor: const Color(0xfffafafa), ), initialRoute: '/', routes: { '/': (context) => MainPage(), }, ); } }
main_page.dart
import 'package:flutter/material.dart'; import 'package:practice_app/first_screen.dart'; import 'package:practice_app/second_screen.dart'; import 'package:practice_app/third_screen.dart'; class MainPage extends StatefulWidget { @override _MainPageState createState() => _MainPageState(); } class _MainPageState extends State<MainPage> { int _selectedIndex = 0; List<Widget> _pageList = [ FirstScreen(), SecondScreen(), ThirdScreen() ]; @override Widget build(BuildContext context) { return Scaffold( body: IndexedStack( index: _selectedIndex, children: _pageList, ), bottomNavigationBar: BottomNavigationBar( items: [ BottomNavigationBarItem( title: Text('First'), icon: Icon(Icons.home) ), BottomNavigationBarItem( title: Text('Second'), icon: Icon(Icons.favorite) ), BottomNavigationBarItem( title: Text('Third'), icon: Icon(Icons.android) ), ] , selectedItemColor: Colors.red, currentIndex: _selectedIndex, onTap: (int index) { setState(() { _selectedIndex = index; }); }, ), ); } }
first_screen.dart
import 'package:flutter/material.dart'; class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: Text('First Screen'), ); } }
second_screen.dart
import 'package:flutter/material.dart'; class SecondScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: Text('Second Screen'), ); } }
third_screen.dart
import 'package:flutter/material.dart'; class ThirdScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: Text('ThirdScreen'), ); } }
となります。 これでアプリを実行すると次のような画面が表示されます。
first | second | third |
---|---|---|
このように表示されたら成功です。 iOS のUITabbarController みたいな画面切り替えが実装できました。 このような動きが実現できれば簡単なアプリが作れそうですね。
注記
最後にBottomNavigationBarの注記になります。
現在のFlutterの不具合なのか、iOSではBottomNavigationBarItemにおけるアイコンは3つまでで4つ以上置こうとすると挙動がおかしくなり
最初の1つ目のアイコンしか表示されません。
では、4つ目のアイコンがおけないのかというと置けます
。
下記の参考ページに解決法が載っています。
つまりはBottomNavigationBarにtype
というプロパティがあり、それにBottomNavigationBarType.fixed
をセットすればいいです。
BottomNavigationBar( type: BottomNavigationBarType.fixed, /// これを追加する items: [ BottomNavigationBarItem( title: Text('First'), icon: Icon(Icons.home) ), BottomNavigationBarItem( title: Text('Second'), icon: Icon(Icons.favorite) ), BottomNavigationBarItem( title: Text('Third'), icon: Icon(Icons.android) ), BottomNavigationBarItem( title: Text('Third'), icon: Icon(Icons.settings) ), ]
これで4つ以上おけるようになります。 以上、注記でした。