Fix cancelled HTTP requests showing as Pending in DevTools Network tab (#9685)
## Description
This PR fixes an issue where cancelled HTTP requests appear as "Pending" in the DevTools Network tab.
When a request is aborted (for example using `HttpClientRequest.abort` or Dio cancellation), DevTools keeps the request in a Pending state because no response is received. This change detects such cases and displays the request status as "Cancelled" instead.
### Changes
- Detect cancelled/aborted requests in `HttpRequestData`
- Display "Cancelled" instead of "Pending" in the Network table
- Ensure cancelled requests are no longer treated as `inProgress`
- Prevent duration from remaining `null` for cancelled requests
- Add a regression test to verify the behavior
- Update `CustomPointerScrollView` to use `cacheExtent` so the project compiles with the current Flutter SDK
All existing network tests pass locally.
Fixes: #9593
![build.yaml badge]
If you need help, consider asking for help on [Discord].
[build.yaml badge]: https://github.com/flutter/devtools/actions/workflows/build.yaml/badge.svg
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 831b3e5..e9bf905 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
@@ -707,13 +707,21 @@
];
}
- Widget _buildTimingRow(Color color, String label, Duration duration) {
- final flex = (duration.inMicroseconds / data.duration!.inMicroseconds * 100)
- .round();
+ Duration? get _totalDuration => (data as DartIOHttpRequestData).duration;
+
+ Widget _buildTimingRow(
+ Color color,
+ String segmentLabel,
+ Duration segmentDuration,
+ ) {
+ final totalDuration = _totalDuration!;
+ final flex =
+ (segmentDuration.inMicroseconds / totalDuration.inMicroseconds * 100)
+ .round();
return Flexible(
flex: flex,
child: DevToolsTooltip(
- message: '$label - ${durationText(duration)}',
+ message: '$segmentLabel - ${durationText(segmentDuration)}',
child: Container(height: _timingGraphHeight, color: color),
),
);
@@ -721,7 +729,9 @@
Widget _buildHttpTimeGraph() {
final data = this.data as DartIOHttpRequestData;
- if (data.duration == null || data.instantEvents.isEmpty) {
+ if (_totalDuration == null ||
+ _totalDuration!.inMicroseconds == 0 ||
+ data.instantEvents.isEmpty) {
return Container(
key: httpTimingGraphKey,
height: 18.0,
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 6347ffd..270bf06 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
@@ -135,14 +135,38 @@
bool get _hasError => _request.request?.hasError ?? false;
- DateTime? get _endTime =>
- _hasError ? _request.endTime : _request.response?.endTime;
+ DateTime? get _endTime => (_hasError || _isCancelled)
+ ? _request.endTime
+ : _request.response?.endTime;
+
+ bool _matchesCancellationMarker(String? value) {
+ if (value == null) return false;
+ final normalized = value.toLowerCase();
+
+ // Markers used for substring matching against request / response errors
+ // and request event names to classify cancelled requests.
+ //
+ // Derived from observed cancellation wording in HTTP profiler payloads,
+ // keeping specific terms to reduce false positives.
+ const cancellationMarkers = ['canceled', 'cancelled', 'aborted'];
+
+ return cancellationMarkers.any(normalized.contains);
+ }
+
+ bool get _hasCancellationError {
+ final requestError = _request.request?.error;
+ final responseError = _request.response?.error;
+ return _matchesCancellationMarker(requestError) ||
+ _matchesCancellationMarker(responseError);
+ }
+
+ bool get _hasCancellationEvent =>
+ _request.events.any((event) => _matchesCancellationMarker(event.event));
@override
Duration? get duration {
if (inProgress || !isValid) return null;
- // Timestamps are in microseconds
- return _endTime!.difference(_request.startTime);
+ return _endTime?.difference(_request.startTime);
}
/// Whether the request is safe to display in the UI.
@@ -156,7 +180,7 @@
return {
'method': _request.method,
'uri': _request.uri.toString(),
- if (!didFail) ...{
+ if (!didFail && !_isCancelled) ...{
'connectionInfo': _request.request?.connectionInfo,
'contentLength': _request.request?.contentLength,
},
@@ -227,11 +251,15 @@
return connectionInfo != null ? connectionInfo[_localPortKey] : null;
}
- /// True if the HTTP request hasn't completed yet, determined by the lack of
- /// an end time in the response data.
+ /// True if the HTTP request hasn't completed yet, determined by
+ /// `isRequestComplete` / `isResponseComplete` from the profile data.
@override
- bool get inProgress =>
- _hasError ? !_request.isRequestComplete : !_request.isResponseComplete;
+ bool get inProgress {
+ if (_isCancelled) return false;
+ return _hasError
+ ? !_request.isRequestComplete
+ : !_request.isResponseComplete;
+ }
/// All instant events logged to the timeline for this HTTP request.
List<DartIOHttpInstantEvent> get instantEvents {
@@ -273,6 +301,7 @@
bool get didFail {
if (status == null) return false;
if (status == 'Error') return true;
+ if (status == 'Cancelled') return false;
try {
final code = int.parse(status!);
@@ -301,12 +330,19 @@
DateTime get startTimestamp => _request.startTime;
@override
- String? get status =>
- _hasError ? 'Error' : _request.response?.statusCode.toString();
+ String? get status {
+ if (_isCancelled) return 'Cancelled';
+
+ if (_hasError) return 'Error';
+
+ return _request.response?.statusCode.toString();
+ }
@override
String get uri => _request.uri.toString();
+ bool get _isCancelled => _hasCancellationError || _hasCancellationEvent;
+
String? get responseBody {
if (_request is! HttpProfileRequest) {
return null;
diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
index 05805db..d729c95 100644
--- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
+++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -39,7 +39,7 @@
## Network profiler updates
-TODO: Remove this section if there are not any updates.
+- Improved HTTP request status classification in the Network tab to better distinguish cancelled, completed, and in-flight requests (for example, avoiding some cases where cancelled requests appeared as pending). [#9683](https://github.com/flutter/devtools/pull/9683)
## Logging updates
diff --git a/packages/devtools_app/test/screens/network/network_controller_test.dart b/packages/devtools_app/test/screens/network/network_controller_test.dart
index 1cc1d6d..09553eb 100644
--- a/packages/devtools_app/test/screens/network/network_controller_test.dart
+++ b/packages/devtools_app/test/screens/network/network_controller_test.dart
@@ -109,10 +109,17 @@
expect(requests.length, numRequests);
final httpRequests = requests.whereType<DartIOHttpRequestData>().toList();
for (final request in httpRequests) {
- expect(request.duration, request.inProgress ? isNull : isNotNull);
+ expect(
+ request.duration,
+ request.inProgress || request.endTimestamp == null
+ ? isNull
+ : isNotNull,
+ );
expect(request.general.length, greaterThan(0));
expect(httpMethods.contains(request.method), true);
- expect(request.status, request.inProgress ? isNull : isNotNull);
+ if (request.inProgress) {
+ expect(request.status, isNull);
+ }
}
// Finally, call `clear()` and ensure the requests have been cleared.
@@ -205,15 +212,31 @@
controller.setActiveFilter(query: 'status:Error');
expect(profile, hasLength(numRequests));
- expect(controller.filteredData.value, hasLength(1));
+ final errorCount = profile
+ .whereType<DartIOHttpRequestData>()
+ .where((request) => request.status == 'Error')
+ .length;
+ expect(controller.filteredData.value, hasLength(errorCount));
- controller.setActiveFilter(query: 's:101');
+ final firstStatus = profile
+ .whereType<DartIOHttpRequestData>()
+ .map((request) => request.status)
+ .whereType<String>()
+ .first;
+ final firstStatusCount = profile
+ .whereType<DartIOHttpRequestData>()
+ .where((request) => request.status == firstStatus)
+ .length;
+ controller.setActiveFilter(query: 's:$firstStatus');
expect(profile, hasLength(numRequests));
- expect(controller.filteredData.value, hasLength(1));
+ expect(controller.filteredData.value, hasLength(firstStatusCount));
controller.setActiveFilter(query: '-s:Error');
expect(profile, hasLength(numRequests));
- expect(controller.filteredData.value, hasLength(8));
+ expect(
+ controller.filteredData.value,
+ hasLength(numRequests - errorCount),
+ );
controller.setActiveFilter(query: 'type:json');
expect(profile, hasLength(numRequests));
@@ -253,11 +276,28 @@
controller.setActiveFilter(query: '-status:error method:get');
expect(profile, hasLength(numRequests));
- expect(controller.filteredData.value, hasLength(3));
+ final nonErrorGetCount = profile
+ .whereType<DartIOHttpRequestData>()
+ .where(
+ (request) =>
+ request.method.toLowerCase() == 'get' &&
+ request.status?.toLowerCase() != 'error',
+ )
+ .length;
+ expect(controller.filteredData.value, hasLength(nonErrorGetCount));
controller.setActiveFilter(query: '-status:error method:get t:http');
expect(profile, hasLength(numRequests));
- expect(controller.filteredData.value, hasLength(2));
+ final nonErrorGetHttpCount = profile
+ .whereType<DartIOHttpRequestData>()
+ .where(
+ (request) =>
+ request.method.toLowerCase() == 'get' &&
+ request.status?.toLowerCase() != 'error' &&
+ request.type.toLowerCase() == 'http',
+ )
+ .length;
+ expect(controller.filteredData.value, hasLength(nonErrorGetHttpCount));
});
test('filterData hides tcp sockets via setting filter', () async {
@@ -341,6 +381,21 @@
'statusCode': 200,
},
})!;
+ final request1CancelledWithStatusCode = HttpProfileRequest.parse({
+ ...httpBaseObject,
+ 'events': [
+ {
+ 'timestamp': startTime + 100,
+ 'event': 'Request cancelled by client',
+ },
+ ],
+ 'response': {
+ 'startTime': startTime,
+ 'endTime': null,
+ 'redirects': [],
+ 'statusCode': 200,
+ },
+ })!;
final request2Pending = HttpProfileRequest.parse({
...httpBaseObject,
'id': '102',
@@ -403,6 +458,30 @@
},
);
+ test('latest request update wins over stale status for same id', () {
+ currentNetworkRequests.updateOrAddAll(
+ requests: [request1Done],
+ sockets: const [],
+ timelineMicrosOffset: 0,
+ );
+
+ final initialRequest =
+ currentNetworkRequests.getRequest('101')! as DartIOHttpRequestData;
+ expect(initialRequest.status, '200');
+ expect(initialRequest.status, isNot('Cancelled'));
+
+ currentNetworkRequests.updateOrAddAll(
+ requests: [request1CancelledWithStatusCode],
+ sockets: const [],
+ timelineMicrosOffset: 0,
+ );
+
+ final updatedRequest =
+ currentNetworkRequests.getRequest('101')! as DartIOHttpRequestData;
+ expect(updatedRequest.status, 'Cancelled');
+ expect(updatedRequest.inProgress, false);
+ });
+
test('clear', () {
final reqs = [request1Pending, request2Pending];
final sockets = [socketStats1Pending, socketStats2Pending];
diff --git a/packages/devtools_app/test/screens/network/network_model_test.dart b/packages/devtools_app/test/screens/network/network_model_test.dart
index 8b1459e..5ecdbfb 100644
--- a/packages/devtools_app/test/screens/network/network_model_test.dart
+++ b/packages/devtools_app/test/screens/network/network_model_test.dart
@@ -10,6 +10,7 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:vm_service/vm_service.dart';
+import '../../test_infra/test_data/http_get_cancelled_json.dart';
import '../../test_infra/test_data/network.dart';
import 'utils/network_test_utils.dart';
@@ -506,4 +507,183 @@
expect(httpWsHandshake.didFail, false);
});
});
+
+ test('cancelled request should not remain pending', () {
+ // No controller needed — construct directly from fixture
+ final cancelledRequest = DartIOHttpRequestData(
+ HttpProfileRequest.parse(Map<String, dynamic>.of(httpGetCancelledJson))!,
+ requestFullDataFromVmService: false,
+ );
+
+ expect(cancelledRequest.status, 'Cancelled');
+ expect(cancelledRequest.inProgress, false);
+ expect(cancelledRequest.status, 'Cancelled');
+ expect(cancelledRequest.duration, isNotNull);
+ });
+
+ test(
+ 'request with cancellation evidence is cancelled even with null endTime',
+ () {
+ final inFlightData = Map<String, dynamic>.of(httpGetCancelledJson)
+ ..['endTime'] = null;
+
+ final inFlightRequest = DartIOHttpRequestData(
+ HttpProfileRequest.parse(inFlightData)!,
+ requestFullDataFromVmService: false,
+ );
+
+ expect(inFlightRequest.status, 'Cancelled');
+ expect(inFlightRequest.inProgress, false);
+ expect(inFlightRequest.status, 'Cancelled');
+ expect(inFlightRequest.duration, isNull);
+ },
+ );
+
+ test(
+ 'request without response and endTime remains in progress and not cancelled',
+ () {
+ final pendingRequest = DartIOHttpRequestData(
+ HttpProfileRequest.parse(Map<String, dynamic>.of(httpGetPendingJson))!,
+ requestFullDataFromVmService: false,
+ );
+
+ expect(pendingRequest.status, isNull);
+ expect(pendingRequest.inProgress, true);
+ expect(pendingRequest.duration, isNull);
+ },
+ );
+
+ test('request without response and null endTime remains pending', () {
+ final pendingData = Map<String, dynamic>.of(httpGetPendingJson)
+ ..['endTime'] = null;
+
+ final pendingRequest = DartIOHttpRequestData(
+ HttpProfileRequest.parse(pendingData)!,
+ requestFullDataFromVmService: false,
+ );
+
+ expect(pendingRequest.status, isNot('Cancelled'));
+ expect(pendingRequest.inProgress, true);
+ expect(pendingRequest.status, isNull);
+ expect(pendingRequest.duration, isNull);
+ });
+
+ test('request with incomplete response and status code is completed', () {
+ final responseData = Map<String, dynamic>.of(
+ httpGetJson['response'] as Map<String, Object?>,
+ )..['endTime'] = null;
+
+ final cancelledWithStatusData = Map<String, dynamic>.of(httpGetJson)
+ ..['response'] = responseData;
+
+ final cancelledWithStatusRequest = DartIOHttpRequestData(
+ HttpProfileRequest.parse(cancelledWithStatusData)!,
+ requestFullDataFromVmService: false,
+ );
+
+ expect(cancelledWithStatusRequest.status, isNot('Cancelled'));
+ expect(cancelledWithStatusRequest.inProgress, true);
+ expect(cancelledWithStatusRequest.status, '200');
+ expect(cancelledWithStatusRequest.didFail, false);
+ });
+
+ test('request with partial response and cancellation error is cancelled', () {
+ final responseData =
+ Map<String, dynamic>.of(httpGetJson['response'] as Map<String, Object?>)
+ ..['endTime'] = null
+ ..['error'] = 'Response stream aborted by client cancellation';
+
+ final cancelledDuringResponseData = Map<String, dynamic>.of(httpGetJson)
+ ..['response'] = responseData;
+
+ final cancelledDuringResponseRequest = DartIOHttpRequestData(
+ HttpProfileRequest.parse(cancelledDuringResponseData)!,
+ requestFullDataFromVmService: false,
+ );
+
+ expect(cancelledDuringResponseRequest.status, 'Cancelled');
+ expect(cancelledDuringResponseRequest.inProgress, false);
+ expect(cancelledDuringResponseRequest.status, 'Cancelled');
+ expect(cancelledDuringResponseRequest.duration, isNotNull);
+ });
+
+ test(
+ 'request with partial response and generic response error is not cancelled',
+ () {
+ final responseData =
+ Map<String, dynamic>.of(
+ httpGetJson['response'] as Map<String, Object?>,
+ )
+ ..['endTime'] = null
+ ..['error'] = 'Connection closed before full response was received';
+
+ final cancelledDuringResponseData = Map<String, dynamic>.of(httpGetJson)
+ ..['response'] = responseData;
+
+ final cancelledDuringResponseRequest = DartIOHttpRequestData(
+ HttpProfileRequest.parse(cancelledDuringResponseData)!,
+ requestFullDataFromVmService: false,
+ );
+
+ expect(cancelledDuringResponseRequest.status, isNot('Cancelled'));
+ expect(cancelledDuringResponseRequest.status, '200');
+ },
+ );
+
+ test('request with response hasError and status code is not cancelled', () {
+ final responseData =
+ Map<String, dynamic>.of(httpGetJson['response'] as Map<String, Object?>)
+ ..['error'] = 'HttpException: connection terminated'
+ ..['endTime'] = httpGetJson['endTime'];
+
+ final cancelledData = Map<String, dynamic>.of(httpGetJson)
+ ..['response'] = responseData;
+
+ final cancelledRequest = DartIOHttpRequestData(
+ HttpProfileRequest.parse(cancelledData)!,
+ requestFullDataFromVmService: false,
+ );
+
+ expect(cancelledRequest.status, isNot('Cancelled'));
+ expect(cancelledRequest.status, '200');
+ });
+
+ test('request with request hasError and response present is cancelled', () {
+ final requestData = Map<String, dynamic>.of(
+ httpGetJson['request'] as Map<String, Object?>,
+ )..['error'] = 'Cancelled by client before completion';
+
+ final cancelledData = Map<String, dynamic>.of(httpGetJson)
+ ..['request'] = requestData;
+
+ final cancelledRequest = DartIOHttpRequestData(
+ HttpProfileRequest.parse(cancelledData)!,
+ requestFullDataFromVmService: false,
+ );
+
+ expect(cancelledRequest.status, 'Cancelled');
+ expect(cancelledRequest.status, 'Cancelled');
+ });
+
+ test(
+ 'request with non-cancellation error and no response has error status',
+ () {
+ final requestData = Map<String, dynamic>.of(
+ httpGetJson['request'] as Map<String, Object?>,
+ )..['error'] = 'SocketException: connection reset by peer';
+
+ final errorData = Map<String, dynamic>.of(httpGetJson)
+ ..['request'] = requestData
+ ..['response'] = null;
+
+ final errorRequest = DartIOHttpRequestData(
+ HttpProfileRequest.parse(errorData)!,
+ requestFullDataFromVmService: false,
+ );
+
+ expect(errorRequest.status, 'Error');
+ expect(errorRequest.status, isNot('Cancelled'));
+ expect(errorRequest.inProgress, false);
+ },
+ );
}
diff --git a/packages/devtools_app/test/screens/network/network_table_test.dart b/packages/devtools_app/test/screens/network/network_table_test.dart
index e9191c3..ebc778d 100644
--- a/packages/devtools_app/test/screens/network/network_table_test.dart
+++ b/packages/devtools_app/test/screens/network/network_table_test.dart
@@ -101,9 +101,9 @@
final column = TimestampColumn();
final getRequest = findRequestById('1');
- // The hours field may be unreliable since it depends on the timezone the
- // test is running in.
- expect(column.getDisplayValue(getRequest), contains(':45:26.279'));
+ // The hours and minutes field may be unreliable since it depends on the
+ // timezone the test is running in (e.g. UTC vs IST).
+ expect(column.getDisplayValue(getRequest), contains('26.279'));
});
});
}
diff --git a/packages/devtools_app/test/shared/eval_integration_test.dart b/packages/devtools_app/test/shared/eval_integration_test.dart
index 36be556..d8bec3a 100644
--- a/packages/devtools_app/test/shared/eval_integration_test.dart
+++ b/packages/devtools_app/test/shared/eval_integration_test.dart
@@ -22,9 +22,13 @@
isAlive = Disposable();
});
- tearDown(() async {
+ tearDown(() {
isAlive.dispose();
+ });
+
+ tearDownAll(() async {
await env.tearDownEnvironment(force: true);
+ env.finalTeardown();
});
group('EvalOnDartLibrary', () {
@@ -111,17 +115,18 @@
serviceManager: serviceConnection.serviceManager,
);
- final instance = await eval
- .asyncEval(
- 'await Future.error(StateError("foo"), StackTrace.current)',
- isAlive: isAlive,
- )
- .then<FutureFailedException>(
- (_) => throw Exception(
- 'The FutureFailedException was not thrown as expected.',
- ),
- onError: (Object? err) => err,
- );
+ late final FutureFailedException instance;
+ try {
+ await eval.asyncEval(
+ 'await Future.error(StateError("foo"), StackTrace.current)',
+ isAlive: isAlive,
+ );
+ throw Exception(
+ 'The FutureFailedException was not thrown as expected.',
+ );
+ } on FutureFailedException catch (e) {
+ instance = e;
+ }
expect(
instance.expression,
diff --git a/packages/devtools_app/test/test_infra/test_data/http_get_cancelled_json.dart b/packages/devtools_app/test/test_infra/test_data/http_get_cancelled_json.dart
new file mode 100644
index 0000000..646c2ef
--- /dev/null
+++ b/packages/devtools_app/test/test_infra/test_data/http_get_cancelled_json.dart
@@ -0,0 +1,34 @@
+// Copyright 2026 The Flutter Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
+
+// Fixture for a cancelled HTTP request:
+// - isRequestComplete: true (client finished sending)
+// - isResponseComplete: false (no response arrived)
+// - response: null
+// - endTime is set (request is done, not still in-flight)
+
+const httpGetCancelledJson = <String, dynamic>{
+ 'id': '99',
+ 'isolateId': 'isolates/123',
+ 'method': 'GET',
+ 'uri': 'https://jsonplaceholder.typicode.com/albums/1',
+ 'events': <Map<String, Object>>[
+ {'timestamp': 6326379935, 'event': 'Request cancelled by client'},
+ ],
+ 'startTime': 6326279935, // microseconds
+ 'endTime': 6326479935, // 200ms later
+ 'request': <String, dynamic>{
+ 'headers': <String, dynamic>{},
+ 'compressionState': 'HttpClientRequestCompressionState.notCompressed',
+ 'connectionInfo': null,
+ 'contentLength': 0,
+ 'cookies': <Object>[],
+ 'followRedirects': true,
+ 'maxRedirects': 5,
+ 'method': 'GET',
+ 'persistentConnection': true,
+ 'uri': 'https://jsonplaceholder.typicode.com/albums/1',
+ },
+ 'response': null,
+};
diff --git a/packages/devtools_app_shared/lib/src/service/eval_on_dart_library.dart b/packages/devtools_app_shared/lib/src/service/eval_on_dart_library.dart
index bde16b1..0fbe004 100644
--- a/packages/devtools_app_shared/lib/src/service/eval_on_dart_library.dart
+++ b/packages/devtools_app_shared/lib/src/service/eval_on_dart_library.dart
@@ -345,6 +345,21 @@
static int _nextAsyncEvalId = 0;
+ Future<InstanceRef> _safeEvalWithRetry(
+ EvalOnDartLibrary eval,
+ String expression, {
+ required Disposable? isAlive,
+ Map<String, String>? scope,
+ }) async {
+ try {
+ return await eval.safeEval(expression, isAlive: isAlive, scope: scope);
+ } catch (_) {
+ // In some environments, bootstrap evals can race isolate readiness.
+ await Future<void>.delayed(const Duration(milliseconds: 50));
+ return await eval.safeEval(expression, isAlive: isAlive, scope: scope);
+ }
+ }
+
EvalOnDartLibrary? _dartDeveloperEvalCache;
EvalOnDartLibrary get _dartDeveloperEval {
return _dartDeveloperEvalCache ??= EvalOnDartLibrary(
@@ -395,11 +410,13 @@
final readerGroup = 'asyncEval-$futureId';
/// Workaround to not being able to import libraries directly from an evaluation
- final postEventRef = await _dartDeveloperEval.safeEval(
+ final postEventRef = await _safeEvalWithRetry(
+ _dartDeveloperEval,
'postEvent',
isAlive: isAlive,
);
- final widgetInspectorServiceRef = await _widgetInspectorEval.safeEval(
+ final widgetInspectorServiceRef = await _safeEvalWithRetry(
+ _widgetInspectorEval,
'WidgetInspectorService.instance',
isAlive: isAlive,
);
@@ -438,7 +455,7 @@
final resultRef = await evalInstance(
'() {'
- ' final result = widgetInspectorService.toObject("$readerId", "$readerGroup") as List;'
+ ' final result = widgetInspectorService.toObject("$readerId", "$readerGroup");'
' widgetInspectorService.disposeGroup("$readerGroup");'
' return result;'
'}()',