Flutter 中的Dialog

2019-09-04 10:04:42 浏览数 (1)

在Flutter中,各种提示框、弹出框是如何实现的呢?今天我们就来聊一聊这个问题。

首先咱们来聊聊Flutter系统内置的Dialog。

Flutter系统内置的Dialog

关于Flutter系统内置的Dialog,我们可以从两个层面去讨论,一个是showDialog层面,一个是showModalBottomSheet层面。

showDialog

showDialog又分为 AlertDialog 和 SimpleDialog 。首先我们来看看AlertDialog。

AlertDialog

代码语言:javascript复制
_showAlertDialog(){
  showDialog(
    barrierDismissible: false,//点击灰色背景的时候是否消失弹出框
    context: context,
    builder: (context){
      return AlertDialog(
        title: Text("这是一个AlertDialog"),
        content: Text("演示AlertDialog的展示效果。您确定要删除吗?"),
        actions: <Widget>[
          FlatButton(
            child: Text("cancel"),
            onPressed: (){
              print("Calcel");
              Navigator.pop(context);//令提示框消失
            },
          ),
          FlatButton(
            child: Text("OK"),
            onPressed: (){
              print("Delete");
              Navigator.pop(context);
            },
          )
        ],
      );
    }
  );
}

效果如下:

需要注意的是,上面的代码中,我们在对应的Button中单独地响应了点击事件,其实我们也可以对Dialog内部的按钮点击事件进行统一处理的。代码如下:

代码语言:javascript复制
_showAlertDialog() async {
  //通过result来记录内部点击事件的结果
  var result = await showDialog(
      barrierDismissible: false, //点击灰色背景的时候是否消失弹出框
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text("这是一个AlertDialog"),
          content: Text("演示AlertDialog的展示效果。您确定要删除吗?"),
          actions: <Widget>[
            FlatButton(
              child: Text("cancel"),
              onPressed: () {
                //令提示框消失,第二个参数就是该点击事件的ID,在外部可以被获取到
                Navigator.pop(context, "cancel");
              },
            ),
            FlatButton(
              child: Text("OK"),
              onPressed: () {
                //令提示框消失,第二个参数就是该点击事件的ID,在外部可以被获取到
                Navigator.pop(context, "OK");
              },
            )
          ],
        );
      });
  //在这里根据result值来进行判断、处理各种事件
  print(result);
}

在删除列表中的某一个项目的时候,我们可以使用alertDialog来进行提示

SimpleDialog

代码语言:javascript复制
_showSimpleDialog() async{
  //获取内部点按事件的结果,并记录为result
  var result = await showDialog(
    barrierDismissible: false,//点击黑色背景的时候是否令提示框消失
    context: context,
    builder: (context){
      return SimpleDialog(
        title: Text("SimpleDialogTitle"),
        children: <Widget>[
          SimpleDialogOption(
            child: Text("OptionA"),
            onPressed: (){
              //令弹框消失,第二个参数是对该操作的标识ID,该标识ID在外部可被获取到,用于统一处理dialog内部的点按事件
              Navigator.pop(context, "OptionA");
            },
          ),
          Divider(),//分割线
          SimpleDialogOption(
            child: Text("OptionB"),
            onPressed: (){
              Navigator.pop(context, "OptionB");
            },
          ),
          Divider(),//分割线
          SimpleDialogOption(
            child: Text("OptionC"),
            onPressed: (){
              Navigator.pop(context, "OptionC");
            },
          ),
          Divider(),//分割线
          SimpleDialogOption(
            child: Text("OptionD"),
            onPressed: (){
              Navigator.pop(context, "OptionD");
            },
          )
        ],
      );
    }
  );

  //通过获取到的内部点按事件的标识ID来统一处理内部点按事件
  print(result);
}

效果如下:

showModalBottomSheet

前面我们讲了通过showDialog来弹出提示框,通过showDialog弹出的提示框都是在页面的中间。接下来我们看看如何从页面底部弹出一个Sheet。

代码语言:javascript复制
_showModalBottomSheet() async {
  //将点击栏目的标识ID传递出来(传递给result)
  var result = await showModalBottomSheet(
      backgroundColor: Colors.pink[50], //弹出的提示框的背景颜色
      context: context,
      builder: (context) {
        return Container(
          height: 300, //这里可以控制弹框的高度
          child: ListView(
            children: <Widget>[
              ListTile(
                title: Text("分享A"),
                //点击之后令提示栏消失,这里的第二个参数是当前点击的栏目的标识ID(可以传递出去)。
                onTap: () => Navigator.pop(context, "A"),
              ),
              Divider(), //分割线
              ListTile(
                title: Text("分享B"),
                onTap: () => Navigator.pop(context, "B"),
              ),
              Divider(),
              ListTile(
                title: Text("分享C"),
                onTap: () => Navigator.pop(context, "C"),
              ),
              Divider(),
              ListTile(
                title: Text("分享D"),
                onTap: () => Navigator.pop(context, "D"),
              ),
              Divider(),
              ListTile(
                title: Text("分享E"),
                onTap: () => Navigator.pop(context, "E"),
              ),
              Divider(),
              ListTile(
                title: Text("分享F"),
                onTap: () => Navigator.pop(context, "F"),
              ),
              Divider(),
              ListTile(
                title: Text("分享G"),
                onTap: () => Navigator.pop(context, "G"),
              ),
              Divider(),
              ListTile(
                title: Text("分享H"),
                onTap: () => Navigator.pop(context, "H"),
              ),
              Divider(),
              ListTile(
                title: Text("分享I"),
                onTap: () => Navigator.pop(context, "I"),
              )
            ],
          ),
        );
      });

  //根据传递出来的点击栏目的标识,我们可以知道点击了哪个栏目,从而可以进行进一步的操作
  print(result);
}

