Skip to content

Routing unencodes encoded hashes in the URL, producing in an incorrect URL #155992

Closed

Description

@DanTup

Steps to reproduce

I'm trying to track down a bug in DevTools where values on the querystring get lost. It happens when a value is a hex colour (#ffffff) that contains a hash. The hash is correctly encoded initially, but Flutter seems to update the URL with an unencoded version in a way that results in window.location.href being incorrect (because the string after the hash is treated as a client-side fragement and not part of the querystring value).

While it might be better if DevTools could avoid reading window.location directly, this feels like incorrect behaviour (for example trying to bookmark the URL or similar would use an incorrect URL).

To reproduce:

  • Navigate to an app that has an encoded hash in a querystring value (for example http://localhost:49774/home?a=ccc%23ddd)
  • Print the value of window.location.href (or check it in the browser address bar) as the app is loading before routing is initialized
  • Print the value of window.location.href (or check it in the browser address bar) after the app has loaded/routing has initialized

Expected results

The value of window.location.href should not change to unescape the hash. This changes the meaning of the URL:

/home?a=b%23c // This means "a" has the value of "b#c"
/home?a=b#c // This means "a" has the value of "b" and there is a client-side fragment "#c"

Actual results

The hash in the URL is unescaped, resulting in a different (incorrect) URL. Bookmarking this, or reading the value with window.location.href will produce invalid results.

Code sample

Code sample
import 'dart:html';
import 'dart:async';
import 'dart:collection';

import 'package:flutter_web_plugins/url_strategy.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  usePathUrlStrategy();
  runApp(const MainApp());
}

final routerDelegate = MyRouterDelegate();
final routeInfromationParser = MyRouteInformationParser();

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

  void printCurrentUrlInfo() {
    print('The current window.location.href is ${window.location.href}');
    var paramValue = Uri.parse(window.location.href).queryParameters['a'];
    print('The current query parameter "a" is ${paramValue}');
  }

  @override
  Widget build(BuildContext context) {
    printCurrentUrlInfo();
    Future.delayed(const Duration(seconds: 1))
        .then((_) => printCurrentUrlInfo());
    return MaterialApp.router(
      builder: (context, child) => child!,
      routerDelegate: routerDelegate,
      routeInformationParser: routeInfromationParser,
    );
  }
}

class MyRouteConfiguration {
  MyRouteConfiguration(this.page, this.params);

  final String page;
  final Map<String, String?> params;
}

class MyRouteInformationParser
    extends RouteInformationParser<MyRouteConfiguration> {
  @override
  Future<MyRouteConfiguration> parseRouteInformation(
    RouteInformation routeInformation,
  ) async {
    var uri = routeInformation.uri;

    // Paths start with /
    final page = uri.path.isNotEmpty ? uri.path.substring(1) : '';
    final configuration = MyRouteConfiguration(page, uri.queryParameters);
    return SynchronousFuture<MyRouteConfiguration>(configuration);
  }

  @override
  RouteInformation restoreRouteInformation(
    MyRouteConfiguration configuration,
  ) {
    return RouteInformation(
        uri: Uri(
      // Paths must always start with /
      path: '/${configuration.page}',
      queryParameters: configuration.params,
    ));
  }
}

class MyRouterDelegate extends RouterDelegate<MyRouteConfiguration>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<MyRouteConfiguration> {
  MyRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();

  static MyRouterDelegate of(BuildContext context) =>
      Router.of(context).routerDelegate as MyRouterDelegate;

  @override
  final GlobalKey<NavigatorState> navigatorKey;

  final _routes = ListQueue<MyRouteConfiguration>();

  @override
  MyRouteConfiguration? get currentConfiguration => _routes.lastOrNull;

  @override
  Widget build(BuildContext context) {
    final uri1 = Uri(path: '/home', queryParameters: {'a': 'bbb'});
    final uri2 = Uri(path: '/home', queryParameters: {'a': 'ccc#ddd'});

    return Navigator(
      key: navigatorKey,
      pages: [
        MaterialPage(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Open DevTools console!'),
              for (var uri in [uri1, uri2])
                TextButton(
                  onPressed: () => window.location.href = uri.toString(),
                  child: Text('Load page as $uri'),
                ),
            ],
          ),
        ),
      ],
      onDidRemovePage: (_) {
        if (_routes.length <= 1) return;
        _routes.removeLast();
        notifyListeners();
      },
    );
  }

  @override
  Future<void> setNewRoutePath(MyRouteConfiguration configuration) async {
    _routes
      ..clear()
      ..add(configuration);

    notifyListeners();
    return SynchronousFuture<void>(null);
  }
}

