[Flutter] Tab内で遷移したPageをAndroid Hardware Back Buttonで閉じる

# 達成したい物

BackButtonを押した時に、インスタっぽい感じでページを閉じて、タブの遷移を行う事を目指します

1. タブ1内でページ遷移
- タブ2を開く
- タブ2内でページ遷移
- Android Hardware Back Buttonで直近開いた順に戻る

![Jun-29-2019 20-28-13.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59518/e9d108d6-236d-d193-60e1-d9229446849b.gif)

# どう実現するか

 工夫せずにタブで画面を構成して、タブ内でページ遷移を行い Back Buttonで戻るとアプリが閉じられると思います. `NavigatorState` に遷移状態が保存されているのですが、それを確認していないためにアプリがバックグラウンドに戻されます.
 `GlobalKey<NavigatorState>` を定義し、それを Navigatorに渡して Back Button が押された際に確認する事によって実現します.

# 重要な箇所

## GlobalKey<NavigatorState>の定義

`GlobalKey<NavigatorState>` を定義します. WillPopScope で呼ぶために必要です

```dart:GlobalKey<NavigatorState>
GlobalKey<NavigatorState> keyTab0 = GlobalKey<NavigatorState>();
GlobalKey<NavigatorState> keyTab1 = GlobalKey<NavigatorState>();
```

## WillPopScope

https://api.flutter.dev/flutter/widgets/WillPopScope-class.html
WillPopScopeでScaffoldが終了される際に処理を実行できます.
`onWillPop` が Future(false) を返せば、そのページは閉じられません(この記事ではアプリがバックグラウンドに戻らない事に利用する).
ここではTab内の NavigatorStateでページが開かれていれば閉じる処理をしています.

```dart:WillPopScope
    return WillPopScope(
      onWillPop: () async {
        if (_currentIndex == 0 && keyTab0.currentState.canPop()) {
          return !await keyTab0.currentState.maybePop();
        } else if (_currentIndex == 1 && keyTab1.currentState.canPop()) {
          return !await keyTab1.currentState.maybePop();
        }
        // ...
      }
```

## GlobalKey<NavigatorState> を Navigatorに渡す

```dart:Navigator()
                return Navigator(
                  key: keyTab0,
                  onGenerateRoute: (RouteSettings settings) {
                    return new MaterialPageRoute(
                      settings: settings,
                      builder: (BuildContext context) => Page1(),
                    );
                  },
                );
```


# code

```dart:main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

GlobalKey keyBottomNavigation = new GlobalKey();
GlobalKey<NavigatorState> keyTab0 = GlobalKey<NavigatorState>();
GlobalKey<NavigatorState> keyTab1 = GlobalKey<NavigatorState>();

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Tab demo',
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  HomeScreen({Key key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => HomeScreenState();
}

class HomeScreenState extends State<HomeScreen> {
  int _currentIndex = 0;

  void changeTab(int index) {
    setState(() {
      _currentIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        if (_currentIndex == 0 && keyTab0.currentState.canPop()) {
          return !await keyTab0.currentState.maybePop();
        } else if (_currentIndex == 1 && keyTab1.currentState.canPop()) {
          return !await keyTab1.currentState.maybePop();
        }
        if (keyTab0.currentState.canPop()) {
          CupertinoTabBar tab = keyBottomNavigation.currentWidget;
          tab.onTap(0);
          return Future.value(false);
        }
        if (keyTab1.currentState.canPop()) {
          CupertinoTabBar tab = keyBottomNavigation.currentWidget;
          tab.onTap(1);
          return Future.value(false);
        }
        return Future.value(true);
      },
      child: CupertinoTabScaffold(
        tabBuilder: (BuildContext context, int index) {
          return new CupertinoTabView(builder: (BuildContext context) {
            switch (index) {
              case 0:
                return Navigator(
                  key: keyTab0,
                  onGenerateRoute: (RouteSettings settings) {
                    return new MaterialPageRoute(
                      settings: settings,
                      builder: (BuildContext context) => Page1(),
                    );
                  },
                );
                break;
              case 1:
                return Navigator(
                  key: keyTab1,
                  onGenerateRoute: (RouteSettings settings) {
                    return new MaterialPageRoute(
                      settings: settings,
                      builder: (BuildContext context) => Page2(),
                    );
                  },
                );
                break;
              case 2:
                return Page3();
                break;
            }
          });
        },
        tabBar: CupertinoTabBar(
          key: keyBottomNavigation,
          onTap: (index) => changeTab(index),
          currentIndex: _currentIndex,
          items: const <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              title: Text('Home'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.bubble_chart),
              title: Text('Support'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.person),
              title: Text('Profile'),
            ),
          ],
        ),
      ),
    );
  }
}

class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Page 1"),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text("Page1"),
              RaisedButton(
                child: Text("Open Page1 in tab"),
                onPressed: () {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (BuildContext context) => Page1()));
                },
              ),
              RaisedButton(
                child: Text("Open Page2 in tab"),
                onPressed: () {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (BuildContext context) => Page2()));
                },
              ),
              RaisedButton(
                child: Text("Open in fullscreen"),
                onPressed: () {
                  Navigator.of(context, rootNavigator: true).push(
                      MaterialPageRoute(
                          builder: (BuildContext context) => Page1()));
                },
              ),
              RaisedButton(
                child: Text("Move to tab 2"),
                onPressed: () {
                  CupertinoTabBar tab = keyBottomNavigation.currentWidget;
                  tab.onTap(1);
                },
              ),
            ],
          ),
        ));
  }
}

class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Page 2"),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text("Page2"),
              RaisedButton(
                child: Text("Open Page1 in tab"),
                onPressed: () {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (BuildContext context) => Page1()));
                },
              ),
              RaisedButton(
                child: Text("Open Page2 in tab"),
                onPressed: () {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (BuildContext context) => Page2()));
                },
              ),
            ],
          ),
        ));
  }
}

class Page3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Page 3"),
      ),
      body: ListView(
        children: <Widget>[
          Container(
            color: Colors.blue[100],
            padding: EdgeInsets.symmetric(vertical: 150),
            child: Text("1"),
          ),
          Container(
            color: Colors.blue[200],
            padding: EdgeInsets.symmetric(vertical: 150),
            child: Text("2"),
          ),
          Container(
            color: Colors.blue[300],
            padding: EdgeInsets.symmetric(vertical: 150),
            child: Text("3"),
          ),
        ],
      ),
    );
  }
}

```

# 参考

- http://bizz84.github.io/2018/07/07/Multiple-Navigators-BottomNavigationBar.html