Skip to content

Adds WebSocket support to Network panel (GSoC'26) - #9968

Open
Victowolf wants to merge 5 commits into
flutter:masterfrom
Victowolf:websocket-support
Open

Adds WebSocket support to Network panel (GSoC'26)#9968
Victowolf wants to merge 5 commits into
flutter:masterfrom
Victowolf:websocket-support

Conversation

@Victowolf

Copy link
Copy Markdown
Contributor

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:io WebSocket connections appeared as opaque SOCKET requests 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

  • Add WebSocket-specific network request modeling.
  • Display WebSocket connections in the Network request table as WEBSOCKET.
  • Display WebSocket connection details in the Overview section, including:
    • URI
    • Connection ID
    • Protocol
    • Connection status
    • Bytes sent/received
    • Frames sent/received
    • Ping/pong counts
    • Start/end timestamps
    • Connection duration
  • Add WebSocket frame inspection in the Network inspector.
  • Display WebSocket lifecycle and frame events such as:
    • Connect
    • Open
    • Send
    • Receive
    • Close
    • Error
    • Ping/Pong
  • Display frame-level information including:
    • Timestamp
    • Direction
    • Opcode
    • Payload size
  • Add WebSocket timing visualization.
  • Handle open connections with pending end time/duration and closed connections with calculated duration.
  • Add and update tests covering WebSocket models, Network table rendering, Overview, timing, and frame inspection.
  • Verify the implementation against a real Dart WebSocket application connected to DevTools through the VM Service.

Tests Extended:

  • network_controller_test.dart
  • network_model_test.dart
  • network_profiler_test.dart
  • network_request_inspector_test.dart
  • network_table_test.dart

Updated UI:

image image image

Pre-launch Checklist

General checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read the Flutter Style Guide recently, and have followed its advice.
  • I signed the CLA.
  • I updated/added relevant documentation (doc comments with ///).

Issues checklist

  • I listed at least one issue that this PR fixes in the description above.

Tests checklist

  • I added new tests to check the change I am made

AI-tooling checklist

  • I did use AI tooling, and...
    • I read the AI contributions guidelines and agree to follow them.
    • I reviewed all AI-generated code before opening this PR.
    • I understand and am able to discuss the code in this PR.
    • I have verifed the accuracy of any AI-generated text included in the PR description.
    • I commit to verifying the accuracy of any AI-generated code or text that I upload in response to review comments.

Feature-change checklist

  • this PR does change the DevTools UI or behavior and...
    • I added an entry to packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md.
    • I included before/after screenshots and/or a GIF demo of the new UI to my PR description.
    • I ran the DevTools app locally to manually verify my changes.

build.yaml badge

If you need help, consider asking for help on Discord.

@gemini-code-assist gemini-code-assist 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.

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.

Comment thread packages/devtools_app/lib/src/screens/network/network_controller.dart Outdated
@Victowolf Victowolf changed the title [dart:io] Adds WebSocket support to Network panel (GSoC'26) Adds WebSocket support to Network panel (GSoC'26) Aug 18, 2026
@Victowolf
Victowolf requested a review from a team as a code owner August 18, 2026 19:08
@crackedhandle

Copy link
Copy Markdown

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?

@Victowolf

Copy link
Copy Markdown
Contributor Author

This PR aims to integrate the entire WebSocket support from vm_service instrumentation to network panel. For long-lived, dense connections, time-interval-based grouping can be added as an enhancement after this PR lands.

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

Just a preliminary review; I haven't tried it or reviewed the tests.

Comment thread packages/devtools_app/lib/src/screens/network/network_controller.dart Outdated
Comment thread packages/devtools_app/lib/src/screens/network/network_model.dart Outdated
Comment thread packages/devtools_app/lib/src/screens/network/network_service.dart Outdated
Comment thread packages/devtools_app/lib/src/screens/network/network_service.dart Outdated
@srawlins
srawlins requested a review from hannah-hyj August 19, 2026 03:59
@Victowolf
Victowolf requested a review from srawlins August 19, 2026 13:57
@Victowolf

Copy link
Copy Markdown
Contributor Author

needs review

@srawlins

Copy link
Copy Markdown
Contributor

I'm adding websocket support to the devtools companion app so I can play with this

@Victowolf

Copy link
Copy Markdown
Contributor Author

I think most tests are failing because the workflow is running on vm_service 15.2

image

@srawlins

srawlins commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Yes definitely change vm_service dependency in packages/devtools_app/pubspec.yaml so that we only get 15.3.0.

Yeah 15.3.0 seems to work well.

@srawlins

Copy link
Copy Markdown
Contributor

/gemini review

@gemini-code-assist gemini-code-assist 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.

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.

Comment on lines +347 to +349
networkService.updateLastWebSocketDataRefreshTime(
alreadyRecordingWebSocket: alreadyRecordingHttp,
);

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.

high

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

Suggested change
networkService.updateLastWebSocketDataRefreshTime(
alreadyRecordingWebSocket: alreadyRecordingHttp,
);
await networkService.updateLastWebSocketDataRefreshTime(
alreadyRecordingWebSocket: alreadyRecordingHttp,
);
References
  1. Prioritize logic, performance on the UI thread, and architectural consistency. (link)

Comment on lines +78 to +88
void updateLastWebSocketDataRefreshTime({
bool alreadyRecordingWebSocket = false,
}) {
if (!alreadyRecordingWebSocket) {
final now = DateTime.now().microsecondsSinceEpoch;
for (final isolateId
in lastWebSocketDataRefreshTimePerIsolate.keys.toList()) {
lastWebSocketDataRefreshTimePerIsolate[isolateId] = now;
}
}
}

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.

high

[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().

Suggested change
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
  1. Prioritize logic, performance on the UI thread, and architectural consistency. (link)

children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(

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.

high

[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
  1. Prioritize logic, performance on the UI thread, and architectural consistency. (link)

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.

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 [],

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.

medium

[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
  1. Avoid raw/magic values and ensure complete data handling for consistency and maintainability. (link)

Comment on lines +634 to +638
DataCell(
// WebSocket timeline events are prefixed with `WebSocket.`.
// Strip the prefix for display in the Event column.
Text(event.event.substring('WebSocket.'.length)),
),

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.

medium

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

Suggested change
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
  1. Ensure defensive programming and safe handling of invalid inputs or states. (link)

Comment on lines 54 to +57
## Network profiler updates

## Network profiler updates

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.

medium

[CONCERN] The ## Network profiler updates header is duplicated in this file. Let's clean this up by removing the duplicate header.

Suggested change
## Network profiler updates
## Network profiler updates
## Network profiler updates
References
  1. Maintainability and consistency in documentation. (link)

@srawlins

Copy link
Copy Markdown
Contributor

I like the feedback from Gemini, most looks valid to me at first glance.

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.

Design and sizing for WebSocket support

3 participants