Flutter 学习之旅(三十) 对话框Dialog

AlertDialog

先来看一下AlertDialog的构造方法

AlertDialog({
    Key key,
///标题
    this.title,
///标题padding
    this.titlePadding,
///标题style
    this.titleTextStyle,
///内容
    this.content,
///padding
    this.contentPadding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0),
///style
    this.contentTextStyle,
/// 动作 widget 列表,
    this.actions,
    this.actionsPadding = EdgeInsets.zero,
///action 溢出 方向
    this.actionsOverflowDirection,
/// action 溢出水平间距离
    this.actionsOverflowButtonSpacing,
///按钮padding
    this.buttonPadding,
///背景色
    this.backgroundColor,
///阴影
    this.elevation,
    this.semanticLabel,
    this.insetPadding = _defaultInsetPadding,
    this.clipBehavior = Clip.none,
    this.shape,
///不可滚动,想要滚动需要嵌套SingleChildScrollView
    this.scrollable = false,
  })

再来看一下创建dialog的方法

Future showDialog({
  @required BuildContext context,
  WidgetBuilder builder,
///点击外部是否消失
  bool barrierDismissible = true,
///外部阴影颜色
  Color barrierColor,
///是否使用安全区域
  bool useSafeArea = true,
  bool useRootNavigator = true,
  RouteSettings routeSettings,
  @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. '
    'This feature was deprecated after v0.2.3.'
  )
  Widget child,
})

例子

Future _getAlertDialog(BuildContext context) {
  return showDialog(
      context: context,
      builder: (context) => AlertDialog(
            title: Text('Title'),

            ///如果内容过长,需要使用SingleChildScrollView
            content: Scrollbar(
              child: SingleChildScrollView(
                physics: BouncingScrollPhysics(),
                child: Text(
                    "这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容..." *
                        20),
              ),
            ),
            actions: [
              FlatButton(
                child: Text('取消'),
                onPressed: () => Navigator.of(context).pop(0),
              ),
              FlatButton(
                child: Text('确定'),
                onPressed: () => Navigator.of(context).pop(1),
              )
            ],
          ),

      ///点击dialog 外部是否消失
      barrierDismissible: true);
}
e6320b087051ba63bf108a8c91ee047.jpg

Future showDialog

showDialog()是Material组件库提供的一个用于弹出Material风格对话框的方法,并返回int类型,取消的时候返回0,确定的时候返回1,比较简单,
这里唯一需要注意的是关闭dialog 方法, Navigator.of(context).pop({Object result}),这个和路由管理关闭页面的方式是一样的

Future showDialog({
  @required BuildContext context,
  WidgetBuilder builder,
  bool barrierDismissible = true,
  Color barrierColor,
  bool useSafeArea = true,
  bool useRootNavigator = true,
  RouteSettings routeSettings,
  @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. '
    'This feature was deprecated after v0.2.3.'
  )
  Widget child,
}) {
  assert(child == null || builder == null);
  assert(barrierDismissible != null);
  assert(useSafeArea != null);
  assert(useRootNavigator != null);
  assert(debugCheckHasMaterialLocalizations(context));

  final ThemeData theme = Theme.of(context, shadowThemeOnly: true);
  return showGeneralDialog(
    context: context,
    pageBuilder: (BuildContext buildContext, Animation animation, Animation secondaryAnimation) {
      final Widget pageChild = child ?? Builder(builder: builder);
      Widget dialog = Builder(
        builder: (BuildContext context) {
          return theme != null
            ? Theme(data: theme, child: pageChild)
            : pageChild;
        }
      );
      if (useSafeArea) {
        dialog = SafeArea(child: dialog);
      }
      return dialog;
    },
    barrierDismissible: barrierDismissible,
    barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
    barrierColor: barrierColor ?? Colors.black54,
    transitionDuration: const Duration(milliseconds: 150),
    transitionBuilder: _buildMaterialDialogTransitions,
    useRootNavigator: useRootNavigator,
    routeSettings: routeSettings,
  );
}

看了一下showDialog 的源码,是对showGeneralDialog这个方法进行了包装,发现了如果使用了MaterialAppTheme 则该dialog使用 MaterialAppTheme , 其他的都是固定封装,

Future showGeneralDialog

Future showGeneralDialog({
  @required BuildContext context,
  @required RoutePageBuilder pageBuilder,
  bool barrierDismissible,
  String barrierLabel,
  Color barrierColor,
  Duration transitionDuration,
  RouteTransitionsBuilder transitionBuilder,
  bool useRootNavigator = true,
  RouteSettings routeSettings,
}) {
  assert(pageBuilder != null);
  assert(useRootNavigator != null);
  assert(!barrierDismissible || barrierLabel != null);
  return Navigator.of(context, rootNavigator: useRootNavigator).push(_DialogRoute(
    pageBuilder: pageBuilder,
    barrierDismissible: barrierDismissible,
    barrierLabel: barrierLabel,
    barrierColor: barrierColor,
    transitionDuration: transitionDuration,
    transitionBuilder: transitionBuilder,
    settings: routeSettings,
  ));
}

而 showGeneralDialog 则是使用 Navigator.push了一个 _DialogRoute ,这里也就解释了关闭dialog 为什么是
Navigator.pop的方法了,

这个例子我为了演示嵌套滑动的效果,我使用SingleChildScrollView

但是我觉得现在大部分dialog的设计应该是这样的


image.png

这种方式如果使用actions 和content 来写的话,想到什么办法,只能利用content 来写所有的布局

image.png

这是我利用content写出来的结果,