效果如下:

需要注意的是,showModalBottomSheet不仅可以实现底部的ActionSheet,还可以根据自己的需求来实现一些其他的自定义弹出内容

第三方库fluttertoast

上面我们介绍了系统内置的几个Dialog,接下来我将为大家介绍一款第三方组件——fluttertoast。

该第三方库的安装以及引用我就不赘述了,大家在pub.dev上直接搜fluttertoast,然后按照文档来即可。

代码语言:javascript复制
_showToast(){
  Fluttertoast.showToast(
    msg: "这是一个fluttertoast,这是一个fluttertoast,这是一个fluttertoast,这是一个fluttertoast,这是一个fluttertoast,这是一个fluttertoast",//提示信息
    gravity: ToastGravity.CENTER,//提示框的方位(上、中、下)
    timeInSecForIos: 3,//提示框的显示时间(仅对iOS有效)
    backgroundColor: Colors.pink,//提示框的背景颜色
    textColor: Colors.yellow,//提示框上的文本颜色
    fontSize: 18,//提示框上的文本字体大小
  );
}

效果如下:

可以使用fluttertoast来进行网络请求状态的提示

如何自定义Dialog

上面我们讲了Flutter的内置提示框,还介绍了一款flutter第三方提示组件fluttertoast,通常情况下,这些就满足我们日常开发需求了。但是有一些特殊的场景,我们还是需要个性化定制一些提示框,那么如何去自定义一款Dialog呢?

代码如下:

代码语言:javascript复制
//自定义Dialog
import 'package:flutter/material.dart';

//自定义Dialog,必须继承自Dialog
class CustomDialog extends Dialog {
  final String title;
  final String content;

  CustomDialog({this.title = "", this.content = ""});

  @override
  Widget build(BuildContext context) {
    //重写build方法,在这里面进行Dialog的自定义
    return Material(
      type: MaterialType.transparency, //透明的效果
      //自定义child,就可以实现我们想要实现的效果了
      child: Center(
        child: Container(
          height: 300,
          width: 300,
          color: Colors.white,
          child: Column(
            children: <Widget>[
              Padding(
                padding: EdgeInsets.all(10),
                child: Stack(
                  children: <Widget>[
                    Align(
                      alignment: Alignment.center,
                      child: Text(this.title),
                    ),
                    Align(
                      alignment: Alignment.centerRight,
                      child: InkWell(
                        onTap: () {
                          Navigator.pop(context);
                        },
                        child: Icon(Icons.close),
                      ),
                    )
                  ],
                ),
              ),
              Divider(),
              Text(this.content)
            ],
          ),
        ),
      ),
    );
  }
}


//使用自定义Dialog
_showCustomDialog(){
  showDialog(
    context: context,
    builder: (context){
      return CustomDialog(
        title: "这里是标题",
        content: "这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容",
      );
    }
  );
}

效果如下:

接下来我们想,如何实现让自定义的Dialog自动消失的功能呢?此时我们可以使用定时器,代码如下:

代码语言:javascript复制
//自定义Dialog
import 'dart:async';

import 'package:flutter/material.dart';

//自定义Dialog,必须继承自Dialog
class CustomDialog extends Dialog {
  final String title;
  final String content;
  final int showTime;//提示款展示时间(单位是秒)

  CustomDialog({this.title = "", this.content = "", this.showTime=3});

  //定时器
  _showTimer(context){
    Timer.periodic(Duration(seconds: this.showTime), (t){
      //定时器结束以后执行的代码
      Navigator.pop(context);//让Dialog消失
      t.cancel();//取消定时器
    });
  }

  @override
  Widget build(BuildContext context) {
    _showTimer(context);//开启定时器

    //重写build方法,在这里面进行Dialog的自定义
    return Material(
      type: MaterialType.transparency, //透明的效果
      //自定义child,就可以实现我们想要实现的效果了
      child: Center(
        child: Container(
          height: 300,
          width: 300,
          color: Colors.white,
          child: Column(
            children: <Widget>[
              Padding(
                padding: EdgeInsets.all(10),
                child: Stack(
                  children: <Widget>[
                    Align(
                      alignment: Alignment.center,
                      child: Text(this.title),
                    ),
                    Align(
                      alignment: Alignment.centerRight,
                      child: InkWell(
                        onTap: () {
                          Navigator.pop(context);
                        },
                        child: Icon(Icons.close),
                      ),
                    )
                  ],
                ),
              ),
              Divider(),
              Text(this.content)
            ],
          ),
        ),
      ),
    );
  }
}


//使用自定义Dialog
_showCustomDialog(){
  showDialog(
    context: context,
    builder: (context){
      return CustomDialog(
        showTime: 1,//提示框展示时间
        title: "这里是标题",
        content: "这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容这里是内容",
      );
    }
  );
}

有以下几点需要说明:

1,上面我们提到的AlertDialog和SimpleDialog,都是在showDialog中的builder函数中返回的,我们自定义的Dialog也是在这个函数中返回。

2,自定义Dialog对象,需要继承自Dialog类。尽管Dialog提供了 child 参数可以用来写视图界面,但是往往会达不到我们想要的效果,因为默认的Dialog背景框是满屏的。如果我们想完全定义界面,就需要重写build函数。

以上。

0 人点赞