[go_router] fixes pop and push to update urls correctly (#2904)

* [go_router] fixes pop and push to update urls correctly

* bump version
diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md
index ff7402e..56459e2 100644
--- a/packages/go_router/CHANGELOG.md
+++ b/packages/go_router/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 5.2.2
+
+- Fixes `pop` and `push` to update urls correctly.
+
 ## 5.2.1
 
 - Refactors `GoRouter.pop` to be able to pop individual pageless route with result.
diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart
index 3bb303c..82a6e6d 100644
--- a/packages/go_router/lib/src/delegate.dart
+++ b/packages/go_router/lib/src/delegate.dart
@@ -133,6 +133,7 @@
       return false;
     }
     _matchList.pop();
+    notifyListeners();
     assert(() {
       _debugAssertMatchListNotEmpty();
       return true;
diff --git a/packages/go_router/lib/src/matching.dart b/packages/go_router/lib/src/matching.dart
index d9027f4..4297249 100644
--- a/packages/go_router/lib/src/matching.dart
+++ b/packages/go_router/lib/src/matching.dart
@@ -48,7 +48,7 @@
 /// The list of [RouteMatch] objects.
 class RouteMatchList {
   /// RouteMatchList constructor.
-  RouteMatchList(List<RouteMatch> matches, this.uri, this.pathParameters)
+  RouteMatchList(List<RouteMatch> matches, this._uri, this.pathParameters)
       : _matches = matches,
         fullpath = _generateFullPath(matches);
 
@@ -82,7 +82,8 @@
   final Map<String, String> pathParameters;
 
   /// The uri of the current match.
-  final Uri uri;
+  Uri get uri => _uri;
+  Uri _uri;
 
   /// Returns true if there are no matches.
   bool get isEmpty => _matches.isEmpty;
@@ -97,8 +98,11 @@
 
   /// Removes the last match.
   void pop() {
+    if (_matches.last.route is GoRoute) {
+      final GoRoute route = _matches.last.route as GoRoute;
+      _uri = _uri.replace(path: removePatternFromPath(route.path, _uri.path));
+    }
     _matches.removeLast();
-
     // Also pop ShellRoutes when there are no subsequent route matches
     while (_matches.isNotEmpty && _matches.last.route is ShellRoute) {
       _matches.removeLast();
diff --git a/packages/go_router/lib/src/parser.dart b/packages/go_router/lib/src/parser.dart
index e15fba3..b83923d 100644
--- a/packages/go_router/lib/src/parser.dart
+++ b/packages/go_router/lib/src/parser.dart
@@ -8,6 +8,7 @@
 import 'package:flutter/widgets.dart';
 
 import 'configuration.dart';
+import 'delegate.dart';
 import 'information_provider.dart';
 import 'logging.dart';
 import 'matching.dart';
@@ -98,6 +99,10 @@
   /// for use by the Router architecture as part of the RouteInformationParser
   @override
   RouteInformation restoreRouteInformation(RouteMatchList configuration) {
+    if (configuration.matches.last is ImperativeRouteMatch) {
+      configuration =
+          (configuration.matches.last as ImperativeRouteMatch).matches;
+    }
     return RouteInformation(
       location: configuration.uri.toString(),
       state: configuration.extra,
diff --git a/packages/go_router/lib/src/path_utils.dart b/packages/go_router/lib/src/path_utils.dart
index 804e0ee..d7a20ff 100644
--- a/packages/go_router/lib/src/path_utils.dart
+++ b/packages/go_router/lib/src/path_utils.dart
@@ -47,10 +47,51 @@
   return RegExp(buffer.toString(), caseSensitive: false);
 }
 
-String _escapeGroup(String group, String name) {
+/// Removes string from the end of the path that matches a `pattern`.
+///
+/// The path parameters can be specified by prefixing them with `:`. The
+/// `parameters` are used for storing path parameter names.
+///
+///
+/// For example:
+///
+///  `path` = `/user/123/book/345`
+///  `pattern` = `book/:id`
+///
+/// The return value = `/user/123`.
+String removePatternFromPath(String pattern, String path) {
+  final StringBuffer buffer = StringBuffer();
+  int start = 0;
+  for (final RegExpMatch match in _parameterRegExp.allMatches(pattern)) {
+    if (match.start > start) {
+      buffer.write(RegExp.escape(pattern.substring(start, match.start)));
+    }
+    final String? optionalPattern = match[2];
+    final String regex =
+        optionalPattern != null ? _escapeGroup(optionalPattern) : '[^/]+';
+    buffer.write(regex);
+    start = match.end;
+  }
+
+  if (start < pattern.length) {
+    buffer.write(RegExp.escape(pattern.substring(start)));
+  }
+
+  if (!pattern.endsWith('/')) {
+    buffer.write(r'(?=/|$)');
+  }
+  buffer.write(r'$');
+  final RegExp regexp = RegExp(buffer.toString(), caseSensitive: false);
+  return path.replaceFirst(regexp, '');
+}
+
+String _escapeGroup(String group, [String? name]) {
   final String escapedGroup = group.replaceFirstMapped(
       RegExp(r'[:=!]'), (Match match) => '\\${match[0]}');
-  return '(?<$name>$escapedGroup)';
+  if (name != null) {
+    return '(?<$name>$escapedGroup)';
+  }
+  return escapedGroup;
 }
 
 /// Reconstructs the full path from a [pattern] and path parameters.
diff --git a/packages/go_router/lib/src/route.dart b/packages/go_router/lib/src/route.dart
index 5e58f0c..4a06b54 100644
--- a/packages/go_router/lib/src/route.dart
+++ b/packages/go_router/lib/src/route.dart
@@ -300,6 +300,7 @@
   /// Navigator instead of the nearest ShellRoute ancestor.
   final GlobalKey<NavigatorState>? parentNavigatorKey;
 
+  // TODO(chunhtai): move all regex related help methods to path_utils.dart.
   /// Match this route against a location.
   RegExpMatch? matchPatternAsPrefix(String loc) =>
       _pathRE.matchAsPrefix(loc) as RegExpMatch?;
diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml
index 64eb598..9fbfc28 100644
--- a/packages/go_router/pubspec.yaml
+++ b/packages/go_router/pubspec.yaml
@@ -1,7 +1,7 @@
 name: go_router
 description: A declarative router for Flutter based on Navigation 2 supporting
   deep linking, data-driven routes and more
-version: 5.2.1
+version: 5.2.2
 repository: https://github.com/flutter/packages/tree/main/packages/go_router
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22
 
diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart
index 35198e0..023fe57 100644
--- a/packages/go_router/test/go_router_test.dart
+++ b/packages/go_router/test/go_router_test.dart
@@ -879,6 +879,138 @@
     });
   });
 
+  group('report correct url', () {
+    final List<MethodCall> log = <MethodCall>[];
+    setUp(() {
+      TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger
+          .setMockMethodCallHandler(SystemChannels.navigation,
+              (MethodCall methodCall) async {
+        log.add(methodCall);
+        return null;
+      });
+    });
+    tearDown(() {
+      TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger
+          .setMockMethodCallHandler(SystemChannels.navigation, null);
+      log.clear();
+    });
+
+    testWidgets('on push', (WidgetTester tester) async {
+      final List<GoRoute> routes = <GoRoute>[
+        GoRoute(
+          path: '/',
+          builder: (_, __) => const DummyScreen(),
+        ),
+        GoRoute(
+          path: '/settings',
+          builder: (_, __) => const DummyScreen(),
+        ),
+      ];
+
+      final GoRouter router = await createRouter(routes, tester);
+
+      log.clear();
+      router.push('/settings');
+      await tester.pumpAndSettle();
+      expect(log, <Object>[
+        isMethodCall('selectMultiEntryHistory', arguments: null),
+        isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{
+          'location': '/settings',
+          'state': null,
+          'replace': false
+        }),
+      ]);
+    });
+
+    testWidgets('on pop', (WidgetTester tester) async {
+      final List<GoRoute> routes = <GoRoute>[
+        GoRoute(
+            path: '/',
+            builder: (_, __) => const DummyScreen(),
+            routes: <RouteBase>[
+              GoRoute(
+                path: 'settings',
+                builder: (_, __) => const DummyScreen(),
+              ),
+            ]),
+      ];
+
+      final GoRouter router =
+          await createRouter(routes, tester, initialLocation: '/settings');
+
+      log.clear();
+      router.pop();
+      await tester.pumpAndSettle();
+      expect(log, <Object>[
+        isMethodCall('selectMultiEntryHistory', arguments: null),
+        isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{
+          'location': '/',
+          'state': null,
+          'replace': false
+        }),
+      ]);
+    });
+
+    testWidgets('on pop with path parameters', (WidgetTester tester) async {
+      final List<GoRoute> routes = <GoRoute>[
+        GoRoute(
+            path: '/',
+            builder: (_, __) => const DummyScreen(),
+            routes: <RouteBase>[
+              GoRoute(
+                path: 'settings/:id',
+                builder: (_, __) => const DummyScreen(),
+              ),
+            ]),
+      ];
+
+      final GoRouter router =
+          await createRouter(routes, tester, initialLocation: '/settings/123');
+
+      log.clear();
+      router.pop();
+      await tester.pumpAndSettle();
+      expect(log, <Object>[
+        isMethodCall('selectMultiEntryHistory', arguments: null),
+        isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{
+          'location': '/',
+          'state': null,
+          'replace': false
+        }),
+      ]);
+    });
+
+    testWidgets('on pop with path parameters case 2',
+        (WidgetTester tester) async {
+      final List<GoRoute> routes = <GoRoute>[
+        GoRoute(
+            path: '/',
+            builder: (_, __) => const DummyScreen(),
+            routes: <RouteBase>[
+              GoRoute(
+                path: ':id',
+                builder: (_, __) => const DummyScreen(),
+              ),
+            ]),
+      ];
+
+      final GoRouter router =
+          await createRouter(routes, tester, initialLocation: '/123/');
+
+      log.clear();
+      router.pop();
+      await tester.pumpAndSettle();
+      expect(log, <Object>[
+        isMethodCall('selectMultiEntryHistory', arguments: null),
+        isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{
+          'location': '/',
+          'state': null,
+          'replace': false
+        }),
+      ]);
+    });
+  });
+
   group('named routes', () {
     testWidgets('match home route', (WidgetTester tester) async {
       final List<GoRoute> routes = <GoRoute>[