Adds WebSocket support to Network panel (GSoC'26) - #9968
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces WebSocket profiling support to the DevTools network screen. It adds a new WebSocket request type, updates the NetworkController and NetworkService to fetch and process WebSocket traffic, and implements UI views (WebSocketFramesView and overview rows) to inspect WebSocket connections and their frames. The review feedback highlights a logical bug in updateLastRefreshTime() where socket refresh is called twice instead of updating the WebSocket refresh time, and suggests wrapping the DataTable in WebSocketFramesView with a horizontal scroll view to prevent layout overflows.
|
For WebSocket frame inspection, should each frame be represented as a separate event in the timeline, or should multiple frames of the same connection be grouped in some way to avoid making the timeline too dense for long-lived connections? |
|
This PR aims to integrate the entire WebSocket support from |
srawlins
left a comment
There was a problem hiding this comment.
Just a preliminary review; I haven't tried it or reviewed the tests.
|
needs review |
|
I'm adding websocket support to the devtools companion app so I can play with this |
|
Yes definitely change vm_service dependency in Yeah 15.3.0 seems to work well. |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces WebSocket support to the DevTools Network Profiler, enabling tracking of WebSocket connections, lifecycle events, frame-level inspection, and connection timing. The reviewer provided critical feedback regarding the timing mechanism, pointing out that using wall-clock time instead of the VM's monotonic timeline clock will break profiling. Additionally, the reviewer raised concerns about potential UI jank due to a lack of virtualization in the frames table, missing serialization for offline support, a potential crash when parsing event names without defensive checks, and a duplicated header in the release notes.
| networkService.updateLastWebSocketDataRefreshTime( | ||
| alreadyRecordingWebSocket: alreadyRecordingHttp, | ||
| ); |
There was a problem hiding this comment.
[MUST-FIX] This call needs to be awaited because updateLastWebSocketDataRefreshTime must be updated to be asynchronous to fetch the VM timeline clock timestamp. Using wall-clock time (DateTime.now()) will cause WebSocket profiling to fail as the VM Service expects monotonic timeline clock timestamps.
| networkService.updateLastWebSocketDataRefreshTime( | |
| alreadyRecordingWebSocket: alreadyRecordingHttp, | |
| ); | |
| await networkService.updateLastWebSocketDataRefreshTime( | |
| alreadyRecordingWebSocket: alreadyRecordingHttp, | |
| ); |
References
- Prioritize logic, performance on the UI thread, and architectural consistency. (link)
| void updateLastWebSocketDataRefreshTime({ | ||
| bool alreadyRecordingWebSocket = false, | ||
| }) { | ||
| if (!alreadyRecordingWebSocket) { | ||
| final now = DateTime.now().microsecondsSinceEpoch; | ||
| for (final isolateId | ||
| in lastWebSocketDataRefreshTimePerIsolate.keys.toList()) { | ||
| lastWebSocketDataRefreshTimePerIsolate[isolateId] = now; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
[MUST-FIX] Using wall-clock time (DateTime.now().microsecondsSinceEpoch) for lastWebSocketDataRefreshTimePerIsolate will break WebSocket profiling. The VM Service's getWebSocketProfile expects monotonic VM timeline clock timestamps for updatedSince (just like getHttpProfile does). This method should be asynchronous and retrieve the VM timeline clock timestamp using service.getVMTimelineMicros().
| void updateLastWebSocketDataRefreshTime({ | |
| bool alreadyRecordingWebSocket = false, | |
| }) { | |
| if (!alreadyRecordingWebSocket) { | |
| final now = DateTime.now().microsecondsSinceEpoch; | |
| for (final isolateId | |
| in lastWebSocketDataRefreshTimePerIsolate.keys.toList()) { | |
| lastWebSocketDataRefreshTimePerIsolate[isolateId] = now; | |
| } | |
| } | |
| } | |
| Future<void> updateLastWebSocketDataRefreshTime({ | |
| bool alreadyRecordingWebSocket = false, | |
| }) async { | |
| if (!alreadyRecordingWebSocket) { | |
| final service = serviceConnection.serviceManager.service; | |
| if (service == null) return; | |
| final timestamp = (await service.getVMTimelineMicros()).timestamp!; | |
| for (final isolateId | |
| in lastWebSocketDataRefreshTimePerIsolate.keys.toList()) { | |
| lastWebSocketDataRefreshTimePerIsolate[isolateId] = timestamp; | |
| } | |
| } | |
| } |
References
- Prioritize logic, performance on the UI thread, and architectural consistency. (link)
| children: [ | ||
| SingleChildScrollView( | ||
| scrollDirection: Axis.horizontal, | ||
| child: DataTable( |
There was a problem hiding this comment.
[CONCERN] Using a standard DataTable inside a ListView for WebSocket frames will render all frames at once without virtualization. For active or long-lived WebSocket connections that accumulate hundreds or thousands of frames, this will cause severe UI thread jank and memory overhead. Consider using a virtualized table widget (like DevTools' custom table/list components) to lazily render only the visible frames.
References
- Prioritize logic, performance on the UI thread, and architectural consistency. (link)
There was a problem hiding this comment.
If this is the way the rest of the table is shown, I wouldn't worry about it for now, but we could write in a TODO comment.
| ..updateOrAddAll( | ||
| requests: httpProfileData, | ||
| sockets: socketStatsData, | ||
| webSockets: const [], |
There was a problem hiding this comment.
[CONCERN] WebSocket connections are currently not serialized in prepareOfflineScreenData and are loaded as an empty list (const []) here. This means WebSocket profiling data will be lost when exporting or importing offline network data. Consider adding WebSocket serialization support to OfflineNetworkData and prepareOfflineScreenData to ensure full offline support.
References
- Avoid raw/magic values and ensure complete data handling for consistency and maintainability. (link)
| DataCell( | ||
| // WebSocket timeline events are prefixed with `WebSocket.`. | ||
| // Strip the prefix for display in the Event column. | ||
| Text(event.event.substring('WebSocket.'.length)), | ||
| ), |
There was a problem hiding this comment.
[MUST-FIX] Calling substring directly assumes that event.event always starts with 'WebSocket.' and is long enough. If an event doesn't match this format, it will throw a RangeError and crash the view. We should defensively check if the string starts with the prefix before stripping it.
| DataCell( | |
| // WebSocket timeline events are prefixed with `WebSocket.`. | |
| // Strip the prefix for display in the Event column. | |
| Text(event.event.substring('WebSocket.'.length)), | |
| ), | |
| DataCell( | |
| // WebSocket timeline events are prefixed with `WebSocket.`. | |
| // Strip the prefix for display in the Event column. | |
| Text( | |
| event.event.startsWith('WebSocket.') | |
| ? event.event.substring('WebSocket.'.length) | |
| : event.event, | |
| ), | |
| ), |
References
- Ensure defensive programming and safe handling of invalid inputs or states. (link)
| ## Network profiler updates | ||
|
|
||
| ## Network profiler updates | ||
|
|
There was a problem hiding this comment.
[CONCERN] The ## Network profiler updates header is duplicated in this file. Let's clean this up by removing the duplicate header.
| ## Network profiler updates | |
| ## Network profiler updates | |
| ## Network profiler updates | |
References
- Maintainability and consistency in documentation. (link)
|
I like the feedback from Gemini, most looks valid to me at first glance. |

Fixes #9507
Tracking issue: Victowolf/GSoC-Progress-Tracking#8
This contribution is part of the Google Summer of Code 2026 project:
"Add WebSocket/gRPC support to Flutter DevTools Network Panel"
This PR adds WebSocket support to the Flutter DevTools Network panel.
Previously,
dart:ioWebSocket connections appeared as opaqueSOCKETrequests in the Network screen. This PR extends the existing networking models and UI to recognize WebSocket profiling data and provide dedicated inspection of WebSocket connections and frame activity.What's included
WEBSOCKET.Tests Extended:
network_controller_test.dartnetwork_model_test.dartnetwork_profiler_test.dartnetwork_request_inspector_test.dartnetwork_table_test.dartUpdated UI:
Pre-launch Checklist
General checklist
///).Issues checklist
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.