Flutter: 如何更改AlertDialog的宽度?

120
我想知道如何更改AlertDialog的默认宽度,我只成功地更改了边框半径:

这是我的代码:

showDialog(
       context: context,
       builder: (_) =>
            new AlertDialog(
               shape: RoundedRectangleBorder(
                   borderRadius: BorderRadius.all(Radius.circular(10.0))
               ),
               content: ProductPreviewScreen(),
            )
    );

期望的结果:

在此输入图片描述 有任何想法吗?

27个回答

139

截至2020年5月,如果您想更改对话框的插页填充,则只需使用Dialog类并覆盖'insetPadding'属性即可。如果需要,您可以使对话框延伸到屏幕边缘。

您还可以通过使对话框表面本身透明,然后添加任何组件来创建一些很酷的自定义对话框。例如:

showDialog(Dialog(
  backgroundColor: Colors.transparent,
  insetPadding: EdgeInsets.all(10),
  child: Stack(
    overflow: Overflow.visible,
    alignment: Alignment.center,
    children: <Widget>[
      Container(
        width: double.infinity,
        height: 200,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(15),
          color: Colors.lightBlue
        ),
        padding: EdgeInsets.fromLTRB(20, 50, 20, 20),
        child: Text("You can make cool stuff!",
          style: TextStyle(fontSize: 24),
          textAlign: TextAlign.center
        ),
      ),
      Positioned(
        top: -100,
        child: Image.network("https://i.imgur.com/2yaf2wb.png", width: 150, height: 150)
      )
    ],
  )
));

结果如下:

对话框示例


1
这是现在的正确解决方案,其他的看起来更像是hack。 - Edmore M Gonese Digolodollarz
我想创建一个固定大小为150 x 150像素的showDialog。 我已将容器的宽度和高度更新为return Container(height:150,width:150,); 但仍然不起作用。 我得到的是一个3:2比例的矩形框而不是正方形,有什么建议吗? - Kamlesh
这不符合所请求的AlertDialog。 - BillyParadise
@BillyParadise,insetPadding确实适用于AlertDialog以及Dialog,但是Dialog没有使用ConstrainedBox实现,而AlertDialog则有。这意味着AlertDialog的大小基于其子元素自适应。为了克服这个问题并使AlertDialog响应insetPadding,请将AlertDialogcontent设置为Center(child: SizedBox(width: MediaQuery.of(context).size.width, child: {YOUR ACTUAL ALERT CONTENT}))。您还可以摆脱Center并将SizeBox.height设置为与宽度相同。参见:https://github.com/flutter/flutter/issues/61154#issuecomment-770627805 - ubiquibacon

100

这比其他答案描述的要简单得多。只需使用构建器在构建(在其他语言中称为实例化)对话框时更改其大小即可。这意味着您还可以查询屏幕大小并根据所述屏幕大小决定需要多少空间。例如,在平板电脑上需要更多的空间,而在手机上则需要较少的空间。如果您需要应用程序栏和其他功能,则可以将Scaffold设置为Container的子项。

showDialog(
  context: context,
  builder: (_) => new AlertDialog(
  shape: RoundedRectangleBorder(
    borderRadius:
      BorderRadius.all(
        Radius.circular(10.0))),
    content: Builder(
      builder: (context) {
        // Get available height and width of the build area of this widget. Make a choice depending on the size.                              
        var height = MediaQuery.of(context).size.height;
        var width = MediaQuery.of(context).size.width;

        return Container(
          height: height - 400,
          width: width - 400,
        );
      },
    ),
  )
);

不同尺寸的示例:

enter image description here

enter image description here

enter image description here

如有需要,可以添加以下内容以去除不必要的内/外边框空间。

          insetPadding: EdgeInsets.zero,
          contentPadding: EdgeInsets.zero,
          clipBehavior: Clip.antiAliasWithSaveLayer,

填充过大。 - shinriyo
20
Shinriyo是一个示例,不要完全复制其大小,需要根据自己的需求进行调整。 - Kent
很抱歉,但对我来说不起作用。谢谢。 - Kamlesh
1
Kamlesh,我每天仍在我的应用程序中使用这段代码。在这里起作用。也许你错过了什么,应该重新检查。 - Kent
1
@Kent 我想创建一个固定大小为150 x 150像素的showDialog。我已经更新了容器的宽度和高度,如下所示: return Container( height: 150, width: 150, ); 但仍然无法正常工作。我得到的是一个3:2比例的矩形框,而不是正方形。有什么建议吗? - Kamlesh

70

使用内置对话框:

  • 为增加宽度:

