)]}'
{
  "commit": "63ee971b4e359bc7fb1bb4c34947dcd3bfeffaed",
  "tree": "2a3e55fb44b533af3032dc82ef9a918bde54c106",
  "parents": [
    "0dfacae24598c6d7def2bcc2fddc4f20afd29518"
  ],
  "author": {
    "name": "David Miguel Lozano",
    "email": "me@davidmiguel.com",
    "time": "Tue Sep 22 23:38:08 2026 +0200"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Tue Sep 22 21:38:08 2026 +0000"
  },
  "message": "[go_router] Fix ShellRoute chrome dropped from semantics tree by ModalBarrier (#12353)\n\nShell chrome painted before a `ShellRoute` or `StatefulShellRoute` navigator (a side rail, or an app bar in a `Row`/`Column` based shell) disappears from the semantics tree. Screen readers cannot reach it at all: the nodes are not merely unnamed, they do not exist.\n\nThe mechanism: every `ModalRoute` builds a `ModalBarrier` wrapped in `BlockSemantics`, which drops the semantics of everything painted before it up to the nearest semantics boundary. The nested `Navigator` that go_router builds for shell routes does not establish such a boundary, so the block escapes the shell\u0027s navigator and prunes the shell\u0027s own chrome. Bottom-nav shells are unaffected only because `Scaffold` happens to paint its body before its bars.\n\nThis PR wraps the navigator built for `ShellRoute`/`StatefulShellRoute` branches in `Semantics(container: true)`, which contains the block. The root navigator is left unwrapped, since it has no earlier-painted siblings by construction. This is the workaround a framework team member confirmed on the linked issue; applying it inside go_router fixes it for every shell consumer without app-side patches.\n\nSemantics tree of a minimal repro (a `Row` shell: 220px sidebar with three nav buttons, routed content on the right), before and after, captured with `debugDumpSemanticsTree`:\n\n\u003cdetails\u003e\n\u003csummary\u003eBefore: 6 nodes, the entire sidebar subtree is missing\u003c/summary\u003e\n\n```\nSemanticsNode#0\n │ Rect.fromLTRB(0.0, 0.0, 2400.0, 1800.0)\n │\n └─SemanticsNode#1\n   │ Rect.fromLTRB(0.0, 0.0, 800.0, 600.0) scaled by 3.0x\n   │ textDirection: ltr\n   │ sortKey: OrdinalSortKey#39327(order: 0.0)\n   │\n   └─SemanticsNode#2\n     │ Rect.fromLTRB(0.0, 0.0, 800.0, 600.0)\n     │ flags: scopesRoute\n     │\n     └─SemanticsNode#3\n       │ Rect.fromLTRB(221.0, 0.0, 800.0, 600.0)\n       │ sortKey: OrdinalSortKey#39327(order: 0.0)\n       │\n       └─SemanticsNode#4\n         │ Rect.fromLTRB(0.0, 0.0, 579.0, 600.0)\n         │ flags: scopesRoute\n         │\n         └─SemanticsNode#5\n             Rect.fromLTRB(85.5, 284.0, 493.5, 316.0)\n             label: \"Dashboard content\"\n             textDirection: ltr\n```\n\nNode `#3` starts at `x\u003d221`, right of the 220px sidebar plus a 1px divider. There is no node anywhere for the sidebar: no title, no navigation container, no buttons. The sidebar is painted before the `/dashboard` route (`#4`, `scopesRoute`) inside the same enclosing semantics scope, which is exactly what that route\u0027s `BlockSemantics` drops.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eAfter: sidebar fully present, routed content unchanged (two nodes for the repro\u0027s own toggle switch omitted for brevity)\u003c/summary\u003e\n\n```\nSemanticsNode#0\n │ Rect.fromLTRB(0.0, 0.0, 2400.0, 1800.0)\n │\n └─SemanticsNode#1\n   │ Rect.fromLTRB(0.0, 0.0, 800.0, 600.0) scaled by 3.0x\n   │ textDirection: ltr\n   │ sortKey: OrdinalSortKey#39327(order: 0.0)\n   │\n   └─SemanticsNode#2\n     │ Rect.fromLTRB(0.0, 0.0, 800.0, 600.0)\n     │ flags: scopesRoute\n     │\n     ├─SemanticsNode#3\n     │   Rect.fromLTRB(16.0, 16.0, 204.0, 76.0)\n     │   label: \"Nested Navigator Semantics\"\n     │   textDirection: ltr\n     │\n     ├─SemanticsNode#4\n     │ │ Rect.fromLTRB(0.0, 92.0, 220.0, 260.0)\n     │ │ label: \"Main navigation\"\n     │ │ textDirection: ltr\n     │ │\n     │ ├─SemanticsNode#5\n     │ │   Rect.fromLTRB(0.0, 0.0, 220.0, 56.0)\n     │ │   actions: focus, tap\n     │ │   flags: isSelected, isButton, hasEnabledState, isEnabled,\n     │ │     isFocusable, hasSelectedState\n     │ │   label: \"Dashboard\"\n     │ │   textDirection: ltr\n     │ │\n     │ ├─SemanticsNode#6\n     │ │   Rect.fromLTRB(0.0, 56.0, 220.0, 112.0)\n     │ │   actions: focus, tap\n     │ │   flags: isButton, hasEnabledState, isEnabled, isFocusable,\n     │ │     hasSelectedState\n     │ │   label: \"Settings\"\n     │ │   textDirection: ltr\n     │ │\n     │ └─SemanticsNode#7\n     │     Rect.fromLTRB(0.0, 112.0, 220.0, 168.0)\n     │     actions: focus, tap\n     │     flags: isButton, hasEnabledState, isEnabled, isFocusable,\n     │       hasSelectedState\n     │     label: \"Reports\"\n     │     textDirection: ltr\n     │\n     └─SemanticsNode#10\n       │ Rect.fromLTRB(221.0, 0.0, 800.0, 600.0)\n       │ sortKey: OrdinalSortKey#39327(order: 0.0)\n       │\n       └─SemanticsNode#11\n         │ Rect.fromLTRB(0.0, 0.0, 579.0, 600.0)\n         │ flags: scopesRoute\n         │\n         └─SemanticsNode#12\n             Rect.fromLTRB(85.5, 284.0, 493.5, 316.0)\n             label: \"Dashboard content\"\n             textDirection: ltr\n```\n\nThe routed content node (\"Dashboard content\") is byte-identical in both dumps. The fix does not change the routed content\u0027s semantics, only whether the chrome painted before the shell navigator survives alongside it.\n\n\u003c/details\u003e\n\nNotes for review:\n\n- Tests: the fix commit adds a `Shell navigator semantics boundary` group to `builder_test.dart` (chrome survives, structural wrap present, root navigator not wrapped), and a second commit adds a `StatefulShellRoute.indexedStack` regression test covering branch switching. Removing the wrap makes the chrome tests fail with `Found 0 widgets with a semantics label`.\n- Version/CHANGELOG: go_router uses batch release, so this PR adds a file under `pending_changelogs/` (`version: patch`) instead of touching `pubspec.yaml` or `CHANGELOG.md`.\n- Interaction with flutter/flutter#181519 (replacing `BlockSemantics` in modal routes with `AccessibilityFocusBlockType.blockSubtree`): the fix establishes a semantics container boundary at the shell navigator, which is where a nested navigator should scope its routes\u0027 blocking regardless of the blocking mechanism. If that migration later makes the containment unnecessary, the wrap stays harmless.\n\nFixes https://github.com/flutter/flutter/issues/135656\nRelated: https://github.com/flutter/flutter/issues/55758, https://github.com/flutter/flutter/issues/150978\n\n## Pre-Review Checklist\n\n[^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "e58662646681368c1c2c9146e83f09f742fc98a4",
      "old_mode": 33188,
      "old_path": "packages/go_router/lib/src/builder.dart",
      "new_id": "e993df6ca19618549849eac1f8e663e0489eb67d",
      "new_mode": 33188,
      "new_path": "packages/go_router/lib/src/builder.dart"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "04521eca99306192cc811ef7dc2cf32d79fb1441",
      "new_mode": 33188,
      "new_path": "packages/go_router/pending_changelogs/shellroute_navigator_semantics_boundary.yaml"
    },
    {
      "type": "modify",
      "old_id": "26dd65f52601a45de67daab35f7bad6543275ae2",
      "old_mode": 33188,
      "old_path": "packages/go_router/test/builder_test.dart",
      "new_id": "4b31ebb6bb18aed92401825c81894e5833de0f13",
      "new_mode": 33188,
      "new_path": "packages/go_router/test/builder_test.dart"
    }
  ]
}
