From e4a909fb098dce2af485a6331a146a5dc7580a99 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 8 Aug 2019 13:36:03 -0700 Subject: [PATCH] Fix mouse region double render (#37344) Fix an issue where MouseRegion will render its children twice. --- .../flutter/lib/src/rendering/proxy_box.dart | 3 +- .../test/widgets/mouse_region_test.dart | 88 +++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart index 2feac8c925..67725daade 100644 --- a/packages/flutter/lib/src/rendering/proxy_box.dart +++ b/packages/flutter/lib/src/rendering/proxy_box.dart @@ -2727,8 +2727,9 @@ class RenderMouseRegion extends RenderProxyBox { offset: offset, ); context.pushLayer(layer, super.paint, offset); + } else { + super.paint(context, offset); } - super.paint(context, offset); } @override diff --git a/packages/flutter/test/widgets/mouse_region_test.dart b/packages/flutter/test/widgets/mouse_region_test.dart index 2523e2234a..91991baa41 100644 --- a/packages/flutter/test/widgets/mouse_region_test.dart +++ b/packages/flutter/test/widgets/mouse_region_test.dart @@ -623,4 +623,92 @@ void main() { expect(exit.single.delta, const Offset(0.0, 0.0)); }); }); + + group('MouseRegion paints child once and only once', () { + testWidgets('When MouseRegion is inactive', (WidgetTester tester) async { + int paintCount = 0; + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: MouseRegion( + onEnter: (PointerEnterEvent e) {}, + child: _PaintDelegateWidget( + onPaint: _VoidDelegate(() => paintCount++), + child: const Text('123'), + ), + ), + ), + ); + + expect(paintCount, 1); + }); + + testWidgets('When MouseRegion is active', (WidgetTester tester) async { + int paintCount = 0; + + final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: MouseRegion( + onEnter: (PointerEnterEvent e) {}, + child: _PaintDelegateWidget( + onPaint: _VoidDelegate(() => paintCount++), + child: const Text('123'), + ), + ), + ), + ); + + expect(paintCount, 1); + await gesture.removePointer(); + }); + }); +} + +// This widget allows you to send a callback that is called during `onPaint. +@immutable +class _PaintDelegateWidget extends SingleChildRenderObjectWidget { + const _PaintDelegateWidget({ + Key key, + Widget child, + this.onPaint, + }) : super(key: key, child: child); + + final _VoidDelegate onPaint; + + @override + RenderObject createRenderObject(BuildContext context) { + return _PaintCallbackObject(onPaint: onPaint?.callback); + } + + @override + void updateRenderObject(BuildContext context, _PaintCallbackObject renderObject) { + renderObject + ..onPaint = onPaint?.callback; + } +} + +class _VoidDelegate { + _VoidDelegate(this.callback); + + void Function() callback; +} + +class _PaintCallbackObject extends RenderProxyBox { + _PaintCallbackObject({ + RenderObject child, + this.onPaint, + }) : super(child); + + void Function() onPaint; + + @override + void paint(PaintingContext context, Offset offset) { + if (onPaint != null) + onPaint(); + super.paint(context, offset); + } }