Flutter DevTools shows HTTP traffic beautifully:
GET /api/users ✅ 200 45ms
POST /api/login ✅ 201 120ms
But when your app uses WebSocket? The Network panel shows nothing:
(empty — no visibility at all)
Developers debugging real-time apps are flying blind. 🙈
ProfileableWebSocket wraps dart:io's WebSocket and silently records every frame:
You type → ProfileableWebSocket → real WebSocket → server
↓ records FrameEvent
Echo back ← ProfileableWebSocket ← real WebSocket ← server
↓ records FrameEvent
prints live table
This is the data model and interception layer needed to bring WebSocket visibility to Flutter DevTools — making it as observable as HTTP traffic is today.
| Feature | Description |
|---|---|
| 🎁 Wrapper | ProfileableWebSocket wraps dart:io WebSocket transparently |
| 📦 Recording | Every frame captured with timestamp, direction, size, and type |
| 🧪 Tested | 5 unit tests against a real local echo server — no mocks |
| 📟 Live Table | CLI app displays a real-time traffic table in the terminal |
dart_websocket_sample/
├── bin/
│ └── main.dart # CLI entry point
├── lib/
│ ├── profileable_websocket.dart # WebSocket wrapper ← core
│ └── frame_event.dart # Frame data model
├── test/
│ └── profileable_websocket_test.dart # 5 passing tests
└── pubspec.yaml
- Dart SDK
>=3.0.0
dart pub get
dart run bin/main.dartdart testdart format .
dart analyzeConnecting to wss://echo.websocket.org ...
Connected. Type a message and press Enter. Ctrl+C to exit.
> Hello Dart!
─────────────────────────────────────────────────────────
# Time Direction Size Type Preview
─────────────────────────────────────────────────────────
1 10:00:01 ↑ SENT 10 B text Hello Da...
2 10:00:01 ↓ RECEIVED 10 B text Hello Da...
─────────────────────────────────────────────────────────
> WebSocket profiling is working!
─────────────────────────────────────────────────────────
# Time Direction Size Type Preview
─────────────────────────────────────────────────────────
1 10:00:01 ↑ SENT 10 B text Hello Da...
2 10:00:01 ↓ RECEIVED 10 B text Hello Da...
3 10:00:05 ↑ SENT 27 B text WebSocke...
4 10:00:05 ↓ RECEIVED 27 B text WebSocke...
─────────────────────────────────────────────────────────
┌─────────────────────────────────────────┐
│ bin/main.dart │ ← CLI loop + table printer
└────────────────────┬────────────────────┘
│ uses
┌────────────────────▼────────────────────┐
│ ProfileableWebSocket │ ← intercepts every frame
│ ┌──────────────────────────────────┐ │
│ │ dart:io WebSocket (_inner) │ │ ← real connection inside
│ └──────────────────────────────────┘ │
│ List<FrameEvent> events │ ← growing log of frames
└────────────────────┬────────────────────┘
│ produces
┌────────────────────▼────────────────────┐
│ FrameEvent │ ← timestamp, direction,
│ │ size, type, data
└─────────────────────────────────────────┘
ProfileableWebSocket uses the Wrapper Pattern — it holds a reference to the real dart:io WebSocket internally and delegates all operations to it, while recording frame metadata before forwarding each frame.
Why a wrapper for the sample? The full GSoC implementation will instrument
dart:io's WebSocket directly — similar to howHttpClient.enableTimelineLoggingworks — so all traffic is captured automatically without requiring a wrapper. The wrapper approach was chosen for this sample to keep it self-contained and easy to run. (Approach confirmed with mentor Samuel Rawlins.)
Made with ❤️ for GSoC 2026 — Dart Organization