mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
This commit is contained in:
parent
9da74f66ca
commit
6d303af97e
|
@ -2293,6 +2293,9 @@ class _WidgetInspectorState extends State<WidgetInspector>
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Be careful changing this build method. The _InspectorOverlayLayer
|
||||
// assumes the root RenderObject for the WidgetInspector will be
|
||||
// a RenderStack with a _RenderInspectorOverlay as the last child.
|
||||
return Stack(children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: _handleTap,
|
||||
|
@ -2441,15 +2444,16 @@ class _RenderInspectorOverlay extends RenderBox {
|
|||
context.addLayer(_InspectorOverlayLayer(
|
||||
overlayRect: Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height),
|
||||
selection: selection,
|
||||
rootRenderObject: parent is RenderObject ? parent as RenderObject : null,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class _TransformedRect {
|
||||
_TransformedRect(RenderObject object)
|
||||
_TransformedRect(RenderObject object, RenderObject ancestor)
|
||||
: rect = object.semanticBounds,
|
||||
transform = object.getTransformTo(null);
|
||||
transform = object.getTransformTo(ancestor);
|
||||
|
||||
final Rect rect;
|
||||
final Matrix4 transform;
|
||||
|
@ -2517,6 +2521,7 @@ class _InspectorOverlayLayer extends Layer {
|
|||
_InspectorOverlayLayer({
|
||||
@required this.overlayRect,
|
||||
@required this.selection,
|
||||
@required this.rootRenderObject,
|
||||
}) : assert(overlayRect != null),
|
||||
assert(selection != null) {
|
||||
bool inDebugMode = false;
|
||||
|
@ -2543,6 +2548,10 @@ class _InspectorOverlayLayer extends Layer {
|
|||
/// (as described at [Layer]).
|
||||
final Rect overlayRect;
|
||||
|
||||
/// Widget inspector root render object. The selection overlay will be painted
|
||||
/// with transforms relative to this render object.
|
||||
final RenderObject rootRenderObject;
|
||||
|
||||
_InspectorOverlayRenderState _lastState;
|
||||
|
||||
/// Picture generated from _lastState.
|
||||
|
@ -2557,16 +2566,21 @@ class _InspectorOverlayLayer extends Layer {
|
|||
return;
|
||||
|
||||
final RenderObject selected = selection.current;
|
||||
|
||||
if (!_isInInspectorRenderObjectTree(selected))
|
||||
return;
|
||||
|
||||
final List<_TransformedRect> candidates = <_TransformedRect>[];
|
||||
for (final RenderObject candidate in selection.candidates) {
|
||||
if (candidate == selected || !candidate.attached)
|
||||
if (candidate == selected || !candidate.attached
|
||||
|| !_isInInspectorRenderObjectTree(candidate))
|
||||
continue;
|
||||
candidates.add(_TransformedRect(candidate));
|
||||
candidates.add(_TransformedRect(candidate, rootRenderObject));
|
||||
}
|
||||
|
||||
final _InspectorOverlayRenderState state = _InspectorOverlayRenderState(
|
||||
overlayRect: overlayRect,
|
||||
selected: _TransformedRect(selected),
|
||||
selected: _TransformedRect(selected, rootRenderObject),
|
||||
tooltip: selection.currentElement.toStringShort(),
|
||||
textDirection: TextDirection.ltr,
|
||||
candidates: candidates,
|
||||
|
@ -2583,6 +2597,9 @@ class _InspectorOverlayLayer extends Layer {
|
|||
final ui.PictureRecorder recorder = ui.PictureRecorder();
|
||||
final Canvas canvas = Canvas(recorder, state.overlayRect);
|
||||
final Size size = state.overlayRect.size;
|
||||
// The overlay rect could have an offset if the widget inspector does
|
||||
// not take all the screen.
|
||||
canvas.translate(state.overlayRect.left, state.overlayRect.top);
|
||||
|
||||
final Paint fillPaint = Paint()
|
||||
..style = PaintingStyle.fill
|
||||
|
@ -2695,6 +2712,24 @@ class _InspectorOverlayLayer extends Layer {
|
|||
}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Return whether or not a render object belongs to this inspector widget
|
||||
/// tree.
|
||||
/// The inspector selection is static, so if there are multiple inspector
|
||||
/// overlays in the same app (i.e. an storyboard), a selected or candidate
|
||||
/// render object may not belong to this tree.
|
||||
bool _isInInspectorRenderObjectTree(RenderObject child) {
|
||||
RenderObject current = child.parent as RenderObject;
|
||||
while (current != null) {
|
||||
// We found the widget inspector render object.
|
||||
if (current is RenderStack
|
||||
&& current.lastChild is _RenderInspectorOverlay) {
|
||||
return rootRenderObject == current;
|
||||
}
|
||||
current = current.parent as RenderObject;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const double _kScreenEdgeMargin = 10.0;
|
||||
|
|
|
@ -512,6 +512,123 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService {
|
|||
expect(inspectorState.selection.candidates.where((RenderObject object) => object is RenderParagraph).length, equals(2));
|
||||
});
|
||||
|
||||
testWidgets('WidgetInspector with Transform above', (WidgetTester tester) async {
|
||||
final GlobalKey childKey = GlobalKey();
|
||||
final GlobalKey repaintBoundaryKey = GlobalKey();
|
||||
|
||||
final Matrix4 mainTransform = Matrix4.identity()
|
||||
..translate(50.0, 30.0)
|
||||
..scale(0.8, 0.8)
|
||||
..translate(100.0, 50.0);
|
||||
|
||||
await tester.pumpWidget(
|
||||
RepaintBoundary(
|
||||
key: repaintBoundaryKey,
|
||||
child: Container(
|
||||
color: Colors.grey,
|
||||
child: Transform(
|
||||
transform: mainTransform,
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: WidgetInspector(
|
||||
selectButtonBuilder: null,
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
child: Center(
|
||||
child: Container(
|
||||
key: childKey,
|
||||
height: 100.0,
|
||||
width: 50.0,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.byKey(childKey));
|
||||
await tester.pump();
|
||||
|
||||
await expectLater(
|
||||
find.byKey(repaintBoundaryKey),
|
||||
matchesGoldenFile('inspector.overlay_positioning_with_transform.png'),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Multiple widget inspectors', (WidgetTester tester) async {
|
||||
// This test verifies that interacting with different inspectors
|
||||
// works correctly. This use case may be an app that displays multiple
|
||||
// apps inside (i.e. a storyboard).
|
||||
final GlobalKey selectButton1Key = GlobalKey();
|
||||
final GlobalKey selectButton2Key = GlobalKey();
|
||||
|
||||
final GlobalKey inspector1Key = GlobalKey();
|
||||
final GlobalKey inspector2Key = GlobalKey();
|
||||
|
||||
final GlobalKey child1Key = GlobalKey();
|
||||
final GlobalKey child2Key = GlobalKey();
|
||||
|
||||
InspectorSelectButtonBuilder selectButtonBuilder(Key key) {
|
||||
return (BuildContext context, VoidCallback onPressed) {
|
||||
return Material(child: RaisedButton(onPressed: onPressed, key: key));
|
||||
};
|
||||
}
|
||||
|
||||
// State type is private, hence using dynamic.
|
||||
// The inspector state is static, so it's enough with reading one of them.
|
||||
dynamic getInspectorState() => inspector1Key.currentState;
|
||||
String paragraphText(RenderParagraph paragraph) {
|
||||
final TextSpan textSpan = paragraph.text as TextSpan;
|
||||
return textSpan.text;
|
||||
}
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: WidgetInspector(
|
||||
key: inspector1Key,
|
||||
selectButtonBuilder: selectButtonBuilder(selectButton1Key),
|
||||
child: Container(
|
||||
key: child1Key,
|
||||
child: const Text('Child 1'),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: WidgetInspector(
|
||||
key: inspector2Key,
|
||||
selectButtonBuilder: selectButtonBuilder(selectButton2Key),
|
||||
child: Container(
|
||||
key: child2Key,
|
||||
child: const Text('Child 2'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final InspectorSelection selection = getInspectorState().selection as InspectorSelection;
|
||||
// The selection is static, so it may be initialized from previous tests.
|
||||
selection?.clear();
|
||||
|
||||
await tester.tap(find.text('Child 1'));
|
||||
await tester.pump();
|
||||
expect(paragraphText(selection.current as RenderParagraph), equals('Child 1'));
|
||||
|
||||
await tester.tap(find.text('Child 2'));
|
||||
await tester.pump();
|
||||
expect(paragraphText(selection.current as RenderParagraph), equals('Child 2'));
|
||||
});
|
||||
|
||||
test('WidgetInspectorService null id', () {
|
||||
service.disposeAllGroups();
|
||||
expect(service.toObject(null), isNull);
|
||||
|
|
Loading…
Reference in a new issue