Unverified Commit ebe72d3f authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Call `markNeedsPaint` when adding overlayChild to `Overlay` (#135941)

Fixes https://github.com/flutter/flutter/issues/134656

`_skipMarkNeesLayout` was meant to only skip `markNeedsLayout` calls. Re-painting is still needed when a child gets added/removed from the `Overlay`.
parent 68d47e55
...@@ -412,10 +412,8 @@ class MouseTracker extends ChangeNotifier { ...@@ -412,10 +412,8 @@ class MouseTracker extends ChangeNotifier {
// hit-test order. // hit-test order.
final PointerExitEvent baseExitEvent = PointerExitEvent.fromMouseEvent(latestEvent); final PointerExitEvent baseExitEvent = PointerExitEvent.fromMouseEvent(latestEvent);
lastAnnotations.forEach((MouseTrackerAnnotation annotation, Matrix4 transform) { lastAnnotations.forEach((MouseTrackerAnnotation annotation, Matrix4 transform) {
if (!nextAnnotations.containsKey(annotation)) { if (annotation.validForMouseTracker && !nextAnnotations.containsKey(annotation)) {
if (annotation.validForMouseTracker && annotation.onExit != null) { annotation.onExit?.call(baseExitEvent.transformed(lastAnnotations[annotation]));
annotation.onExit!(baseExitEvent.transformed(lastAnnotations[annotation]));
}
} }
}); });
...@@ -426,8 +424,8 @@ class MouseTracker extends ChangeNotifier { ...@@ -426,8 +424,8 @@ class MouseTracker extends ChangeNotifier {
).toList(); ).toList();
final PointerEnterEvent baseEnterEvent = PointerEnterEvent.fromMouseEvent(latestEvent); final PointerEnterEvent baseEnterEvent = PointerEnterEvent.fromMouseEvent(latestEvent);
for (final MouseTrackerAnnotation annotation in enteringAnnotations.reversed) { for (final MouseTrackerAnnotation annotation in enteringAnnotations.reversed) {
if (annotation.validForMouseTracker && annotation.onEnter != null) { if (annotation.validForMouseTracker) {
annotation.onEnter!(baseEnterEvent.transformed(nextAnnotations[annotation])); annotation.onEnter?.call(baseEnterEvent.transformed(nextAnnotations[annotation]));
} }
} }
} }
......
...@@ -1032,6 +1032,10 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox ...@@ -1032,6 +1032,10 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox
assert(!_skipMarkNeedsLayout); assert(!_skipMarkNeedsLayout);
_skipMarkNeedsLayout = true; _skipMarkNeedsLayout = true;
adoptChild(child); adoptChild(child);
// The Overlay still needs repainting when a deferred child is added. Usually
// `markNeedsLayout` implies `markNeedsPaint`, but here `markNeedsLayout` is
// skipped when the `_skipMarkNeedsLayout` flag is set.
markNeedsPaint();
_skipMarkNeedsLayout = false; _skipMarkNeedsLayout = false;
// After adding `child` to the render tree, we want to make sure it will be // After adding `child` to the render tree, we want to make sure it will be
...@@ -1045,6 +1049,9 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox ...@@ -1045,6 +1049,9 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox
assert(!_skipMarkNeedsLayout); assert(!_skipMarkNeedsLayout);
_skipMarkNeedsLayout = true; _skipMarkNeedsLayout = true;
dropChild(child); dropChild(child);
// The Overlay still needs repainting when a deferred child is dropped. See
// the comment in `_addDeferredChild`.
markNeedsPaint();
_skipMarkNeedsLayout = false; _skipMarkNeedsLayout = false;
} }
......
...@@ -13,23 +13,6 @@ import '../widgets/semantics_tester.dart'; ...@@ -13,23 +13,6 @@ import '../widgets/semantics_tester.dart';
const String tooltipText = 'TIP'; const String tooltipText = 'TIP';
const double _customPaddingValue = 10.0; const double _customPaddingValue = 10.0;
void _ensureTooltipVisible(GlobalKey key) {
// This function uses "as dynamic"to defeat the static analysis. In general
// you want to avoid using this style in your code, as it will cause the
// analyzer to be unable to help you catch errors.
//
// In this case, we do it because we are trying to call internal methods of
// the tooltip code in order to test it. Normally, the state of a tooltip is a
// private class, but by using a GlobalKey we can get a handle to that object
// and by using "as dynamic" we can bypass the analyzer's type checks and call
// methods that we aren't supposed to be able to know about.
//
// It's ok to do this in tests, but you really don't want to do it in
// production code.
// ignore: avoid_dynamic_calls
(key.currentState as dynamic).ensureTooltipVisible();
}
void main() { void main() {
test('TooltipThemeData copyWith, ==, hashCode basics', () { test('TooltipThemeData copyWith, ==, hashCode basics', () {
expect(const TooltipThemeData(), const TooltipThemeData().copyWith()); expect(const TooltipThemeData(), const TooltipThemeData().copyWith());
...@@ -113,7 +96,7 @@ void main() { ...@@ -113,7 +96,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center prefer above fits - ThemeData.tooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center prefer above fits - ThemeData.tooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
late final OverlayEntry entry; late final OverlayEntry entry;
addTearDown(() => entry..remove()..dispose()); addTearDown(() => entry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
...@@ -152,7 +135,7 @@ void main() { ...@@ -152,7 +135,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
/********************* 800x600 screen /********************* 800x600 screen
...@@ -172,7 +155,7 @@ void main() { ...@@ -172,7 +155,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center prefer above fits - TooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center prefer above fits - TooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
late final OverlayEntry entry; late final OverlayEntry entry;
addTearDown(() => entry..remove()..dispose()); addTearDown(() => entry..remove()..dispose());
...@@ -211,7 +194,7 @@ void main() { ...@@ -211,7 +194,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
/********************* 800x600 screen /********************* 800x600 screen
...@@ -231,7 +214,7 @@ void main() { ...@@ -231,7 +214,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center prefer above does not fit - ThemeData.tooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center prefer above does not fit - ThemeData.tooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
late final OverlayEntry entry; late final OverlayEntry entry;
addTearDown(() => entry..remove()..dispose()); addTearDown(() => entry..remove()..dispose());
...@@ -271,7 +254,7 @@ void main() { ...@@ -271,7 +254,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
// we try to put it here but it doesn't fit: // we try to put it here but it doesn't fit:
...@@ -302,7 +285,7 @@ void main() { ...@@ -302,7 +285,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center prefer above does not fit - TooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center prefer above does not fit - TooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
late final OverlayEntry entry; late final OverlayEntry entry;
addTearDown(() => entry..remove()..dispose()); addTearDown(() => entry..remove()..dispose());
...@@ -341,7 +324,7 @@ void main() { ...@@ -341,7 +324,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
// we try to put it here but it doesn't fit: // we try to put it here but it doesn't fit:
...@@ -372,7 +355,7 @@ void main() { ...@@ -372,7 +355,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center preferBelow fits - ThemeData.tooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center preferBelow fits - ThemeData.tooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
late final OverlayEntry entry; late final OverlayEntry entry;
addTearDown(() => entry..remove()..dispose()); addTearDown(() => entry..remove()..dispose());
await tester.pumpWidget( await tester.pumpWidget(
...@@ -411,7 +394,7 @@ void main() { ...@@ -411,7 +394,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pumpAndSettle(); // faded in, show timer started (and at 0.0) await tester.pumpAndSettle(); // faded in, show timer started (and at 0.0)
/********************* 800x600 screen /********************* 800x600 screen
...@@ -430,7 +413,7 @@ void main() { ...@@ -430,7 +413,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center prefer below fits - TooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip verticalOffset, preferBelow; center prefer below fits - TooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
late final OverlayEntry entry; late final OverlayEntry entry;
addTearDown(() => entry..remove()..dispose()); addTearDown(() => entry..remove()..dispose());
...@@ -469,7 +452,7 @@ void main() { ...@@ -469,7 +452,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pumpAndSettle(); // faded in, show timer started (and at 0.0) await tester.pumpAndSettle(); // faded in, show timer started (and at 0.0)
/********************* 800x600 screen /********************* 800x600 screen
...@@ -488,7 +471,7 @@ void main() { ...@@ -488,7 +471,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip margin - ThemeData', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip margin - ThemeData', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
late final OverlayEntry entry; late final OverlayEntry entry;
addTearDown(() => entry..remove()..dispose()); addTearDown(() => entry..remove()..dispose());
...@@ -519,7 +502,7 @@ void main() { ...@@ -519,7 +502,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent!.parent!.parent!.parent!.parent! as RenderBox; final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent!.parent!.parent!.parent!.parent! as RenderBox;
...@@ -547,7 +530,7 @@ void main() { ...@@ -547,7 +530,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip margin - TooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip margin - TooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
late final OverlayEntry entry; late final OverlayEntry entry;
addTearDown(() => entry..remove()..dispose()); addTearDown(() => entry..remove()..dispose());
...@@ -575,7 +558,7 @@ void main() { ...@@ -575,7 +558,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent!.parent!.parent!.parent!.parent! as RenderBox; final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent!.parent!.parent!.parent!.parent! as RenderBox;
...@@ -603,7 +586,7 @@ void main() { ...@@ -603,7 +586,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip message textStyle - ThemeData.tooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip message textStyle - ThemeData.tooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: ThemeData( theme: ThemeData(
tooltipTheme: const TooltipThemeData( tooltipTheme: const TooltipThemeData(
...@@ -623,7 +606,7 @@ void main() { ...@@ -623,7 +606,7 @@ void main() {
), ),
), ),
)); ));
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
final TextStyle textStyle = tester.widget<Text>(find.text(tooltipText)).style!; final TextStyle textStyle = tester.widget<Text>(find.text(tooltipText)).style!;
...@@ -633,7 +616,7 @@ void main() { ...@@ -633,7 +616,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip message textStyle - TooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip message textStyle - TooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: TooltipTheme( home: TooltipTheme(
data: const TooltipThemeData(), data: const TooltipThemeData(),
...@@ -652,7 +635,7 @@ void main() { ...@@ -652,7 +635,7 @@ void main() {
), ),
), ),
)); ));
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
final TextStyle textStyle = tester.widget<Text>(find.text(tooltipText)).style!; final TextStyle textStyle = tester.widget<Text>(find.text(tooltipText)).style!;
...@@ -701,7 +684,7 @@ void main() { ...@@ -701,7 +684,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip decoration - ThemeData.tooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip decoration - ThemeData.tooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
const Decoration customDecoration = ShapeDecoration( const Decoration customDecoration = ShapeDecoration(
shape: StadiumBorder(), shape: StadiumBorder(),
color: Color(0x80800000), color: Color(0x80800000),
...@@ -734,7 +717,7 @@ void main() { ...@@ -734,7 +717,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent!.parent!.parent!.parent! as RenderBox; final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent!.parent!.parent!.parent! as RenderBox;
...@@ -745,7 +728,7 @@ void main() { ...@@ -745,7 +728,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip decoration - TooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip decoration - TooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
const Decoration customDecoration = ShapeDecoration( const Decoration customDecoration = ShapeDecoration(
shape: StadiumBorder(), shape: StadiumBorder(),
color: Color(0x80800000), color: Color(0x80800000),
...@@ -778,7 +761,7 @@ void main() { ...@@ -778,7 +761,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent!.parent!.parent!.parent! as RenderBox; final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent!.parent!.parent!.parent! as RenderBox;
...@@ -789,7 +772,7 @@ void main() { ...@@ -789,7 +772,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip height and padding - ThemeData.tooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip height and padding - ThemeData.tooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
const double customTooltipHeight = 100.0; const double customTooltipHeight = 100.0;
const double customPaddingVal = 20.0; const double customPaddingVal = 20.0;
...@@ -821,7 +804,7 @@ void main() { ...@@ -821,7 +804,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
final RenderBox tip = tester.renderObject(find.ancestor( final RenderBox tip = tester.renderObject(find.ancestor(
...@@ -839,7 +822,7 @@ void main() { ...@@ -839,7 +822,7 @@ void main() {
}); });
testWidgetsWithLeakTracking('Tooltip height and padding - TooltipTheme', (WidgetTester tester) async { testWidgetsWithLeakTracking('Tooltip height and padding - TooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey(); final GlobalKey<TooltipState> key = GlobalKey<TooltipState>();
const double customTooltipHeight = 100.0; const double customTooltipHeight = 100.0;
const double customPaddingValue = 20.0; const double customPaddingValue = 20.0;
late final OverlayEntry entry; late final OverlayEntry entry;
...@@ -868,7 +851,7 @@ void main() { ...@@ -868,7 +851,7 @@ void main() {
), ),
), ),
); );
_ensureTooltipVisible(key); key.currentState!.ensureTooltipVisible();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
final RenderBox tip = tester.renderObject(find.ancestor( final RenderBox tip = tester.renderObject(find.ancestor(
......
...@@ -777,6 +777,65 @@ void main() { ...@@ -777,6 +777,65 @@ void main() {
verifyTreeIsClean(); verifyTreeIsClean();
}); });
group('Adding/removing overlay child causes repaint', () {
// Regression test for https://github.com/flutter/flutter/issues/134656.
const Key childKey = Key('child');
final OverlayEntry overlayEntry = OverlayEntry(
builder: (BuildContext context) {
return RepaintBoundary(
child: OverlayPortal(
controller: controller1,
overlayChildBuilder: (BuildContext context) => const SizedBox(),
child: const SizedBox(key: childKey),
),
);
},
);
final Widget widget = Directionality(
key: GlobalKey(debugLabel: 'key'),
textDirection: TextDirection.ltr,
child: Overlay(initialEntries: <OverlayEntry>[overlayEntry]),
);
tearDown(overlayEntry.remove);
tearDownAll(overlayEntry.dispose);
testWidgetsWithLeakTracking('Adding child', (WidgetTester tester) async {
controller1.hide();
await tester.pumpWidget(widget);
final RenderBox renderTheater = tester.renderObject<RenderBox>(find.byType(Overlay));
final RenderBox renderChild = tester.renderObject<RenderBox>(find.byKey(childKey));
assert(!renderTheater.debugNeedsPaint);
assert(!renderChild.debugNeedsPaint);
controller1.show();
await tester.pump(null, EnginePhase.layout);
expect(renderTheater.debugNeedsPaint, isTrue);
expect(renderChild.debugNeedsPaint, isFalse);
// Discard the dirty render tree.
await tester.pumpWidget(const SizedBox());
});
testWidgetsWithLeakTracking('Removing child', (WidgetTester tester) async {
controller1.show();
await tester.pumpWidget(widget);
final RenderBox renderTheater = tester.renderObject<RenderBox>(find.byType(Overlay));
final RenderBox renderChild = tester.renderObject<RenderBox>(find.byKey(childKey));
assert(!renderTheater.debugNeedsPaint);
assert(!renderChild.debugNeedsPaint);
controller1.hide();
await tester.pump(null, EnginePhase.layout);
expect(renderTheater.debugNeedsPaint, isTrue);
expect(renderChild.debugNeedsPaint, isFalse);
// Discard the dirty render tree.
await tester.pumpWidget(const SizedBox());
});
});
testWidgetsWithLeakTracking('Adding/Removing OverlayPortal in LayoutBuilder during layout', (WidgetTester tester) async { testWidgetsWithLeakTracking('Adding/Removing OverlayPortal in LayoutBuilder during layout', (WidgetTester tester) async {
final GlobalKey widgetKey = GlobalKey(debugLabel: 'widget'); final GlobalKey widgetKey = GlobalKey(debugLabel: 'widget');
final GlobalKey overlayKey = GlobalKey(debugLabel: 'overlay'); final GlobalKey overlayKey = GlobalKey(debugLabel: 'overlay');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment