Tooltip update can cause assert (#7338)
We were trying to update the tooltip overlay entry, but that cannot work because the overlay entry might have already built. Instead, we keep the old value. Fixes #7151
This commit is contained in:
@@ -110,18 +110,6 @@ class _TooltipState extends State<Tooltip> 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<Tooltip> 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();
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Null> 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<Null> 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.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user