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

Allow `OverlayPortal` to be added/removed from the tree in a layout callback (#130670)

Fixes https://github.com/flutter/flutter/issues/130668
parent d64cc479
......@@ -936,14 +936,6 @@ class _RenderTheater extends RenderBox with ContainerRenderObjectMixin<RenderBox
@override
void redepthChildren() => visitChildren(redepthChild);
void _adoptDeferredLayoutBoxChild(_RenderDeferredLayoutBox child) {
adoptChild(child);
}
void _dropDeferredLayoutBoxChild(_RenderDeferredLayoutBox child) {
dropChild(child);
}
Alignment? _alignmentCache;
Alignment get _resolvedAlignment => _alignmentCache ??= AlignmentDirectional.topStart.resolve(textDirection);
......@@ -1704,13 +1696,13 @@ final class _OverlayEntryLocation extends LinkedListEntry<_OverlayEntryLocation>
// This call is allowed even when this location is invalidated.
// See _OverlayPortalElement.activate.
assert(_overlayChildRenderBox == null, '$_overlayChildRenderBox');
_theater._adoptDeferredLayoutBoxChild(child);
_theater._addDeferredChild(child);
_overlayChildRenderBox = child;
}
void _deactivate(_RenderDeferredLayoutBox child) {
// This call is allowed even when this location is invalidated.
_theater._dropDeferredLayoutBoxChild(child);
_theater._removeDeferredChild(child);
_overlayChildRenderBox = null;
}
......
......@@ -655,6 +655,63 @@ void main() {
verifyTreeIsClean();
});
testWidgets('Adding/Removing OverlayPortal in LayoutBuilder during layout', (WidgetTester tester) async {
final GlobalKey widgetKey = GlobalKey(debugLabel: 'widget');
final GlobalKey overlayKey = GlobalKey(debugLabel: 'overlay');
controller1.hide();
late StateSetter setState;
Size size = Size.zero;
final Widget overlayPortal = OverlayPortal(
key: widgetKey,
controller: controller1,
overlayChildBuilder: (BuildContext context) => const Placeholder(),
child: const Placeholder(),
);
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay(
key: overlayKey,
initialEntries: <OverlayEntry>[
OverlayEntry(
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter stateSetter) {
setState = stateSetter;
return Center(
child: SizedBox.fromSize(
size: size,
child: LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
// This layout callback adds/removes an OverlayPortal during layout.
return constraints.maxHeight > 0 ? overlayPortal : const SizedBox();
}),
),
);
}
);
}
),
],
),
),
);
controller1.show();
await tester.pump();
expect(tester.takeException(), isNull);
// Adds the OverlayPortal from within a LayoutBuilder, in a layout callback.
setState(() { size = const Size(300, 300); });
await tester.pump();
expect(tester.takeException(), isNull);
// Removes the OverlayPortal from within a LayoutBuilder, in a layout callback.
setState(() { size = Size.zero; });
await tester.pump();
expect(tester.takeException(), isNull);
});
testWidgets('Change overlay constraints', (WidgetTester tester) async {
final GlobalKey widgetKey = GlobalKey(debugLabel: 'widget outer');
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