14
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-06-29

達成したい物

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

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

Jun-29-2019 20-28-13.gif

どう実現するか

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

重要な箇所

GlobalKeyの定義

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

GlobalKey
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でページが開かれていれば閉じる処理をしています.

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 を Navigatorに渡す

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

code

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"),
          ),
        ],
      ),
    );
  }
}

参考

14
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?