達成したい物
BackButtonを押した時に、インスタっぽい感じでページを閉じて、タブの遷移を行う事を目指します
- タブ1内でページ遷移
- タブ2を開く
- タブ2内でページ遷移
- Android Hardware Back Buttonで直近開いた順に戻る
どう実現するか
工夫せずにタブで画面を構成して、タブ内でページ遷移を行い Back Buttonで戻るとアプリが閉じられると思います. NavigatorState
に遷移状態が保存されているのですが、それを確認していないためにアプリがバックグラウンドに戻されます.
GlobalKey<NavigatorState>
を定義し、それを Navigatorに渡して Back Button が押された際に確認する事によって実現します.
重要な箇所
GlobalKeyの定義
GlobalKey<NavigatorState>
を定義します. WillPopScope で呼ぶために必要です
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でページが開かれていれば閉じる処理をしています.
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に渡す
return Navigator(
key: keyTab0,
onGenerateRoute: (RouteSettings settings) {
return new MaterialPageRoute(
settings: settings,
builder: (BuildContext context) => Page1(),
);
},
);
code
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"),
),
],
),
);
}
}