Future _getAlertDialog(BuildContext context) {
  return showDialog(
      context: context,
      builder: (context) => AlertDialog(
//            title: Container(
//              alignment: Alignment.center,
//              child: Text('Title'),
//            ),
            /// 修改内容边距
            contentPadding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
            ///  修改圆角大小
            shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.all(Radius.circular(12))),

            ///如果内容过长,需要使用 SingleChildScrollView
            content: SizedBox(
              height: 180,
              child: Column(
                children: [
                  SizedBox(
                    height: 120,
                    ///包裹内容,为了使滑动开始位置是从距离30的位置开始
                    child: Padding(
                      padding: const EdgeInsets.fromLTRB(20, 30, 20, 10),
                      child: Scrollbar(
                        child: SingleChildScrollView(
                          physics: BouncingScrollPhysics(),
                          child:  Text(
                              "这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容...这个是内容..." *
                                  20),
                        ),
                      ),
                    )
                  ),
                  /// 水平横线
                  Divider(
                    height: 10,
                  ),
                  /// 为了均分按钮 ,
                  Row(
                    children: [
                      Expanded(
                        flex: 1,
                        child: FlatButton(
                          child: Text(
                            '取消',
                            style:
                                TextStyle(color: Colors.black87, fontSize: 18),
                          ),
                          onPressed: () => Navigator.of(context).pop(0),
                        ),
                      ),
                      SizedBox(
                        height: 30,
                        child:VerticalDivider(
                          width:10,
                        ),
                      ),
                      Expanded(
                        flex: 1,
                        child: FlatButton(
                          child: Text(
                            '确定',
                            style: TextStyle(
                                color: Colors.blueAccent, fontSize: 18),
                          ),
                          onPressed: () => Navigator.of(context).pop(1),
                        ),
                      )
                    ],
                  ),
                ],
              ),
            ),
          ),

      ///点击dialog 外部是否消失
      barrierDismissible: true);
}

其实这中类似ios样式的控件Flutter在设计之初也想到了,在Cupertino 组件中就有CupertinoDialog ,不过到了现在版本已经被放弃了,使用CupertinoAlertDialog替代了CupertinoDialog 使用方式和dialog是一致的,说实话android机器上显示的这个dialog 确实和ios的效果没啥差别,官方合适很良心的

例子

 getCupertinoAlertDialog(BuildContext context)  {
  return showDialog(context: context,builder: (con){
    return  CupertinoAlertDialog(
      title: Text("This is Title"),
      content: Text('This is content'*10),
      actions: [
        FlatButton(
          onPressed: (){
            Navigator.of(context).pop(0);
          },
          child: Text('取消'),
        ),
        FlatButton(
          onPressed: (){
            Navigator.of(context).pop(0);
          },
          child: Text('中间的'),
        ),
        FlatButton(
          onPressed: (){
            Navigator.of(context).pop(1);
          },
          child: Text('确定'),
        ),
      ],
    );
  });
  }

效果图


image.png
image.png

SimpleDialog

再开看一下他的构造方法

SimpleDialog({
    Key key,
    this.title,
    this.titlePadding = const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0),
    this.titleTextStyle,
    this.children,
    this.contentPadding = const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 16.0),
    this.backgroundColor,
    this.elevation,
    this.semanticLabel,
    this.shape,
  }) 

例子

_getSimpleDialog(BuildContext context) async {
  var lan = await showDialog(
    context: context,
    builder: (context) => SimpleDialog(
      title: Text('Title'),
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 6),
          child: SimpleDialogOption(
            child: Text('这个是第一个'),
            onPressed: () => Navigator.of(context).pop(0),
          ),
        ),
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 6),
          child: SimpleDialogOption(
            child: Text('这个是第二个'),
            onPressed: () => Navigator.of(context).pop(1),
          ),
        )
      ],
    ),
  );
  printString('language is ${lan}');
}

效果图


image.png

这里的子条目使用的是SimpleDialogOption ,他相当如一个button

showModalBottomSheet

在介绍Scaffold 的时候介绍过showModalBottomSheet 创建一个类似bottomsheetdialog,

这里写一个日期选择
先来样式


image.png
  void selectDate(BuildContext context) async {
    DateTime dateTime = DateTime.now();

    var date = await showModalBottomSheet(
      context: context,
      builder: (context) => Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Row(
            children: [
              Expanded(
                  flex: 1,
                  child: GestureDetector(
                    onTap: () {
                      Navigator.of(context).pop();
                    },
                    child: Padding(
                      padding: const EdgeInsets.all(19),
                      child: Text('取消'),
                    ),
                  )),
              Expanded(
                  flex: 1,
                  child: GestureDetector(
                    onTap: () {
                      Navigator.of(context).pop(dateTime);
                    },
                    child: Padding(
                      padding: const EdgeInsets.all(10),
                      child: Text('确定', textDirection: TextDirection.rtl),
                    ),
                  )),
            ],
          ),
          SizedBox(
            height: 200,
            child: CupertinoDatePicker(
              onDateTimeChanged: (date) {
                dateTime = date;
              },
              mode: CupertinoDatePickerMode.date,
              maximumDate: DateTime.now().add(Duration(days: 20)),
              minimumDate: DateTime.now().add(Duration(days: -20)),
            ),
          )
        ],
      ),
    );

    printString('date:${date ?? ''}');
  }

我学习flutter的整个过程都记录在里面了
https://www.jianshu.com/c/36554cb4c804

最后附上demo 地址

https://github.com/tsm19911014/tsm_flutter

你可能感兴趣的:(Flutter 学习之旅(三十) 对话框Dialog)