Unverified Commit 1d5e23a2 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Add Overlay.wrap for convenience (#139823)

Towards https://github.com/flutter/flutter/issues/137875.

The convenient `Overlay.wrap` function makes it easy to wrap a child with an Overlay so other visual elements can float on top of the child. This is useful if you want to get things like text selection working (i.e. with a SelectionArea) without using a Navigator.
parent fb37f9fa
......@@ -440,6 +440,20 @@ class Overlay extends StatefulWidget {
this.clipBehavior = Clip.hardEdge,
});
/// Wrap the provided `child` in an [Overlay] to allow other visual elements
/// (packed in [OverlayEntry]s) to float on top of the child.
///
/// This is a convenience method over the regular [Overlay] constructor: It
/// creates an [Overlay] and puts the provided `child` in an [OverlayEntry]
/// at the bottom of that newly created Overlay.
static Widget wrap({
Key? key,
Clip clipBehavior = Clip.hardEdge,
required Widget child,
}) {
return _WrappingOverlay(key: key, clipBehavior: clipBehavior, child: child);
}
/// The entries to include in the overlay initially.
///
/// These entries are only used when the [OverlayState] is initialized. If you
......@@ -804,6 +818,39 @@ class OverlayState extends State<Overlay> with TickerProviderStateMixin {
}
}
class _WrappingOverlay extends StatefulWidget {
const _WrappingOverlay({super.key, this.clipBehavior = Clip.hardEdge, required this.child});
final Clip clipBehavior;
final Widget child;
@override
State<_WrappingOverlay> createState() => _WrappingOverlayState();
}
class _WrappingOverlayState extends State<_WrappingOverlay> {
late final OverlayEntry _entry = OverlayEntry(
opaque: true,
builder: (BuildContext context) {
return widget.child;
}
);
@override
void didUpdateWidget(_WrappingOverlay oldWidget) {
super.didUpdateWidget(oldWidget);
_entry.markNeedsBuild();
}
@override
Widget build(BuildContext context) {
return Overlay(
clipBehavior: widget.clipBehavior,
initialEntries: <OverlayEntry>[_entry],
);
}
}
/// Special version of a [Stack], that doesn't layout and render the first
/// [skipCount] children.
///
......
......@@ -1561,6 +1561,38 @@ void main() {
);
});
});
testWidgets('Overlay.wrap', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay.wrap(
child: const Center(
child: Text('Hello World'),
),
),
),
);
final State overlayState = tester.state(find.byType(Overlay));
expect(find.text('Hello World'), findsOneWidget);
expect(find.text('Bye, bye'), findsNothing);
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Overlay.wrap(
child: const Center(
child: Text('Bye, bye'),
),
),
),
);
expect(find.text('Hello World'), findsNothing);
expect(find.text('Bye, bye'), findsOneWidget);
expect(tester.state(find.byType(Overlay)), same(overlayState));
});
}
class StatefulTestWidget extends StatefulWidget {
......
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