AlertDialog(
  title: Text("AlertDialog"),
  insetPadding: EdgeInsets.zero,
)
  • 减小宽度:

    AlertDialog(
      title: Text("AlertDialog"),
      insetPadding: EdgeInsets.symmetric(horizontal: 100),
    )
    

  • 使用自定义对话框:

    在此输入图片描述

    调用此方法:

    showGeneralDialog(
      context: context,
      barrierColor: Colors.black.withOpacity(0.5),
      pageBuilder: (_, __, ___) {
        return Material(
          color: Colors.transparent,
          child: Center(
            child: Container(
              color: Colors.white, // Dialog background
              width: 120, // Dialog width
              height: 50, // Dialog height
              child: SingleChildScrollView(
                child: Column(
                  children: [
                    Text('I am a small Dialog'),
                  ],
                ),
              ),
            ),
          ),
        );
      },
    );
    

    我想在Flutter中制作一个200 x 200像素的对话框,你能告诉我如何实现吗?谢谢。 - Kamlesh
    1
    @Kamlesh 目前你无法使用 AlertDialog 来实现这个功能,你需要创建自己的小部件。请参考此答案以开始操作。 - CopsOnRoad
    我喜欢简短的回答! - Samuel Nde
    @Kamlesh 我还没有尝试过,但我认为对于你的情况,你可能想要在所选大小的ContainerSizedBox内使用对话框。 - Samuel Nde
    仅设置insetPadding是不起作用的。您还需要将内容包装在SizedBox中。请参见我上面的答案。 - Nao Kreuzeder

    23

    你可以尝试像这里建议的那样,用ConstrainedBox小部件包装你的AlertDialog小部件,并为maxWidth参数设置所需的值。

    更新

    我刚刚查看了AlertDialog小部件的父级Dialog小部件的代码,并发现它使用minWidth为280像素的ConstrainedBox小部件包装其子级。这就是为什么我们无法更改AlertDialog小部件宽度的原因。

    幸运的是,我们有两个选择。第一种选择是修改dialog.dart文件中Dialog小部件的默认minWidth。请注意,更改此设置将影响所有使用Dialog小部件的flutter项目。

    //inside dialog.dart
    
    class Dialog extends StatelessWidget {
    
    ...
    
    @override
    Widget build(BuildContext context) {
      final DialogTheme dialogTheme = DialogTheme.of(context);
      return AnimatedPadding(
        padding: MediaQuery.of(context).viewInsets + const EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),
        duration: insetAnimationDuration,
        curve: insetAnimationCurve,
        child: MediaQuery.removeViewInsets(
          removeLeft: true,
          removeTop: true,
          removeRight: true,
          removeBottom: true,
          context: context,
          child: Center(
            child: ConstrainedBox(
              constraints: const BoxConstraints(minWidth: 280.0), // You can set your desired value for minWidth here
              child: Material(
                elevation: 24.0,
    
                ...
    

    您可以像这样使用AlertDialog:

    showDialog(
      context: context,
      builder: (_) => AlertDialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(10.0))
          ),
        contentPadding: EdgeInsets.all(0.0),
        content: ProductPreviewScreen(),
        )
      )
    );
    
    另一种方法是创建我们自己的定制对话框。

    创建我们自己的定制对话框是另外一种方式。

    showDialog(
      context: context,
      builder: (_) => Center( // Aligns the container to center
        child: Container( // A simplified version of dialog. 
          width: 100.0,
          height: 56.0,
          color: Colors.pink,
          )
        )
     );
    

    我尝试使用Center widget或Align widget包装ConstrainedBox。它确实改变了宽度,但有些不对劲。我还将AlertDialog的contentPadding参数设置为零,这消除了AlertDialog中内容的默认填充。 - Jerome Escalante
    @JeromeEscalante 我试着用一个带有minWidth的中心小部件来包装它,但它仍然不起作用...当你尝试时,宽度是否已经改变了? - Julien
    2
    我不认为使用材料警报对话框可以实现这个。你可以自己编写对话框来实现这个功能。我认为材料警报对话框已经在一个固定大小的小部件内了。 - Janpan
    @Julien,我认为日本是正确的。我们要么创建自己定制的对话框并使用showDialog函数显示它,要么可以更改dialog.dart文件中对话框小部件的某些参数。 - Jerome Escalante

    9

    尝试调整insetWidth和future builder等方法对我无效 - 只需编辑content属性的宽度即可完美解决。

    showDialog(
        context: context,
        builder: (context) {
          Future.delayed(Duration(seconds: 1000), () {
            Navigator.of(context).pop(true);
          });
          return AlertDialog(
            insetPadding: EdgeInsets.all(8.0),
            title: Text(
              "Time to go pro!",
              textAlign: TextAlign.center,
            ),
            content: Container(
              width: MediaQuery.of(context).size.width,
              child: BuySheet(
                  applePayEnabled: applePayEnabled,
                  googlePayEnabled: googlePayEnabled,
                  applePayMerchantId: applePayMerchantId,
                  squareLocationId: squareLocationId),
            ),
          );
        });
    

    Result of the above code


    1
    太好了!insetPadding + 容器的宽度可以创建全宽对话框。 - Jerry

    9
    使用Padding小部件。
     Padding(
       padding: EdgeInsets.only(left: 50.0, right: 50.0),
       child://AlertDialog or any other Dialog you can use
             Dialog(
                    elevation: 0.0,
                    backgroundColor: Colors.transparent,
                    child: Container(
                      width: 10.0,
                      height: 50.0,
                      color: Colors.red,
                    )
                ))
    

    添加填充顶层... Padding(padding: "..",child: - BIS Tech

    7

    对我有效的解决方案。

    设置AlertDialog的insetPadding。

    此外,将内容包装在SizedBox中,并将宽度设置为MediaQuery.of(context).size.width

    return AlertDialog(
      content: SizedBox(
        width: MediaQuery.of(context).size.width,
        child: const Text("Content"),
      ),
      insetPadding: const EdgeInsets.all(10),
    );
    

    仅设置insetPadding是无效的。


    这可能适用于当您希望AlertDialog比其默认宽度小的情况,但它不能使其大于其默认大小,这正是OP所要求的。 - Regular Jo
    1
    不,这也适用于如果您想要使AlertDialog更大。我正在使用此代码,它有效。 - Nao Kreuzeder

    7
    我终于找到了一种改变AlertDialog宽度的方法。 只需用Container包装“content”,并将宽度设置为它即可。
    return AlertDialog(
    ...
    content: Container(
       width: MediaQuery.of(context).size.width*0.45,
       child: ...
    

    修改宽度的AlertDialog


    1
    所有其他的解决方案都更加复杂或使用了其他的小部件。 - Felipe Carvalho

    7

    使用 Dialog()

    Dialog(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
      insetPadding: EdgeInsets.all(15),
      child: SingleChildScrollView(
        child: Container(),
        ),
      ),
    );
    

    最好在回答中提供一些细节,以便将来参考 Stack Overflow 社区。 - Kiran Maniya

    7
    我的解决方案是将Dialog包含在一个小部件中,通过修改MediaQueryData来克服Dialog类添加的额外填充。
    import 'package:myapp/widgets/dialog_inset_defeat.dart';
    ...
    
    showDialog(
        context: context,
        builder: (_) => DialogInsetDefeat(
          context: context,
          child: SimpleDialog(...),
        )
    );
    

    ...或使用showDialogWithInsets()来自定义值:

    showDialogWithInsets(
        context: context,
        edgeInsets: EdgeInsets.symmetric(horizontal: 8),
        builder: (_) => SimpleDialog(...),
        )
    );
    

    文件对话框_inset_defeat.dart

       import 'package:flutter/material.dart';
    
    /// A widget to defeat the hard coded insets of the [Dialog] class which
    /// are [EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0)].
    ///
    /// See also:
    ///
    ///  * [Dialog], for dialogs that have a message and some buttons.
    ///  * [showDialog], which actually displays the dialog and returns its result.
    ///  * <https://material.io/design/components/dialogs.html>
    ///  * <https://dev59.com/fFQJ5IYBdhLWcg3wbFIW
    class DialogInsetDefeat extends StatelessWidget {
      final BuildContext context;
      final Widget child;
      final deInset = EdgeInsets.symmetric(horizontal: -40, vertical: -24);
      final EdgeInsets edgeInsets;
    
      DialogInsetDefeat({@required this.context, @required this.child, this.edgeInsets});
    
      @override
      Widget build(BuildContext context) {
        var netEdgeInsets = deInset + (edgeInsets ?? EdgeInsets.zero);
        return MediaQuery(
          data: MediaQuery.of(context).copyWith(viewInsets: netEdgeInsets),
          child: child,
        );
      }
    }
    
    /// Displays a Material dialog using the above DialogInsetDefeat class.
    /// Meant to be a drop-in replacement for showDialog().
    ///
    /// See also:
    ///
    ///  * [Dialog], on which [SimpleDialog] and [AlertDialog] are based.
    ///  * [showDialog], which allows for customization of the dialog popup.
    ///  * <https://material.io/design/components/dialogs.html>
    Future<T> showDialogWithInsets<T>({
      @required BuildContext context,
      bool barrierDismissible = true,
      @required WidgetBuilder builder,
      EdgeInsets edgeInsets,
    }) {
      return showDialog(
        context: context,
        builder: (_) => DialogInsetDefeat(
          context: context,
          edgeInsets: edgeInsets,
          child: Builder(builder: builder),
        ),
        // Edited! barrierDismissible: barrierDismissible = true,
        barrierDismissible: barrierDismissible,
      );
    }
    

    从Flutter 1.8.3版本开始,对我来说可行。但你的情况可能不同。


    请注意,这实际上是一个绕过工具包中硬编码值的折衷方法。Flutter的后续版本可能会允许更多的灵活性。它确实尝试与现有框架一起工作,而不是替换它,尽管每个解决方案都有其适用场所。 - Cirec Beback
    @Crec Beback 无法通过在容器外部轻敲来关闭。 - shinriyo
    @shinriyo 我已经有一段时间没有测试了。仅通过视觉检查我的代码,我预计会出现相反的错误,即始终为“barrierDismissible”。我已经进行了更新。 - Cirec Beback
    Gyuri Majercsik建议我的解决方案没有处理键盘插图,可以通过将MediaQuery.of(context).viewInsets添加到netEdeInsets中来轻松修复。我无法测试,但假设如果这不是真的,他就不会添加这个。 - Cirec Beback
    我想制作一个200 x 200像素的固定大小对话框。在Flutter中我该怎么做?请给予建议。谢谢。 - Kamlesh

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接