Flutterの画面遷移をわかりやすく解説
はじめに
Flutterでアプリを作るとき、複数の画面に切り替える機会は多いですよね。 たとえばホーム画面から詳細画面へ移動したり、ログイン画面からメイン画面に進んだりする必要があります。 こうした画面遷移の仕組みをしっかり押さえることで、アプリの構造を整理しやすくなるでしょう。 また、ユーザーが直観的に操作しやすいアプリを作るためにも、画面遷移は重要なテーマではないでしょうか。 ここではFlutterの画面遷移に注目し、基本的なNavigatorクラスの使い方から名前付きルートの設定、実務における活用シーンまでを順に解説します。
Flutterの画面遷移の概要
Flutterでは、Navigator というクラスが画面遷移の中心になります。 AndroidやiOSの世界で考えると、画面ごとにActivityやViewControllerが切り替わるイメージがあるかもしれません。 しかしFlutterは単一のWidgetツリーの中で画面を差し替える仕組みをとるため、ネイティブとは少し違う考え方で画面を管理します。
画面遷移とルートの関係
画面を切り替えるたびに、新しいWidget(ページ)を積み重ねるように管理しているのが特徴です。 これはルート(Route)と呼ばれる概念で、現在の画面や過去の画面を「スタック」のように積んでいきます。 このため、ユーザーが戻るボタンを押すと一つ前のルートに戻るという操作が可能になります。 実務では、注文履歴画面や詳細画面などを次々に開くようなシーンがあるかもしれません。 そんな場面でNavigatorクラスを使い、画面をスムーズに切り替えていくわけです。
画面遷移とユーザー体験
複数の機能を持つアプリを作ると、10画面以上に分割されることも珍しくありません。 ユーザーが求める情報へ素早くアクセスできるようにするため、各画面の遷移設計は慎重に行いたいところです。 どこからどこへ移動できるのかを明確にしておくと、実装上も管理しやすくなります。
Navigatorクラスを使った画面遷移
pushとpop
Flutterの画面遷移でまず覚えておきたいのがNavigator.push と Navigator.pop です。 新しい画面へ遷移するときはpush、戻るときはpopを呼び出します。 たとえば以下のコードは、ボタンを押すとSecondPageという画面へ移動するサンプルです。
import 'package:flutter/material.dart'; class FirstPage extends StatelessWidget { const FirstPage({Key? key}) : super(key: key); Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('First Page'), ), body: Center( child: ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => SecondPage()), ); }, child: Text('画面遷移'), ), ), ); } } class SecondPage extends StatelessWidget { const SecondPage({Key? key}) : super(key: key); Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Second Page'), ), body: Center( child: ElevatedButton( onPressed: () { Navigator.pop(context); }, child: Text('戻る'), ), ), ); } }
SecondPageへ移動したいときはNavigator.push。 戻りたいときはNavigator.pop。 これだけで2つの画面を簡単に切り替えることができます。
pushReplacementとpushAndRemoveUntil
新しい画面に移ったとき、前の画面を完全に置き換えたい場合もあるかもしれません。 そんなときにはpushReplacement を使います。 また、スタックに積み重なった画面を一気に削除したい場合は、pushAndRemoveUntil が便利です。 たとえば、ログイン画面からホーム画面に進み、その後はログイン画面に戻らないようにする場合などに活用できます。
ElevatedButton( onPressed: () { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => HomePage()), ); }, child: Text('ログインしてホームへ'), );
ログイン成功後はユーザーが戻るボタンを押してもログイン画面に戻らないようにしたいとき、pushReplacementを使うと便利ですね。
名前付きルートを使った画面遷移
routesプロパティの設定
シンプルなアプリならpushとpopだけでも十分かもしれません。 しかし画面数が増えてくると、どの画面を呼び出すのかをコード中に毎回書くのは大変ですよね。 そんなときに役立つのが名前付きルートです。
以下のようにMaterialAppのroutesプロパティに、ルート名とWidgetを紐づけることができます。
import 'package:flutter/material.dart'; void main() { runApp(MaterialApp( initialRoute: '/', routes: { '/': (context) => FirstPage(), '/second': (context) => SecondPage(), }, )); } class FirstPage extends StatelessWidget { const FirstPage({Key? key}) : super(key: key); Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('First Page')), body: Center( child: ElevatedButton( onPressed: () { Navigator.pushNamed(context, '/second'); }, child: Text('名前付きルートで画面遷移'), ), ), ); } } class SecondPage extends StatelessWidget { const SecondPage({Key? key}) : super(key: key); Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Second Page')), body: Center( child: ElevatedButton( onPressed: () { Navigator.pop(context); }, child: Text('戻る'), ), ), ); } }
このようにすることで、文字列(例: '/second'
)を指定して画面を切り替えられます。
画面数が多いプロジェクトでは、この方法だとコードの見通しが良くなるでしょう。
onGenerateRouteを使った柔軟な管理
プロジェクトによっては、画面によって処理を分けたいケースもありますよね。 たとえば特定のパラメータを渡すかどうかで画面を出し分けたい、といった場面です。 その場合はonGenerateRoute でルート生成をカスタマイズできます。
MaterialApp( onGenerateRoute: (settings) { if (settings.name == '/third') { final args = settings.arguments as String?; return MaterialPageRoute( builder: (context) => ThirdPage(data: args), ); } // それ以外のルートへの対応 return MaterialPageRoute( builder: (context) => FirstPage(), ); }, );
このようにすれば、遷移先の画面へ特定のパラメータを受け渡したり、状況に応じて異なるWidgetを出すなどの柔軟な管理が可能になります。
Navigator 2.0を活用した画面遷移
Flutterには、より宣言的なアプローチで画面遷移を管理できるNavigator 2.0という仕組みも存在します。 主に中〜大規模アプリケーションで、状態管理や画面管理を一括して扱いたい場合に注目されがちです。 Navigator 2.0を使うと、URLベースのアプリにも近い感覚で遷移をコントロールできます。
しかし初学者にとってはコードの記述量が多く、理解がやや難しいかもしれません。 実務で複雑な認証フローや深い画面階層を扱う必要があるときに、導入を検討してみると良いでしょう。 たとえば、Webとモバイルの両方で似たような画面構成を提供するアプリを作るときなどに、Navigator 2.0の設計思想が役立ちます。
実務での活用例
ユーザ認証フローへの応用
画面遷移は認証フローでも多用されます。 ログイン画面からログイン成功後にホーム画面へ進むとき、先ほど紹介したpushReplacementを使えば戻るボタンでログイン画面に逆戻りする心配はありません。 またユーザーがまだログインしていない場合、特定の画面に遷移する前にログイン画面を挟むなど、複雑なフローを構築することもできます。
ここでは、ログインを想定したサンプルコードを少しだけ紹介します。 以下のようにユーザー情報が存在するかどうかで画面を振り分けることができます。
if (user == null) { Navigator.pushReplacementNamed(context, '/login'); } else { Navigator.pushReplacementNamed(context, '/home'); }
画面遷移とユーザー情報の管理を組み合わせると、より実践的なアプリに近づくでしょう。
アプリの設定画面への遷移
どのアプリでも「設定」画面を設けることが多いと思います。 設定画面には通知やテーマ変更などの項目を置くことが多いですよね。 こうした部分を別の画面に切り出しておくと、メインの画面との責務分担がはっきりするのではないでしょうか。
設定画面を名前付きルートで実装し、メイン画面でボタンを押すと/settings
へ移動するイメージはシンプルで管理しやすいです。
もし設定画面からさらに別の詳細設定画面へ進む場合も、名前付きルートを使い回せば迷わずに済みます。
画面遷移を効率化するポイント
適切なルーティング設計
画面遷移の設計を進めるうえで、いきなりコードを書くのではなく、画面遷移図やルーティング一覧をまとめておくと便利です。 特に大きなアプリでは、あとから「どこがどのルートを使っているのか」がわからなくなりがちです。 事前に一覧を作っておくと、自分のコードをよりスムーズに読み返せるようになります。
画面遷移のフローを簡単に紙やホワイトボードに書いてみるだけでも、後々の混乱を減らしやすくなります。
大規模アプリケーションでの注意点
画面の数が10や20を超えてくると、各画面で使われるルート名やWidgetの管理が煩雑になりがちです。 そこで、Navigator 2.0や専用のルーティングパッケージ(たとえば「go_router」など)を採用するケースがあります。 こうした仕組みを使うと、ルート設定を1箇所に集約できるなどのメリットが得られます。 ただし、導入に伴いコード量が増えることもあるので、プロジェクトの規模やチームメンバーの知識レベルを考慮して選ぶことが大切です。
まとめ
Flutterで画面遷移を実装する方法を見てきました。 Navigatorクラスのpushとpop、pushReplacementといった基本的な操作を覚えておけば、まずはシンプルなアプリなら問題なく作れるでしょう。 名前付きルートを使うと画面の増加にも対応しやすく、さらにNavigator 2.0や外部パッケージなどを導入すると、大規模アプリでも管理しやすい仕組みを作れます。 実務ではユーザ認証フローや設定画面の実装など、多様なシーンで画面遷移の考え方が重要になります。 ぜひ自分のアプリで複数の画面を扱うときには、今回の内容を活用してみてください。