目次
概要
画面下にボタンを羅列して画面を切り替える方法がある。その方法が今回の記事。
まず、TabBarを表示するベースとなる画面を作る。
その画面とは別に、その表示する画面のファイルを作成して、ベースとなる画面で表示するという本心だ。
ファイルの数が多いが、「表示する画面」の4つファイルは同じような構成のため、数に圧倒されないで欲しい。
そして、今回は画像を表示しているが、画像でなくてもTextを表示するだけでも何も問題ない。
ソースコード
アプリ実行部分
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:concentration/TabBar/MainTabBarView.dart';
void main() async{
  runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        useMaterial3: true,
      ),
      home: MainTabBarView(),
    );
  }
}
画面全体
import 'package:concentration/TabBar/Screens/FourthView.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'MainTabBarViewModel.dart';
import 'Screens/FirstView.dart';
import 'Screens/SecondView.dart';
import 'Screens/ThirdView.dart';
class MainTabBarView extends ConsumerWidget {
  MainTabBarView({Key? key}) : super(key: key);
  MainTabBarViewModel _viewModel = MainTabBarViewModel();
  List<Widget> display = [
    const FirstView(),
    const SecondView(),
    const ThirdView(),
    const FourthView()
  ];
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    _viewModel = ref.watch(mainTabModelProvider);
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(_viewModel.title),
      ),
      body: display[_viewModel.currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'First'),
          BottomNavigationBarItem(icon: Icon(Icons.chat), label: 'Second'),
          BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Third'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Fourth'),
        ],
        currentIndex: _viewModel.currentIndex,
        onTap: (int index) {
          _viewModel.selectedTab(index);
        },
        selectedItemColor: Colors.white,
        unselectedItemColor: Colors.white54,
        backgroundColor: Colors.blue,
        type: BottomNavigationBarType.fixed,
      ),
    );
  }
}
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'MainTabBarRepository.dart';
final mainTabModelProvider = ChangeNotifierProvider((ref) => MainTabBarViewModel(repository: ref.read(MainTabBarRepositoryProvider)));
class MainTabBarViewModel extends ChangeNotifier {
  MainTabBarRepository? repository;
  int currentIndex = 0;
  String title = "First";
  MainTabBarViewModel({this.repository});
  void selectedTab(int index) {
    currentIndex = index;
    switch (currentIndex) {
      case 0:
        title = "First";
      case 1:
        title = "Second";
      case 2:
        title = "Third";
      case 3:
        title = "Fourth";
    }
    notifyListeners();
  }
}
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'MainTabBarModel.dart';
final MainTabBarRepositoryProvider = Provider((ref) => MainTabBarRepositoryImpl(model: ref.read(mainTabBarModelProvider)));
abstract class MainTabBarRepository {
  
}
class MainTabBarRepositoryImpl implements MainTabBarRepository {
  MainTabBarRepositoryImpl({required MainTabBarModel model}): _model = model;
  final MainTabBarModel _model;
}
import 'package:flutter_riverpod/flutter_riverpod.dart';
final mainTabBarModelProvider = Provider((ref) => MainTabBarModel());
class MainTabBarModel {
}
表示する画面
libと同じ階層にあるimagesディレクトリに画像を以下の名前の画像を追加しておく。
- 01_img.PNG
- 02_img.PNG
- 03_img.PNG
- 04_img.PNG
画像の追加方法はこちら。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class FirstView extends ConsumerWidget {
  const FirstView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
        body: Container(
          alignment: Alignment.center,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Container(
                  width: 200,
                  height: 200,
                  decoration: const BoxDecoration(
                      image: DecorationImage(
                        image: AssetImage('images/01_img.PNG'),
                        fit: BoxFit.contain,
                      )
                  )
              ),
            ],
          ),
        )
    );
  }
}
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SecondView extends ConsumerWidget {
  const SecondView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
        body: Container(
          alignment: Alignment.center,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Container(
                  width: 200,
                  height: 200,
                  decoration: const BoxDecoration(
                      image: DecorationImage(
                        image: AssetImage('images/02_img.PNG'),
                        fit: BoxFit.contain,
                      )
                  )
              ),
            ],
          ),
        )
    );
  }
}
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ThirdView extends ConsumerWidget {
  const ThirdView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
        body: Container(
          alignment: Alignment.center,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Container(
                  width: 200,
                  height: 200,
                  decoration: const BoxDecoration(
                      image: DecorationImage(
                        image: AssetImage('images/03_img.PNG'),
                        fit: BoxFit.contain,
                      )
                  )
              ),
            ],
          ),
        )
    );
  }
}
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class FourthView extends ConsumerWidget {
  const FourthView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
        body: Container(
          alignment: Alignment.center,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Container(
                  width: 200,
                  height: 200,
                  decoration: const BoxDecoration(
                      image: DecorationImage(
                        image: AssetImage('images/04_img.PNG'),
                        fit: BoxFit.contain,
                      )
                  )
              ),
            ],
          ),
        )
    );
  }
}デモ動画
詳細
今回の主役はこちらだ。
List<Widget> display = [
  const FirstView(),
  const SecondView(),
  const ThirdView(),
  const FourthView()
];bottomNavigationBar: BottomNavigationBar(
  items: const [
    BottomNavigationBarItem(icon: Icon(Icons.home), label: 'First'),
    BottomNavigationBarItem(icon: Icon(Icons.chat), label: 'Second'),
    BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Third'),
    BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Fourth'),
  ],
  currentIndex: _viewModel.currentIndex,
  onTap: (int index) {
    _viewModel.selectedTab(index);
  },
  selectedItemColor: Colors.white,
  unselectedItemColor: Colors.white54,
  backgroundColor: Colors.blue,
  type: BottomNavigationBarType.fixed,
),| 引数名 | 設定内容 | 
| bottomNavigationBar | BottomNavigationBarオブジェクトを設定する | 
| items | BottomNavigationBarItemをメニューの数だけ格納する | 
| currentIndex | 在の表示している画面の番号を設定する。(displayの添字) | 
| selectedItemColor | 選択されている時のボタンの色を設定する | 
| unselectedItemColor | 選択されていない時のボタンの色を設定する | 
| backgroundColor | タブバーの背景色を設定する | 
| type | BottomNavigationBarTypeアイコンの表示方法を設定する | 
まず、こうすることで画面の下にタブバーを表示することができる。
では、今度は更新処理だ。今回はアーキテクチャのことも考えて、 Repositoryクラスなどを作成している。
まず、タブバーのボタンを押下すると以下の処理が行われる。
onTap: (int index) {
  _viewModel.selectedTab(index);
},そして、viewModelのselectedTabの処理が行われる。
void selectedTab(int index) {
  currentIndex = index;
  switch (currentIndex) {
    case 0:
      title = "First";
    case 1:
      title = "Second";
    case 2:
      title = "Third";
    case 3:
      title = "Fourth";
  }
  notifyListeners();
}こうすることで、以下で表示しているタイトルと表示するビューを変更している。
List<Widget> display = [
  const FirstView(),
  const SecondView(),
  const ThirdView(),
  const FourthView()
];return Scaffold(
  appBar: AppBar(
    backgroundColor: Theme.of(context).colorScheme.inversePrimary,
    title: Text(_viewModel.title),
  ),
  body: display[_viewModel.currentIndex],ただ、変数を更新しただけでは画面は更新されない。そのためにも以下のメソッドをタップした時に行われるviewModelクラスのメソッド内で呼び出す必要がある。
notifyListeners();