Repository: flutter
Issue: Interleaved nested Navigators and Predictive Back
Labels: platform-android,framework,f: routes,has reproducible steps,P2,team-framework,triaged-framework,found in release: 3.22,found in release: 3.24

### Steps to reproduce

PredictiveBackPageTransitionsBuilder seems to break a certain case of using nested Navigators.

 1. Run the app given below on an Android device with [predictive back enabled](https://github.com/flutter/flutter/issues/109513#issuecomment-1666220080).
 2. The home page is inside of a nested Navigator. So the root Navigator and nested Navigator both have 1 route each.
 3. Tap to push a route to the nested Navigator. The root Navigator has 1 route and nested Navigator has 2.
 4. Tap to push a route to the root Navigator. The root Navigator and nested Navigator both have 2 routes.
 5. Perform a system back gesture.

### Expected results

The current route (the root Navigator's second of 2 routes) is popped.

### Actual results

Nothing appears to happen.

Really, the nested Navigator's second of 2 routes is popped, but the user can't see that. Performing another system back gesture pops the root Navigator's second route and the user sees the nested Navigator's first and only route.

Using a different PageTransitionsBuilder works fine.

### Code sample

<details <summary>Code sample</summary>

```dart
import 'package:flutter/material.dart';

void main() async {
  runApp(const _MyApp());
}

class _MyApp extends StatelessWidget {
  const _MyApp();

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        useMaterial3: false,
        pageTransitionsTheme: const PageTransitionsTheme(
          builders: <TargetPlatform, PageTransitionsBuilder>{
            // If you use a different transition, there is no bug.
            TargetPlatform.android: PredictiveBackPageTransitionsBuilder(),
          },
        ),
      ),
      title: 'Flutter Demo',
      routes: {
        '/': (BuildContext context) {
          return _MyNestedNavigator();
        },
        'nav1leaf': (BuildContext context) {
          return const Nav1LeafPage();
        },
      },
    );
  }
}

class _MyNestedNavigator extends StatelessWidget {
  _MyNestedNavigator();

  final GlobalKey<NavigatorState> _nestedNavigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    //return Navigator(
    return NavigatorPopHandler(
      onPop: () {
        _nestedNavigatorKey.currentState!.maybePop();
      },
      child: Navigator(
        key: _nestedNavigatorKey,
        initialRoute: 'nav2home',
        onGenerateRoute: (RouteSettings settings) {
          return switch (settings.name) {
            'nav2home' => MaterialPageRoute(
              builder: (BuildContext context) => const MyHomePage(),
            ),
            'nav2leaf' => MaterialPageRoute(
              builder: (BuildContext context) => const Nav2LeafPage(),
            ),
            _ => MaterialPageRoute(
              builder: (BuildContext context) => const Text('404'),
            ),
          };
        },
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Home page of Navigator 2'),
            const SizedBox(height: 16, width: double.infinity),
            ElevatedButton(
              child: const Text('Open nav 2 leaf'),
              onPressed: () => Navigator.of(context).pushNamed('nav2leaf'),
            ),
          ],
        ),
      ),
    );
  }
}

class Nav2LeafPage extends StatelessWidget {
  const Nav2LeafPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.blue,
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          const Text('Leaf page of Navigator 2'),
          const SizedBox(height: 16, width: double.infinity),
          ElevatedButton(
            child: const Text('Open nav 1 leaf'),
            onPressed: () => Navigator.of(context, rootNavigator: true).pushNamed('nav1leaf'),
          ),
        ],
      ),
    );
  }
}

class Nav1LeafPage extends StatelessWidget {
  const Nav1LeafPage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text('Leaf of Navigator 1'),
        ],
      ),
    );
  }
}

```

</details>


<details><summary>Bonus code sample! From 152578</summary>

```dart
import 'package:flutter/material.dart';

void main() async {
  runApp(const _MyApp());
}

class _MyApp extends StatelessWidget {
  const _MyApp();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        useMaterial3: false,
        pageTransitionsTheme: const PageTransitionsTheme(
          builders: <TargetPlatform, PageTransitionsBuilder>{
            TargetPlatform.android: PredictiveBackPageTransitionsBuilder(),
          },
        ),
      ),
      title: 'Flutter Demo',
      routes: {
        '/': (BuildContext context) {
          return _NestedNavigatorPage();
        },
        'nav1leaf': (BuildContext context) {
          return const _Nav1LeafPage();
        },
      },
    );
  }
}

class _NestedNavigatorPage extends StatelessWidget {
  _NestedNavigatorPage();

  final GlobalKey<NavigatorState> _nestedNavigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return Navigator(
      key: _nestedNavigatorKey,
      initialRoute: 'nav2home',
      onGenerateRoute: (RouteSettings settings) {
        return switch (settings.name) {
          'nav2home' => MaterialPageRoute(
            builder: (BuildContext context) => const _Nav2HomePage(),
          ),
          'nav2leaf' => MaterialPageRoute(
            builder: (BuildContext context) => const _Nav2LeafPage(),
          ),
          _ => MaterialPageRoute(
            builder: (BuildContext context) => const Text('404'),
          ),
        };
      },
    );
  }
}

class _Nav2HomePage extends StatelessWidget {
  const _Nav2HomePage();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.blueAccent,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Home page of Navigator 2'),
            const SizedBox(height: 16, width: double.infinity),
            ElevatedButton(
              child: const Text('Open nav 2 leaf page'),
              onPressed: () => Navigator.of(context).pushNamed('nav2leaf'),
            ),
          ],
        ),
      ),
    );
  }
}

class _Nav2LeafPage extends StatelessWidget {
  const _Nav2LeafPage();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.blue,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Leaf page of Navigator 2'),
            const SizedBox(height: 16, width: double.infinity),
            ElevatedButton(
              child: const Text('Open nav 1 leaf page'),
              onPressed: () => Navigator.of(context, rootNavigator: true).pushNamed('nav1leaf'),
            ),
          ],
        ),
      ),
    );
  }
}

class _Nav1LeafPage extends StatelessWidget {
  const _Nav1LeafPage();

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text('Leaf of Navigator 1'),
        ],
      ),
    );
  }
}

```

</details>


### Screenshots or Video

<details open>
<summary>Video demonstration</summary>


[Screencast from 2024-07-30 13-30-36.webm](https://github.com/user-attachments/assets/759d3f89-f5fd-4120-ac96-1f8477a479c6)

</details>

### Logs

_No response_

### Flutter Doctor output

<details <summary>Doctor output</summary>

```console
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel master, 3.22.0-35.0.pre.918, on Debian GNU/Linux rodete 6.6.15-2rodete2-amd64, locale en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Chrome - develop for the web
[✓] Linux toolchain - develop for Linux desktop
[✓] Android Studio (version 2023.1)
[✓] Android Studio (version 2023.2)
[✓] Connected device (3 available)
[✓] Network resources

• No issues found!

```

</details>


### Why does this happen?
This bug is reproduced only when using the PredictiveBackPageTransitionsBuilder and not with any other transition. The reason is that when using any other transition, the back gesture is sent via handlePopRoute and handled by WidgetsApp. WidgetsApp calls maybePop on the root Navigator (which might be caught by PopScope which then pops the nested Navigator, if needed). This all works.

However with predictive back, it's sent via startBackGesture and handled by _PredictiveBackGestureDetector. This seems to call handleStartBack on the _PredictiveBackGestureDetector for the nested Navigator, not the root one.

### How can it be fixed
Maybe [_PredictiveBackGestureDetector.handleStartBack](https://github.com/flutter/flutter/blob/dc6a15995e3c4f17316533045e2233b18941d82c/packages/flutter/lib/src/material/predictive_back_page_transitions_builder.dart#L122) needs to be smart enough to return false when it's not the topmost route? It might be hard to determine that in a nested Navigator scenario, though.

Duplicate: https://github.com/flutter/flutter/issues/152578