🌴

[Flutter×Riverpod]BottomNavigationBarを勉強してみた

2022/08/27に公開

はじめに

下記のUrasanさんによる「BottomNavigationBarを本気で学ぶ」を参考にBottomNavigationBarの勉強をしてました。
本業で扱っているアプリがRiverpodを使用してのアプリ開発を行なっていて、全くRiverpod×、BottomNavigationBarの理解できていなかったため、夏休み中に勉強しよう!と考えました。

今回、Urasanさんの記事を軸にRiverpodを使用した、BottomNavigationBarのざっくり記事になります。
https://zenn.dev/urasan/articles/5bb85a54fb23fb

ディレクトリ、ファイル構成

lib --
      | riverpod ------------------------
      |                                  |
       - bottomnavigationbar.dart         - app_bottom_navigation_bar.dart
       - main.dart                        - screen_index_provider.dart
       - page1.dart                       - tab_Index_provider.dart
       - page2.dart
       - page3.dart
       - page4.dart

メインとなるファイルはBottomNavigationBarPageAppBottomNavigationBar
の二つとなります。

BottomNavigationBarPage

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:prefectures47/page1.dart';
import 'package:prefectures47/page2.dart';
import 'package:prefectures47/page3.dart';
import 'package:prefectures47/page4.dart';
import 'package:prefectures47/riverpod/app_bottom_navigation_bar.dart';
import 'package:prefectures47/riverpod/screen_index_provider.dart';
import 'package:prefectures47/riverpod/tab_Index_provider.dart';

class BottomNavigationBarPage extends ConsumerStatefulWidget {
  const BottomNavigationBarPage({Key? key}) : super(key: key);

  
  _BottomNavigationBarPageState createState() =>
      _BottomNavigationBarPageState();
}

class _BottomNavigationBarPageState
    extends ConsumerState<BottomNavigationBarPage> {
  static final List<Widget> _pageList = [
    //1ページ
    const PageWidget1(),
    //2ページ
    const PageWidget2(),
    //3ページ
    const PageWidget3(),
    //4ページ
    const PageWidget4(),
  ];

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Riverpod Test'),
      ),
      body: _pageList.elementAt(ref.watch(screenIndexProvaider)),
      bottomNavigationBar: AppBottomNavigationBar(
        curretPage: ref.watch(tabIndexProvider),
        tap: (tab) => _onTap(tab, context),
      ),
    );
  }

  void _onTap(int tab, BuildContext context) {
    switch (tab) {
      case 0:
        ref.read(tabIndexProvider.state).state = tab;
        break;
      case 1:
        ref.read(tabIndexProvider.state).state = tab;
        break;
      case 2:
        ref.read(tabIndexProvider.state).state = tab;
        break;
      case 3:
        ref.read(tabIndexProvider.state).state = tab;
        break;
    }
  }
}

ConsumerStatefulWidget とは

公式から一部引用。
ref オブジェクトが使用できるようにするため
StatefulWidget+State の代わりに ConsumerStatefulWidget+ConsumerState を継承する。
https://riverpod.dev/ja/docs/concepts/reading/

class BottomNavigationBarPage extends ConsumerStatefulWidget {

ref.watch を使ってプロバイダを監視する

ref.watch: プロバイダの値を取得した上で、その変化を監視する。値が変化すると、その値に依存するウィジェットやプロバイダの更新が行われる。
※上記の内容は公式から引用
screenIndexProvaiderを監視する。

body: _pageList.elementAt(ref.watch(screenIndexProvaider.state).state),
curretPage: ref.watch(tabIndexProvider),
        tap: (tab) => _onTap(tab, context),
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:prefectures47/riverpod/tab_Index_provider.dart';

// 画面が切り替わった場合でも、どのスクリーンを表示させているのかを保持するプロバイダー
final screenIndexProvaider = StateProvider<int>((ref) {
  // タブの状態を読みとる
  final tabIndex = ref.watch(tabIndexProvider.state).state;
  return tabIndex;
});
import 'package:flutter_riverpod/flutter_riverpod.dart';

// どのタブを表示しているのか状態を保持するプロバイダー
final tabIndexProvider = StateProvider<int>((ref) => 0);

AppBottomNavigationBar

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class AppBottomNavigationBar extends StatefulWidget {
  const AppBottomNavigationBar(
      {required this.curretIndex, required this.onTap});

  final int curretIndex;
  final ValueChanged<int> onTap;
  
  _AppBottomNavigationBarState createState() => _AppBottomNavigationBarState();
}

class _AppBottomNavigationBarState extends State<AppBottomNavigationBar> {
  
  Widget build(BuildContext context) {
    return Consumer(builder: (context, ref, child) {
      return BottomNavigationBar(
        items: <BottomNavigationBarItem>[
          BottomNavigationBarItem(
              icon: _bottomNavigationBarIcon(index: 0), label: 'ページ1'),
          BottomNavigationBarItem(
              icon: _bottomNavigationBarIcon(index: 1), label: 'ページ2'),
          BottomNavigationBarItem(
              icon: _bottomNavigationBarIcon(index: 2), label: 'ページ3'),
          BottomNavigationBarItem(
              icon: _bottomNavigationBarIcon(index: 3), label: 'ページ4'),
        ],
        currentIndex: widget.curretIndex,
        onTap: widget.onTap,
        type: BottomNavigationBarType.fixed,
      );
    });
  }
}

Widget _bottomNavigationBarIcon({required index}) {
  return Container();
}

プロパティ

プロパティ 説明
currentIndex 現在アクティブなBottomNavigationBarItemの項目へのインデックス
onTap ページ1つがタップされたときに呼び出し
type BottomNavigationBarのレイアウトと動作を定義

https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html

終わりに

エンジニア初学者なので、まだまだ詳しく説明できていない部分がありますが、
旧Providerだとツリー構造なので、近くの値からしか取得できませんがRiverpodはグローバルに値を取得できるのでとても便利ですね!

これからFlutterエンジニア目指すかたは、Riverpodでアプリを作ると好印象間違いないでしょう!

下記コミュニティは、私がエンジニア未経験から転職まで導いていただいたコミュニティ。
興味があれば是非!!
https://flutteruniv.com/

Discussion

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