Repository navigation
Fix Network toolbar and footer overflow on narrow screens - #10057
NemesisDevX wants to merge 2 commits into
Conversation
The Network screen toolbar and the shared footer status line overflowed and overlapped at narrow widths. Network toolbar: below 600 px the search field moves to its own row, and below 900 px the filter field moves to its own row. Layout at 900 px and above is unchanged. Footer (StatusLine, shared by all screens): footer text is truncated instead of overflowing. Below 900 px the video tutorial link and, when DevTools is embedded, the embedded actions move into a More actions menu. Each menu item runs the same action as its inline counterpart. Below 600 px the disconnected status shows an icon with a tooltip instead of text. Fixes flutter#10004 Assisted-by: Devin AI
There was a problem hiding this comment.
Code Review
This pull request improves responsiveness on narrow screens for the DevTools status line and the Network screen toolbar, preventing layout overflows and overlaps. It wraps status line items in flexible widgets, moves secondary actions into an overflow menu on compact widths, and wraps the Network screen's search and filter fields onto their own rows when space is limited. Comprehensive responsive layout tests and test utilities are also added. The review feedback suggests using Flexible instead of Expanded for the page status in the status line to maintain proper spacing, and utilizing GlobalKeys for the search and filter fields in the Network screen to preserve widget state and focus during responsive reparenting.
| pageStatus, | ||
| // The page status needs to be flexible so that the status line can | ||
| // shrink to fit narrow screens without overflowing. | ||
| Expanded(child: pageStatus), |
There was a problem hiding this comment.
[CONCERN] Using Expanded forces the pageStatus widget to occupy all remaining horizontal space, which overrides the MainAxisAlignment.spaceBetween alignment of the parent Row. This can cause visual layout inconsistencies and push other footer elements to the extreme edges even when there is plenty of screen width.\n\nUsing Flexible (which defaults to FlexFit.loose) allows the pageStatus to only take up the space it actually needs when it is short, preserving the intended spaceBetween spacing, while still constraining it and allowing it to shrink (preventing overflow) on narrow screens.
| Expanded(child: pageStatus), | |
| Flexible(child: pageStatus), |
There was a problem hiding this comment.
Done — switched to Flexible. With Expanded the page status absorbed all leftover space inside its own box, which changed the even spaceBetween spacing the footer had before this change. Flexible restores the previous wide-screen spacing while still shrinking (and ellipsizing) when space is constrained. Added tests asserting the status keeps its natural width on wide screens and that the connection status stays right-aligned.
| final searchField = SearchField<NetworkController>( | ||
| searchController: controller, | ||
| searchFieldWidth: screenWidth <= MediaSize.xs | ||
| ? defaultSearchFieldWidth | ||
| : wideSearchFieldWidth, | ||
| ); | ||
| final filterField = StandaloneFilterField<NetworkRequest>( | ||
| controller: controller, | ||
| filteredItem: 'request', | ||
| ); |
There was a problem hiding this comment.
[CONCERN] When the screen width crosses the responsive thresholds (MediaSize.xs or MediaSize.s), the searchField and filterField are reparented to different rows in the widget tree. In Flutter, moving a widget to a different position in the tree causes it to lose its state (including focus and cursor position) unless a GlobalKey is used.\n\nTo preserve focus and text selection during resizing, please define two GlobalKeys in your _NetworkProfilerControlsState class and pass them to the fields:\n\ndart\n// In _NetworkProfilerControlsState:\nfinal _searchFieldKey = GlobalKey();\nfinal _filterFieldKey = GlobalKey();\n
| final searchField = SearchField<NetworkController>( | |
| searchController: controller, | |
| searchFieldWidth: screenWidth <= MediaSize.xs | |
| ? defaultSearchFieldWidth | |
| : wideSearchFieldWidth, | |
| ); | |
| final filterField = StandaloneFilterField<NetworkRequest>( | |
| controller: controller, | |
| filteredItem: 'request', | |
| ); | |
| final searchField = SearchField<NetworkController>(\n key: _searchFieldKey,\n searchController: controller,\n searchFieldWidth: screenWidth <= MediaSize.xs\n ? defaultSearchFieldWidth\n : wideSearchFieldWidth,\n );\n final filterField = StandaloneFilterField<NetworkRequest>(\n key: _filterFieldKey,\n controller: controller,\n filteredItem: 'request',\n ); |
There was a problem hiding this comment.
Done — the fields now have stable GlobalKeys held by _NetworkProfilerControlsState. Verified in a widget test that resizes across the 600 and 900 breakpoints in both directions: without keys, each reparent recreated the filter field's State and internal TextEditingController (selection and focus lost, text re-seeded from the normalized query), and the remounted search field's autofocus stole focus. With keys, text, selection, focus, and State identity survive every crossing.
Give the Network search and filter fields stable keys so their widget state (text, selection, and focus) survives being reparented between toolbar rows when the window crosses the 600 and 900 pixel breakpoints. Previously each reparent recreated the filter field's text controller and focus, losing the selection and focus, and the remounted search field's autofocus stole focus. Wrap the footer's page status in Flexible instead of Expanded so it keeps its natural width and the status line's spaceBetween alignment keeps spacing even on wide screens, matching the layout before the responsive change. It still shrinks below its natural width, and ellipsizes, when space is constrained. Assisted-by: Devin AI
Fixes #10004
The Network screen toolbar and the shared footer status line overflowed and overlapped at narrow widths (328 to 599 px). The toolbar's search and filter fields were squeezed into one row beside fixed controls. The footer's page status, device text and embedded actions did not fit.
What changes
StatusLine, shared by all screens): footer text is truncated with an ellipsis instead of overflowing. Below 900 px the video tutorial link and, when DevTools is embedded, the Settings, Feedback, About and extension-settings actions move into a "More actions" popup. Each item calls the same callback as its inline control, and the tutorial item records the same analytics event. Below 600 px the disconnected status shows an icon with a tooltip instead of text.DevToolsScaffold.defaultActionsnow returnsList<ScaffoldAction>(wasList<Widget>), so the footer can reuse the existing actions. The other caller inapp.dartis unchanged.Reviewer notes:
Before and after
Before and after captures at 328 px and 411 px, and separate 1400 px desktop comparisons, were taken with headless Chromium against the upstream base (
9ac04e2) and this branch, using the same networking test app. They are attached below as public GitHub attachments.Attachments, all public GitHub user attachments. The GIF and images were captured by scripts that drove headless Chromium against real pages. This is agent-operated capture, not manual testing.
Corrected 9.8-second GIF. Three scenes: 328 px standalone, 411 px embedded footer, and the More actions menu open at 411 px. Each frame shows the upstream base on the left and this branch on the right.
328 px standalone. Base on the left, this branch on the right.
411 px embedded footer. Base on the left, this branch on the right.
411 px embedded, More actions menu open. The popup overlaps part of the footer while it is open.
1400 px desktop, standalone. Base on top, this branch below. The top toolbar band and the request details pane are pixel-identical between builds. The footer request count differs with traffic.
1400 px desktop, embedded. Base on top, this branch below.
Tests
Run on the repository-pinned candidate SDK (Flutter
3.50.0-0.2.pre, revisionfab991537bf). Results below are split by commit because the test selection changed when the review follow-up added tests.Initial commit
3d3804d:flutter testonstatus_line_test.dartandnetwork_responsive_test.dartflutter teston framework, network, logging, extension, memory diff and property editor suitesdart format --set-exit-if-changed(changed files)dart analyze(changed files) anddart analyze libdt presubmit(without--fix)flutter_lintsresolution warnings in fixture format checks.Review follow-up commit
416fe20(addresses the two review comments:Flexiblepage status, stable keys for the search/filter fields):flutter testonstatus_line_test.dartandnetwork_responsive_test.dartflutter testontest/framework,test/screens/networkandtest/screens/loggingdart analyze(changed files)dt presubmit(without--fix)dart analyze --fatal-infosanddart format --set-exit-if-changedacross the repo. Warnings: nonfatal analytics network exceptions, andflutter_lintsresolution warnings in fixture format checks.flutter test test/(fulldevtools_appsuite, last run on3d3804d; not rerun for416fe20, which only touches the footer and toolbar covered above)+1721 ~12 -1, and+1720 ~12 -1with exit 1). The base9ac04e2run also failed (+1649 ~12 -1). The failures are a CPU-profile benchmark threshold (GenerateFromCpuSamplesBenchmark, expected under 120000) and an intermittent "used after being disposed" error onValueNotifier<StackFrameAndSourcePosition?>. Both also occur on the base. This change does not touch those tests, and I did not alter them. CI is the reference for the full suite.Not run: golden tests,
integration_test/tests, and macOS or Windows desktop builds.Known limitations (pre-existing, not changed here)
PopupMenuButtonbehaves the same on this SDK. The widget tests cover arrow keys under the Flutter test binding, not in a browser.AI assistance disclosure
Implementation and tests were developed with AI assistance (Devin, an AI software agent), which also ran the scripted browser checks. GPT-6, an AI model, reviewed the change and the evidence. The contributor directed and supervised this work, has reviewed the submitted changes, understands them, and takes responsibility for them. An earlier series of agent-authored development commits is not part of this PR. This PR contains a single commit with the same tree as the tested branch.
Checklist
Pre-launch checklist (boxes left unchecked are for the contributor to confirm personally):
General checklist:
///).Issues checklist:
contributions-welcomeorgood-first-issuelabel. (Not applicable: DevTools Network tab header and footer toolbar controls overlap on narrow screens #10004 has neither label.)contributions-welcomeorgood-first-issuelabel. I understand this means my PR might take longer to be reviewed.Tests checklist:
AI-tooling checklist:
Feature-change checklist:
packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md.If you need help, consider asking for help on Discord.