diff --git a/packages/flutter/lib/src/material/tooltip.dart b/packages/flutter/lib/src/material/tooltip.dart index 9a99b242ce..f055ef506b 100644 --- a/packages/flutter/lib/src/material/tooltip.dart +++ b/packages/flutter/lib/src/material/tooltip.dart @@ -110,18 +110,6 @@ class _TooltipState extends State with SingleTickerProviderStateMixin { _removeEntry(); } - @override - void didUpdateConfig(Tooltip oldConfig) { - super.didUpdateConfig(oldConfig); - if (_entry != null && - (config.message != oldConfig.message || - config.height != oldConfig.height || - config.padding != oldConfig.padding || - config.verticalOffset != oldConfig.verticalOffset || - config.preferBelow != oldConfig.preferBelow)) - _entry.markNeedsBuild(); - } - void ensureTooltipVisible() { if (_entry != null) { _timer?.cancel(); @@ -129,22 +117,24 @@ class _TooltipState extends State with SingleTickerProviderStateMixin { _controller.forward(); return; // Already visible. } - RenderBox box = context.findRenderObject(); - Point target = box.localToGlobal(box.size.center(Point.origin)); - _entry = new OverlayEntry(builder: (BuildContext context) { - return new _TooltipOverlay( - message: config.message, - height: config.height, - padding: config.padding, - animation: new CurvedAnimation( - parent: _controller, - curve: Curves.fastOutSlowIn - ), - target: target, - verticalOffset: config.verticalOffset, - preferBelow: config.preferBelow - ); - }); + final RenderBox box = context.findRenderObject(); + final Point target = box.localToGlobal(box.size.center(Point.origin)); + // We create this widget outside of the overlay entry's builder to prevent + // updated values from happening to leak into the overlay when the overlay + // rebuilds. + final Widget overlay = new _TooltipOverlay( + message: config.message, + height: config.height, + padding: config.padding, + animation: new CurvedAnimation( + parent: _controller, + curve: Curves.fastOutSlowIn + ), + target: target, + verticalOffset: config.verticalOffset, + preferBelow: config.preferBelow + ); + _entry = new OverlayEntry(builder: (BuildContext context) => overlay); Overlay.of(context, debugRequiredFor: config).insert(_entry); GestureBinding.instance.pointerRouter.addGlobalRoute(_handlePointerEvent); _controller.forward(); diff --git a/packages/flutter/test/material/tooltip_test.dart b/packages/flutter/test/material/tooltip_test.dart index 1c921bbb02..c51d79ad14 100644 --- a/packages/flutter/test/material/tooltip_test.dart +++ b/packages/flutter/test/material/tooltip_test.dart @@ -473,4 +473,36 @@ void main() { semantics.dispose(); }); + + testWidgets('Tooltip overlay does not update', (WidgetTester tester) async { + Widget buildApp(String text) { + return new MaterialApp( + home: new Center( + child: new Tooltip( + message: text, + child: new Container( + width: 100.0, + height: 100.0, + decoration: new BoxDecoration( + backgroundColor: Colors.green[500] + ) + ) + ) + ) + ); + } + + await tester.pumpWidget(buildApp(tooltipText)); + await tester.longPress(find.byType(Tooltip)); + expect(find.text(tooltipText), findsOneWidget); + await tester.pumpWidget(buildApp('NEW')); + expect(find.text(tooltipText), findsOneWidget); + await tester.tapAt(const Point(5.0, 5.0)); + await tester.pump(); + await tester.pump(const Duration(seconds: 1)); + expect(find.text(tooltipText), findsNothing); + await tester.longPress(find.byType(Tooltip)); + expect(find.text(tooltipText), findsNothing); + }); + } diff --git a/packages/flutter_test/lib/src/controller.dart b/packages/flutter_test/lib/src/controller.dart index 74df4e29ac..2e1d044870 100644 --- a/packages/flutter_test/lib/src/controller.dart +++ b/packages/flutter_test/lib/src/controller.dart @@ -267,6 +267,26 @@ class WidgetController { }); } + /// Dispatch a pointer down / pointer up sequence (with a delay of + /// [kLongPressTimeout] + [kPressTimeout] between the two events) at the + /// center of the given widget, assuming it is exposed. If the center of the + /// widget is not exposed, this might send events to another + /// object. + Future longPress(Finder finder, { int pointer: 1 }) { + return longPressAt(getCenter(finder), pointer: pointer); + } + + /// Dispatch a pointer down / pointer up sequence at the given location with + /// a delay of [kLongPressTimeout] + [kPressTimeout] between the two events. + Future longPressAt(Point location, { int pointer: 1 }) { + return TestAsyncUtils.guard(() async { + TestGesture gesture = await startGesture(location, pointer: pointer); + await pump(kLongPressTimeout + kPressTimeout); + await gesture.up(); + return null; + }); + } + /// Attempts a fling gesture starting from the center of the given /// widget, moving the given distance, reaching the given velocity. ///