flutter开发实战-flutter实现类似iOS的Alert提示框与sheet菜单效果

news2024/11/25 0:38:58

flutter开发实战-flutter实现类似iOS的Alert提示框与sheet菜单效果

在开发过程中,经常使用到提示框Dialog,与sheet,使用到了flutter的showDialog与showModalBottomSheet
我这里类似alert弹窗直接调用 flutter 中提供的showDialog()函数显示对话框。
我这里类似Sheet底部弹窗调用 flutter 中提供的showModalBottomSheet从屏幕下方弹出一个对话框。

效果图如下

在这里插入图片描述

在这里插入图片描述

一、定义dialog内容

定义dialog内容,实现标题、内容描述、按钮操作等功能。

class DialogItem {
  String? title;
  String? color;

  DialogItem({this.title, this.color});
}

class ShowAlertDialog extends StatefulWidget {
  const ShowAlertDialog({
    Key? key,
    this.contentAlign = TextAlign.left,
    required this.onTap,
    this.itemTitles,
    this.content,
    this.title,
    this.children,
  }) : super(key: key);

  // 内容区域布局
  final TextAlign contentAlign;

  final String? title;

  final String? content;

  // 点击返回index 0 1
  final Function onTap;

  //按钮
  final List<DialogItem>? itemTitles;

  final List<Widget>? children;

  
  State<ShowAlertDialog> createState() => _ShowAlertDialogState();
}

class _ShowAlertDialogState extends State<ShowAlertDialog> {
  
