[Widget Inspector] Handle null exceptions calling renderObject
(#163642)
Fixes https://github.com/flutter/devtools/issues/8905 Based on the stacktrace in https://github.com/flutter/devtools/issues/8905: * This call to `renderObject` can throw a null-exception:39b4951f8f/packages/flutter/lib/src/widgets/framework.dart (L3745)
* That exception is thrown here:39b4951f8f/packages/flutter/lib/src/widgets/framework.dart (L6534)
I've been unable to figure out a way to get into a state that reproduces this. Instead, this PR simply handles the exception and returns `null` (because we already gracefully handle the case where the `renderObject` is `null`.
This commit is contained in:
parent
c0e6f90652
commit
b7bea22ab8
@ -2068,7 +2068,7 @@ mixin WidgetInspectorService {
|
|||||||
}
|
}
|
||||||
final Object? value = node.value;
|
final Object? value = node.value;
|
||||||
if (value is Element) {
|
if (value is Element) {
|
||||||
final RenderObject? renderObject = value.renderObject;
|
final RenderObject? renderObject = _renderObjectOrNull(value);
|
||||||
if (renderObject is RenderParagraph) {
|
if (renderObject is RenderParagraph) {
|
||||||
additionalPropertiesJson['textPreview'] = renderObject.text.toPlainText();
|
additionalPropertiesJson['textPreview'] = renderObject.text.toPlainText();
|
||||||
}
|
}
|
||||||
@ -2160,7 +2160,7 @@ mixin WidgetInspectorService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final RenderObject? renderObject =
|
final RenderObject? renderObject =
|
||||||
object is Element ? object.renderObject : (object as RenderObject?);
|
object is Element ? _renderObjectOrNull(object) : (object as RenderObject?);
|
||||||
if (renderObject == null || !renderObject.attached) {
|
if (renderObject == null || !renderObject.attached) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -2224,7 +2224,7 @@ mixin WidgetInspectorService {
|
|||||||
InspectorSerializationDelegate delegate,
|
InspectorSerializationDelegate delegate,
|
||||||
) {
|
) {
|
||||||
final Object? value = node.value;
|
final Object? value = node.value;
|
||||||
final RenderObject? renderObject = value is Element ? value.renderObject : null;
|
final RenderObject? renderObject = value is Element ? _renderObjectOrNull(value) : null;
|
||||||
if (renderObject == null) {
|
if (renderObject == null) {
|
||||||
return const <String, Object>{};
|
return const <String, Object>{};
|
||||||
}
|
}
|
||||||
@ -2320,7 +2320,7 @@ mixin WidgetInspectorService {
|
|||||||
final Object? object = toObject(id);
|
final Object? object = toObject(id);
|
||||||
bool succeed = false;
|
bool succeed = false;
|
||||||
if (object != null && object is Element) {
|
if (object != null && object is Element) {
|
||||||
final RenderObject? render = object.renderObject;
|
final RenderObject? render = _renderObjectOrNull(object);
|
||||||
final ParentData? parentData = render?.parentData;
|
final ParentData? parentData = render?.parentData;
|
||||||
if (parentData is FlexParentData) {
|
if (parentData is FlexParentData) {
|
||||||
parentData.fit = flexFit;
|
parentData.fit = flexFit;
|
||||||
@ -2338,7 +2338,7 @@ mixin WidgetInspectorService {
|
|||||||
final dynamic object = toObject(id);
|
final dynamic object = toObject(id);
|
||||||
bool succeed = false;
|
bool succeed = false;
|
||||||
if (object != null && object is Element) {
|
if (object != null && object is Element) {
|
||||||
final RenderObject? render = object.renderObject;
|
final RenderObject? render = _renderObjectOrNull(object);
|
||||||
final ParentData? parentData = render?.parentData;
|
final ParentData? parentData = render?.parentData;
|
||||||
if (parentData is FlexParentData) {
|
if (parentData is FlexParentData) {
|
||||||
parentData.flex = factor;
|
parentData.flex = factor;
|
||||||
@ -2362,7 +2362,7 @@ mixin WidgetInspectorService {
|
|||||||
final Object? object = toObject(id);
|
final Object? object = toObject(id);
|
||||||
bool succeed = false;
|
bool succeed = false;
|
||||||
if (object != null && object is Element) {
|
if (object != null && object is Element) {
|
||||||
final RenderObject? render = object.renderObject;
|
final RenderObject? render = _renderObjectOrNull(object);
|
||||||
if (render is RenderFlex) {
|
if (render is RenderFlex) {
|
||||||
render.mainAxisAlignment = mainAxisAlignment;
|
render.mainAxisAlignment = mainAxisAlignment;
|
||||||
render.crossAxisAlignment = crossAxisAlignment;
|
render.crossAxisAlignment = crossAxisAlignment;
|
||||||
@ -2543,6 +2543,12 @@ mixin WidgetInspectorService {
|
|||||||
_clearStats();
|
_clearStats();
|
||||||
_resetErrorCount();
|
_resetErrorCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Safely get the render object of an [Element].
|
||||||
|
///
|
||||||
|
/// If the element is not yet mounted, the result will be null.
|
||||||
|
RenderObject? _renderObjectOrNull(Element element) =>
|
||||||
|
element.mounted ? element.renderObject : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accumulator for a count associated with a specific source location.
|
/// Accumulator for a count associated with a specific source location.
|
||||||
|
@ -4837,6 +4837,32 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
|
|||||||
expect(parentData['offsetY'], equals('293.0'));
|
expect(parentData['offsetY'], equals('293.0'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'ext.flutter.inspector.getLayoutExplorerNode does not throw for unmounted widget',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
// Mount the Row widget.
|
||||||
|
await pumpWidgetForLayoutExplorer(tester);
|
||||||
|
|
||||||
|
// Get the id of the Row widget.
|
||||||
|
final Element rowElement = tester.element(find.byType(Row));
|
||||||
|
service.setSelection(rowElement, group);
|
||||||
|
final String id = service.toId(rowElement, group)!;
|
||||||
|
|
||||||
|
// Unmount the Row widget.
|
||||||
|
await tester.pumpWidget(const Placeholder());
|
||||||
|
|
||||||
|
// Verify that the call to getLayoutExplorerNode for the Row widget
|
||||||
|
// does not throw an exception.
|
||||||
|
expect(
|
||||||
|
() => service.testExtension(
|
||||||
|
WidgetInspectorServiceExtensions.getLayoutExplorerNode.name,
|
||||||
|
<String, String>{'id': id, 'groupName': group, 'subtreeDepth': '1'},
|
||||||
|
),
|
||||||
|
returnsNormally,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
testWidgets('ext.flutter.inspector.getLayoutExplorerNode for RenderBox with FlexParentData', (
|
testWidgets('ext.flutter.inspector.getLayoutExplorerNode for RenderBox with FlexParentData', (
|
||||||
WidgetTester tester,
|
WidgetTester tester,
|
||||||
) async {
|
) async {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user