Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug?] in didPopNext and didPop call order #26902

Open
LiviuStefanLucaTAB opened this issue Jan 22, 2019 · 8 comments
Open

[Bug?] in didPopNext and didPop call order #26902

LiviuStefanLucaTAB opened this issue Jan 22, 2019 · 8 comments
Labels
f: routes Navigator, Router, and related APIs. found in release: 3.3 Found to occur in 3.3 found in release: 3.7 Found to occur in 3.7 framework flutter/packages/flutter repository. See also f: labels. has reproducible steps The issue has been confirmed reproducible and is ready to work on P2 Important issues not at the top of the work list team-framework Owned by Framework team triaged-framework Triaged by Framework team

Comments

@LiviuStefanLucaTAB
Copy link

LiviuStefanLucaTAB commented Jan 22, 2019

Hi,

The following code is the logic from RouteObserver which calls any RouteAware widgets when a route is popped.

@override
  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
    if (route is R && previousRoute is R) {
      final List<RouteAware> previousSubscribers = _listeners[previousRoute]?.toList();

      if (previousSubscribers != null) {
        for (RouteAware routeAware in previousSubscribers) {
          routeAware.didPopNext();
        }
      }

      final List<RouteAware> subscribers = _listeners[route]?.toList();

      if (subscribers != null) {
        for (RouteAware routeAware in subscribers) {
          routeAware.didPop();
        }
      }
    }
  }

In my app I have a feature flag that I turn on/off based on the page that I'm showing, and rely on the RouteAware callbacks. I wrap all the pages where I want to enable the feature flag in a RouteAware widget, which turns the global flag to TRUE if didPush(starting the page)/didPopNext(coming back from the next page), or FALSE if didPop(closing the page)/didPushNext(going to the next page).

The problem comes when I have two sequential pages that turn ON the feature flag and the second one navigates back to the first one, because the logic calls previousRouteSubscribers first, so, didPopNext on page A is called first, and didPop on page B is called second, which turns my flag TRUE and then FALSE, which is not what I want. My first instinct would be that you first want to call the callbacks registered by page B because we are disposing of it, and want to clean it first, and then the callbacks for page A, because we are going to it. Is this the intended order, or should there be a mechanism to chose what order in which you want the callback fired, or should I not rely on this api for what I'm trying to achieve?

Minimal reproducible example:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

final routeObserver = RouteObserver<PageRoute>();

ValueNotifier<bool> isFeatureFlagEnabled = ValueNotifier(false);

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorObservers: [routeObserver],
      title: 'Flutter Demo',
      theme: ThemeData(
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: Page1(),
    );
  }
}

// Feature Flag ENABLED
class Page3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FeatureFlagWidget(
      child: Scaffold(
        appBar: AppBar(
          leading: BackButton(),
          title: Text('Page 3'),
          centerTitle: true,
        ),
        body: SafeArea(
            child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              AnimatedBuilder(
                animation: isFeatureFlagEnabled,
                builder: (BuildContext context, Widget child) {
                  return Text('Feature flag is enabled is '
                      '${isFeatureFlagEnabled.value.toString().toUpperCase()}');
                },
              ),
            ],
          ),
        )),
      ),
    );
  }
}

// Feature Flag NOT ENABLED
class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: BackButton(),
        title: Text('Page 1'),
        centerTitle: true,
      ),
      body: SafeArea(
          child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            AnimatedBuilder(
              animation: isFeatureFlagEnabled,
              builder: (BuildContext context, Widget child) {
                return Text('Feature flag is enabled is '
                    '${isFeatureFlagEnabled.value.toString().toUpperCase()}');
              },
            ),
            RaisedButton(
              onPressed: () {
                _pushMaterialPageRoute(context, Page2());
              },
              child: Text('Go to Page 2'),
            ),
          ],
        ),
      )),
    );
  }
}

// Feature Flag ENABLED
class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("hello");
    return FeatureFlagWidget(
      child: Scaffold(
        appBar: AppBar(
          leading: BackButton(),
          title: Text('Page 2'),
          centerTitle: true,
        ),
        body: SafeArea(
            child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              AnimatedBuilder(
                animation: isFeatureFlagEnabled,
                builder: (BuildContext context, Widget child) {
                  return Text('Feature flag is enabled is '
                      '${isFeatureFlagEnabled.value.toString().toUpperCase()}');
                },
              ),
              RaisedButton(
                onPressed: () {
                  _pushMaterialPageRoute(context, Page3());
                },
                child: Text('Go to Page 3'),
              ),
            ],
          ),
        )),
      ),
    );
  }
}

