【交互 widget】Flutter Dialog

625 阅读2分钟

Dialog 是 material 风格的 widget。Dialog 定义了 最基本的属性。可以直接使用 Dialog 自定义 Dialog 的内容。

源码分析

Dialog 是一个 StatelessWidget widget ,所以只要了解他都组合了哪些 widget ,都起到了什么作用就好了。

dialog 样式

dialog 的样式 主要有三个属性 backgroundColor,elevation,shape,分别对应背景色,阴影和形状。 最后决定属性值的是 参数,dialogTheme 和 defaults。

build 方法的前三句就是取样式的。

final ThemeData theme = Theme.of(context);
final DialogTheme dialogTheme = DialogTheme.of(context);
final DialogTheme defaults = theme.useMaterial3 ? _DialogDefaultsM3(context) :

dialog 自身的参数优先级最高,其次是 dialogTheme 最后是 defaults。你可能会疑问,后面两个值是哪里来的?

如果是全局的样式,可以在 MaterialApp 定义

 MaterialApp(
      theme: ThemeData(
        dialogTheme: DialogTheme(
        alignment: Alignment.center,
        elevation: 6.0,
        shape: const RoundedRectangleBorder(
            borderRadius: BorderRadius.only(
                topLeft: Radius.circular(28.0),
                topRight: Radius.circular(28.0),
                bottomLeft: Radius.circular(28.0),
                bottomRight: Radius.circular(28.0))))))

在 Dialog 的父级,用 Theme widget 也可以定义 dialogTheme。

如果没有定义 dialogTheme,会用默认值。_DialogDefaultsM3_DialogDefaultsM2 都是 DialogTheme 的子类。规定了 Material Design 2 和 Material Design 3 的默认值。

useMaterial3 是一个临时变量,一旦 Design 3 成为稳定版,就都会使用 Design 3,useMaterial3 会被删除。

size

constraints: const BoxConstraints(minWidth: 280.0),

dialog的最小宽度是280。高度根据内容自适应,一般不需要设置 dialog 的高度,除非内容太多,需要限制最大高度。

手机的宽度 一共也就 400 左右,最小宽度已经 限制为 280了,还得有 padding,所以宽度方面再大也很有限了,如果有精确匹配 child 宽度的需要,可以用 IntrinsicWidth 把 child 包起来。

如果想要宽度 小于 280,用 insetPadding 参数。

动画效果

   final EdgeInsets effectivePadding = MediaQuery.of(context).viewInsets + (insetPadding ?? EdgeInsets.zero);
    return AnimatedPadding(
      padding: effectivePadding,
      duration: insetAnimationDuration,
      curve: insetAnimationCurve,
     ...

最外层是用 AnimatedPadding 包裹的,它的用处主要是当 键盘弹起的时候,可以通过动画的方式让 dialog 向上弹起。viewInsets.bottom 的值是键盘的高度。可以通过参数 insetAnimationDuration 定义动画的 时间,参数 insetAnimationCurve 自定义动画的缓动曲线。

从这里可以看出,如果我们要改变 dialog 的大小,最好通过改变参数 insetPadding 的方式,因为这样会有动画效果。

使用 Dialog

源码已经分析完了,通过一个实例来看下如何用 Dialog。

showDialog(
  context: context,
  builder: (context) {
    return Dialog(
      child: Container(
       height: 200,
         color: Colors.green,
         child: ElevatedButton(
           child:const Text('close'),
           onPressed: () {
             Navigator.of(context).pop();
           },
         )),
   );
 });

使用 showDialog 显示 dialog,用 Navigator.of(context).pop(); 关闭 dialog。

Dialog 只是一个空壳,里面的内容可以完全自定义。

AlertDialogSimpleDialog,是 Dialog 的子类,如果你的需求正好和他们匹配,也可以使用它们。

如果内容过高,用 SingleChildScrollView 包起来。

Dialog 只是 StatelessWidget,如果 child 需要保持状态用 StatefulWidget 包起来。