Navigator.pop() メソッドを使って、新しい画面から元の画面にデータを返す方法について触れています。
(具体的な例として、ユーザに2つの選択肢を提示する新しい画面を開き、ユーザが選択した内容を元の画面に返すシナリオが紹介されています。)
1.ホーム画面の定義
最初に、アプリのメイン画面(ホーム画面)を定義します。(この画面には、新しい選択画面を起動するボタンが必要です。)
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
//Scaffold で基本的な画面構成を作成
return Scaffold(
appBar: AppBar(
title: const Text('Returning Data Demo'), //アプリバーのタイトル
),
body: const Center(
//ボディ部分に SelectionButton ウィジェットを配置
child: SelectionButton(),
),
);
}
}
2.選択画面を起動するボタンの追加
ホーム画面のボタンを押すと選択画面(SelectionScreen)が起動されます。(選択画面では、2つの選択肢をユーザに提示します。)
class SelectionButton extends StatefulWidget {
const SelectionButton({super.key});
@override
State<SelectionButton> createState() => _SelectionButtonState();
}
class _SelectionButtonState extends State<SelectionButton> {
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
//ボタンが押された時に新しい画面(SelectionScreen)へ遷移
_navigateAndDisplaySelection(context);
},
//ボタンのラベル
child: const Text('Pick an option, any option!'),
);
}
Future<void> _navigateAndDisplaySelection(BuildContext context) async {
//Navigator.push で新しい画面を開き、結果を待つ
final result = await Navigator.push(
context,
//SelectionScreen に遷移
MaterialPageRoute(builder: (context) => const SelectionScreen()),
);
//context が有効か確認
if (!context.mounted) return;
//戻ってきたデータを Snackbar で表示
ScaffoldMessenger.of(context)
//以前の Snackbar を削除
..removeCurrentSnackBar()
//結果を Snackbar で表示
..showSnackBar(SnackBar(content: Text('$result')));
}
}
3.(2つの)ボタンによる選択画面の表示
選択画面の2つのボタンのうち、どちらかのボタンを押すと、その選択内容を元のホーム画面に返す処理が行われます。
class SelectionScreen extends StatelessWidget {
const SelectionScreen({super.key});
@override
Widget build(BuildContext context) {
//SelectionScreen の内容を Scaffold で設定
return Scaffold(
appBar: AppBar(
title: const Text('Pick an option'), //選択画面のタイトル
),
body: Center(
child: Column(
//中央に揃えられた列
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () {
//"Yep!" を返して画面を閉じる
Navigator.pop(context, 'Yep!');
},
child: const Text('Yep!'), //"Yep!" ボタン
),
),
Padding(
padding: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () {
//"Nope." を返して画面を閉じる
Navigator.pop(context, 'Nope.');
},
child: const Text('Nope.'), //"Nope." ボタン
),
)
],
),
),
);
}
}
4.ボタンによる選択画面のクローズ
次に、両方のボタンの onPressed() コールバックを更新します。
最初の画面にデータを返すには、オプションで result と呼ばれる 2 番目の引数を受け入れる Navigator.pop() メソッドを使用します。結果は全て、SelectionButton 内の Future に返される。
ElevatedButton(
onPressed: () {
//画面を閉じ "Yep!" を戻す
Navigator.pop(context, 'Yep!');
},
child: const Text('Yep!'),
)
ElevatedButton(
onPressed: () {
//画面を閉じ "Nope!" を戻す
Navigator.pop(context, 'Nope.');
},
child: const Text('Nope.'),
)
5.ホーム画面のスナックバーからの選択
ホーム画面では、ユーザが選んだ選択肢を受け取り、それに基づいて何らかのアクションを実行できます。
(ここでは、SelectionButton の _navigateAndDisplaySelection() メソッドを使用して、結果を表示するスナックバーを表示します。)
Future<void> _navigateAndDisplaySelection(BuildContext context) async {
//Navigator.push で新しい画面を開き、結果を待つ
final result = await Navigator.push(
context,
MaterialPageRoute(
//SelectionScreen に遷移
builder: (context) => const SelectionScreen()),
);
//context が有効か確認
if (!context.mounted) return;
//戻ってきたデータを Snackbar で表示
ScaffoldMessenger.of(context)
//以前の Snackbar を削除
..removeCurrentSnackBar()
//結果を Snackbar で表示
..showSnackBar(SnackBar(content: Text('$result')));
}
}
6.まとめ
この方法は、異なる画面間でデータをやり取りする際に非常に便利です。
(例えば、ユーザーの入力結果や選択内容を別の画面に渡し、それに基づいて次のアクションを決定するアプリで使われます。)
簡単な実装の流れとしては、ホーム画面から新しい画面を Navigator.push() で表示し、その画面から Navigator.pop() でデータを返すことで、元の画面に戻りつつデータを渡すことが可能です。
7.インタラクティブな例
この例では、新しい画面でユーザーが選択したデータ(”Yep!”や”Nope.”)を元の画面に返し、元の画面でその選択内容に応じて動作(スナックバーの表示など)を行っています。
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
title: 'Returning Data',
home: HomeScreen(), //ホーム画面として HomeScreen を設定
),
);
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
//Scaffold で基本的な画面構成を作成
return Scaffold(
appBar: AppBar(
title: const Text('Returning Data Demo'), //アプリバーのタイトル
),
body: const Center(
//ボディ部分に SelectionButton ウィジェットを配置
child: SelectionButton(),
),
);
}
}
class SelectionButton extends StatefulWidget {
const SelectionButton({super.key});
@override
State<SelectionButton> createState() => _SelectionButtonState();
}
class _SelectionButtonState extends State<SelectionButton> {
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
//ボタンが押された時に新しい画面(SelectionScreen)へ遷移
_navigateAndDisplaySelection(context);
},
child: const Text('Pick an option, any option!'), //ボタンのラベル
);
}
Future<void> _navigateAndDisplaySelection(BuildContext context) async {
//Navigator.push で新しい画面を開き、結果を待つ
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SelectionScreen()), //SelectionScreen に遷移
);
if (!context.mounted) return; //context が有効か確認
//戻ってきたデータを Snackbar で表示
ScaffoldMessenger.of(context)
..removeCurrentSnackBar() //以前の Snackbar を削除
..showSnackBar(SnackBar(content: Text('$result'))); //結果を Snackbar で表示
}
}
class SelectionScreen extends StatelessWidget {
const SelectionScreen({super.key});
@override
Widget build(BuildContext context) {
//SelectionScreen の内容を Scaffold で設定
return Scaffold(
appBar: AppBar(
title: const Text('Pick an option'), //選択画面のタイトル
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center, //中央に揃えられた列
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () {
//"Yep!" を返して画面を閉じる
Navigator.pop(context, 'Yep!');
},
child: const Text('Yep!'), //"Yep!" ボタン
),
),
Padding(
padding: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () {
//"Nope." を返して画面を閉じる
Navigator.pop(context, 'Nope.');
},
child: const Text('Nope.'), //"Nope." ボタン
),
)
],
),
),
);
}
}