Repository navigation
Description
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
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
danagbemava-nc commentedon Oct 1, 2024
Reproducible using the code sample provided above.
flutter doctor -v
chunhtai commentedon Oct 8, 2024
cc @Hangyujin since you were working on query parameter encoding before
[web] Stop percent-decoding URLs written to browser history (flutter#…
github-actions commentedon Oct 17, 2026
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 -vand a minimal reproduction of the issue.[web] Stop percent-decoding URLs written to browser history (flutter#…