🫠

Flutter 3.19以上、popとpush時、画面がrebuildする問題

2024/04/16に公開

問題

Flutter 3.19.xでpopとpush時に画面がrebuildする

3.19.3にアップグレードした後、画面Aから画面Bにpushして画面Aに戻ると、画面Aのrebuildが発生する。
3.13に戻るとrebuildが発生しないという不思議な現象が発生した。
画面AにModalRoute.ofを使っていることがわかった。

final arguments = ModalRoute.of(context)?.settings.arguments;

問題関連のissues:
https://github.com/flutter/flutter/issues/146132

原因

直接の原因は3.19でModalRouteに対しての変更
#112567を対応するため
#130841
このissuesは、主にWEB上でTabキーを押してフォーカスを切り替えることに関連している問題です。

https://github.com/flutter/flutter/issues/112567
https://github.com/flutter/flutter/pull/130841

packages/flutter/lib/src/widgets/routes.dart

  
  void didChangeNext(Route<dynamic>? nextRoute) {
    super.didChangeNext(nextRoute);
    changedInternalState();
  }

  
  void didPopNext(Route<dynamic> nextRoute) {
    super.didPopNext(nextRoute);
    changedInternalState();
  }

  
  void changedInternalState() {
    super.changedInternalState();
    setState(() { /* internal state already changed */ });
    _modalBarrier.markNeedsBuild();
    // No need to mark dirty if this method is called during build phase.
    if (SchedulerBinding.instance.schedulerPhase != SchedulerPhase.persistentCallbacks) {
      setState(() { /* internal state already changed */ });
      _modalBarrier.markNeedsBuild();
    }
    _modalScope.maintainState = maintainState;
  }

didChangeNextdidPopNextは画面的pushpop対応している、このPRではchangedInternalStateを呼び出して、setStateが発生する。
setState内部の説明を省略、簡単に言えばModalRoute.ofを使うのが原因です。
ModalRoute.ofが使われると、内部的には現在のページのBuildContextが依存関係に追加される。

解決案

公式の対応を待つ、MediaQuery.sizeOf(context)みたいに ModalRoute.argumentsOf(context)にするらしい
ただこれではiOSの締切に間に合わない
https://github.com/flutter/flutter/issues/146132

/// replace ModalRoute.of(context)
class MyModalRoute {

  static ModalRoute<dynamic>? of(BuildContext context) {
    ModalRoute<dynamic>? route;
    context.visitAncestorElements((element) {
      if(element.widget.runtimeType.toString() == '_ModalScopeStatus') {
        dynamic widget = element.widget;
        route = widget.route as ModalRoute;
        return false;
      }
      return true;
    });
    return route;
  }
}

カスタムしたModalRouteを使う
https://github.com/flutter/flutter/pull/145389#issuecomment-2049169033

#130841の対応の一部をコメントアウトする

packages/flutter/lib/src/widgets/routes.dart
+  // @override
+  // void didChangeNext(Route<dynamic>? nextRoute) {
+  //   super.didChangeNext(nextRoute);
+  //   changedInternalState();
+  // }
+
+  // @override
+  // void didPopNext(Route<dynamic> nextRoute) {
+  //   super.didPopNext(nextRoute);
+  //   changedInternalState();
+  // }

この対応自体はWEB上でTabキーを押してフォーカスを切り替える問題ので、アプリとしてほぼ影響ないです。
一時の対応として、コメントアウトして、公式の対応を待った方がいいと思う。

# Enter your local flutter directory
cd /Users/lxf/fvm/versions/3.19.3

# Download patch
curl -O https://raw.githubusercontent.com/LinXunFeng/flutter_assets/main/patch/01_rollbak_3_19_routes_change/0001-Roll-back-changes-to-routes.dart.patch

# Apply patch
git apply 0001-Roll-back-changes-to-routes.dart.patch

参考:
https://github.com/flutter/flutter/issues/146132
https://github.com/flutter/flutter/pull/145389

Discussion