概要
Flutterを使ってアプリを開発しています。アプリ開発で画面の移動にはナビゲーションを使います。今回のブログポストではFlutterでスタックナビゲーションを使って画面を移動する方法について説明します。
このブログポストで紹介するソースコードは下記のリンクで確認できます。
Stack
スタックナビゲーションは画面の上に画面を表示する方式で画面を移動します。画面の上に画面を表示する時はpush
を、上に表示された画面を削除する時にはpop
を使います。
それでは例題を見てスタックナビゲーションを理解してみましょう。次のコマンドを使ってスタックナビゲーションのためプロジェクトを生成します。
flutter create my_app
cd stack
次はmain.dart
ファイルを開いて下記のように修正してスタックナビゲーションを使ってみます。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Navigator'),
),
body: Center(
child: ElevatedButton(
child: Text('Second Screen'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => SecondScreen(),
),
);
},
),
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Second Screen'),
),
body: Center(
child: ElevatedButton(
child: Text('Home Screen'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}
そしたらソースコードを一つづつ詳しくみてみましょう。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
ここまでは基本的アプリを生成することと同じです。MaterialApp
を使うと、アプリが実行された時、一番最初表示される画面をhome
パラメータに設定することができます。ここに私はHome
ウィジェットを設定しました。
次はスタックナビゲーションを使うため二つのStatelessWidget
を生成しました。一つはMaterialApp
のhome
に設定したHome
ウィジェットとスタックナビゲーションを使ってHomeウィジェット上に表示するSecondScreen
ウィジェットを生成しました。
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: ...,
body: Center(
child: ElevatedButton(
child: Text('Second Screen'),
onPressed: () {...},
),
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:...,
body: Center(
child: ElevatedButton(
child: Text('Home Screen'),
onPressed: () {...},
),
),
);
}
}
二つのウィジェットはScaffold
を使って基本的同じ構造を持っています。画面の真ん中にElevatedButton
ウィジェットを使ってボタンを表示しました。そして、各ボタンを押した時、スタックナビゲーションを使って画面の移動を実装しました。
次は各ボタンを押した時、スタックナビゲーションを呼び出す部分をみてみましょう。一旦Home
画面からSecondScreen
に移動するコードをみてみましょう。
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(),
),
);
},
スタックナビゲーションで、新しい画面を画面の上に表示するためにはNavigator
ウィジェットのpush
関数を使う必要があります。push
関数をコールする時、ウィジェットツーリの位置情報を持ってるcontext
とMaterialPageRoute
のbuilderを使って画面の上に表示するウィジェットをパラメータで設定します。
このようにコードを作成するとHome
ウィジェットに表示されたボタンを押すと、Home画面の上に、SecondScreen
ウィジェットが表示されます。
次はSecondScreen
でHome
画面に戻るため、SecondScreen
を削除するコードを確認してみましょう。
onPressed: () {
Navigator.pop(context);
},
Navigator
ウィジェットのpop
をコールすると、現在表示された画面が削除されるし、現在表示された画面の下にあった画面が表示されます。pop
関数をコールする時には現在位置情報を持ってるcontext
を渡さなきゃならないです。
Named routes
Flutterで複数画面を管理する時、Named routes
を使います。
次はNamed routes
を使ってスタックナビゲーションを使ってみましょう。まず、main.dart
ファイルを次のように修正します。
import 'package:flutter/material.dart';
import 'ScreenA.dart';
import 'ScreenB.dart';
import 'ScreenC.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
debugShowCheckedModeBanner: false,
initialRoute: 'ScreenA',
routes: {
'ScreenA': (context) => ScreenA(),
'ScreenB': (context) => ScreenB(),
'ScreenC': (context) => ScreenC(),
},
);
}
}
Named routes
を使うためにはMaterialApp
でroutes
に画面の名前と名前に合う画面のウィジェットを宣言します。また、home
パラメータの代わりでinitialRoute
を使ってアプリが実行された後最初表示されるウィジェットを定義します。
今回の例題では各画面ウィジェットをlib/ScreenA.dart
、lib/ScreenB.dart
、lib/ScreenC.dart
ファイルを生成した後、そのファイルにコードを作成しました。
このように生成したウィジェットをmain.dart
ファイルに次のようにインポートしました。
import 'ScreenA.dart';
import 'ScreenB.dart';
import 'ScreenC.dart';
各ファイルのコードをもっと詳しくみてみましょう。ScreenA.dart
ファイルを内容は次のようです。
import 'package:flutter/material.dart';
class ScreenA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen A'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Screen B'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenB');
},
),
ElevatedButton(
child: Text('Second C'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenC');
},
),
],
),
),
);
}
}
StatelessWidget
を継承して、Scafford
ウィジェットを使った簡単な画面のウィジェットです。上で説明したソースコードと同じ構造なので説明は省略します。ここではNamed routes
を使ったスタックナビゲーションを使う部分だけ説明します。
onPressed: () {
Navigator.pushNamed(context, 'ScreenB');
},
Named routes
を使ってスタックナビゲーションを使うためにはNavigator
ウィジェットのpushNamed
関数を使います。この時ウィジェットツーリの情報を持ってるcontenxt
と移動したい画面の名前をパラメータで渡します。
ScreenB.dart
とScreenC.dart
は同じ構造を持ってますので、コードだけ共有します。
- ScreenB.dart
import 'package:flutter/material.dart';
class ScreenB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen B'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Back'),
onPressed: () {
Navigator.pop(context);
},
),
ElevatedButton(
child: Text('Second A'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenA');
},
),
ElevatedButton(
child: Text('Second C'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenC');
},
),
],
),
),
);
}
}
- ScreenC.dart
import 'package:flutter/material.dart';
class ScreenC extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen C'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Back'),
onPressed: () {
Navigator.pop(context);
},
),
ElevatedButton(
child: Text('Second A'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenA');
},
),
ElevatedButton(
child: Text('Second B'),
onPressed: () {
Navigator.pushNamed(context, 'ScreenB');
},
),
],
),
),
);
}
}
Named routesを使うともっと簡単に画面の移動することができるし、色んな画面を管理するのに適しています。
popUntil
スタックナビゲーションを使うと、たくさんの画面がたまることがあります。このように溜まった画面を一回で戻るためにはpopUntil
を使います。
popUntil
の使い方を確認するため、ScreenC.dart
ファイルを次のように修正します。
import 'package:flutter/material.dart';
class ScreenC extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen C'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('Home'),
onPressed: () {
Navigator.popUntil(context, (route) => route.isFirst);
},
),
],
),
),
);
}
}
画面を最初の画面であるScreenA
まで戻るため、ScreenC
では次のようなコードを使いました。
onPressed: () {
Navigator.popUntil(context, (route) => route.isFirst);
},
最初の画面に戻るためNavigator
のpopUntil
を使いましたし、route.isFirst
を設定しました。このようにpopUntil
を使うと最初の画面に戻ることができます。または、下記のように使うと特定した画面まで戻ることもできます。
onPressed: () {
Navigator.popUntil(context, ModalRoute.withName('ScreenA'));
},
上のように使うと、Named routes
に定義した名前を使ってその画面まで戻ることができます。
automaticallyImplyLeading
Flutterでスタックナビゲーションを使って画面を移動すると、特に設定をしなくても左上に以前の画面に戻れるボタンが生成されることが確認できます。
普通はこの機能を使うので、特に問題ないですが、時々この機能を使いたくない時もあります。この時、使えることがautomaticallyImplyLeading
オプションです。
次のようにAppBarウィジェとのautomaticallyImplyLeading
オプションを使うと、自動で生成される戻るボタンを消すことができます。
AppBar(
...
automaticallyImplyLeading: false,
)
Swipe back
Flutterでスタックナビゲーションを使うと、基本的スワイプで戻る(Swipe back)機能を使って以前の画面に戻ることができます。
もし、この機能を使いたくない場合、次のようにWillPopScope
ウィジェットのonWillPop
を使ってスワイプで戻る機能を無効化することができます。
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
...,
),
);
}
上のようにスタックナビゲーションを使って表示される画面をWillPopScope
ウィジェットにつつみむ後、onWillPop
パラメータをfalse
で設定したら、スワイプで戻る機能を無効化することができます。
フォーカス
ナビゲーションを使う時、現在の画面(A)から他の画面(B)を表示した後、また現在の画面(A)に戻る時、特定な関数をコールしたり何かの動作を実行したい時があります。この時、次のようにpushNamed
関数を使って解決することができます。
Navigator.pushNamed(context, 'B').then((value) {
// Do something after widget is focused(visible).
});
このようにpushNamed
関数使って次のページへ移動する時、then
を使って次のページがpop
された後、特定な動作を実行できるコールバック(Callback)関数を登録することができます。
フルモーダル
基本的スタックナビゲーションは右から左にスライドしながら画面に表示されます。次のコードを使うとスタックナビゲーションを下から上にスライドしながら表示させることができます。
import 'package:flutter/cupertino.dart';
...
Navigator.push(
context,
CupertinoPageRoute(
fullscreenDialog: true,
builder: (_) => SecondScreen(),
),
);
完了
これでFlutterでスタックナビゲーションを使う方法についてみてみました。スタックナビゲーションはアプリの画面の移動に一番多く使えるので、今回よく覚えておきましょう。
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Deku
が開発したアプリを使ってみてください。Deku
が開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。