class FeatureFlagWidget extends StatefulWidget {
  FeatureFlagWidget({this.child});

  final Widget child;

  @override
  _FeatureFlagWidgetState createState() => _FeatureFlagWidgetState();
}

class _FeatureFlagWidgetState extends State<FeatureFlagWidget> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    routeObserver.subscribe(this, ModalRoute.of(context));
  }

  @override
  void dispose() {
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  void didPush() {
    print("didPush");
    // To avoid updating while widget is building
    Future.microtask(() {
      isFeatureFlagEnabled.value = true;
    });
  }

  @override
  void didPopNext() {
    print("didPopNext");
    isFeatureFlagEnabled.value = true;
  }

  @override
  void didPop() {
    print("didPop");
    isFeatureFlagEnabled.value = false;
  }

  @override
  void didPushNext() {
    print("didPushNext");
    isFeatureFlagEnabled.value = false;
  }

  @override
  Widget build(BuildContext context) => widget.child;
}

void _pushMaterialPageRoute(BuildContext context, Widget child) =>
    Navigator.of(context).push(
        MaterialPageRoute<void>(builder: (BuildContext context) => child));

When you run the example, see the when you go forward Page 1 -> Page 2 -> Page 3, feature flag is enabled on Page 2 and Page 3, but when you come back from Page 3 to Page 2, the flag is false. That is because didPopNext(of Page 2) got called before didPop(of Page 3).

Thanks!

@zoechi zoechi added framework flutter/packages/flutter repository. See also f: labels. f: routes Navigator, Router, and related APIs. labels Jan 23, 2019
@zoechi zoechi added this to the Goals milestone Jan 23, 2019
@zoechi
Copy link
Contributor

zoechi commented Jan 23, 2019

Please add the output of flutter doctor -v.

@LiviuStefanLucaTAB
Copy link
Author

[✓] Flutter (Channel master, v1.1.10-pre.135, on Mac OS X 10.12.6 16G29, locale en-GB)
    • Flutter version 1.1.10-pre.135 at /Users/lucaliviu-stefan/flutter/flutter
    • Framework revision d8d36bc7e8 (4 days ago), 2019-01-19 11:59:42 -0800
    • Engine revision 898b4f8da4
    • Dart version 2.1.1 (build 2.1.1-dev.2.0 2e5453ddb4)

[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at /Users/lucaliviu-stefan/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-28, build-tools 28.0.3
    • ANDROID_HOME = /Users/lucaliviu-stefan/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
    • All Android licenses accepted.

[✓] iOS toolchain - develop for iOS devices (Xcode 9.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 9.1, Build version 9B55
    • ios-deploy 1.9.4
    • CocoaPods version 1.5.2

[✓] Android Studio (version 3.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 30.0.1
    • Dart plugin version 181.5656
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)

[✓] IntelliJ IDEA Community Edition (version 2018.3.3)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    • Flutter plugin version 31.3.4
    • Dart plugin version 183.5153.38

@jsroest
Copy link

jsroest commented Jan 7, 2020

I have exactly the same issue.
I expect the event 'didPop' to fire first on the source page and after that, 'didPopNext' on the destination page.

@LiviuStefanLucaTAB Have you found a workaround?
@zoechi Any progress on this issue?

@kf6gpe kf6gpe added the P2 Important issues not at the top of the work list label May 29, 2020
@Hixie Hixie removed this from the None. milestone Aug 17, 2020
@darshankawar
Copy link
Member

Issue replicable on latest stable (1.20.2).
When coming back from page 3 to page 2, it first calls didPopNext and then didPop.
But when coming back from page 2 to page 1, it calls didPop.

I/flutter (13620): hello
I/flutter (13620): didPush
I/flutter (13620): didPushNext
I/flutter (13620): didPush
I/flutter (13620): didPopNext
I/flutter (13620): didPop
I/flutter (13620): didPop

@darshankawar darshankawar added found in release: 1.20 Found to occur in 1.20 has reproducible steps The issue has been confirmed reproducible and is ready to work on labels Sep 1, 2020
@antonkrasov
Copy link

Hey guys, have the same issue, have you found any workaround? Thanks!
@jsroest @LiviuStefanLucaTAB

@jsroest
Copy link

jsroest commented Jul 16, 2021

Hey guys, have the same issue, have you found any workaround? Thanks!
@jsroest @LiviuStefanLucaTAB

Hi @antonkrasov

I did make a custom navigator based on Navigator 2.0 with events like onTop, willShow, isShown and mayPop. It was the only way I could get this working reliable and it is a navigation pattern that I implemented on some other platforms.

Working with the default didPop and didPopNext got way too complex for me and felt like mixing business logic with UI logic.

You can find it here:
https://github.com/jsroest/rubigo_navigator

That version is working, but not production-ready. It's more like an early demo/proof of concept.

@danagbemava-nc
Copy link
Member

I see the same logs as reported above on the latest versions of flutter.

flutter: hello
flutter: didPush
flutter: didPushNext
flutter: didPush
flutter: didPopNext
flutter: didPop
flutter: didPop
updated sample
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

final routeObserver = RouteObserver<PageRoute>();

ValueNotifier<bool> isFeatureFlagEnabled = ValueNotifier(false);

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorObservers: [routeObserver],
      title: 'Flutter Demo',
      theme: ThemeData(
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: const Page1(),
    );
  }
}

