Revert "Adds a parent scope TraversalEdgeBehavior and fixes modal rou… (#134565)

Reverts
https://github.com/flutter/flutter/commit/0f3bd90d9b108904cc0390e899c2b3c6ee2e76e0
to unblock the flutter roll.

Co-authored-by: chunhtai <47866232+chunhtai@users.noreply.github.com>
diff --git a/packages/flutter/lib/src/widgets/app.dart b/packages/flutter/lib/src/widgets/app.dart
index 7a52463..d8d649c 100644
--- a/packages/flutter/lib/src/widgets/app.dart
+++ b/packages/flutter/lib/src/widgets/app.dart
@@ -1685,7 +1685,6 @@
             },
           onUnknownRoute: _onUnknownRoute,
           observers: widget.navigatorObservers!,
-          routeTraversalEdgeBehavior: kIsWeb ? TraversalEdgeBehavior.leaveFlutterView : TraversalEdgeBehavior.parentScope,
           reportsRouteUpdateToEngine: true,
         ),
       );
diff --git a/packages/flutter/lib/src/widgets/focus_traversal.dart b/packages/flutter/lib/src/widgets/focus_traversal.dart
index f67080c..203e132 100644
--- a/packages/flutter/lib/src/widgets/focus_traversal.dart
+++ b/packages/flutter/lib/src/widgets/focus_traversal.dart
@@ -120,16 +120,6 @@
   /// address bar, escape an `iframe`, or focus on HTML elements other than
   /// those managed by Flutter.
   leaveFlutterView,
-
-  /// Allows focus to traverse up to parent scope.
-  ///
-  /// When reaching the edge of the current scope, requesting the next focus
-  /// will look up to the parent scope of the current scope and focus the focus
-  /// node next to the current scope.
-  ///
-  /// If there is no parent scope above the current scope, fallback to
-  /// [closedLoop] behavior.
-  parentScope,
 }
 
 /// Determines how focusable widgets are traversed within a [FocusTraversalGroup].
@@ -196,60 +186,6 @@
     );
   }
 
-  /// Request focus on a focus node as a result of a tab traversal.
-  ///
-  /// If the `node` is a [FocusScopeNode], this method will recursively find
-  /// the next focus from its descendants until it find a regular [FocusNode].
-  ///
-  /// Returns true if this method focused a new focus node.
-  bool _requestTabTraversalFocus(
-    FocusNode node, {
-    ScrollPositionAlignmentPolicy? alignmentPolicy,
-    double? alignment,
-    Duration? duration,
-    Curve? curve,
-    required bool forward,
-  }) {
-    if (node is FocusScopeNode) {
-      if (node.focusedChild != null) {
-        // Can't stop here as the `focusedChild` may be a focus scope node
-        // without a first focus. The first focus will be picked in the
-        // next iteration.
-        return _requestTabTraversalFocus(
-          node.focusedChild!,
-          alignmentPolicy: alignmentPolicy,
-          alignment: alignment,
-          duration: duration,
-          curve: curve,
-          forward: forward,
-        );
-      }
-      final List<FocusNode> sortedChildren = _sortAllDescendants(node, node);
-      if (sortedChildren.isNotEmpty) {
-        _requestTabTraversalFocus(
-          forward ? sortedChildren.first : sortedChildren.last,
-          alignmentPolicy: alignmentPolicy,
-          alignment: alignment,
-          duration: duration,
-          curve: curve,
-          forward: forward,
-        );
-        // Regardless if _requestTabTraversalFocus return true or false, a first
-        // focus has been picked.
-        return true;
-      }
-    }
-    final bool nodeHadPrimaryFocus = node.hasPrimaryFocus;
-    requestFocusCallback(
-      node,
-      alignmentPolicy: alignmentPolicy,
-      alignment: alignment,
-      duration: duration,
-      curve: curve,
-    );
-    return !nodeHadPrimaryFocus;
-  }
-
   /// Returns the node that should receive focus if focus is traversing
   /// forwards, and there is no current focus.
   ///
