🕹️

FlutterでBottomNavigationBarを作る

2022/09/02に公開

Riverpodで作ってみる

ネットの記事を参考にしたのですが、コードの書き方が古くて、書き直したりしました!
結構大変でしたね。
StatefulWidgetで作る方法は情報が多いのですが、riverpodやProviderで作られた情報は少ないので、今回挑戦して作ってみました!

完成品のソースコード
https://github.com/sakurakotubaki/BottomNavigationBar-Riverpod

元になっている公式ドキュメントのコードはこちら
https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html

今回使用したStateProviderを使って、UIの状態を監視しています。
enumを操作するのに使っています。

こちらが公式ドキュメント
https://riverpod.dev/ja/docs/providers/state_provider/

こちらがサンプルになります

enum型でボタンの種類の定数を作りRiverpodのProviderで状態の変更の監視を行います。

provider/provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
// enumは列挙型と呼ばれている固定数の定数値を表すために使用される
// 特別な種類のクラスです。
enum TabType { home, map, profile }
// StateProviderを使用して、enum型のTabTypeを型として使い状態管理をする。
// 初期の状態だとhomeの定数を初期値として使うので、HomeScreenが選択される。
final tabTypeProvider = StateProvider<TabType>((ref) => TabType.home);

今回の参考にさせていただいたコードだと、定数には家、地図、プロフィールのボタンを押すとそれぞれのページへ移動する仕組みになっております。

家のページのコード

screens/home_screen.dart
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(child: Container(child: Text('Home', style: TextStyle(fontSize: 50, color: Colors.blueAccent),))),
    );
  }
}

地図のページのコード

screens/map_screen.dart
import 'package:flutter/material.dart';

class MapScreen extends StatelessWidget {
  const MapScreen({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Map'),
      ),
      body: Center(child: Container(child: Text('Map', style: TextStyle(fontSize: 50, color: Colors.green),))),
    );
  }
}

プロフィールのページのコード

screens/profile_screen.dart
import 'package:flutter/material.dart';

class ProfileScreen extends StatelessWidget {
  const ProfileScreen({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ProfileScreen'),
      ),
      body: Center(child: Container(child: Text('ProfileScreen', style: TextStyle(fontSize: 50, color: Colors.orange),))),
    );
  }
}

BottomNavigationBarを操作するコード

main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_bottom_navigation/provider/provider.dart';
import 'package:riverpod_bottom_navigation/screens/home_screen.dart';
import 'package:riverpod_bottom_navigation/screens/map_screen.dart';
import 'package:riverpod_bottom_navigation/screens/profile_screen.dart';

void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      // ThemeDataでAppBar全体の色を統一する!
      theme: ThemeData(
        // 16進数の色を使うときは、Color(0xffの後に16進数の色を使用する)
        appBarTheme: const AppBarTheme(color: Color(0xff62abf5)),
      ),
      title: 'app',
      home: ScreenContainer(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class ScreenContainer extends ConsumerWidget {
  const ScreenContainer({Key? key}) : super(key: key);

  
  Widget build(BuildContext context, WidgetRef ref) {
    // ref.watchで変数の状態の変化を監視する。
    final tabType = ref.watch(tabTypeProvider.state);
    // List型で画面遷移先のページを定義する。
    // 参考にしたコードは、final _screensと書かれていた!
    List<Widget> _screens = [
      HomeScreen(),
      MapScreen(),
      ProfileScreen(),
    ];
    return Scaffold(
      body: _screens[tabType.state.index],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: tabType.state.index,
        onTap: (int selectIndex) {
          tabType.state = TabType.values[selectIndex];
        },
        selectedItemColor: Colors.black,
        unselectedItemColor: Colors.grey,
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.map),
            label: 'map',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.person),
            label: 'person',
          ),
        ],
      ),
    );
  }
}

最後に

BottomNavigationBarを作ってみて、FlutterでListとenumの組み合わせ方と、riverpodの連携をすることを今回学べたので、良いoutputになったと思います!

Discussion