Screenshots or Video

Screenshots / Video demonstration

image

hash.mp4

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[!] Flutter (Channel master, 3.26.0-1.0.pre.232, on Microsoft Windows [Version 10.0.22631.4169], locale en-GB)
    • Flutter version 3.26.0-1.0.pre.232 on channel master at D:\Dev\Google\Flutter\Flutter main
    ! Warning: `dart` on your path resolves to D:\Tools\Dart\Stable\bin\dart.exe, which is not inside your current Flutter SDK checkout at
      D:\Dev\Google\Flutter\Flutter main. Consider adding D:\Dev\Google\Flutter\Flutter main\bin to the front of your path.
    • Upstream repository [email protected]:DanTup/flutter.git
    • FLUTTER_GIT_URL = [email protected]:DanTup/flutter.git
    • Framework revision f7b363929d (5 days ago), 2024-09-26 08:04:29 -0400
    • Engine revision 6328a0597b
    • Dart version 3.6.0 (build 3.6.0-279.0.dev)
    • DevTools version 2.40.0-dev.1
    • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and
      upgrades.

[✓] Windows Version (Installed version of Windows is version 10 or higher)

[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0-rc3)
    • Android SDK at C:\Users\danny\AppData\Local\Android\sdk
    • Platform android-35, build-tools 35.0.0-rc3
    • Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java
    • Java version OpenJDK Runtime Environment (build 17.0.9+0--11185874)
    • All Android licenses accepted.

[✓] Chrome - develop for the web
    • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[✓] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.10.5)
    • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
    • Visual Studio Community 2022 version 17.10.35122.118
    • Windows 10 SDK version 10.0.22621.0

[✓] Android Studio (version 2023.2)
    • Android Studio at C:\Program Files\Android\Android Studio
    • 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 17.0.9+0--11185874)

[✓] VS Code (version 1.93.1)
    • VS Code at C:\Users\danny\AppData\Local\Programs\Microsoft VS Code
    • Flutter extension version 3.99.20240930

[✓] VS Code (version 1.93.0-insider)
    • VS Code at C:\Users\danny\AppData\Local\Programs\Microsoft VS Code Insiders
    • Flutter extension version 3.95.20240801

[✓] Connected device (3 available)
    • Windows (desktop) • windows • windows-x64    • Microsoft Windows [Version 10.0.22631.4169]
    • Chrome (web)      • chrome  • web-javascript • Google Chrome 129.0.6668.70
    • Edge (web)        • edge    • web-javascript • Microsoft Edge 127.0.2651.86

[✓] Network resources
    • All expected network resources are available.

! Doctor found issues in 1 category.

Activity

  1. danagbemava-nc commented on Oct 1, 2024

    @danagbemava-nc
    Member

    Reproducible using the code sample provided above.

    flutter doctor -v
    [✓] Flutter (Channel stable, 3.24.3, on macOS 15.0 24A335 darwin-arm64, locale en-US)
        • Flutter version 3.24.3 on channel stable at /Users/deanli/dev/stable
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 2663184aa7 (3 weeks ago), 2024-09-11 16:27:48 -0500
        • Engine revision 36335019a8
        • Dart version 3.5.3
        • DevTools version 2.37.3
    
    [!] Flutter (Channel master, 3.26.0-1.0.pre.281, on macOS 15.0 24A335 darwin-arm64, locale en-US)
        • Flutter version 3.26.0-1.0.pre.281 on channel master at /Users/deanli/dev/master
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 8027caaf10 (9 hours ago), 2024-09-30 23:48:24 -0400
        • Engine revision 6ee04ed763
        • Dart version 3.6.0 (build 3.6.0-304.0.dev)
        • DevTools version 2.40.0-dev.2
    
    
  2. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    f: routesNavigator, Router, and related APIs.
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    and removed
    in triagePresently being triaged by the triage team
    on Oct 1, 2024
  3. added
    P2Important issues not at the top of the work list
    on Oct 8, 2024
  4. chunhtai commented on Oct 8, 2024

    @chunhtai
    Contributor

    cc @Hangyujin since you were working on query parameter encoding before

  5. github-actions commented on Oct 17, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  6. locked as resolved and limited conversation to collaborators on Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listf: routesNavigator, Router, and related APIs.found in release: 3.24Found to occur in 3.24found in release: 3.26Found to occur in 3.26frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions