すらぼうの開発ノート

モバイルアプリエンジニアのメモ

【Flutter】BottomNavigationBarでページを切り替える機能を実装する

アプリを開発していると、画像のようにページ下部に画面遷移用のナビゲーションバーを実装したい時がある。

zozotown

Flutterではこのような機能を、ScaffoldウィジェットBottomNavigationBarを利用することで簡単に実装できる。

本エントリではBottomNavigationBarの基本的な使い方、動作フロー、実際のアプリをベースに説明をする。

BottomNavigationBarとは

api.flutter.dev

BottomNavigationBarはmaterialデザインで使用されるページ下部に表示される画面切り替え用のナビゲーションバー用ウィジェット

BottomNavigationBarを使用する際に必要になることが多いプロパティを説明する。

プロパティ

items ※必須

BottomNavigationBar上に表示される各アイテム。 このアイテムをタップすることで表示を切り替える。

List<BottomNavigationBarItem>型のリストを渡す。

またアイテム数は2以上が必要。

currentIndex

現在選択されているアイテムのインデックスを指定する。 この値を変更しないと、再ビルドしても表示が切り替わらないので注意。

int型を渡す。インデックスは0から始まり、左から1ずつ増える。

onTap

アイテムをタップされた際にコールされるイベントハンドラ。 引数にはタップされたアイテムのインデックスが渡される。

BottomNavigationBarの動作フロー

一般的には次のようなフローで動作する。

  1. ウィジェットがビルドされる。この時currentIndexが読み取られ、インデックスに該当するitemsのアイテムにフォーカスが当たる
  2. ユーザーがアイテムをタップする
  3. onTapに渡されているハンドラメソッドがコールされる。
  4. ハンドラメソッド内でsetState()がコールされてcurrentIndexが更新される
  5. 再ビルドされてフォーカスの当たるアイテムが変わる

次に実際にアプリのコードに沿って動作を説明する。


作成アプリ

次のgif画像のアプリを作成する。

gif image

仕様

bottomNavigationBarに表示されている項目を選択することでメインコンテンツを切り替える。

ソースコード

import 'package:flutter/material.dart';

// 1
void main() {
  runApp(const MyApp());
}

// 2
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'test app',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: HomeWidget());
  }
}

// 3
class HomeWidget extends StatefulWidget {
  const HomeWidget({Key? key}) : super(key: key);

  @override
  State<HomeWidget> createState() => _HomeWidgetState();
}

// 4
class _HomeWidgetState extends State<HomeWidget> {
  // 5
  List<Widget> pages = [
    Container(
      color: Colors.red,
    ),
    Container(
      color: Colors.green,
    ),
    Container(
      color: Colors.blue,
    ),
  ];

  // 6
  List<BottomNavigationBarItem> items = [
    BottomNavigationBarItem(icon: Icon(Icons.home), label: "home"),
    BottomNavigationBarItem(icon: Icon(Icons.person), label: "user"),
    BottomNavigationBarItem(icon: Icon(Icons.settings), label: "setting"),
  ];

  // 7
  int _selectedItemIndex = 0;

  // 8
  void updateSelectedItemIndex(newItemIndex) {
    setState(() {
      _selectedItemIndex = newItemIndex;
    });
  }

  // 9
  @override
  Widget build(BuildContext context) {
    
    // 10
    return Scaffold(

      // 11
      body: pages[_selectedItemIndex],

      // 12
      bottomNavigationBar: BottomNavigationBar(
          // 13
          currentIndex: _selectedItemIndex,

          // 14
          onTap: updateSelectedItemIndex,

          // 15
          items: items),
    );
  }
}

上のコードについて番号ごとに説明していく。

1 Flutterアプリのエントリーポイント

2 ウィジェットツリーのルートウィジェット。 ここでMaterialAppをビルドする。

3 StatefulWidgetを継承したHomeWidget。 このウィジェットでUIを作成する。

4 HomeWidgetStateインスタンス

5 実際にコンテンツ部分に表示するウィジェットのリスト。 コンテンツが切り替わったことがわかりやすいように、それぞれ背景色を変えている。

6 BottomNavigationBarItemのリスト。

7 現在選択されているBottomNavigationBarItemの番号。 リストのインデックスと同様に0から始まり1ずつ増える。

8 BottomNavigationBarItemがタップされた際にコールされるイベントハンドラメソッド。 setState()をコールして選択されているBottomNavigationBarItemが切り替わったことを Flutterフレームワークに伝える。

9 HomeWidgetStateインスタンスのビルドメソッド。 ここにUIとして表示したいウィジェットを記述する。

10 Scaffoldウィジェットをビルドする。

11 Scaffoldウィジェットbodyにはpagesを渡す。 この時のインデックスは_selectedItemIndexを渡し、選択されたインデックスに対応するコンテンツを表示する。

12 ScaffoldbottomNavigationBarBottomNavigationBarウィジェットを渡す。

13 現在選択されているbottomNavigationBarItemのインデックス。 _selectedItemIndexを渡すことでsetState()がコールされて再ビルドが生じた際に 現在選択されているインデックスを更新できる。

14 BottomNavigationBarItemがタップされた際にコールされるイベントハンドラメソッド。 updateSelectedItemIndexを渡す。この時引数の形式を揃える必要がある。

15 BottomNavigationBarウィジェット部分に表示される各アイテム。 6で説明したitemsリストを渡す。