  Widget build(BuildContext context) {
    return Material(
      color: Colors.transparent,
      child: Center(
        // ClipRRect 创建圆角矩形 要不然发现下边button不是圆角
        child: ClipRRect(
          borderRadius: BorderRadius.circular(4.0),
          child: Container(
            color: Color(0xFFFFFFFF),
            width: 300,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                SizedBox(
                  height: 25.0,
                ),
                _buildTitleWidget(),
                SizedBox(
                  height: (_hasTitleAndContent() ? 10.0 : 0.0),
                ),
                _buildContentWidget(),
                SizedBox(height: 25),
                Container(
                  decoration: BoxDecoration(
                    border: Border(
                      bottom: BorderSide(
                        color: Color(0xFFf5f5f5),
                        width: 1,
                      ),
                    ),
                  ),
                ),
                _buildItemWidget(),
              ],
            ),
          ),
        ),
      ),
    );
  }

  bool _hasTitle() {
    if (widget.title != null && widget.title!.isNotEmpty) {
      return true;
    }

    return false;
  }

  bool _hasContent() {
    if (widget.content != null && widget.content!.isNotEmpty) {
      return true;
    }

    return false;
  }

  bool _hasTitleAndContent() {
    if (_hasTitle() && _hasContent()) {
      return true;
    }
    return false;
  }

  Widget _buildTitleWidget() {
    bool aHasTitle = _hasTitle();
    if (aHasTitle) {
      return Container(
        padding: EdgeInsets.symmetric(horizontal: 8.0),
        child: Text(
          "${widget.title}",
          textAlign: TextAlign.center,
          maxLines: 1,
          overflow: TextOverflow.ellipsis,
          style: TextStyle(
            fontSize: 17,
            fontWeight: FontWeight.w600,
            fontStyle: FontStyle.normal,
            color: Color(0xFF1A1A1A),
            decoration: TextDecoration.none,
          ),
        ),
      );
    }

    return Container();
  }

  Widget _buildContentWidget() {
    bool aHasContent = _hasContent();
    if (aHasContent) {
      return Container(
        padding: EdgeInsets.symmetric(horizontal: 8.0),
        child: Text(
          "${widget.content}",
          textAlign: TextAlign.center,
          style: TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.w500,
            fontStyle: FontStyle.normal,
            color: Color(0xFF333333),
            decoration: TextDecoration.none,
          ),
        ),
      );
    }

    return Container();
  }

  Widget _buildItemWidget() {
    if (widget.children != null && widget.children!.isNotEmpty) {
      return Container(
        height: 44.0,
        child: Row(
          children: widget.children!.map((res) {
            int index = widget.children!.indexOf(res);
            return Expanded(
              flex: 1,
              child: GestureDetector(
                onTap: () {
                  Navigator.pop(context);
                  widget.onTap(index);
                },
                child: Container(
                  height: 44,
                  alignment: Alignment.center,
                  child: res,
                  decoration: BoxDecoration(
                    border: Border(
                      right: BorderSide(
                        color: Color(0xFFF5F5F5),
                        width: 1,
                      ),
                    ),
                  ),
                ),
              ),
            );
          }).toList(),
        ),
      );
    }

    if (widget.itemTitles != null && widget.itemTitles!.isNotEmpty) {
      return Container(
        height: 44,
        child: Row(
          children: widget.itemTitles!.map((res) {
            int index = widget.itemTitles!.indexOf(res);
            return buildItem(res, index);
          }).toList(),
        ),
      );
    }

    return Container();
  }

  Widget buildItem(DialogItem item, int index) {
    String? title = item.title;
    String? color = item.color;
    Color textColor = ColorUtil.hexColor(0x333333);
    if (color != null && color.isNotEmpty) {
      textColor = ColorUtil.hexColorString(color);
    }
    return Expanded(
      flex: 1,
      child: GestureDetector(
        onTap: () {
          Navigator.pop(context);
          widget.onTap(index);
        },
        child: Container(
          height: 44,
          alignment: Alignment.center,
          child: Text(
            "${title}",
            style: TextStyle(
              color: textColor,
              fontSize: 16,
            ),
          ),
          decoration: BoxDecoration(
            border: Border(
              right: BorderSide(
                color: Color(0xFFF5F5F5),
                width: 1,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

二、定义sheet底部弹窗内容

底部弹窗内容一般为选项,分为多个选项,最后为取消按钮。

class ShowSheetDialog extends StatefulWidget {
  const ShowSheetDialog({
    Key? key,
    required this.onTap,
    required this.items,
    required this.title,
    this.cancelTitle,
  }) : super(key: key);

  //按钮title
  final List<DialogItem> items;

  //点击事件回调 0开始
  final Function onTap;

  //标题 可选
  final String title;

  //取消 可选
  final String? cancelTitle;

  
  State<ShowSheetDialog> createState() => _ShowSheetDialogState();
}

class _ShowSheetDialogState extends State<ShowSheetDialog> {
  
  Widget build(BuildContext context) {
    double viewPaddingBottom = MediaQuery.of(context).viewPadding.bottom;

    return ClipRRect(
      borderRadius: BorderRadius.only(
        topLeft: Radius.circular(8.0),
        topRight: Radius.circular(8.0),
      ),
      child: Container(
        color: ColorUtil.hexColor(0xf7f7f7),
        padding: EdgeInsets.only(bottom: viewPaddingBottom),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            _itemTitle(context),
            Column(
              mainAxisSize: MainAxisSize.min,
              children: widget.items.map((item) {
                int index = widget.items.indexOf(item);
                return GestureDetector(
                  onTap: () {
                    Navigator.pop(context);
                    if (widget.onTap != null) {
                      widget.onTap(index);
                    }
                  },
                  child: _itemCreate(item),
                );
              }).toList(),
            ),
            GestureDetector(
              child: Padding(
                padding: EdgeInsets.only(top: 10),
                child: _itemCreate(DialogItem(
                    title: widget.cancelTitle ?? "取消", color: "333333")),
              ),
              onTap: () {
                Navigator.pop(context);
              },
            )
          ],
        ),
      ),
    );
  }

  Widget _itemTitle(BuildContext context) {
    //有标题的情况下
    if (widget.title != null && widget.title.isNotEmpty) {
      return Container(
        alignment: Alignment.center,
        width: MediaQuery.of(context).size.width,
        height: 40,
        child: Text(
          "${widget.title}",
          textAlign: TextAlign.center,
          style: TextStyle(
            fontSize: 14,
            fontWeight: FontWeight.w500,
            fontStyle: FontStyle.normal,
            color: ColorUtil.hexColor(0x777777),
            decoration: TextDecoration.none,
          ),
        ),
      );
    }

    return Container();
  }

  Widget _itemCreate(DialogItem item) {
    String title = item.title ?? "";
    String? color = item.color;
    Color textColor = ColorUtil.hexColor(0x333333);
    if (color != null && color.isNotEmpty) {
      textColor = ColorUtil.hexColorString(color);
    }

    return Container(
      height: 50,
      width: MediaQuery.of(context).size.width,
      child: Center(
        child: Text(
          "${title}",
          textAlign: TextAlign.center,
          style: TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.w500,
            fontStyle: FontStyle.normal,
            color: textColor,
            decoration: TextDecoration.none,
          ),
        ),
      ),
      decoration: BoxDecoration(
        color: Colors.white,
        border: Border(
          top: BorderSide(
            color: ColorUtil.hexColor(0xf7f7f7),
            width: 1.0,
          ),
        ),
      ),
    );
  }
}

三、调用flutter的showDialog方法弹窗

showDialog方法弹窗

/// 显示对话框的内容
class ShowDialogContent {
  String? title;
  String? message;
  String? ok;
  String? cancel;
  String? okColor;
  String? cancelColor;

  ShowDialogContent({this.title, this.message, this.ok, this.cancel});

  ShowDialogContent.fromJson(Map<String, dynamic> json) {
    title = json['title'];
    message = json['message'];
    ok = json['ok'];
    okColor = json['okColor'];
    cancel = json['cancel'];
    cancelColor = json['cancelColor'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['title'] = this.title;
    data['message'] = this.message;
    data['ok'] = this.ok;
    data['okColor'] = this.okColor;
    data['cancel'] = this.cancel;
    data['cancelColor'] = this.cancelColor;
    return data;
  }
}

class DialogUtil {
//显示中间弹窗
  static void popDialog(BuildContext context, Widget widget) {
    showDialog(
        context: context,
        barrierDismissible: true,
        builder: (ctx) {
          return widget;
        });
  }

  //返回上一级
  static void pop(BuildContext context) {
    Navigator.pop(context);
  }

// 显示处理弹出框
  static void showAlertDialog(
      {required ShowDialogContent dialogContent, OnDialogCallback? callback}) {
    DialogUtil.popDialog(
      OneContext().context!,
      ShowAlertDialog(
        title: dialogContent.title,
        content: dialogContent.message,
        onTap: (int index) {
          Map result = Map();
          result["index"] = index;
          if (callback != null) {
            callback(result);
          }
        },
        children: [
          Text(
            dialogContent.cancel ?? "",
            textAlign: TextAlign.center,
            softWrap: true,
            style: TextStyle(
              fontSize: 16,
              fontWeight: FontWeight.w500,
              fontStyle: FontStyle.normal,
              color: dialogContent.cancelColor != null
                  ? ColorUtil.hexColorString(dialogContent.cancelColor!)
                  : ColorUtil.hexColorString("333333"),
              decoration: TextDecoration.none,
            ),
          ),
          Text(
            dialogContent.ok ?? "",
            textAlign: TextAlign.center,
            softWrap: true,
            style: TextStyle(
              fontSize: 16,
              fontWeight: FontWeight.w500,
              fontStyle: FontStyle.normal,
              color: dialogContent.okColor != null
                  ? ColorUtil.hexColorString(dialogContent.okColor!)
                  : ColorUtil.hexColorString("333333"),
              decoration: TextDecoration.none,
            ),
          ),
        ],
      ),
    );
  }
}

四、调用flutter的bottomSheetDialog方法底部弹窗

class DialogUtil {
	
  //显示底部弹窗
  static void bottomSheetDialog(
    BuildContext context,
    Widget widget, {
    bool? isScrollControlled,
    bool? enableDrag,
    Color? backgroundColor,
  }) {
    showModalBottomSheet(
      context: context,
      isScrollControlled: isScrollControlled ?? true,
      enableDrag: enableDrag ?? true,
      backgroundColor: backgroundColor ?? Colors.white,
      builder: (ctx) {
        return widget;
      },
    );
  }

  // 显示处理chekbox
  static void showCheckBox(
      {required String title,
      required List<Map> optionList,
      String? cancelTitle,
      OnDialogCallback? callback}) {
    // {"title":"标题","titleColor":"FFFFFF"}
    List<DialogItem> items = [];
    for (Map map in optionList) {
      String? title = map["title"];
      String? titleColor = map["titleColor"];
      DialogItem item = DialogItem(title: title, color: titleColor);
      items.add(item);
    }

    DialogUtil.bottomSheetDialog(
      OneContext().context!,
      ShowSheetDialog(
        onTap: (int index) {
          Map result = Map();
          result["index"] = index;
          if (callback != null) {
            callback(result);
          }
        },
        items: items,
        title: title,
        cancelTitle: cancelTitle,
      ),
      enableDrag: false,
      backgroundColor: Colors.transparent,
    );
  }

}

五、小结

flutter开发实战-flutter实现类似iOS的Alert提示框与sheet菜单效果,使用到了flutter的showDialog与showModalBottomSheet来显示对话框与屏幕下方弹出一个对话框功能。

学习记录,每天不停进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/752289.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QML语法--第二篇

链接: QML Book中文版(QML Book In Chinese) QML语言描述了用户界面元素的形状和行为。用户界面能够使用JavaScript来提供修饰&#xff0c;或者增加更加复杂的逻辑。 从QML元素的层次结构来理解是最简单的学习方式。子元素从父元素上继承了坐标系统&#xff0c;它的x,y坐标总…

社交圈..

社交圈 - 题目 - Daimayuan Online Judge 思路&#xff1a;我们能够想到&#xff0c;如果i,j是并列的&#xff0c;则l[i]与r[j]会有重合的部分&#xff0c;那么其实重合的部分越多越好&#xff0c;其实就是让l[i]与r[i]的差值越小越好&#xff0c;同时要让越…

使用qemu创建ubuntu-base文件系统,并安装PM相关内核模块

目录 一、配置镜像二、使用qemu模拟nvdimm&#xff08;安装PM相关内核模块&#xff09;运行记录 遇到的一些问题1、ext4文件系统损坏问题&#xff1a;系统启动时&#xff0c;遇到ext4的报错信息解决办法&#xff1a;2、内核模块未成功加载3、qemu报错4、主机终端无法正常打开 流…

一、对象的概念(2)

本章概要 复用继承 “是一个”与“像是一个”的关系 多态 复用 一个类经创建和测试后&#xff0c;理应是可复用的。然而很多时候&#xff0c;由于程序员没有足够的编程经验和远见&#xff0c;我们的代码复用性并不强。 代码和设计方案的复用性是面向对象程序设计的优点之一…

MacOS Sonoma 14.0 (23A5286i) Beta3 带 OC 0.9.3 and FirPE 双分区原版黑苹果镜像

7 月 12 日苹果发布了 macOS Sonoma 14.0 Beta3 第二个版本&#xff08;内部版本号&#xff1a;23A5286i&#xff09;和 tvOS 17 Beta3&#xff08;内部版本号&#xff1a; 21J5303h&#xff09;&#xff0c;同时还面向iOS / iPadOS 17 更新发布 Beta3 更新。 一、镜像下载&…

力扣每日一题2023.7.14在二叉树中分配硬币

题目: 示例: 分析: 给一个二叉树,二叉树的值总和等于整个二叉树的节点数,我们一次可以移动一个节点的一个值到相邻节点,问我们需要移动多少次才可以把值的总和平均分配每个节点(即每个节点的值都为1). 我们可以确定的是,如果某个节点的左子树共有n个节点,而节点值总和为m,那么…

【2023,学点儿新Java-35】强制类型转换的执行时机、风险与安全 | 基本类型转换、引用类型转换

前情提要&#xff1a; 【2023&#xff0c;学点儿新Java-34】基本数据类型变量 运算规则&#xff1a;自动类型提升、强制类型转换 | 为什么标识符的声明规则里要求不能数字开头&#xff1f;&#xff08;通俗地讲解——让你豁然开朗&#xff01;&#xff09;【2023&#xff0c;学…

嵌入式Linux开发实操(五):embedded linux嵌入式Linux开发

前言: embedded linux开发有个好处就是开源的,总的来说涉及五个部分: 1、工具链Toolchain:为目标设备创建代码需要的编译器和其他工具。其他一切都取决于工具链。 2、引导程序Bootloader:它初始化板并加载Linux kernal。 3、内核kernal:这是系统的core核心,管理系统…

ELK-日志服务【redis-配置使用】

目录 环境 【1】redis配置 【2】filebeat配置 【3】对接logstash配置 【4】验证 【5】安全配置&#xff1a;第一种&#xff1a;kibana-nginx访问控制 【6】第二种&#xff1a;在ES-主节点-配置TLS 【7】kibana配置密码 【8】logstash添加用户密码 环境 es-01,kibana 1…

一文详解什么是数据库分片

概要 应用程序正在变得越来越好&#xff0c;它拥有更多的功能、更多的活跃用户&#xff0c;并且每天都会收集更多的数据。但数据库现在导致应用程序的其余部分变慢。数据库分片可能是问题的答案&#xff0c;但许多人不知道它是什么&#xff0c;最重要的是何时使用它。在本文中我…

Android13 编译错误汇总

1. error: New setting keys are not allowed 一版是在Settings中添加了新的字段导致的 解决&#xff1a; 在你的字段上面加上SuppressLint("NoSettingsProvider") 继续编译应该会出现 按照提示 make api-stubs-docs-non-updatable-update-current-api 然后再…

领域驱动设计(四) - 战略设计 - 【2/2】核心域提取和模型能力设计

模型的复杂性必须通过重构和知识的消化才能把关键的领域、最有价值的部分&#xff08;core domain&#xff09;、优先级提取出来。让团队而把主要精力放在core domain上而不要为无关的细节分散注意力&#xff0c;这有益于&#xff1a; 帮助团队成员掌握系统的总体设计以便更好…

基于多场景的考虑虑热网网损的太阳能消纳能力评估研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

微信小程序使用字体图标——链接引入

目录 1.下载字体图标 1.选择需要的图标加入购物车添加到项目 2.查看项目 3.生成在线链接 4.复制生成的链接 等下放到iconfont.json中​编辑 2.引入链接 1.下载 2.生成iconfont.json文件 3. 在iconfont.json中 放入生成的链接 4.需要重新编译小程序之后在终端执行 5…

二维码识别 OCR 原理及如何应用于物流和仓储管理中

摘要 在传统的物流和仓储管理中&#xff0c;人工操作容易出现错误和低效率。然而&#xff0c;随着二维码技术的普及和二维码识别OCR接口的应用&#xff0c;物流和仓储管理实现了更高水平的自动化和智能化。通过扫描和解码二维码&#xff0c;物流和仓储管理系统可以实现货物跟踪…

yapi的部署和安装

安装Node.js环境 wget https://nodejs.org/dist/v14.15.4/node-v14.15.4-linux-x64.tar.xz 或者直接浏览器下载传输到服务器上。 https://nodejs.org/zh-cn/download/tar -xvf node-v14.15.4-linux-x64.tar.xz 太高版本不行&#xff0c;install会报错。16开头的。 配置环境…

使用cpolar内网穿透,将Tomcat网页发布到公共互联网

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个轻量级的服务器&#xff0c;不仅名字很有趣&#xff0…

前端JavaScript入门-day07

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 深入对象 创建对象三种方式 1. 利用对象字面量创建对象 2. 利用 new Object 创建对象 3. 利用构造函数创建对…

Java阶段五Day06

Java阶段五Day06 文章目录 Java阶段五Day06问题解析阶段性架构图 Dubbo组件远程调用RPC概括DubboDubbo调用案例调用业务选择和dubbo角色环境准备dubbo远程调用原理 问题解析 阶段性架构图 这个架构图&#xff0c;由于nacos的功能&#xff0c;可以实现服务治理&#xff08;服务…

精确长延时电路/数字式长延时电路设计

精确长延时电路 该电路由CD4060组成定时器的时基电路&#xff0c;由电路产生的定时时基脉冲&#xff0c;通过内部分频器分频后输出时基信号。在通过外设的分频电路分频&#xff0c;取得所需要的定时控制时间。 一、电路工作原理 电路原理如图13 所示。 通电后&#xff0c;时…