Skip to content

[flutter_web_plugins] HashUrlStrategy drops/overwrites query parameters #116415

Closed

Description

@Bungeefan

This code ignores the pathname and query parameters (search), if there is an internalUrl.

Before initial routing:
http://localhost:45389/?example=true
After routing:
http://localhost:45389/#/menu
After routing (with fix):
http://localhost:45389/?example=true#/menu

return internalUrl.isEmpty
? '${_platformLocation.pathname}${_platformLocation.search}'
: '#$internalUrl';

In our use case, it would be important to keep these parameters in the URL, even when routing in Flutter.
And as with other technologies (e.g. React Router), the parameters remain unchanged, and the hash routing is appended.

Example fix:

return "${_platformLocation.pathname}${_platformLocation.search}${internalUrl.isEmpty ? '' : '#$internalUrl'}";

Workaround:
Extend HashUrlStrategy with the fix and set it via setUrlStrategy from flutter_web_plugins.

Activity

  1. darshankawar commented on Dec 2, 2022

    @darshankawar
    Member

    Thanks for the report @Bungeefan
    Can you provide a complete minimal code sample that shows the current behavior along with flutter doctor -v ?

  2. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Dec 2, 2022
  3. Bungeefan commented on Dec 2, 2022

    @Bungeefan
    ContributorAuthor

    Sure.

    Code sample

    Watch the URL change with the Flutter HashUrlStrategy and then uncomment the line before runApp.

    Run command:

    flutter run -d chrome --web-launch-url "http://localhost:12345/routing?example=true" --web-port 12345
    

    You probably need to add the following to your pubspec.yml:

    flutter_web_plugins:
      sdk: flutter

    main.dart

    import 'package:flutter/material.dart';
    import 'package:flutter_web_plugins/flutter_web_plugins.dart';
    
    void main() {
      // Uncomment to demonstrate fix
      // setUrlStrategy(const FixedHashUrlStrategy());
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          routes: {
            '/': (context) => const MyHomePage(),
            '/test': (context) => const TestScreen(),
          },
          initialRoute: "/",
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      const MyHomePage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Text("Flutter Demo Home Page")),
          body: Center(
            child: ElevatedButton(
              onPressed: () => Navigator.of(context).pushNamed("/test"),
              child: const Text("Press me to route"),
            ),
          ),
        );
      }
    }
    
    class TestScreen extends StatelessWidget {
      const TestScreen({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: const Center(child: Text("Test Screen")),
        );
      }
    }
    
    class FixedHashUrlStrategy extends HashUrlStrategy {
      final PlatformLocation _platformLocation;
    
      const FixedHashUrlStrategy(
          [this._platformLocation = const BrowserPlatformLocation()])
          : super(_platformLocation);
    
      @override
      String prepareExternalUrl(String internalUrl) {
        return "${_platformLocation.pathname}${_platformLocation.search}${internalUrl.isEmpty ? '' : '#$internalUrl'}";
      }
    }
    flutter doctor -v
    [✓] Flutter (Channel stable, 3.3.9, on Manjaro Linux 6.0.8-1-MANJARO, locale de_AT.UTF-8)
        • Flutter version 3.3.9 on channel stable at /home/shamader/tools/flutter/flutter
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision b8f7f1f986 (vor 10 Tagen), 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 /mnt/Daten/tools/Android/Sdk
        • Platform android-33, build-tools 33.0.0
        • Java binary at: /home/shamader/.local/share/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335/jre/bin/java
        • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
        • All Android licenses accepted.
    
    [✓] Chrome - develop for the web
        • CHROME_EXECUTABLE = /usr/bin/chromium
    
    [✗] Linux toolchain - develop for Linux desktop
        • clang version 14.0.6
        ✗ CMake is required for Linux development.
          It is likely available from your distribution (e.g.: apt install cmake), or can be downloaded from https://cmake.org/download/
        ✗ ninja is required for Linux development.
          It is likely available from your distribution (e.g.: apt install ninja-build), or can be downloaded from https://github.com/ninja-build/ninja/releases
        • pkg-config version 1.8.0
    
    [✓] Android Studio (version 2021.3)
        • Android Studio at /home/shamader/.local/share/JetBrains/Toolbox/apps/AndroidStudio/ch-0/213.7172.25.2113.9123335
        • Flutter plugin version 71.0.3
        • Dart plugin version 213.7433
        • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    
    [✓] Connected device (3 available)
        • M2103K19PG (mobile) • xwcm955djrrslnpj • android-arm64  • Android 12 (API 31)
        • Linux (desktop)     • linux            • linux-x64      • Manjaro Linux 6.0.8-1-MANJARO
        • Chrome (web)        • chrome           • web-javascript • Chromium 107.0.5304.121 Arch Linux
    
    [✓] HTTP Host Availability
        • All required HTTP hosts are available
    
    ! Doctor found issues in 1 category.
    
  4. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Dec 2, 2022
  5. darshankawar commented on Dec 5, 2022

    @darshankawar
    Member

    Thanks for the details. Using the code sample provided, seeing the same behavior as reported.

    stable, master flutter doctor -v
    [✓] Flutter (Channel stable, 3.3.9, on macOS 12.2.1 21D62 darwin-x64, locale
        en-GB)
        • Flutter version 3.3.9 on channel stable at
          /Users/dhs/documents/fluttersdk/flutter
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision b8f7f1f986 (12 days ago), 2022-11-23 06:43:51 +0900
        • Engine revision 8f2221fbef
        • Dart version 2.18.5
        • DevTools version 2.15.0
    
    [!] Xcode - develop for iOS and macOS (Xcode 12.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        ! Flutter recommends a minimum Xcode version of 13.
          Download the latest version or update via the Mac App Store.
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] VS Code (version 1.62.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.21.0
    
    [✓] Connected device (5 available)
        • SM G975F (mobile)       • RZ8M802WY0X • android-arm64   • Android 11 (API 30)
        • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
          ios            • iOS 14.4.1 18D61
        • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
          ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
        • macOS (desktop)            • macos                                    •
          darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
        • Chrome (web)               • chrome                                   •
          web-javascript • Google Chrome 98.0.4758.80
    
    [✓] HTTP Host Availability
        • All required HTTP hosts are available
    
    ! Doctor found issues in 1 category.
    
    [!] Flutter (Channel master, 3.7.0-1.0.pre.11, on macOS 12.2.1 21D62 darwin-x64,
        locale en-GB)
        • Flutter version 3.7.0-1.0.pre.11 on channel master at
          /Users/dhs/documents/fluttersdk/flutter
        ! Warning: `flutter` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/flutter, which is not inside
          your current Flutter SDK checkout at
          /Users/dhs/documents/fluttersdk/flutter. Consider adding
          /Users/dhs/documents/fluttersdk/flutter/bin to the front of your path.
        ! Warning: `dart` on your path resolves to
          /Users/dhs/Documents/Fluttersdk/flutter/bin/dart, which is not inside your
          current Flutter SDK checkout at /Users/dhs/documents/fluttersdk/flutter.
          Consider adding /Users/dhs/documents/fluttersdk/flutter/bin to the front
          of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision ce94230281 (2 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.
    
    
    [!] Xcode - develop for iOS and macOS (Xcode 12.3)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        ! Flutter recommends a minimum Xcode version of 13.
          Download the latest version or update via the Mac App Store.
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] VS Code (version 1.62.0)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.21.0
    
    [✓] Connected device (5 available)
        • SM G975F (mobile)       • RZ8M802WY0X • android-arm64   • Android 11 (API 30)
        • Darshan's iphone (mobile)  • 21150b119064aecc249dfcfe05e259197461ce23 •
          ios            • iOS 14.4.1 18D61
        • iPhone 12 Pro Max (mobile) • A5473606-0213-4FD8-BA16-553433949729     •
          ios            • com.apple.CoreSimulator.SimRuntime.iOS-14-3 (simulator)
        • macOS (desktop)            • macos                                    •
          darwin-x64     • Mac OS X 10.15.4 19E2269 darwin-x64
        • Chrome (web)               • chrome                                   •
          web-javascript • Google Chrome 98.0.4758.80
    
    [✓] HTTP Host Availability
        • All required HTTP hosts are available
    
    ! Doctor found issues in 1 category.
    
    
    
    
  6. added
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    platform-webWeb applications specifically
    and removed
    in triagePresently being triaged by the triage team
    on Dec 5, 2022
  7. mdebbar commented on Dec 16, 2022

    @mdebbar
    Contributor

    Interesting. Is there a specific browser that this happens on?

    In Chrome, if I do history.pushState(null, null, '#foo') it doesn't override pathname and search in the url. It just appends the hash part.

  8. 9 remaining items

  9. darshankawar commented on Apr 5, 2023

    @darshankawar
    Member

    Reopening based on above comment.

  10. zbarbuto commented on May 3, 2023

    @zbarbuto

    BTW I found this to be a better workaround for the prepareExternalUrl override:

        return "${_platformLocation.pathname}${internalUrl.isEmpty ? '' : '#$internalUrl'}${_platformLocation.search}";

    This will redirect example.com?foo=bar to something like example.com/#/home?foo=bar which is needed to consume the query parameters in a route.

  11. Bungeefan commented on May 3, 2023

    @Bungeefan
    ContributorAuthor

    @zbarbuto
    No, I don't think that that would solve the issue, as this would move the original search part of the normal URL behind the Hashtag, which makes it a whole different thing and would probably cause problems.

    The goal was to keep the original "browser" search parameters where they were and not drop them.
    You would still be able to use something like example.com/?browser_foo=bar#/home?flutter_foo=bar.
    internalUrl already includes the Flutter-specific search parameters, which has to be behind the Hashtag to work!

    This should be a fully usable and correct URL without impacting any existing and future Flutter routing.
    It just allows the usage of plain "old" search parameters, as used in times before there were even any JavaScript.

  12. zbarbuto commented on May 3, 2023

    @zbarbuto

    You're right.

    I think it might be a misunderstanding on my part (or even an issue with the routing package I'm using). After initial redirects to e.g. example.com/?code=1234#/home the route widget was not able to get access to the query parameter (it was null) but if it was at example.com/#/home?code=1234 it was fine.

    However if it's behind the hash as in your setup I can at least still get to it via window.location.search as the original problem is that it was getting dropped either way.

  13. Bungeefan commented on May 3, 2023

    @Bungeefan
    ContributorAuthor

    Yeah, because the first query parameters are not the same as the one behind the hashtag, in fact, you can put anything behind the hashtag and parse it as you like, it just makes sense to use the same format again, therefore many JavaScript frameworks (including Flutter) use it that way.

    By the way, if you want to retrieve the first query parameters without importing dart:html you can use Uri.base.queryParameters instead.
    This works on all platforms without a conditional import!

  14. added a commit that references this issue on May 9, 2023
    cab29b2
  15. mdebbar commented on May 9, 2023

    @mdebbar
    Contributor

    I apologize for the delay in replying.

    I confirmed that you are right, for some reason, the path and query params get deleted as soon as the flutter app starts. It's weird because I tried doing history.pushState({}, null, '#/') on a few websites and the query params weren't removed.

    My suspicion is that this is caused by our <base href="/">. Maybe that's causing history.pushState() to behave differently.

    Anyway, thanks for looking into this and fixing it!

  16. added
    r: fixedIssue is closed as already fixed in a newer version
    on May 10, 2023
  17. github-actions commented on May 24, 2023

    @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.

  18. locked as resolved and limited conversation to collaborators on May 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

assigned for triageissue is assigned to a domain expert for further triagef: routesNavigator, Router, and related APIs.found in release: 3.3Found to occur in 3.3found in release: 3.7Found to occur in 3.7has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-webWeb applications specificallyr: fixedIssue is closed as already fixed in a newer version

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions