diff --git a/packages/devtools_app/lib/src/screens/network/network_model.dart b/packages/devtools_app/lib/src/screens/network/network_model.dart index 8a6697fbe65..9ccc3c5217a 100644 --- a/packages/devtools_app/lib/src/screens/network/network_model.dart +++ b/packages/devtools_app/lib/src/screens/network/network_model.dart @@ -29,6 +29,9 @@ abstract class NetworkRequest int? get port; + int? get requestBytes => null; + int? get responseBytes => null; + bool get didFail; /// True if the request hasn't completed yet. @@ -160,6 +163,12 @@ class Socket extends NetworkRequest { @override int get port => _socket.port; + @override + int get requestBytes => writeBytes; + + @override + int get responseBytes => readBytes; + // TODO(kenz): what determines a web socket request failure? @override bool get didFail => false; diff --git a/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart b/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart index 831b3e52052..19eb83daf9c 100644 --- a/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart +++ b/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart @@ -625,6 +625,7 @@ class NetworkRequestOverviewView extends StatelessWidget { } List _buildGeneralRows(BuildContext context) { + final bytes = data.responseBytes; return [ // TODO(kenz): show preview for requests (png, response body, proto) _buildRow( @@ -658,6 +659,14 @@ class NetworkRequestOverviewView extends StatelessWidget { ), const SizedBox(height: defaultSpacing), ], + + _buildRow( + context: context, + title: 'Response Size', + child: _valueText(bytes != null ? _formatBytes(bytes) : '-'), + ), + const SizedBox(height: defaultSpacing), + if (data.contentType != null) ...[ _buildRow( context: context, @@ -669,6 +678,20 @@ class NetworkRequestOverviewView extends StatelessWidget { ]; } + // Output Formats: + // - 512 → "512 B" + // - 2048 → "2.0 KB" + // - 1048576 → "1.0 MB" + // Values are rounded to one decimal place for KB and MB. + String _formatBytes(int? bytes) { + if (bytes == null) return '-'; + if (bytes < 1024) return '$bytes B'; + if (bytes < 1024 * 1024) { + return '${(bytes / 1024).toStringAsFixed(1)} KB'; + } + return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; + } + List _buildTimingOverview(BuildContext context) { return [ _buildRow( diff --git a/packages/devtools_app/lib/src/screens/network/network_screen.dart b/packages/devtools_app/lib/src/screens/network/network_screen.dart index 5171c5e6736..070bf270941 100644 --- a/packages/devtools_app/lib/src/screens/network/network_screen.dart +++ b/packages/devtools_app/lib/src/screens/network/network_screen.dart @@ -371,6 +371,7 @@ class NetworkRequestsTable extends StatelessWidget { statusColumn, typeColumn, durationColumn, + const ResponseSizeColumn(), timestampColumn, actionsColumn, ]; @@ -405,6 +406,34 @@ class NetworkRequestsTable extends StatelessWidget { } } +// Output Formats: +// - 512 → "512 B" +// - 2048 → "2.0 KB" +// - 1048576 → "1.0 MB" +// Values are rounded to one decimal place for KB and MB. +String _formatBytes(int? bytes) { + if (bytes == null) return '-'; + if (bytes < 1024) return '$bytes B'; + if (bytes < 1024 * 1024) { + return '${(bytes / 1024).toStringAsFixed(1)} KB'; + } + return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; +} + +class ResponseSizeColumn extends ColumnData { + const ResponseSizeColumn() + : super('Size', alignment: ColumnAlignment.right, fixedWidthPx: 90); + + @override + int? getValue(NetworkRequest dataObject) => dataObject.responseBytes; + + @override + String getDisplayValue(NetworkRequest dataObject) { + final bytes = dataObject.responseBytes; + return bytes != null ? _formatBytes(bytes) : '-'; + } +} + class AddressColumn extends ColumnData implements ColumnRenderer { AddressColumn() @@ -415,8 +444,8 @@ class AddressColumn extends ColumnData ); @override - String getValue(NetworkRequest dataObject) { - return dataObject.uri; + String getValue(NetworkRequest data) { + return data.uri; } @override diff --git a/packages/devtools_app/lib/src/shared/http/http_request_data.dart b/packages/devtools_app/lib/src/shared/http/http_request_data.dart index 6347ffdbf8d..b5e716b28b4 100644 --- a/packages/devtools_app/lib/src/shared/http/http_request_data.dart +++ b/packages/devtools_app/lib/src/shared/http/http_request_data.dart @@ -227,6 +227,27 @@ class DartIOHttpRequestData extends NetworkRequest { return connectionInfo != null ? connectionInfo[_localPortKey] : null; } + @override + int? get responseBytes { + final headers = responseHeaders; + final contentLength = headers?['content-length']; + + if (contentLength is String) { + return int.tryParse(contentLength); + } + if (contentLength is List && contentLength.isNotEmpty) { + final first = contentLength.first; + + if (first is int) { + return first; + } + if (first is String) { + return int.tryParse(first); + } + } + return null; + } + /// True if the HTTP request hasn't completed yet, determined by the lack of /// an end time in the response data. @override diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index 6893cc7d4f4..25a74c25d19 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -39,6 +39,9 @@ TODO: Remove this section if there are not any updates. ## Network profiler updates +- Added response size ("Res Size") column to the Network tab and displayed response size in the request inspector overview. - + [#9744](https://github.com/flutter/devtools/pull/9744) + - Added a filter setting to hide HTTP-profiler socket data. [#9698](https://github.com/flutter/devtools/pull/9698) ## Logging updates