skip to Main Content

Problem:
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"),
            ],
          ),
        ),
      ),
    );
  }

enter image description here

2

Answers


  1. 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"),
                        ],
                      ),
                    ),
                  ),
                ),
              ],
            ),
          );
        }
    
    Login or Signup to reply.
  2. 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),
                ),
              ),
          ],
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search