【Flutter】タブのある画面にタブのページを指定して遷移する

# 実現したいこと
Flutterにおいてタブのある画面に遷移したとき、基本的には最初のタブが開いていると思いますが、場合によっては任意のタブを指定して画面遷移したい場合があると思います。

このような場合の実装方法について解説します。

## 自分の環境
macOS Monterey 12.2.1（21D62）
Flutter 2.8.1
Dart SDK version: 2.15.1 (stable)
VSCode 1.67.2

# タブ画面の実装
現在のタブのページを保持する必要があるため、ステートフルな画面である必要があります。

詳細は以下公式ドキュメントなどを参考にしてください。（ドキュメントはステートレスなのでステートフルに書き換えること）
https://docs.flutter.dev/cookbook/design/tabs

※すでにステートフルなタブの画面が実装されている方は **「タブを指定できるようにする」** に飛んでください。

[実装イメージ]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574728/4091ee9b-f10f-6b93-f700-8e800f82f4d5.png)

[コード]
``` tab_bar_demo.dart
class TabBarDemo extends StatefulWidget {
  const TabBarDemo({ Key? key }) : super(key: key);

  @override
  State<TabBarDemo> createState() => _TabBarDemoState();
}

class _TabBarDemoState extends State<TabBarDemo> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            bottom: const TabBar(
              tabs: [
                Tab(icon: Icon(Icons.directions_car)),
                Tab(icon: Icon(Icons.directions_transit)),
                Tab(icon: Icon(Icons.directions_bike)),
              ],
            ),
            title: const Text('Tabs Demo'),
          ),
          body: const TabBarView(
            children: [
              Icon(Icons.directions_car),
              Icon(Icons.directions_transit),
              Icon(Icons.directions_bike),
            ],
          ),
        ),
      ),
    );
  }
}
```

# タブを指定できるようにする

上記までの実装でタブのある画面は実装できますが、タブを指定して遷移できるようにするには更に手を加える必要があります。

引数でページを指定できるようにし、読み込み時に指定したページへ移動するイメージです。

1. 指定するページの変数(tabPage)を追加する（0始まりとなる）
1. 変数の追加に伴ってコンストラクタを書き換える（指定しない場合は最初のタブが表示されるように初期値を0にしておく）
1. タブページのステートに`with SingleTickerProviderStateMixin`を追記する
1. タブページのステートにタブコントローラーの変数を追加する
1. タブコントローラをタブバーに指定する（constがあるとエラーが出るためはずす）
1. 初期化処理を追加し、その中でタブコントローラを初期化（`length`はタブの数と同じにする）
1. 初期化処理の中でanimateToメソッドによりタブを指定
1. dispose内でタブコントローラの削除を行う

``` tab_bar_demo.dart
class TabBarDemo extends StatefulWidget {
  final int tabPage; // 1. 追加

  const TabBarDemo({    // 2. 変更
    Key? key,           // 2. 変更
    this.tabPage = 0    // 2. 変更
  }) : super(key: key); // 2. 変更

  @override
  State<TabBarDemo> createState() => _TabBarDemoState();
}

class _TabBarDemoState extends State<TabBarDemo> with SingleTickerProviderStateMixin { // 3. 変更
  late TabController _tabController; // 4. 追加

  @override                                                 // 6. 追加
  void initState() {                                        // 6. 追加
    _tabController = TabController(length: 3, vsync: this); // 6. 追加
    super.initState();                                      // 7. 追加
  }                                                         // 6. 追加

  @override                   // 8. 追加
  void dispose() {            // 8. 追加
    _tabController.dispose(); // 8. 追加
    super.dispose();          // 8. 追加
  }                           // 8. 追加

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            bottom: TabBar(               // 5. 変更
              controller: _tabController, // 5. 追加
              tabs: const [               // 5. 変更
                Tab(icon: Icon(Icons.directions_car)),
                Tab(icon: Icon(Icons.directions_transit)),
                Tab(icon: Icon(Icons.directions_bike)),
              ],
            ),
            title: const Text('Tabs Demo'),
          ),
          body: const TabBarView(
            children: [
              Icon(Icons.directions_car),
              Icon(Icons.directions_transit),
              Icon(Icons.directions_bike),
            ],
          ),
        ),
      ),
    );
  }
}
```

# タブを指定して画面遷移する
TabBarDemoの画面に初期ページの引数を指定できるようになったため、任意のページを指定して遷移する。
（0始まりなので、1と指定したら2つめのタブが最初に表示される）

``` .dart
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (_) => TabBarDemo(tabPage: 1),
  ),
);
```

# ソースコード
https://github.com/ruemura3/SpecifyTabTransition

Twitter→[@ruemura3](https://twitter.com/ruemura3)