@@ -416,21 +352,10 @@
   @protected
   Iterable<FocusNode> sortDescendants(Iterable<FocusNode> descendants, FocusNode currentNode);
 
-  static Iterable<FocusNode> _getDescendantsWithoutExpandingScope(FocusNode node) {
-    final List<FocusNode> result = <FocusNode>[];
-    for (final FocusNode child in node.children) {
-      if (child is! FocusScopeNode) {
-        result.addAll(_getDescendantsWithoutExpandingScope(child));
-      }
-      result.add(child);
-    }
-    return result;
-  }
-
-  static Map<FocusNode?, _FocusTraversalGroupInfo> _findGroups(FocusScopeNode scope, _FocusTraversalGroupNode? scopeGroupNode, FocusNode currentNode) {
+  Map<FocusNode?, _FocusTraversalGroupInfo> _findGroups(FocusScopeNode scope, _FocusTraversalGroupNode? scopeGroupNode, FocusNode currentNode) {
     final FocusTraversalPolicy defaultPolicy = scopeGroupNode?.policy ?? ReadingOrderTraversalPolicy();
     final Map<FocusNode?, _FocusTraversalGroupInfo> groups = <FocusNode?, _FocusTraversalGroupInfo>{};
-    for (final FocusNode node in _getDescendantsWithoutExpandingScope(scope)) {
+    for (final FocusNode node in scope.descendants) {
       final _FocusTraversalGroupNode? groupNode = FocusTraversalGroup._getGroupNode(node);
       // Group nodes need to be added to their parent's node, or to the "null"
       // node if no parent is found. This creates the hierarchy of group nodes
@@ -463,7 +388,7 @@
 
   // Sort all descendants, taking into account the FocusTraversalGroup
   // that they are each in, and filtering out non-traversable/focusable nodes.
-  static List<FocusNode> _sortAllDescendants(FocusScopeNode scope, FocusNode currentNode) {
+  List<FocusNode> _sortAllDescendants(FocusScopeNode scope, FocusNode currentNode) {
     final _FocusTraversalGroupNode? scopeGroupNode = FocusTraversalGroup._getGroupNode(scope);
     // Build the sorting data structure, separating descendants into groups.
     final Map<FocusNode?, _FocusTraversalGroupInfo> groups = _findGroups(scope, scopeGroupNode, currentNode);
@@ -548,42 +473,30 @@
     if (focusedChild == null) {
       final FocusNode? firstFocus = forward ? findFirstFocus(currentNode) : findLastFocus(currentNode);
       if (firstFocus != null) {
-        return _requestTabTraversalFocus(
+        requestFocusCallback(
           firstFocus,
           alignmentPolicy: forward ? ScrollPositionAlignmentPolicy.keepVisibleAtEnd : ScrollPositionAlignmentPolicy.keepVisibleAtStart,
-          forward: forward,
         );
+        return true;
       }
     }
     focusedChild ??= nearestScope;
     final List<FocusNode> sortedNodes = _sortAllDescendants(nearestScope, focusedChild);
-    assert(sortedNodes.contains(focusedChild));
 
+    assert(sortedNodes.contains(focusedChild));
+    if (sortedNodes.length < 2) {
+      // If there are no nodes to traverse to, like when descendantsAreTraversable
+      // is false or skipTraversal for all the nodes is true.
+      return false;
+    }
     if (forward && focusedChild == sortedNodes.last) {
       switch (nearestScope.traversalEdgeBehavior) {
         case TraversalEdgeBehavior.leaveFlutterView:
           focusedChild.unfocus();
           return false;
-        case TraversalEdgeBehavior.parentScope:
-          final FocusScopeNode? parentScope = nearestScope.enclosingScope;
-          if (parentScope != null && parentScope != FocusManager.instance.rootScope) {
-            focusedChild.unfocus();
-            parentScope.nextFocus();
-            // Verify the focus really has changed.
-            return focusedChild.enclosingScope?.focusedChild != focusedChild;
-          }
-          // No valid parent scope. Fallback to closed loop behavior.
-          return _requestTabTraversalFocus(
-            sortedNodes.first,
-            alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd,
-            forward: forward,
-          );
         case TraversalEdgeBehavior.closedLoop:
-          return _requestTabTraversalFocus(
-            sortedNodes.first,
-            alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd,
-            forward: forward,
-          );
+          requestFocusCallback(sortedNodes.first, alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd);
+          return true;
       }
     }
     if (!forward && focusedChild == sortedNodes.first) {
@@ -591,26 +504,9 @@
         case TraversalEdgeBehavior.leaveFlutterView:
           focusedChild.unfocus();
           return false;
-        case TraversalEdgeBehavior.parentScope:
-          final FocusScopeNode? parentScope = nearestScope.enclosingScope;
-          if (parentScope != null && parentScope != FocusManager.instance.rootScope) {
-            focusedChild.unfocus();
-            parentScope.previousFocus();
-            // Verify the focus really has changed.
-            return focusedChild.enclosingScope?.focusedChild != focusedChild;
-          }
-          // No valid parent scope. Fallback to closed loop behavior.
-          return _requestTabTraversalFocus(
-            sortedNodes.last,
-            alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart,
-            forward: forward,
-          );
         case TraversalEdgeBehavior.closedLoop:
-          return _requestTabTraversalFocus(
-            sortedNodes.last,
-            alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart,
-            forward: forward,
-          );
+          requestFocusCallback(sortedNodes.last, alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart);
+          return true;
       }
     }
 
@@ -618,11 +514,11 @@
     FocusNode? previousNode;
     for (final FocusNode node in maybeFlipped) {
       if (previousNode == focusedChild) {
-        return _requestTabTraversalFocus(
+        requestFocusCallback(
           node,
           alignmentPolicy: forward ? ScrollPositionAlignmentPolicy.keepVisibleAtEnd : ScrollPositionAlignmentPolicy.keepVisibleAtStart,
-          forward: forward,
         );
+        return true;
       }
       previousNode = node;
     }
diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart
index bc57d8a..ccf3fbf 100644
--- a/packages/flutter/lib/src/widgets/navigator.dart
+++ b/packages/flutter/lib/src/widgets/navigator.dart
@@ -1146,7 +1146,9 @@
 /// The default value of [Navigator.routeTraversalEdgeBehavior].
 ///
 /// {@macro flutter.widgets.navigator.routeTraversalEdgeBehavior}
-const TraversalEdgeBehavior kDefaultRouteTraversalEdgeBehavior = TraversalEdgeBehavior.parentScope;
+const TraversalEdgeBehavior kDefaultRouteTraversalEdgeBehavior = kIsWeb
+  ? TraversalEdgeBehavior.leaveFlutterView
+  : TraversalEdgeBehavior.closedLoop;
 
 /// A widget that manages a set of child widgets with a stack discipline.
 ///
diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart
index fc954fa..b51cb4f 100644
--- a/packages/flutter/lib/src/widgets/routes.dart
+++ b/packages/flutter/lib/src/widgets/routes.dart
@@ -834,9 +834,7 @@
   late Listenable _listenable;
 
   /// The node this scope will use for its root [FocusScope] widget.
-  final FocusScopeNode focusScopeNode = FocusScopeNode(
-    debugLabel: '$_ModalScopeState Focus Scope',
-  );
+  final FocusScopeNode focusScopeNode = FocusScopeNode(debugLabel: '$_ModalScopeState Focus Scope');
   final ScrollController primaryScrollController = ScrollController();
 
   @override
@@ -938,8 +936,6 @@
                     controller: primaryScrollController,
                     child: FocusScope(
                       node: focusScopeNode, // immutable
-                      // Only top most route can participate in focus traversal.
-                      skipTraversal: !widget.route.isCurrent,
                       child: RepaintBoundary(
                         child: AnimatedBuilder(
                           animation: _listenable, // immutable
@@ -1709,25 +1705,10 @@
   }
 
   @override
-  void didChangeNext(Route<dynamic>? nextRoute) {
-    super.didChangeNext(nextRoute);
-    changedInternalState();
-  }
-
-  @override
-  void didPopNext(Route<dynamic> nextRoute) {
-    super.didPopNext(nextRoute);
-    changedInternalState();
-  }
-
-  @override
   void changedInternalState() {
     super.changedInternalState();
-    // 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();
-    }
+    setState(() { /* internal state already changed */ });
+    _modalBarrier.markNeedsBuild();
     _modalScope.maintainState = maintainState;
   }
 
diff --git a/packages/flutter/test/material/icon_button_test.dart b/packages/flutter/test/material/icon_button_test.dart
index 02c6ad0..fce7855 100644
--- a/packages/flutter/test/material/icon_button_test.dart
+++ b/packages/flutter/test/material/icon_button_test.dart
@@ -4,7 +4,6 @@
 
 import 'dart:ui';
 
-import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/rendering.dart';
 import 'package:flutter_test/flutter_test.dart';
@@ -793,10 +792,6 @@
   testWidgetsWithLeakTracking("Disabled IconButton can't be traversed to when disabled.", (WidgetTester tester) async {
     final FocusNode focusNode1 = FocusNode(debugLabel: 'IconButton 1');
     final FocusNode focusNode2 = FocusNode(debugLabel: 'IconButton 2');
-    addTearDown(() {
-      focusNode1.dispose();
-      focusNode2.dispose();
-    });
 
     await tester.pumpWidget(
       wrap(
@@ -826,8 +821,11 @@
     expect(focusNode1.nextFocus(), isFalse);
     await tester.pump();
 
-    expect(focusNode1.hasPrimaryFocus, !kIsWeb);
+    expect(focusNode1.hasPrimaryFocus, isTrue);
     expect(focusNode2.hasPrimaryFocus, isFalse);
+
+    focusNode1.dispose();
+    focusNode2.dispose();
   });
 
   group('feedback', () {
diff --git a/packages/flutter/test/widgets/actions_test.dart b/packages/flutter/test/widgets/actions_test.dart
index 6a24630..b59f17f 100644
--- a/packages/flutter/test/widgets/actions_test.dart
+++ b/packages/flutter/test/widgets/actions_test.dart
@@ -965,7 +965,7 @@
         expect(buttonNode2.hasFocus, isFalse);
         primaryFocus!.nextFocus();
         await tester.pump();
-        expect(buttonNode1.hasFocus, isFalse);
+        expect(buttonNode1.hasFocus, isTrue);
         expect(buttonNode2.hasFocus, isFalse);
       },
     );
diff --git a/packages/flutter/test/widgets/focus_traversal_test.dart b/packages/flutter/test/widgets/focus_traversal_test.dart
index 04cf497..ff0b8c9 100644
--- a/packages/flutter/test/widgets/focus_traversal_test.dart
+++ b/packages/flutter/test/widgets/focus_traversal_test.dart
@@ -430,92 +430,6 @@
 
   });
 
-  testWidgets('Nested navigator does not trap focus', (WidgetTester tester) async {
-    final FocusNode node1 = FocusNode();
-    final FocusNode node2 = FocusNode();
-    final FocusNode node3 = FocusNode();
-    await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: FocusTraversalGroup(
-          policy: ReadingOrderTraversalPolicy(),
-          child: FocusScope(
-            child: Column(
-              children: <Widget>[
-                Focus(
-                  focusNode: node1,
-                  child: const SizedBox(width: 100, height: 100),
-                ),
-                SizedBox(
-                  width: 100,
-                  height: 100,
-                  child: Navigator(
-                    pages: <Page<void>>[
-                      MaterialPage<void>(
-                        child: Focus(
-                          focusNode: node2,
-                          child: const SizedBox(width: 100, height: 100),
-                        ),
-                      ),
-                    ],
-                    onPopPage: (_, __) => false,
-                  ),
-                ),
-                Focus(
-                  focusNode: node3,
-                  child: const SizedBox(width: 100, height: 100),
-                ),
-              ],
-            ),
-          ),
-        ),
-      ),
-    );
-
-    node1.requestFocus();
-    await tester.pump();
-
-    expect(node1.hasFocus, isTrue);
-    expect(node2.hasFocus, isFalse);
-    expect(node3.hasFocus, isFalse);
-
-    node1.nextFocus();
-    await tester.pump();
-    expect(node1.hasFocus, isFalse);
-    expect(node2.hasFocus, isTrue);
-    expect(node3.hasFocus, isFalse);
-
-    node2.nextFocus();
-    await tester.pump();
-    expect(node1.hasFocus, isFalse);
-    expect(node2.hasFocus, isFalse);
-    expect(node3.hasFocus, isTrue);
-
-    node3.nextFocus();
-    await tester.pump();
-    expect(node1.hasFocus, isTrue);
-    expect(node2.hasFocus, isFalse);
-    expect(node3.hasFocus, isFalse);
-
-    node1.previousFocus();
-    await tester.pump();
-    expect(node1.hasFocus, isFalse);
-    expect(node2.hasFocus, isFalse);
-    expect(node3.hasFocus, isTrue);
-
-    node3.previousFocus();
-    await tester.pump();
-    expect(node1.hasFocus, isFalse);
-    expect(node2.hasFocus, isTrue);
-    expect(node3.hasFocus, isFalse);
-
-    node2.previousFocus();
-    await tester.pump();
-    expect(node1.hasFocus, isTrue);
-    expect(node2.hasFocus, isFalse);
-    expect(node3.hasFocus, isFalse);
-  });
-
   group(ReadingOrderTraversalPolicy, () {
     testWidgets('Find the initial focus if there is none yet.', (WidgetTester tester) async {
       final GlobalKey key1 = GlobalKey(debugLabel: '1');
diff --git a/packages/flutter/test/widgets/navigator_test.dart b/packages/flutter/test/widgets/navigator_test.dart
index 9511193..886de4a 100644
--- a/packages/flutter/test/widgets/navigator_test.dart
+++ b/packages/flutter/test/widgets/navigator_test.dart
@@ -1487,34 +1487,29 @@
         return result;
       },
     ));
-    final List<String> expected = <String>['building page 1 - false'];
-    expect(log, expected);
+    expect(log, <String>['building page 1 - false']);
     key.currentState!.pushReplacement(PageRouteBuilder<int>(
       pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
         log.add('building page 2 - ${ModalRoute.of(context)!.canPop}');
         return const Placeholder();
       },
     ));
-    expect(log, expected);
+    expect(log, <String>['building page 1 - false']);
     await tester.pump();
-    expected.add('building page 2 - false');
-    expected.add('building page 1 - false'); // page 1 is rebuilt again because isCurrent changed.
-    expect(log, expected);
+    expect(log, <String>['building page 1 - false', 'building page 2 - false']);
     await tester.pump(const Duration(milliseconds: 150));
-    expect(log, expected);
+    expect(log, <String>['building page 1 - false', 'building page 2 - false']);
     key.currentState!.pushReplacement(PageRouteBuilder<int>(
       pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
         log.add('building page 3 - ${ModalRoute.of(context)!.canPop}');
         return const Placeholder();
       },
     ));
-    expect(log, expected);
+    expect(log, <String>['building page 1 - false', 'building page 2 - false']);
     await tester.pump();
-    expected.add('building page 3 - false');
-    expected.add('building page 2 - false'); // page 2 is rebuilt again because isCurrent changed.
-    expect(log, expected);
+    expect(log, <String>['building page 1 - false', 'building page 2 - false', 'building page 3 - false']);
     await tester.pump(const Duration(milliseconds: 200));
-    expect(log, expected);
+    expect(log, <String>['building page 1 - false', 'building page 2 - false', 'building page 3 - false']);
   });
 
   testWidgets('route semantics', (WidgetTester tester) async {