skip to Main Content

I tried these widget and these don’t work.

      showDialog(
        context: context,
        builder: (BuildContext context) {
          return Positioned(
          .....,
          child: Dialog( child: .....),
          );
        },
      );
      showGeneralDialog(
        context: context,
        pageBuilder: (BuildContext buildContext, Animation animation, Animation secondaryAnimation) {
          return Positioned(
            .....,
            child: .....,
          );
        },
      );

The only dialog function working is showMenu

      showMenu(
        items: [],
        context: context,
        position: .....,
      );

But this function is hard to custom. ex. It can’t remove space.

For example, I using this at this moment.

import 'package:flutter/material.dart';

class PositionDialogCustomWidget extends StatefulWidget {
  final Widget child;
  final Widget dialog;
  final double left;
  final double top;

  const PositionDialogCustomWidget({super.key, required this.child, required this.dialog, required this.left, required this.top});

  @override
  State<PositionDialogCustomWidget> createState() => _PositionDialogCustomWidgetState();
}

class _PositionDialogCustomWidgetState extends State<PositionDialogCustomWidget> {
  //controller
  bool _isShowDialog = false;

  //basic widget
  late final _overlayEntry = OverlayEntry(
    builder: (BuildContext context) => Positioned(
      left: widget.left,
      top: widget.top,
      child: Material(child: widget.dialog),
    ),
  );

  void _closeDialog() {
    if (!_isShowDialog) return;
    _overlayEntry.remove();
    Navigator.pop(context);
    _isShowDialog = false;
  }

  void _showDialog() {
    _isShowDialog = true;
    showDialog(
      context: context,
      barrierColor: Colors.transparent,
      builder: (BuildContext context) => GestureDetector(onTap: _closeDialog),
    );

    Overlay.of(context).insert(_overlayEntry);
  }

  @override
  void dispose() {
    _closeDialog();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return InkWell(onTap: _showDialog, child: widget.child);
  }
}

example result from this widget

This custom widget can show the dialog widget at this position and this can click outside of the dialog widget area to remove this dialog.
But this solution is not direct. It is an indirect solution like using the Stack widget.

Is there any way to set the position for any show dialog function?

3

Answers


  1. Chosen as BEST ANSWER

    From this answer below:

    Positioned should only be used inside a Stack widget. From the documentation:

    A Positioned widget must be a descendant of a Stack, and the path from the Positioned widget to its enclosing Stack must contain only StatelessWidgets or StatefulWidgets (not other kinds of widgets, like RenderObjectWidgets).

    Try using an Align widget instead.

    There is also alignment property for Dialog which you can control how to align the Dialog.

    From this answer, the Positioned widget can't be used with the dialog function. So I do this:

    I tried to use the Align widget instead of the Positioned widget by calculating the align value from the position value and I got this function to do that.

    Alignment getAlignmentFromPosition({required double left, required double top}) {
      final screenSize = MediaQuery.of(context).size;
      //the align value range is -1 to 1
      double alignmentX = (left / screenSize.width) * 2 - 1;
      double alignmentY = (top / screenSize.height) * 2 - 1;
      return Alignment(alignmentX, alignmentY);
    }
    

    and I tried to test that with the example below and it worked successfully.

        return Scaffold(
          body: SafeArea(
            child: Column(
              children: [
                const Text('title'),
                const SizedBox(height: 200),
                //test for custom widget
                StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
                  Alignment getAlignmentFromPosition({required double left, required double top}) {
                    final screenSize = MediaQuery.of(context).size;
                    double alignmentX = (left / screenSize.width) * 2 - 1;
                    double alignmentY = (top / screenSize.height) * 2 - 1;
                    return Alignment(alignmentX, alignmentY);
                  }
    
                  return IconButton(
                    onPressed: () {
                      showDialog(
                        context: context,
                        builder: (BuildContext context) => Align(
                          alignment: getAlignmentFromPosition(left: 50, top: 100),
                          child: const Icon(Icons.ac_unit),
                        ),
                        barrierColor: Colors.transparent,
                      );
                    },
                    icon: const Icon(Icons.accessibility),
                  );
                }),
              ],
            ),
          ),
        );
    

    I implement this solution to create a custom widget below.

    import 'package:flutter/material.dart';
    
    class PositionDialogByAlignCustomWidget extends StatelessWidget {
      final Widget child;
      final Widget dialog; //warning: must use `Material` widget to use theme data.
      final ({double left, double top}) Function() position; //some position values require UI build finished before assigned. It will error when used directly, so we fix it by using it in the function (ex. the position value from RenderBox).
      final bool isBarrierColor;
    
      const PositionDialogByAlignCustomWidget({
        super.key,
        required this.child,
        required this.dialog,
        required this.position,
        this.isBarrierColor = true,
      });
    
      @override
      Widget build(BuildContext context) {
        //controller
        bool isShowDialog = false;
    
        Alignment getAlignmentFromPosition() {
          final positionResult = position();
          final screenSize = MediaQuery.of(context).size;
          double alignmentX = (positionResult.left / screenSize.width) * 2 - 1;
          double alignmentY = (positionResult.top / screenSize.height) * 2 - 1;
          return Alignment(alignmentX, alignmentY);
        }
    
        return InkWell(
          onTap: () async {
            if (isShowDialog) return;
            isShowDialog = true;
            await showDialog(
              context: context,
              builder: (BuildContext context) => Align(alignment: getAlignmentFromPosition(), child: dialog),
              barrierColor: isBarrierColor ? null : Colors.transparent,
            );
            isShowDialog = false;
          },
          child: child,
        );
      }
    }
    
    /*
    Using like this:
    return PositionDialogByAlignCustomWidget(
      position: () => (left: 10, top: 10),
      dialog: const Icon(Icons.ac_unit),
      child: const Icon(Icons.accessibility),
    );
    */
    

    example result


  2. Positioned should only be used inside a Stack widget. From the documentation:

    A Positioned widget must be a descendant of a Stack, and the path from the Positioned widget to its enclosing Stack must contain only StatelessWidgets or StatefulWidgets (not other kinds of widgets, like RenderObjectWidgets).

    Try using an Align widget instead.

    There is also alignment property for Dialog which you can control how to align the Dialog.

    Login or Signup to reply.
  3. You could use a Stack widget as parent, then you can use the Positioned, something like this:

    Result:

    enter image description here

      showDialog(
                      context: context,
                      builder: (BuildContext context) {
                        return const MyCustomDialog();
                      },
                    );
    ...
    
    
    class MyCustomDialog extends StatelessWidget {
      const MyCustomDialog({super.key});
    
      @override
      Widget build(BuildContext context) {
        return const Stack(
          children: [
            Positioned(
              left: 100,
              right: 0,
              bottom: 0,
              child: Dialog(
                child: FlutterLogo(size: 200),
              ),
            ),
          ],
        );
      }
    }
    

    More info about Stack: https://api.flutter.dev/flutter/widgets/Stack-class.html

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search