// Feature Flag ENABLED
class Page3 extends StatelessWidget {
  const Page3({super.key});

  @override
  Widget build(BuildContext context) {
    return FeatureFlagWidget(
      child: Scaffold(
        appBar: AppBar(
          leading: const BackButton(),
          title: const Text('Page 3'),
          centerTitle: true,
        ),
        body: SafeArea(
            child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              AnimatedBuilder(
                animation: isFeatureFlagEnabled,
                builder: (BuildContext context, Widget? child) {
                  return Text('Feature flag is enabled is '
                      '${isFeatureFlagEnabled.value.toString().toUpperCase()}');
                },
              ),
            ],
          ),
        )),
      ),
    );
  }
}

// Feature Flag NOT ENABLED
class Page1 extends StatelessWidget {
  const Page1({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: const BackButton(),
        title: const Text('Page 1'),
        centerTitle: true,
      ),
      body: SafeArea(
          child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            AnimatedBuilder(
              animation: isFeatureFlagEnabled,
              builder: (BuildContext context, Widget? child) {
                return Text('Feature flag is enabled is '
                    '${isFeatureFlagEnabled.value.toString().toUpperCase()}');
              },
            ),
            ElevatedButton(
              onPressed: () {
                _pushMaterialPageRoute(context, const Page2());
              },
              child: const Text('Go to Page 2'),
            ),
          ],
        ),
      )),
    );
  }
}

// Feature Flag ENABLED
class Page2 extends StatelessWidget {
  const Page2({super.key});

  @override
  Widget build(BuildContext context) {
    print("hello");
    return FeatureFlagWidget(
      child: Scaffold(
        appBar: AppBar(
          leading: const BackButton(),
          title: const Text('Page 2'),
          centerTitle: true,
        ),
        body: SafeArea(
            child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              AnimatedBuilder(
                animation: isFeatureFlagEnabled,
                builder: (BuildContext context, Widget? child) {
                  return Text('Feature flag is enabled is '
                      '${isFeatureFlagEnabled.value.toString().toUpperCase()}');
                },
              ),
              ElevatedButton(
                onPressed: () {
                  _pushMaterialPageRoute(context, const Page3());
                },
                child: const Text('Go to Page 3'),
              ),
            ],
          ),
        )),
      ),
    );
  }
}

class FeatureFlagWidget extends StatefulWidget {
  const FeatureFlagWidget({super.key, required this.child});

  final Widget child;

  @override
  State<FeatureFlagWidget> createState() => _FeatureFlagWidgetState();
}

