[読書会]画面からデータを戻す

 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つのボタンのうち、どちらかのボタンを押すと、その選択内容を元のホーム画面に返す処理が行われます。

選択画面の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 に返される。

画面を閉じ ‘ Yep ‘ を戻す:
ElevatedButton(
  onPressed: () {
    //画面を閉じ "Yep!" を戻す
    Navigator.pop(context, 'Yep!');
  },
  child: const Text('Yep!'),
)
画面を閉じ ‘ Nope ‘ を戻す:
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.”)を元の画面に返し、元の画面でその選択内容に応じて動作(スナックバーの表示など)を行っています。

この例では、新しい画面でユーザーが選択したデータ(”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." ボタン
              ),
            )
          ],
        ),
      ),
    );
  }
}

コメントを残す