💨

Flutter 3.16からWillPopScopeがDeprecatedになりました

2023/11/17に公開

'WillPopScope.new' is deprecated

Flutterを3.16.0にアップデートしたら以下の警告が出るようになりましたね。

info: 'WillPopScope.new' is deprecated and shouldn't be used. Use PopScope instead. This feature was deprecated after v3.12.0-1.0.pre. 

こちらへの対処法を記載しておきます。

そもそもWillPopScopeとは

このあたりが参考になるかと思います。
https://qiita.com/isekiryu/items/bfc3915372553ec5102a

簡単に言うと、Androidで言うところの戻るボタンを押した時の動作を変更できるものでした。
onWillPopに指定する関数の返却値がfalseの場合は戻るボタンが効かなくなります。

元々のコード

例えば以下のようなコードの場合

    WillPopScope(
      onWillPop: () async {
        return false;
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: const Text('some widgets'),
      ),
    );

PopScopeを使う

上のコードであれば以下のコードにすればいいのでシンプルになります

-   WillPopScope(
-     onWillPop: () async {
-       return false;
-     },
+   PopScope(
+     canPop: false,
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: const Text('some widgets'),
      ),
    );

厳密には動作が違う

さて、ほとんどの使い方では問題にならないと思いますが、
厳密には動作やできることが違うので注意が必要です。

問題点 確認ダイアログ出し方が一癖ある

編集画面などで、戻るキーを押された場合、
「保存されていませんが、戻っても大丈夫ですか?」といった内容のダイアログを出している場合、
このPopScopeは普通には使えません。
canPopがbool値のみ受け付けるからです。

    WillPopScope(
      onWillPop: () async {
        final bool? shouldPop = await _showBackDialog();
        return shouldPop ?? false;
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: const Text('some widgets'),
      ),
    );

この場合、はこのようにします

    PopScope(
      canPop: false, // 戻るキーの動作で戻ることを一旦防ぐ
      onPopInvoked: (didPop) async {
        if (didPop) {
          return;
        }
        final NavigatorState navigator = Navigator.of(context);
        final bool? shouldPop = await _showBackDialog();// ダイアログで戻るか確認
        if (shouldPop ?? false) {
          navigator.pop(); // 戻るを選択した場合のみpopを明示的に呼ぶ
        }
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: const Text('some widgets'),
      ),
    );
  }

ただしこの場合、popでアプリを終了しようとした場合、画面が真っ黒になるという問題があります。
ですので、アプリを終了する場合SystemNavigator.pop();を呼びます。

 PopScope(
      canPop: false, // 戻るキーの動作で戻ることを一旦防ぐ
      onPopInvoked: (didPop) async {
        if (didPop) {
          return;
        }
        final NavigatorState navigator = Navigator.of(context);
        final bool? shouldPop = await _showBackDialog(); // ダイアログで戻るか確認
        if (shouldPop ?? false) {
          if (navigator.canPop()) {
            navigator.pop(); // 戻るを選択した場合のみpopを明示的に呼ぶ
          } else {
            SystemNavigator.pop(); // アプリを閉じる
          }
        }
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: const Text('some widgets'),
      ),
    );

まとめ

WillPopScopeからPopScopeになることでほとんどの場合シンプルに書けそうですが、
確認ダイアログを挟む場合は面倒ですね。
他にも対処が必要な場合があると思いますが、その場合は公式のMigration Guideをご覧ください。
参考文献として貼っておきます。

参考文献
https://docs.flutter.dev/release/breaking-changes/android-predictive-back#migrating-from-willpopscope-to-popscope

最後に(宣伝)

友人と個人開発でゲーミフィケーション記録アプリ「HibaQuest」を作っています。
https://hiba.quest

このアプリでもWillPopScopeは多用していました。

Discussion