class _FeatureFlagWidgetState extends State<FeatureFlagWidget> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
  }

  @override
  void dispose() {
    routeObserver.unsubscribe(this);
    super.dispose();
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }

  @override
  void didPush() {
    print("didPush");
    // To avoid updating while widget is building
    Future.microtask(() {
      isFeatureFlagEnabled.value = true;
    });
  }

  @override
  void didPopNext() {
    print("didPopNext");
    isFeatureFlagEnabled.value = true;
  }

  @override
  void didPop() {
    print("didPop");
    isFeatureFlagEnabled.value = false;
  }

  @override
  void didPushNext() {
    print("didPushNext");
    isFeatureFlagEnabled.value = false;
  }

  @override
  Widget build(BuildContext context) => widget.child;
}

void _pushMaterialPageRoute(BuildContext context, Widget child) =>
    Navigator.of(context).push(
        MaterialPageRoute<void>(builder: (BuildContext context) => child));
flutter doctor -v
[✓] Flutter (Channel stable, 3.3.9, on macOS 13.0.1 22A400 darwin-arm, locale en-GB)
    • Flutter version 3.3.9 on channel stable at /Users/nexus/dev/sdks/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision b8f7f1f986 (10 days ago), 2022-11-23 06:43:51 +0900
    • Engine revision 8f2221fbef
    • Dart version 2.18.5
    • DevTools version 2.15.0

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    • Android SDK at /Users/nexus/Library/Android/sdk
    • Platform android-33, build-tools 33.0.0
    • Java binary at: /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14B47b
    • CocoaPods version 1.11.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[!] Android Studio
    • Android Studio at /Applications/Android Studio Preview.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    ✗ Unable to find bundled Java version.
    • Try updating or re-installing Android Studio.

[✓] Android Studio (version 2021.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[!] Android Studio
    • Android Studio at /Applications/Android Studio Preview 2.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    ✗ Unable to find bundled Java version.
    • Try updating or re-installing Android Studio.

[✓] Android Studio (version 2021.3)
    • Android Studio at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[✓] Android Studio (version 2021.3)
    • Android Studio at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9014738/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[✓] VS Code (version 1.73.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.54.0

[✓] Connected device (3 available)
    • iPhone 14 Pro (mobile) • 4F72110C-F38B-4CF9-93C4-4D6042148D28 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-16-1 (simulator)
    • macOS (desktop)        • macos                                • darwin-arm64   • macOS 13.0.1 22A400 darwin-arm
    • Chrome (web)           • chrome                               • web-javascript • Google Chrome 108.0.5359.71

[✓] HTTP Host Availability
    • All required HTTP hosts are available

! Doctor found issues in 2 categories.
[!] Flutter (Channel master, 3.7.0-2.0.pre.9, on macOS 13.0.1 22A400 darwin-arm64, locale en-GB)
    • Flutter version 3.7.0-2.0.pre.9 on channel master at /Users/nexus/dev/sdks/flutters
    ! Warning: `flutter` on your path resolves to /Users/nexus/dev/sdks/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/nexus/dev/sdks/flutters. Consider adding /Users/nexus/dev/sdks/flutters/bin to the front of your path.
    ! Warning: `dart` on your path resolves to /Users/nexus/dev/sdks/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/nexus/dev/sdks/flutters. Consider adding /Users/nexus/dev/sdks/flutters/bin to the front of your path.
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision ce94230281 (7 hours ago), 2022-12-01 19:13:07 -0800
    • Engine revision 025aefc7af
    • Dart version 2.19.0 (build 2.19.0-444.0.dev)
    • DevTools version 2.20.0
    • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
    • Android SDK at /Users/nexus/Library/Android/sdk
    • Platform android-33, build-tools 33.0.0
    • Java binary at: /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14B47b
    • CocoaPods version 1.11.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[!] Android Studio
    • Android Studio at /Applications/Android Studio Preview.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    ✗ Unable to find bundled Java version.
    • Try updating or re-installing Android Studio.

[✓] Android Studio (version 2021.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[!] Android Studio
    • Android Studio at /Applications/Android Studio Preview 2.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    ✗ Unable to find bundled Java version.
    • Try updating or re-installing Android Studio.

[✓] Android Studio (version 2021.3)
    • Android Studio at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[✓] Android Studio (version 2021.3)
    • Android Studio at /Users/nexus/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9014738/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[✓] VS Code (version 1.73.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.54.0

[✓] Connected device (3 available)
    • iPhone 14 Pro (mobile) • 4F72110C-F38B-4CF9-93C4-4D6042148D28 • ios            • com.apple.CoreSimulator.SimRuntime.iOS-16-1 (simulator)
    • macOS (desktop)        • macos                                • darwin-arm64   • macOS 13.0.1 22A400 darwin-arm64
    • Chrome (web)           • chrome                               • web-javascript • Google Chrome 108.0.5359.71

[✓] HTTP Host Availability
    • All required HTTP hosts are available

! Doctor found issues in 3 categories.

@danagbemava-nc danagbemava-nc added found in release: 3.3 Found to occur in 3.3 found in release: 3.7 Found to occur in 3.7 and removed found in release: 1.20 Found to occur in 1.20 labels Dec 2, 2022
@wszak
Copy link

wszak commented Jun 2, 2023

Came across this problem too. My workaround is to create my own RouteObserver class - I copy its code from original RouteObserver and change didPop so that subscribers loop is before the previousSubscribers loop:

class MyRouteObserver<R extends Route<dynamic>> extends NavigatorObserver {
  final Map<R, Set<RouteAware>> _listeners = <R, Set<RouteAware>>{};

  /// Whether this observer is managing changes for the specified route.
  ///
  /// If asserts are disabled, this method will throw an exception.
  @visibleForTesting
  bool debugObservingRoute(R route) {
    late bool contained;
    assert(() {
      contained = _listeners.containsKey(route);
      return true;
    }());
    return contained;
  }

  /// Subscribe [routeAware] to be informed about changes to [route].
  ///
  /// Going forward, [routeAware] will be informed about qualifying changes
  /// to [route], e.g. when [route] is covered by another route or when [route]
  /// is popped off the [Navigator] stack.
  void subscribe(RouteAware routeAware, R route) {
    final Set<RouteAware> subscribers = _listeners.putIfAbsent(route, () => <RouteAware>{});
    if (subscribers.add(routeAware)) {
      routeAware.didPush();
    }
  }

  /// Unsubscribe [routeAware].
  ///
  /// [routeAware] is no longer informed about changes to its route. If the given argument was
  /// subscribed to multiple types, this will unregister it (once) from each type.
  void unsubscribe(RouteAware routeAware) {
    final List<R> routes = _listeners.keys.toList();
    for (final R route in routes) {
      final Set<RouteAware>? subscribers = _listeners[route];
      if (subscribers != null) {
        subscribers.remove(routeAware);
        if (subscribers.isEmpty) {
          _listeners.remove(route);
        }
      }
    }
  }


// my changes BELOW:


  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
    if (route is R && previousRoute is R) {
      final List<RouteAware>? previousSubscribers = _listeners[previousRoute]?.toList();

      final List<RouteAware>? subscribers = _listeners[route]?.toList();

      if (subscribers != null) {
        for (final RouteAware routeAware in subscribers) {
          routeAware.didPop();
        }
      }

      if (previousSubscribers != null) {
        for (final RouteAware routeAware in previousSubscribers) {
          routeAware.didPopNext();
        }
      }
    }
  }

  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    if (route is R && previousRoute is R) {
      final Set<RouteAware>? previousSubscribers = _listeners[previousRoute];

      if (previousSubscribers != null) {
        for (final RouteAware routeAware in previousSubscribers) {
          routeAware.didPushNext();
        }
      }
    }
  }
}

Then use MyRouteObserver instead of RouteObserver.
Result will be:

flutter: hello
flutter: didPush
flutter: didPushNext
flutter: didPush
flutter: didPop
flutter: didPopNext
flutter: didPop

@flutter-triage-bot flutter-triage-bot bot added team-framework Owned by Framework team triaged-framework Triaged by Framework team labels Jul 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
f: routes Navigator, Router, and related APIs. found in release: 3.3 Found to occur in 3.3 found in release: 3.7 Found to occur in 3.7 framework flutter/packages/flutter repository. See also f: labels. has reproducible steps The issue has been confirmed reproducible and is ready to work on P2 Important issues not at the top of the work list team-framework Owned by Framework team triaged-framework Triaged by Framework team
Projects
None yet
Development

No branches or pull requests

9 participants