Skip to content

Instantly share code, notes, and snippets.

@Luckey-Elijah
Created June 18, 2024 16:01
Show Gist options
  • Save Luckey-Elijah/eddb92a2c5ee76bfb679758e10bcd28f to your computer and use it in GitHub Desktop.
Save Luckey-Elijah/eddb92a2c5ee76bfb679758e10bcd28f to your computer and use it in GitHub Desktop.
A useful OverlayPortal widget wrapper for building based on the anchor/target's position.
import 'positioned_overlay_builder.dart';
import 'package:flutter/material.dart';
class LogoutButton extends StatelessWidget {
const LogoutButton({super.key});
@override
Widget build(BuildContext context) {
return PositionedOverlayBuilder(
anchorBuilder: (context, controller) {
return TextButton.icon(
iconAlignment: IconAlignment.end,
onPressed: controller.toggle,
label: const Text('Logout'),
icon: const Icon(Icons.logout),
);
},
overlayChildBuilder: (context, controller, size, origin) {
final bottomCenter = size.bottomCenter(origin);
return Positioned(
left: bottomCenter.dx,
top: bottomCenter.dy,
child: Card(
elevation: 20,
borderOnForeground: false,
child: Column(
children: [
const Padding(
padding: EdgeInsets.all(8),
child: Text('Are you sure?'),
),
Row(
children: [
TextButton(
onPressed: () {},
child: const Text('Logout'),
),
TextButton(
onPressed: controller.hide,
child: const Text('Cancel'),
),
],
),
],
),
),
);
},
);
}
}
import 'package:flutter/widgets.dart';
class PositionedOverlayBuilder extends StatefulWidget {
const PositionedOverlayBuilder({
required this.anchorBuilder,
required this.overlayChildBuilder,
this.dismissible = true,
this.barrierColor,
this.debugLabel,
super.key,
});
final String? debugLabel;
final Color? barrierColor;
final bool dismissible;
final Widget Function(
BuildContext context,
OverlayPortalController controller,
) anchorBuilder;
final Widget Function(
BuildContext context,
OverlayPortalController controller,
Size size,
Offset origin,
) overlayChildBuilder;
@override
State<PositionedOverlayBuilder> createState() =>
_PositionedOverlayBuilderState();
}
class _PositionedOverlayBuilderState extends State<PositionedOverlayBuilder> {
late final controller =
OverlayPortalController(debugLabel: widget.debugLabel);
late final key = GlobalKey(debugLabel: widget.debugLabel);
Offset getWidgetOrigin(RenderBox renderBox) {
return renderBox.localToGlobal(Offset.zero);
}
RenderBox getRenderBox() =>
key.currentContext!.findRenderObject()! as RenderBox;
@override
Widget build(BuildContext context) {
return OverlayPortal.targetsRootOverlay(
key: key,
controller: controller,
overlayChildBuilder: (context) {
final renderBox = getRenderBox();
final origin = getWidgetOrigin(renderBox);
return Stack(
children: [
ModalBarrier(
dismissible: widget.dismissible,
onDismiss: controller.hide,
color: widget.barrierColor,
),
widget.overlayChildBuilder(
context,
controller,
renderBox.size,
origin,
),
],
);
},
child: widget.anchorBuilder(
context,
controller,
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment