Flutter 60: 图解基本 Dialog 对话框小结

      Dialog 在日常开发中应用广泛,大家也对此很熟悉;小菜以前也整理过关于自定义 Dialog 的小博客,今天小菜系统的学习一下最基本的 Dialog

      Dialog 一般不直接使用,Flutter 提供了便利的 AlertDialog / SimpleDialog / AboutDialog / CupertinoDialog / CupertinoAlertDialog 等多种对话框样式,小菜重点尝试前三种 Android Type Dialog;但对于自定义对话框可继承 Dialog 进行处理;

AlertDialog

源码分析

const AlertDialog({
    Key key,
    this.title,     // 标题内容
    this.titlePadding,  // 标题与周围边距
    this.titleTextStyle,    // 标题样式
    this.content,   // 消息内容
    this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),      // 消息内容与周围边距
    this.contentTextStyle,  // 消息内容样式
    this.actions,   // 操作按钮组合
    this.backgroundColor,   // 对话框背景色
    this.elevation,     // 对话框阴影
    this.semanticLabel,     // 对话框语义化标签
    this.shape,     // 对话框形状
}) 

      分析源码,AlertDialog 各个属性都比较清楚,灵活性较高;小菜由简易难逐渐尝试;

案例尝试

  1. 小菜尝试日常最常见的 Dialog
showDialog(context: context,
    builder: (context) {
      return AlertDialog(title: Text('AlertDialog', style: TextStyle(color: Colors.blueAccent)),
          content: Text('我是 AlertDialog 对话框!'),
          actions: <Widget>[
            FlatButton(child: Text("确定"), onPressed: () => Navigator.of(context).pop()),
            FlatButton(child: Text("取消"), onPressed: () => Navigator.of(context).pop())
          ]);
    });
  1. 小菜尝试对上述 Dialog 添加一些个性化;
    a. titleTextStylecontentTextStyle 不能改变标题和内容中已设置过的样式;
    b. shape 为对话框样式,如果设置为 CircleBorder 圆形背景效果时以宽高较小的尺寸为直径;
    c. actions 按钮个数最多可设置三个;
showDialog(context: context,
    builder: (context) {
      return AlertDialog(
          title: Text('AlertDialog', style: TextStyle(color: Colors.blueAccent)),
          titlePadding: EdgeInsets.all(20.0),
          titleTextStyle: TextStyle(color: Colors.pinkAccent, fontSize: 18.0, fontWeight: FontWeight.w600),
          content: Text('我是 AlertDialog 对话框!'),
          contentPadding: EdgeInsets.all(30.0),
          contentTextStyle: TextStyle(color: Colors.pinkAccent, fontSize: 16.0, fontWeight: FontWeight.w400),
          backgroundColor: Colors.greenAccent.withOpacity(0.7),
          elevation: 10.0,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20.0))),
          actions: <Widget>[
            FlatButton(child: Text("确定"), onPressed: () => Navigator.of(context).pop()),
            FlatButton(child: Text("取消"), onPressed: () => Navigator.of(context).pop())
          ]);
    });
  1. 小菜尝试 List AlertDialog
    a. Dialog 默认宽度是固定的,高度也有最大限度,若元素大小超过最大宽高则会溢出;
    b. AlertDialog 可以自由设置点击事件,并非只有 actions 设置;
showDialog(context: context,
    builder: (context) {
      return AlertDialog(
          title: Row(children: <Widget>[
            Image.asset('images/ic_launcher.png', scale: 2.0),
            Padding(child: Text('Alert List'), padding: EdgeInsets.only(left: 12.0)) ]),
          content: ListView.builder(itemCount: 30,
              itemBuilder: (BuildContext context, int index) {
                return ListTile(title: Text('当前 index = $index'), onTap: () => Navigator.of(context).pop(index));
              }),
          elevation: 10.0,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(14.0))));
    });
  1. 小菜尝试自定义选择对话框;
showDialog(context: context,
    builder: (context) {
      return AlertDialog(
          title: Row(children: <Widget>[
            Image.asset('images/ic_launcher.png', scale: 2.0),
            Padding(child: Text('Alert 性别选择'), padding: EdgeInsets.only(left: 12.0)) ]),
          titleTextStyle: TextStyle(color: Colors.pinkAccent, fontSize: 18.0, fontWeight: FontWeight.w600),
          content: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
            Row(children: <Widget>[
              Expanded(child: GestureDetector(child: Container(child: Image.asset('images/icon_type_boy.png'), width: 105.0),
                      onTap: () {
                        Navigator.of(context).pop();
                        Toast.show('AlertDialog Boy!', context, duration: Toast.LENGTH_SHORT,gravity: Toast.BOTTOM);
                      })),
              Expanded(child: GestureDetector(child: Container(child: Image.asset('images/icon_type_girl.png'), width: 105.0),
                      onTap: () {
                        Navigator.of(context).pop();
                        Toast.show('AlertDialog Girl!', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
                      })) ]),
            Row(children: <Widget>[
              Expanded(child: Center(child: Text('男生', style: TextStyle(color: Colors.blueAccent, fontSize: 16.0, fontWeight: FontWeight.w300)))),
              Expanded(child: Center(child: Text('女生'))) ])
          ]),
          contentTextStyle: TextStyle(color: Colors.pinkAccent, fontSize: 16.0, fontWeight: FontWeight.w300),
          contentPadding: EdgeInsets.fromLTRB(24.0, 10.0, 24.0, 0.0),
          elevation: 10.0,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(14.0))),
          actions: <Widget>[
            FlatButton(child: Text("确定"), onPressed: () => Navigator.of(context).pop()),
            FlatButton(child: Text("取消"), onPressed: () => Navigator.of(context).pop())
          ]);
    });

SimpleDialog

源码分析

const SimpleDialog({
    Key key,
    this.title,     // 标题内容
    this.titlePadding = const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0),  // 标题与周围边距
    this.children,   // 消息内容
    this.contentPadding = const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0),      // 消息内容与周围边距
    this.backgroundColor,  // 对话框背景色
    this.elevation,     // 对话框阴影
    this.semanticLabel,     // 对话框语义化标签
    this.shape,     // 对话框形状
})

      分析源码,SimpleDialogAlertDialog 要简单,只是单独多一个 titlePadding;消息主体默认是 List<Widget>;基本 SimpleDialog 可实现的效果 AlertDialog 均可实现;

案例尝试

  1. 小菜尝试最常见的选择对话框;小菜采用了 SimpleDialogOption 选项 Widget,默认是占满一行;
showDialog(context: context,
    builder: (context) {
      return SimpleDialog(
          title: Text('SimpleDialog', style: TextStyle(color: Colors.blueAccent)),
          children: <Widget>[
            Padding(child: Text('我是 SimpleDialog 对话框?'), padding: EdgeInsets.all(20.0)),
            SimpleDialogOption(child: Text('Yes'), onPressed: () => Navigator.pop(context)),
            SimpleDialogOption(child: Text('No'), onPressed: () => Navigator.pop(context))
          ]);
    });
  1. 小菜尝试 List SimpleDialog;需注意内容主体为 List<Widget> 方式,使用 ListView 时要注意冲突;
showDialog(context: context,
    builder: (context) {
      return SimpleDialog(
          title: Row(children: <Widget>[
            Image.asset('images/ic_launcher.png', scale: 2.0),
            Padding(child: Text('Simple List'), padding: EdgeInsets.only(left: 12.0))
          ]),
          children: <Widget>[
            Container(height: 400.0,
                child: ListView.builder(itemCount: 30,
                    itemBuilder: (BuildContext context, int index) {
                      return ListTile(title: Text('当前 index = $index'), onTap: () => Navigator.of(context).pop(index));
                    }))],
          elevation: 10.0,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(14.0))));
    });
  1. 小菜尝试自定义选择对话框;
showDialog(context: context, barrierDismissible: false,
    builder: (context) {
      return SimpleDialog(
          title: Row(children: <Widget>[
            Image.asset('images/ic_launcher.png', scale: 2.0),
            Padding(child: Text('Simple 性别选择'), padding: EdgeInsets.only(left: 12.0))
          ]),
          children: <Widget>[
            Row(children: <Widget>[
              Expanded(child: GestureDetector(child: Container(child: Image.asset('images/icon_type_boy.png'), width: 105.0),
                      onTap: () { Toast.show('SimpleDialog Boy!', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM); })),
              Expanded(child: GestureDetector(child: Container( child: Image.asset('images/icon_type_girl.png'), width: 105.0),
                      onTap: () { Toast.show('SimpleDialog Girl!', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM); })) ]),
            Padding(padding: EdgeInsets.symmetric(vertical: 14.0)),
                child: Row(children: <Widget>[
                  Expanded(child: Center(child: Text('男生', style: TextStyle(color: Colors.blueAccent, fontSize: 16.0, fontWeight: FontWeight.w300)))),
                  Expanded(child: Center(child: Text('女生', style: TextStyle( color: Colors.pinkAccent, fontSize: 16.0, fontWeight: FontWeight.w300))))
                ])],
          elevation: 10.0,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(14.0))));
    });

UnconstrainedBox + SizedBox

      Flutter 的对话框中均未提供更改宽度的属性,高度可以自适应;小菜采用 UnconstrainedBox + SizedBox 可以实现对话框的宽度更改,首先用 UnconstrainedBox 抵消 showDialog 对宽度的限制;之后采用 SizedBox 设置对话框宽度;注意此时设置高度并没有效果依旧自适应;对话框宽度以 SizedBox 设置的 width 为主,child 的宽度无效;

showDialog(context: context, barrierDismissible: false,
    builder: (context) {
      return UnconstrainedBox(
          constrainedAxis: Axis.vertical,
          child: SizedBox(width: 180.0, height: 180.0,
              child: AlertDialog(content: Icon(Icons.ac_unit))));
    });

AboutDialog

      Flutter 提供了特殊的 AboutDialog,适用于应用说明或版本相关;

源码分析

const AboutDialog({
    Key key,
    this.applicationName,       // 应用名称
    this.applicationVersion,    // 版本说明
    this.applicationIcon,       // 应用图标
    this.applicationLegalese,   // 法律声明
    this.children,              // 消息内容
})

      分析源码可知,AboutDialog 继承自 AlertDialog 但对于自定义内容较少,对于 applicationName / applicationVersion / applicationLegalese 仅提供字符串方式,无法调整样式;且默认有版权和取消按钮;

案例尝试

      AboutDialog 类似于系统对话框,整体效果我们无法调整,对于主体内容 children 部分,与 SimpleDialog 类似,无法延迟加载模型组件,对于 ListView 等需明确高度;

showDialog(context: context,
    barrierDismissible: false,
    builder: (context) {
      return AboutDialog(
          applicationIcon: Container(child: Image.asset('images/icon_hzw02.jpg'), width: 80.0),
          applicationName: 'Flutter Dialog',
          applicationLegalese: '所有解释权归本人所有!',
          applicationVersion: 'V1.5.2',
          children: <Widget>[
            Padding(padding: EdgeInsets.only(top: 10.0), child: Text('1. AboutDialog!')),
            Padding(padding: EdgeInsets.only(top: 10.0), child: Text('2. SimpleDialog!')),
            Padding(padding: EdgeInsets.only(top: 10.0), child: Text('3. AlertDialog!'))
          ]);
    });

showAboutDialog

      Flutter 针对 AboutDialog 提供了简易的 showAboutDialog 方法;

源码分析

void showAboutDialog({
  @required BuildContext context,
  String applicationName,       // 应用名称
  String applicationVersion,    // 版本说明
  Widget applicationIcon,       // 应用图标
  String applicationLegalese,   // 法律声明
  List<Widget> children,        // 消息内容
})

      分析源码,showAboutDialog 是简化版的 AboutDialog,参数几乎全部一致;差别在于 showDialog 方式可以设置点击遮罩是否关闭对话框,而 showAboutDialog 不支持;

案例尝试

showAboutDialog(context: context,
    applicationIcon: Container(child: Image.asset('images/icon_hzw02.jpg'), width: 80.0),
    applicationName: 'Flutter Dialog',
    applicationLegalese: '所有解释权归本人所有!',
    applicationVersion: 'V1.5.2',
    children: <Widget>[
      Padding(padding: EdgeInsets.only(top: 10.0), child: Text('1. AboutDialog!')),
      Padding(padding: EdgeInsets.only(top: 10.0), child: Text('2. SimpleDialog!')),
      Padding(padding: EdgeInsets.only(top: 10.0), child: Text('3. AlertDialog!'))
    ]);

showDialog

源码分析

Future<T> showDialog<T>({
  @required BuildContext context,
  bool barrierDismissible = true,   // 遮罩层点击是否关闭对话框
  @Deprecated(
    'Instead of using the "child" argument, return the child from a closure '
    'provided to the "builder" argument. This will ensure that the BuildContext '
    'is appropriate for widgets built in the dialog.'
  ) Widget child,
  WidgetBuilder builder,
})

      分析源码,showDialog 采用 builder 方式取代 child 方式;而实际上 showDialog 是对 showGeneralDialog 的封装,默认的遮罩层颜色和渐进渐出的动画效果;

showGeneralDialog

源码分析

Future<T> showGeneralDialog<T>({
  @required BuildContext context,
  @required RoutePageBuilder pageBuilder,   // 对话框内部绘制
  bool barrierDismissible,      // 遮罩层点击是否关闭对话框
  String barrierLabel,      // 语义化标签
  Color barrierColor,       // 遮罩层颜色
  Duration transitionDuration,      // 动画持续时长
  RouteTransitionsBuilder transitionBuilder,    // 动画过程
})

      分析源码,showGeneralDialog 提供了更丰富的对话框设计;而实际也是对 Navigator.push 的封装;

案例尝试

      小菜重现以前博客中实现的简易对话框:由底部弹出且透明度由 0.0 到 1.0;测试 barrierColor 进入和退出时都是渐变符合动画效果,与采用 Navigator 打开页面动画方式不同;

showGeneralDialog(context: context,
    pageBuilder: (buildContext, _, __) {
      return Center(child: Container(
              height: 200.0, width: 200.0,
              decoration: BoxDecoration(color: Colors.greenAccent, borderRadius: BorderRadius.circular(5.0)),
              child: Icon(Icons.ac_unit, color: Colors.white)));
    },
    barrierDismissible: false,
    barrierColor: Colors.pink.withOpacity(0.2),
    transitionDuration: Duration(milliseconds: 1500),
    transitionBuilder: (context, animation, secondaryAnimation, child) {
      return SlideTransition(
          position: Tween<Offset>(begin: Offset(0.0, 1.0), end: Offset(0.0, 0.0)).animate(CurvedAnimation(parent: animation, curve: Curves.fastOutSlowIn)),
          child: FadeTransition(opacity: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(parent: animation, curve: Curves.linear)),
              child: child));
    });

      虽然我们经常自定义 Dialog,但还是需要对系统基础的 Dialog 有所认知;以上是小菜的测试过程,如有错误请多多指导!

来源: 阿策小和尚

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容