[go: nahoru, domu]

Skip to content

Commit

Permalink
Fix LayoutExplorer cycle (#115526)
Browse files Browse the repository at this point in the history
* Fix LayoutExplorer cycle

* fix tests

* Update widget_inspector.dart

* Update widget_inspector.dart

* review

* expandPropertyValues
  • Loading branch information
LongCatIsLooong committed Nov 28, 2022
1 parent b22ab51 commit ccc277c
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 79 deletions.
150 changes: 75 additions & 75 deletions packages/flutter/lib/src/widgets/widget_inspector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2038,90 +2038,90 @@ mixin WidgetInspectorService {
subtreeDepth: subtreeDepth,
service: this,
addAdditionalPropertiesCallback: (DiagnosticsNode node, InspectorSerializationDelegate delegate) {
final Map<String, Object> additionalJson = <String, Object>{};
final Object? value = node.value;
if (value is Element) {
final RenderObject? renderObject = value.renderObject;
if (renderObject != null) {
additionalJson['renderObject'] =
renderObject.toDiagnosticsNode().toJsonMap(
final RenderObject? renderObject = value is Element ? value.renderObject : null;
if (renderObject == null) {
return const <String, Object>{};
}

final DiagnosticsSerializationDelegate renderObjectSerializationDelegate = delegate.copyWith(
subtreeDepth: 0,
includeProperties: true,
expandPropertyValues: false,
);
final Map<String, Object> additionalJson = <String, Object>{
'renderObject': renderObject.toDiagnosticsNode().toJsonMap(renderObjectSerializationDelegate),
};

final AbstractNode? renderParent = renderObject.parent;
if (renderParent is RenderObject && subtreeDepth > 0) {
final Object? parentCreator = renderParent.debugCreator;
if (parentCreator is DebugCreator) {
additionalJson['parentRenderElement'] =
parentCreator.element.toDiagnosticsNode().toJsonMap(
delegate.copyWith(
subtreeDepth: 0,
includeProperties: true,
),
);
// TODO(jacobr): also describe the path back up the tree to
// the RenderParentElement from the current element. It
// could be a surprising distance up the tree if a lot of
// elements don't have their own RenderObjects.
}
}

final AbstractNode? renderParent = renderObject.parent;
if (renderParent is RenderObject && subtreeDepth > 0) {
final Object? parentCreator = renderParent.debugCreator;
if (parentCreator is DebugCreator) {
additionalJson['parentRenderElement'] =
parentCreator.element.toDiagnosticsNode().toJsonMap(
delegate.copyWith(
subtreeDepth: 0,
includeProperties: true,
),
);
// TODO(jacobr): also describe the path back up the tree to
// the RenderParentElement from the current element. It
// could be a surprising distance up the tree if a lot of
// elements don't have their own RenderObjects.
}
}

try {
if (!renderObject.debugNeedsLayout) {
// ignore: invalid_use_of_protected_member
final Constraints constraints = renderObject.constraints;
final Map<String, Object>constraintsProperty = <String, Object>{
'type': constraints.runtimeType.toString(),
'description': constraints.toString(),
};
if (constraints is BoxConstraints) {
constraintsProperty.addAll(<String, Object>{
'minWidth': constraints.minWidth.toString(),
'minHeight': constraints.minHeight.toString(),
'maxWidth': constraints.maxWidth.toString(),
'maxHeight': constraints.maxHeight.toString(),
});
}
additionalJson['constraints'] = constraintsProperty;
}
} catch (e) {
// Constraints are sometimes unavailable even though
// debugNeedsLayout is false.
try {
if (!renderObject.debugNeedsLayout) {
// ignore: invalid_use_of_protected_member
final Constraints constraints = renderObject.constraints;
final Map<String, Object>constraintsProperty = <String, Object>{
'type': constraints.runtimeType.toString(),
'description': constraints.toString(),
};
if (constraints is BoxConstraints) {
constraintsProperty.addAll(<String, Object>{
'minWidth': constraints.minWidth.toString(),
'minHeight': constraints.minHeight.toString(),
'maxWidth': constraints.maxWidth.toString(),
'maxHeight': constraints.maxHeight.toString(),
});
}
additionalJson['constraints'] = constraintsProperty;
}
} catch (e) {
// Constraints are sometimes unavailable even though
// debugNeedsLayout is false.
}

try {
if (renderObject is RenderBox) {
additionalJson['isBox'] = true;
additionalJson['size'] = <String, Object>{
'width': renderObject.size.width.toString(),
'height': renderObject.size.height.toString(),
};

final ParentData? parentData = renderObject.parentData;
if (parentData is FlexParentData) {
additionalJson['flexFactor'] = parentData.flex!;
additionalJson['flexFit'] =
describeEnum(parentData.fit ?? FlexFit.tight);
} else if (parentData is BoxParentData) {
final Offset offset = parentData.offset;
additionalJson['parentData'] = <String, Object>{
'offsetX': offset.dx.toString(),
'offsetY': offset.dy.toString(),
};
}
} else if (renderObject is RenderView) {
additionalJson['size'] = <String, Object>{
'width': renderObject.size.width.toString(),
'height': renderObject.size.height.toString(),
};
}
} catch (e) {
// Not laid out yet.
try {
if (renderObject is RenderBox) {
additionalJson['isBox'] = true;
additionalJson['size'] = <String, Object>{
'width': renderObject.size.width.toString(),
'height': renderObject.size.height.toString(),
};

final ParentData? parentData = renderObject.parentData;
if (parentData is FlexParentData) {
additionalJson['flexFactor'] = parentData.flex!;
additionalJson['flexFit'] =
describeEnum(parentData.fit ?? FlexFit.tight);
} else if (parentData is BoxParentData) {
final Offset offset = parentData.offset;
additionalJson['parentData'] = <String, Object>{
'offsetX': offset.dx.toString(),
'offsetY': offset.dy.toString(),
};
}
} else if (renderObject is RenderView) {
additionalJson['size'] = <String, Object>{
'width': renderObject.size.width.toString(),
'height': renderObject.size.height.toString(),
};
}
} catch (e) {
// Not laid out yet.
}
return additionalJson;
},
Expand Down Expand Up @@ -3633,12 +3633,12 @@ class InspectorSerializationDelegate implements DiagnosticsSerializationDelegate
}

@override
DiagnosticsSerializationDelegate copyWith({int? subtreeDepth, bool? includeProperties}) {
DiagnosticsSerializationDelegate copyWith({int? subtreeDepth, bool? includeProperties, bool? expandPropertyValues}) {
return InspectorSerializationDelegate(
groupName: groupName,
summaryTree: summaryTree,
maxDescendentsTruncatableNode: maxDescendentsTruncatableNode,
expandPropertyValues: expandPropertyValues,
expandPropertyValues: expandPropertyValues ?? this.expandPropertyValues,
subtreeDepth: subtreeDepth ?? this.subtreeDepth,
includeProperties: includeProperties ?? this.includeProperties,
service: service,
Expand Down
39 changes: 35 additions & 4 deletions packages/flutter/test/widgets/widget_inspector_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import 'dart:convert';
import 'dart:math';
import 'dart:ui' as ui;

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -3549,7 +3550,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
_CreationLocation location = knownLocations[id]!;
expect(location.file, equals(file));
// ClockText widget.
expect(location.line, equals(59));
expect(location.line, equals(60));
expect(location.column, equals(9));
expect(location.name, equals('ClockText'));
expect(count, equals(1));
Expand All @@ -3559,7 +3560,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
location = knownLocations[id]!;
expect(location.file, equals(file));
// Text widget in _ClockTextState build method.
expect(location.line, equals(97));
expect(location.line, equals(98));
expect(location.column, equals(12));
expect(location.name, equals('Text'));
expect(count, equals(1));
Expand All @@ -3586,7 +3587,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
location = knownLocations[id]!;
expect(location.file, equals(file));
// ClockText widget.
expect(location.line, equals(59));
expect(location.line, equals(60));
expect(location.column, equals(9));
expect(location.name, equals('ClockText'));
expect(count, equals(3)); // 3 clock widget instances rebuilt.
Expand All @@ -3596,7 +3597,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
location = knownLocations[id]!;
expect(location.file, equals(file));
// Text widget in _ClockTextState build method.
expect(location.line, equals(97));
expect(location.line, equals(98));
expect(location.column, equals(12));
expect(location.name, equals('Text'));
expect(count, equals(3)); // 3 clock widget instances rebuilt.
Expand Down Expand Up @@ -4437,6 +4438,36 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
expect(mainAxisAlignment, equals('center'));
expect(crossAxisAlignment, equals('start'));
});

testWidgets('ext.flutter.inspector.getLayoutExplorerNode does not throw StackOverflowError',(WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/115228
const Key leafKey = ValueKey<String>('ColoredBox');
await tester.pumpWidget(
CupertinoApp(
home: CupertinoPageScaffold(
child: Builder(
builder: (BuildContext context) => ColoredBox(key: leafKey, color: CupertinoTheme.of(context).primaryColor),
),
),
),
);

final Element leaf = tester.element(find.byKey(leafKey));
service.setSelection(leaf, group);
final DiagnosticsNode diagnostic = leaf.toDiagnosticsNode();
final String id = service.toId(diagnostic, group)!;

Object? error;
try {
await service.testExtension(
WidgetInspectorServiceExtensions.getLayoutExplorerNode.name,
<String, String>{'id': id, 'groupName': group, 'subtreeDepth': '1'},
);
} catch (e) {
error = e;
}
expect(error, isNull);
});
});

test('ext.flutter.inspector.structuredErrors', () async {
Expand Down

0 comments on commit ccc277c

Please sign in to comment.