Skip to content

Fix page.query in main() and query strings in TemplateRoute - #6942

Open
ndonkoHenri wants to merge 9 commits into
flet-1.1.0from
fix/page-query-initial-route-5898
Open

ndonkoHenri wants to merge 9 commits into
flet-1.1.0from
fix/page-query-initial-route-5898

Conversation

@ndonkoHenri

@ndonkoHenri ndonkoHenri commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #5898

page.query in main()

QueryString kept its own copy of the URL, which only a route change refreshed (through page.query()). Before the first route change, so in main(), page.query.to_dict was {} and get() raised KeyError. A web app opened at /?name=Joe couldn't read name.

The stored copy caused two more bugs:

  • Double decoding. The query string was percent-decoded twice: q=dom%26dogs read back as q="dom", and push_route("/search", q="salt & pepper") as q="salt ".
  • Wrong path on desktop. The copy was page.url + page.route, so path and to_dict were wrong wherever page.url isn't an http URL. On desktop, page.query.path came back as /tmp/flet.sock/store.

QueryString now reads page.route every time. It drops a # fragment and splits at the first ?; it doesn't use urlsplit(), which reads a route starting with // as a host. The undocumented QueryString.url attribute and __call__ (page.query()) are gone; nothing in the repo used them.

TemplateRoute and query strings

TemplateRoute.match() ran the template against the whole route, query string included:

Route Template Before Now
/books/42?tab=reviews /books/:id id == "42?tab=reviews" id == "42"
/store?ref=ad /store no match match
/books/42?next=/books/43 /books/:id no match id == "42"

It now matches only the path, as the declarative Router already does, and also ignores a fragment. The client builds page.route from the full URI, so a deep link can carry a fragment. This also fixes the routing guide's own TemplateRoute(page.route) example and the trolli example for routes with a query string.

Docs

  • A QueryString reference page. page.query had a one-line entry and an unlinked type, so get(), to_dict and path weren't documented anywhere.
  • A "Query strings" section in the Navigation and Routing guide, plus a note in its Route templates section.
  • The guide's API links now use the xref form.

Tests

  • test_query_string.py opens a real Session the way the client connects, then checks:
    • that page.query follows page.route in main(), in on_route_change and after later route changes;
    • how the route splits into path, query and fragment;
    • that push_route parameters come back decoded once.
  • test_template_route.py covers path-only matching.
  • Results: 18 cases. On flet-1.1.0 without this change, 13 fail; the 5 that pass guard the new path/query/fragment splitting.
  • Earlier browser run: a web app opened at /?name=Joe&q=dom%26dogs gave {} and a KeyError in main() before the fix, and {'name': 'Joe', 'q': 'dom&dogs'} after it.

There's no integration test. FletTestApp always starts the app on /, because Flutter's test binding forces the initial route, so opening the app on a route with a query string can't be reproduced there.

Summary by Sourcery

Fix route query parsing and template matching so query strings and fragments are handled consistently across app startup and navigation.

Bug Fixes:

  • Make page.query read and decode parameters directly from the current route, including on initial app launch, without double decoding or incorrect path handling.
  • Make TemplateRoute match route paths independently of query strings and fragments.

Enhancements:

  • Remove the obsolete cached QueryString URL and refresh API, while keeping query results synchronized with page.route.

Documentation:

  • Add comprehensive QueryString API documentation and document query-string handling and path-only template matching in the Navigation and Routing guide.

Tests:

  • Add coverage for query parsing across initial routes, route changes, fragments, encoded values, navigation parameters, and page lifetime.
  • Add coverage for TemplateRoute matching with query strings and fragments and clearing parameters after failed matches.

Chores:

  • Update the changelog with the routing fixes and documentation additions.

QueryString cached a URL that only route changes refreshed, so in
main() page.query was empty for an app opened with a query string.
Parse page.route on every read instead, which also stops
percent-encoded values from being decoded twice.
page.query had a one-line entry on the Page reference and no page for its
QueryString type, so get(), to_dict and path were documented nowhere.

- Rewrite the QueryString class docstring and get()'s docstring in Google
  style; get() reads to_dict directly.
- Add website/docs/types/querystring.md and its sidebar entry.
- Add a "Query strings" section to the Navigation and Routing guide, and
  note that TemplateRoute should match page.query.path when routes carry a
  query string, since the last template parameter otherwise captures it.
TemplateRoute.match() ran the template against the whole route, query
string included. The last parameter captured the query string
(`/books/42?tab=reviews` gave `id == "42?tab=reviews"`), and a template
without a trailing parameter didn't match at all (`/store?ref=ad` against
`/store`), nor did any route whose query string contains a `/`. Match the
part before the first `?`, as the declarative Router already does.

QueryString now splits the route at the first `?` as well, instead of
with urlsplit(), which reads a route starting with `//` as a host name.

The routing guide's note to match `page.query.path` is replaced by a
description of the new behavior.
The client builds page.route from the full URI, so a deep link or URL
with a `#` fragment reaches Python as `/books/42?tab=reviews#details`.
Splitting only at `?` put the fragment into the last query value, and
TemplateRoute's last parameter captured it. Drop the fragment before
splitting.

Also document that parameters without a value (`?debug`, `?debug=`) are
left out, and say in the routing guide that `page.query` reflects a
navigation once the route has changed, e.g. in on_route_change.
Link API symbols with the xref form instead of relative page paths, and
link `get()` in the Query strings section to QueryString.get. The
`page.views` links now point at BasePage.views instead of the top of the
Page reference.
q="flet" and page=2 read like the flet package and the page object. Use q="shoes", limit=20 and "salt & pepper" instead.
test_query_string.py opens a real Session the way the client connects
and checks that page.query follows page.route in main(), in
on_route_change and after later route changes, how the route splits into
path, query and fragment, that push_route parameters read back decoded
once, and that a query outliving its page is empty.

test_template_route.py checks that templates match the route path
whatever its query string and fragment, and that a failed match clears
the previous parameters.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The implementation is focused, documented, and covered by regression tests for the reported routing failures.

0 open findings

What changed in this PR

Fixes routing query parsing at startup and ensures route templates ignore query strings and fragments.

Changes:

  • Parse page.query directly from the current route.
  • Match TemplateRoute against path-only routes.
  • Add documentation, changelog entries, and regression tests.
File Description
CHANGELOG.md Records routing fixes and documentation.
sdk/​python/​packages/​flet/​src/​flet/​controls/​page.py Removes cached query refresh behavior.
sdk/​python/​packages/​flet/​src/​flet/​controls/​query_string.py Parses queries dynamically from page.route.
sdk/​python/​packages/​flet/​src/​flet/​controls/​template_route.py Excludes queries and fragments from matching.
sdk/​python/​packages/​flet/​tests/​test_query_string.py Tests startup, navigation, decoding, and lifetime behavior.
sdk/​python/​packages/​flet/​tests/​test_template_route.py Tests path-only template matching.
website/​docs/​cookbook/​navigation-and-routing.md Documents query strings and updates API links.
website/​docs/​types/​querystring.md Adds the QueryString API reference.
website/​sidebars.yml Adds QueryString to navigation.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Deploying flet-website-v2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2ce3ae0
Status: ✅  Deploy successful!
Preview URL: https://e2e93906.flet-website-v2.pages.dev
Branch Preview URL: https://fix-page-query-initial-route.flet-website-v2.pages.dev

View logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants