[go_router] Should `GoRouter.of(context)` rebuild the widget when `GoRouter` notifies its listeners?

This issue has been tracked since 2022-09-23.

Steps to Reproduce

Using GoRouter.of(context) doesn't make the widget rebuild if the GoRouter notifies its listener. This forces me to wrap my widget with an AnimatedBuilder(animation: GoRouter.of(context)).
Shouldn't GoRouter.of(context) imply a rebuild of the widget when GoRouter notifies its listener? If not, I'm curious to know why it wouldn't be an expected behavior

  1. Execute flutter test on the code sample (see "Code sample" section below) (or you can check out https://github.com/ValentinVignal/flutter_app_stable/tree/go-router/of-context)
  2. Notice the logs (see section below), here is a high level translation
The widget builds with /a, and it cannot pop since there is only 1 page in the history.
The animated builder builds with /a, and it cannot pop since there is only 1 page in the history.
/b is being pushed.
The widget builds with /b, and it can pop since there are 2 pages in the history.
The animated builder builds with /b, and it can pop since there are 2 pages in the history.
The animated builder already built from the first page /a rebuilds because GoRouter notifies its listeners; and it can pop since there are 2 pages in the history. Notice that the widget itself doesn't rebuild, only the animated builder.
/b is being popped. We go back to /a.
An animated builder rebuilds because GoRouter notifies its listeners; and it cannot pop since there is only 1 page in the history. Notice that the widget itself doesn't rebuild, only the animated builder.
The other animated builder rebuilds because GoRouter notifies its listeners; and it cannot pop since there is only 1 page in the history. Notice that the widget itself doesn't rebuild, only the animated builder.

Some context:
When doing some UI logic with GoRouter.of(context), I need my widgets to rebuild when the router state changes.
An example is this GoRouterBackButton that displays a BackButton when there is a page to pop.
This is useful when the page stack contains transparent/non-opaque pages (they look like a drawer/dialog/bottom sheet) that don't block the interactions with pages behind.

Expected results:

I was expecting the widgets to rebuild when the router state changes.

Actual results:

The widgets don't rebuild when the router state changes.

Code sample

Or you can check out https://github.com/ValentinVignal/flutter_app_stable/tree/go-router/of-context

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';

class GoRouterBackButton extends StatelessWidget {
  const GoRouterBackButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final router = GoRouter.of(context);
    print(
      'GoRouterBackButton.build - canPop ${router.canPop()}',
    );
    return AnimatedBuilder(
      // This forces the widget to rebuild when a page is pushed/popped. This is
      // useful when the page pushed is transparent.
      animation: router,
      builder: (context, child) {
        print(
          'GoRouterBackButton.build.AnimatedBuilder.builder - canPop ${router.canPop()}',
        );
        return Visibility(
          visible: router.canPop(),
          child: child!,
        );
      },
      child: BackButton(
        onPressed: router.pop,
      ),
    );
  }
}

void main() {
  testWidgets(
      'It should display a back button when there is a page to pop and pop it when pressed',
      (tester) async {
    final router = GoRouter(
      initialLocation: '/a',
      routes: [
        GoRoute(
          path: '/a',
          builder: (_, __) => const Material(child: GoRouterBackButton()),
        ),
        GoRoute(
          path: '/b',
          builder: (_, __) => const Material(child: GoRouterBackButton()),
        ),
      ],
    );
    final widget = MaterialApp.router(
      routerConfig: router,
    );
    await tester.pumpWidget(widget);

    expect(
      find.byType(BackButton),
      findsNothing,
      reason: 'When there is nothing to pop, it should not display anything',
    );

    print('Before push');
    router.push('/b');
    await tester.pumpAndSettle(); // Navigate between the pages.
    expect(router.canPop(), true);

    expect(
      find.byType(BackButton),
      findsOneWidget,
      reason: 'It should display a back button when there is a page to pop',
    );

    print('Before pop');
    await tester.tap(find.byType(BackButton));
    await tester.pumpAndSettle(); // Navigate between the pages.

    expect(
      find.byType(BackButton),
      findsNothing,
      reason: 'When there is nothing to pop, it should not display anything',
    );
  });
}
Logs
00:02 +0: It should display a back button when there is a page to pop and pop it when pressed                                                                                                                                                                        
GoRouterBackButton.build - canPop false
GoRouterBackButton.build.AnimatedBuilder.builder - canPop false
Before push
GoRouterBackButton.build - canPop true
GoRouterBackButton.build.AnimatedBuilder.builder - canPop true
GoRouterBackButton.build.AnimatedBuilder.builder - canPop true
Before pop
GoRouterBackButton.build.AnimatedBuilder.builder - canPop false
GoRouterBackButton.build.AnimatedBuilder.builder - canPop false
00:02 +1: All tests passed! 
Analyzing flutter_app_stable...                                         

   info • Avoid `print` calls in production code • test/widget_test.dart:18:5 • avoid_print
   info • Avoid `print` calls in production code • test/widget_test.dart:26:9 • avoid_print
   info • Avoid `print` calls in production code • test/widget_test.dart:69:5 • avoid_print
   info • Avoid `print` calls in production code • test/widget_test.dart:80:5 • avoid_print

4 issues found. (ran in 2.6s)
[✓] Flutter (Channel stable, 3.3.2, on macOS 11.6.8 20G730 darwin-x64, locale en-GB)
    • Flutter version 3.3.2 on channel stable at /Users/valentin/flutter/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision e3c29ec00c (9 days ago), 2022-09-14 08:46:55 -0500
    • Engine revision a4ff2c53d8
    • Dart version 2.18.1
    • DevTools version 2.15.0

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
    • Android SDK at /usr/local/Caskroom/android-sdk/4333796
    • Platform android-33, build-tools 30.0.2
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7590822)
    • All Android licenses accepted.

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

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

[✓] Android Studio (version 2021.1)
    • 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.11+0-b60-7590822)

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

[✓] Connected device (2 available)
    • macOS (desktop) • macos  • darwin-x64     • macOS 11.6.8 20G730 darwin-x64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 105.0.5195.125

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

• No issues found!
exaby73 wrote this answer on 2022-09-23

Hello @ValentinVignal. Thank you for filing this issue. I am unsure if this is expected or not. Labelling for insights from the team.

More Details About Repo
Owner Name flutter
Repo Name flutter
Full Name flutter/flutter
Language Dart
Created Date 2015-03-06
Updated Date 2022-10-04
Star Count 145499
Watcher Count 3567
Fork Count 23393
Issue Count 11206

YOU MAY BE INTERESTED

Issue Title Created Date Updated Date