[Widget]StatefullWidgetでタブ切り替えをする

動作環境: Flutter 2.8.1 Dart 2.15.1
LINEでこのページを共有するTwitterでこのページを共有するこのページをはてなブックマークに追加

タブ切り替えをStateful Widgetで実装する方法を紹介します。
Stateful Widgetの場合はDefaultTabControllerを使わなくてもタブ切り替えを実装することが可能です。

DefaultTabControllerを使ったケースはこちらをご覧ください。

まずはサンプルコードの全体をご覧ください。

lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'タブバーを試す',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const TabBarScreen(),
);
}
}
class TabBarScreen extends StatefulWidget {
const TabBarScreen({Key? key}) : super(key: key);
@override
_TabBarScreenState createState() => _TabBarScreenState();
}
class _TabBarScreenState extends State<TabBarScreen>
with TickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('タブバーのサンプル'),
bottom: TabBar(
controller: _tabController,
tabs: const [
Tab(
icon: Icon(Icons.home),
text: 'ホーム',
),
Tab(
icon: Icon(Icons.access_time_sharp),
text: '通知',
),
Tab(
icon: Icon(Icons.build),
text: '設定',
),
],
onTap: (index) {
print('$index番目の画面になりました');
},
),
),
body: TabBarView(
controller: _tabController,
children: const [
Center(
child: Text(
'ホーム画面',
style: TextStyle(fontSize: 40.0),
),
),
Center(
child: Text(
'通知画面',
style: TextStyle(fontSize: 40.0),
),
),
Center(
child: Text(
'設定画面',
style: TextStyle(fontSize: 40.0),
),
),
],
),
);
}
}

ひとつひとつポイントを見ていきましょう。
以下の30行目からはじまる_TabBarScreenStateのコードを見てください。

lib/main.dart
class _TabBarScreenState extends State<TabBarScreen>
with TickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}

32行目のところでTabConrtollerの変数を保持しています。
この_tabControllerがタブの切り替えを制御するクラスです。
Stateless Widgetのケースで使用していたDefaultControllerの代わりに必要になります。

37行目のようにinitStateの中でTabControllerの実体を作成しています。
このとき、length:で必要なタブの数を指定します。

この37行目のコードを書くのに必要なのが、31行目で指定しているTickerProviderStateMixinです。
タブの切り替え時にアニメーションの演出が入りますが、そのために必要なパラメーターです。

TabControllerは内部でAnimationControllerを使用していて、vsync:にアニメーションを動作させるクラスのインスタンスを指定してあげます。この場合はthis = _TabBarScreenStateですね。

あとは、タブの具体的な準備です。
以下のコードを見てください。

lib/main.dart
appBar: AppBar(
title: const Text('タブバーのサンプル'),
bottom: TabBar(
controller: _tabController,
tabs: const [
Tab(
icon: Icon(Icons.home),
text: 'ホーム',
),
Tab(
icon: Icon(Icons.access_time_sharp),
text: '通知',
),
Tab(
icon: Icon(Icons.build),
text: '設定',
),
],
onTap: (index) {
print('$index番目の画面になりました');
},
),
),

AppBarbottom:の部分にタブバーの具体的な設定を書いていきます。
52行目で_tabControllerを指定することで、DefaultTabControllerを使わずにタブ切り替えを実現できます。
また、67行目のようにonTap:でタブを切り替えたときのイベントを受け取ることが可能です。
indexには切り替え後のタブ番号が設定されてきます。

タブごとの内容表示も必要です。
以下のコードを見てください。

lib/main.dart
body: TabBarView(
controller: _tabController,
children: const [
Center(
child: Text(
'ホーム画面',
style: TextStyle(fontSize: 40.0),
),
),
Center(
child: Text(
'通知画面',
style: TextStyle(fontSize: 40.0),
),
),
Center(
child: Text(
'設定画面',
style: TextStyle(fontSize: 40.0),
),
),
],
),

ここでも73行目で_tabControllerを指定しています。
children:にはタブごとの表示内容を設定します。

今回はサンプルなので、CenterTextのシンプルな内容ですが、
実際にはかなり複雑化すると思います。
タブごとに別のWidgetとして切り出して読み込む形にするのが現実的です。

DefaultTabControllerを使うパターンよりもコードのネストが1階層減るため見やすくなるのも利点だと思います。
Flutterのコードはただでさえネストが激しくなりがちなので・・
タブバーのサンプル