I’m trying to position a dialog (e.g., a tooltip, dropdown, or modal) adjacent to a specific element on the page. The goal is to keep the dialog consistently positioned relative to the element, regardless of the screen size or resolution.
Currently, the dialog’s position shifts when the screen size changes, which leads to misalignment or overlap with other elements. I need a solution that ensures the dialog stays anchored next to the target element without breaking the layout on different devices or when the screen is resized.
Question:
How can I position a dialog next to specific page elements in a way that its placement remains consistent across different screen sizes?
This is my code:
void onActionButtonPressed() {
showDialog(
barrierColor: Colors.transparent,
context: pageContext,
builder: (context) => Align(
alignment: const Alignment(0.52, -0.65),
child: Container(
constraints: const BoxConstraints(maxWidth: 320, maxHeight: 235),
padding: const EdgeInsets.fromLTRB(24, 24, 24, 32),
decoration: BoxDecoration(
color: CustomColors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(
width: 1,
color: CustomColors.outline,
)),
child: const Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("TEXT HERE"),
],
),
),
),
);
}
Asmir Alić is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
4
You can use the Positioned widget in the Stack instead of relying on the Align widget with static alignment values.Here is the modified code
void onActionButtonPressed(BuildContext context) {
RenderBox renderBox = context.findRenderObject() as RenderBox;
Offset offset = renderBox.localToGlobal(Offset.zero);
Size size = renderBox.size;
showDialog(
barrierColor: Colors.transparent,
context: context,
builder: (context) => Stack(
children: [
Positioned(
left: offset.dx,
top: offset.dy + size.height,
child: Material(
color: Colors.transparent,
child: Container(
constraints: const BoxConstraints(maxWidth: 320, maxHeight: 235),
padding: const EdgeInsets.fromLTRB(24, 24, 24, 32),
decoration: BoxDecoration(
color: CustomColors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(
width: 1,
color: CustomColors.outline,
),
),
child: const Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("TEXT HERE"),
],
),
),
),
),
],
),
);
}
1
You can use CompositedTransformTarget
and CompositedTransformFollower
or OverLayPortal for this. Here is an example
class _SpecificPositionedDialog extends StatefulWidget {
const _SpecificPositionedDialog({super.key});
@override
State<_SpecificPositionedDialog> createState() => _SpecificPositionedDialogState();
}
class _SpecificPositionedDialogState extends State<_SpecificPositionedDialog> {
final LayerLink layerLink = LayerLink();
bool showLink = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
CompositedTransformTarget(
link: layerLink,
child: ElevatedButton(
onPressed: () {
showLink = !showLink;
setState(() {});
},
child: const Text("Show"),
),
),
if (showLink)
CompositedTransformFollower(
link: layerLink,
showWhenUnlinked: false,
targetAnchor: Alignment.bottomCenter,
followerAnchor: Alignment.topCenter,
child: Container(
height: 200,
width: 200,
color: Colors.red.withOpacity(.2),
),
),
],
);
}
}
1