[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;
|
||||
if (value is Element) {
|
||||
final RenderObject? renderObject = value.renderObject;
|
||||
final RenderObject? renderObject = _renderObjectOrNull(value);
|
||||
if (renderObject is RenderParagraph) {
|
||||
additionalPropertiesJson['textPreview'] = renderObject.text.toPlainText();
|
||||
}
|
||||
@ -2160,7 +2160,7 @@ mixin WidgetInspectorService {
|
||||
return null;
|
||||
}
|
||||
final RenderObject? renderObject =
|
||||
object is Element ? object.renderObject : (object as RenderObject?);
|
||||
object is Element ? _renderObjectOrNull(object) : (object as RenderObject?);
|
||||
if (renderObject == null || !renderObject.attached) {
|
||||
return null;
|
||||
}
|
||||
@ -2224,7 +2224,7 @@ mixin WidgetInspectorService {
|
||||
InspectorSerializationDelegate delegate,
|
||||
) {
|
||||
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) {
|
||||
return const <String, Object>{};
|
||||
}
|
||||
@ -2320,7 +2320,7 @@ mixin WidgetInspectorService {
|
||||
final Object? object = toObject(id);
|
||||
bool succeed = false;
|
||||
if (object != null && object is Element) {
|
||||
final RenderObject? render = object.renderObject;
|
||||
final RenderObject? render = _renderObjectOrNull(object);
|
||||
final ParentData? parentData = render?.parentData;
|
||||
if (parentData is FlexParentData) {
|
||||
parentData.fit = flexFit;
|
||||
@ -2338,7 +2338,7 @@ mixin WidgetInspectorService {
|
||||
final dynamic object = toObject(id);
|
||||
bool succeed = false;
|
||||
if (object != null && object is Element) {
|
||||
final RenderObject? render = object.renderObject;
|
||||
final RenderObject? render = _renderObjectOrNull(object);
|
||||
final ParentData? parentData = render?.parentData;
|
||||
if (parentData is FlexParentData) {
|
||||
parentData.flex = factor;
|
||||
@ -2362,7 +2362,7 @@ mixin WidgetInspectorService {
|
||||
final Object? object = toObject(id);
|
||||
bool succeed = false;
|
||||
if (object != null && object is Element) {
|
||||
final RenderObject? render = object.renderObject;
|
||||
final RenderObject? render = _renderObjectOrNull(object);
|
||||
if (render is RenderFlex) {
|
||||
render.mainAxisAlignment = mainAxisAlignment;
|
||||
render.crossAxisAlignment = crossAxisAlignment;
|
||||
@ -2543,6 +2543,12 @@ mixin WidgetInspectorService {
|
||||
_clearStats();
|
||||
_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.
|
||||
|
@ -4837,6 +4837,32 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
|
||||
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', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
|
Loading…
x
Reference in New Issue
Block a user