【Flutter】タブ内のボタンでページ遷移（画面遷移）

# タブ内のページ遷移するボタン
Flutterで、**タブ内のボタンを押したら、新しいページ遷移される**仕組みを作ってみましょう。デモページは以下をご覧ください、（上タブと下タブでは、コードの構成が少し異なります。）

[上タブのデモページ](https://codepen.io/masa_mf3rs/pen/wvKLqOb)
[下タブのデモページ](https://codepen.io/masa_mf3rs/pen/abvrepm)

<img width="221" alt="2020-05-29 (3).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/471258/b9e288a4-e91c-e085-4b66-ee24f23c5160.png">
↑下タブ版の例

> 上タブ… DefaultTabController（上部に設置するタブ）
> 下タブ… BottomNavigationBar（下部に設置するタブ）

## 1. まずはタブとそのページを作ってみる
上タブは[flutter.dev/docs](https://flutter.dev/docs/cookbook/design/tabs)を、
下タブは、[flutter.dev/flutter/material](https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html)を
参考に、タブとそのページのみを作成してみましょう。

## 2. ページ遷移を作成する
以下のURL
[flutter.dev/docs](https://flutter.dev/docs/cookbook/navigation/navigation-basics
) を参考に、ページ遷移を作成する

## 3. そのまま実装すると、エラーが発生する！
#### 上タブ版のエラー

```
Navigator operation requested with a context that does not include a Navigator.
```
このエラーメッセージは、「**Navigator.pushで、Navigatorが見つからない**」といっています。NavigatorオブジェクトはMaterialApp内で生成されますが、そのままのコードだと、MaterialAppのContextへ辿ることができません。
参考：[Flutterの公式YouTube](https://www.youtube.com/watch?v=xXNOkIuSYuA)

#### 下タブ版のエラー

```
Error: Getter not found: 'context'.
```
このエラーメッセージは、「**ページ1の画面にはcontextがない**」といっています。

## 解決法
#### 上タブ版の解決策（どちらでもOK）
- DefaultTabControllerをBuilderで囲む
または
- DefaultTabController全体を別Widgetに切り出す
（今回は、2個目の切り出す方法を採用）

#### 下タブ版の解決策
- ページ1、ページ2それぞれを別Widgetを別クラスに切り出す

## まとめ

#### 上タブ版
```dart:main.dart
import 'package:flutter/material.dart';

// 最初に呼び出される関数（MyApp関数）
void main() {
  runApp(TabBarDemo());
}

// 全体
class TabBarDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: TabPage(),
    );
  }
}

// 2つのタブの画面
class TabPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      // Scaffold を使うことで、 appBar を利用できる
      child: Scaffold(
        appBar: AppBar(
          // appBar の bottomの部分 に作成
          bottom: TabBar(
            tabs: [
              Tab(icon: Icon(Icons.home)),
              Tab(icon: Icon(Icons.directions_walk)),
            ],
          ),
          title: Text('Demo (top)'),
        ),
        body: TabBarView(
          children: [

            // ページ1
            Center(
              child: RaisedButton(
                child: Text('ページ3へ'),
                color: Colors.orange,
                textColor: Colors.white,
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => Page3(),
                    ),
                  );
                },
              ),
            ),
            
            // ページ2
            Center(
              child: Text('ページ2'),
            ),
            
          ],
        ),
      ),
    );
  }
}

// ページ3（遷移するページ）
class Page3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ページ3"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('戻る'),
        ),
      ),
    );
  }
}

```

#### 下タブ版
```dart:main.dart
import 'package:flutter/material.dart';

// 最初にMyAppを実行する
void main() => runApp(MyApp());

// 初期設定
class MyApp extends StatelessWidget {
  static const String _title = 'Flutter Code Sample';
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: MyStatefulWidget(),
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
    );
  }
}

// 状態管理
class MyStatefulWidget extends StatefulWidget {
  MyStatefulWidget({Key key}) : super(key: key);
  @override
  // 更新
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  // 初期状態での選択は左のタブ
  int _selectedIndex = 0;
  
  static List<Widget> _widgetOptions = <Widget>[
    // ページ1の画面
    Page1(),
    // ページ2の画面
    Page2(),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('DEMO (bottom)'),
      ),
      body: Center(
        child: _widgetOptions.elementAt(_selectedIndex),
      ),

      // 下のナビゲーションボタン
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text('ページ1'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.directions_walk),
            title: Text('ページ2'),
          ),
        ],

        // 選択をナビゲーションアイコンに反映
        currentIndex: _selectedIndex,
        // 選択したときはオレンジ色にする
        selectedItemColor: Colors.amber[800],
        // タップできるように
        onTap: _onItemTapped,
      ),
    );
  }
}

// （ページ1）左ページ
class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        child: Text('ページ3へ'),
        color: Colors.orange,
        textColor: Colors.white,
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => NewPage(),
            ),
          );
        },
      ),
    );
  }
}

// （ページ2）右ページ
class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      'ページ2',
    );
  }
}


// ページ3（遷移するページ）
class NewPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ページ3"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('戻る'),
        ),
      ),
    );
  }
}
```
これでページ遷移の仕組みが完成しました！

## 最後に
Flutterの初学者の方々のお役に立てれば幸いです。もし誤り等ございましたら、コメントいただければと思います。