Flutter-底部弹出框(Widget层级)

需求
  • 支持底部弹出对话框。
  • 支持手势滑动关闭。
  • 支持在widget中嵌入引用。
  • 支持底部弹出框弹出后不影响其他操作。
  • 支持弹出框中内容固定头部和下面列表时,支持触摸头部并在列表不在头部的时候支持滑动关闭
简述

通过上面的需求可知,就是在界面中可以支持底部弹出一个弹出框,但是又不影响除了这个弹出框外其他操作,同时支持手势滑动关闭。想到这里我们其实会想到一个控件DraggableScrollableSheet,Flutter提供一种可以支持在widget中引入的底部弹出布局,同时不影响其他的操作,所以我就使用这个控件测试了下,发现一个问题,就是当底部弹出框内容为上面有个头部,底部是个列表时,每次都需要列表滑动到顶部的时候才可以手势滑动关闭,所以需要支持触摸顶部布局也支持手势滑动关闭,所以在此控件上做一个修改。

效果

bottom_sheet.gif

代码如下
`import 'package:flutter/material.dart';

/// 底部弹出Widget
/// 1、支持手势下拉关闭
/// 2、支持动画弹出收起
/// 3、支持弹出无法关闭
class DragBottomSheetWidget extends StatefulWidget {
  DragBottomSheetWidget({
    required Key key,
    required this.builder,
    this.duration,
    this.childHeightRatio = 0.8,
    this.onStateChange,
  }) : super(key: key);

  Function(bool)? onStateChange;

  final double childHeightRatio;

  final Duration? duration;

  final ScrollableWidgetBuilder builder;

  @override
  State<DragBottomSheetWidget> createState() => DragBottomSheetWidgetState();
}

class DragBottomSheetWidgetState extends State<DragBottomSheetWidget>
    with TickerProviderStateMixin {
  final controller = DraggableScrollableController();

  //是否可以关闭
  bool isCanClose = true;

  //是否展开
  bool isExpand = false;

  double verticalDistance = 0;

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  Future show({bool isCanClose = true}) {
    return controller
        .animateTo(1,
            duration: widget.duration ?? const Duration(milliseconds: 200),
            curve: Curves.linear)
        .then((value) {
      if (!isCanClose) {
        setState(() {
          this.isCanClose = isCanClose;
        });
      }
    });
  }

  void hide() {
    controller.animateTo(
      0,
      duration: widget.duration ?? const Duration(milliseconds: 200),
      curve: Curves.linear,
    );
  }

  void _dragJumpTo(double y) {
    var size = y / MediaQuery.of(context).size.height;
    controller.jumpTo(widget.childHeightRatio - size);
  }

  void _dragEndChange() {
    controller.size >= widget.childHeightRatio / 2
        ? controller.animateTo(
            1,
            duration: widget.duration ?? const Duration(milliseconds: 200),
            curve: Curves.linear,
          )
        : hide();
  }

  @override
  Widget build(BuildContext context) {
    return NotificationListener<DraggableScrollableNotification>(
      onNotification: (notification) {
        if (notification.extent == widget.childHeightRatio) {
          if (!isExpand) {
            isExpand = true;
            widget.onStateChange?.call(true);
          }
        } else if (notification.extent < 0.00001) {
          if (isExpand) {
            isExpand = false;
            widget.onStateChange?.call(false);
          }
        }
        return true;
      },
      child: DraggableScrollableSheet(
        initialChildSize: isCanClose && !isExpand ? 0 : widget.childHeightRatio,
        minChildSize: isCanClose ? 0 : widget.childHeightRatio,
        maxChildSize: widget.childHeightRatio,
        expand: true,
        snap: true,
        controller: controller,
        builder: (BuildContext context, ScrollController scrollController) {
          return GestureDetector(
            behavior: HitTestBehavior.translucent,
            onVerticalDragDown: (details) {
              verticalDistance = 0;
            },
            onVerticalDragUpdate: (details) {
              verticalDistance += details.delta.dy;
              _dragJumpTo(verticalDistance);
            },
            onVerticalDragEnd: (details) {
              _dragEndChange();
            },
            child: widget.builder.call(context, scrollController),
          );
        },
      ),
    );
  }
}

使用

import 'package:flutter/material.dart';
import 'package:flutter_xy/widgets/xy_app_bar.dart';
import 'package:flutter_xy/xydemo/darg/drag_bottom_sheet_widget.dart';

class DragBottomSheetPage extends StatefulWidget {
  const DragBottomSheetPage({super.key});

  @override
  State<DragBottomSheetPage> createState() => _DragBottomSheetPageState();
}

class _DragBottomSheetPageState extends State<DragBottomSheetPage> {
  final GlobalKey<DragBottomSheetWidgetState> bottomSheetKey =
      GlobalKey<DragBottomSheetWidgetState>();

  bool isExpand = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.deepPurple.withAlpha(50),
      appBar: XYAppBar(
        title: "底部弹出布局",
        backgroundColor: Colors.transparent,
        titleColor: Colors.white,
        onBack: () {
          Navigator.pop(context);
        },
      ),
      body: Stack(
        children: [
          Positioned(
              top: 0,
              child: Column(
                mainAxisSize: MainAxisSize.max,
                children: [
                  InkWell(
                    onTap: () {
                      if (isExpand) {
                        bottomSheetKey.currentState?.hide();
                      } else {
                        bottomSheetKey.currentState?.show();
                      }
                    },
                    child: Container(
                      width: MediaQuery.of(context).size.width - 60,
                      alignment: Alignment.center,
                      height: 50,
                      margin: const EdgeInsets.symmetric(horizontal: 30),
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(32),
                        color: Colors.deepPurple,
                      ),
                      child: Text(
                        isExpand ? "收起" : "弹出",
                        style: const TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.w600,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(height: 20),
                ],
              )),
          Positioned(
            child: DragBottomSheetWidget(
              key: bottomSheetKey,
              onStateChange: (isExpand) {
                setState(() {
                  this.isExpand = isExpand;
                });
              },
              builder:
                  (BuildContext context, ScrollController scrollController) {
                return Container(
                  alignment: Alignment.topLeft,
                  child: Stack(
                    children: [
                      Container(
                        height: 50,
                        decoration: const BoxDecoration(
                          borderRadius: BorderRadius.only(
                              topLeft: Radius.circular(16),
                              topRight: Radius.circular(16)),
                          color: Colors.yellow,
                        ),
                      ),
                      Expanded(
                        child: Container(
                          margin: const EdgeInsets.only(top: 50),
                          child: ListView.builder(
                            itemCount: 500,
                            controller: scrollController,
                            itemBuilder: (context, index) {
                              return index % 2 == 0
                                  ? Container(
                                      height: 100,
                                      width: MediaQuery.of(context).size.width,
                                      color: Colors.red,
                                    )
                                  : Container(
                                      height: 100,
                                      width: MediaQuery.of(context).size.width,
                                      color: Colors.blue,
                                    );
                            },
                          ),
                        ),
                      ),
                    ],
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

完整代码见:https://github.com/yixiaolunhui/flutter_xy

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

20240319在WIN10下给K6000按照驱动程序

20240319在WIN10下给K6000按照驱动程序 2024/3/19 18:12 http://nvidia.cn/ Skip to main content 驱动程序下载 NVIDIA>驱动下载> NVIDIA RTX / Quadro Desktop and Notebook Driver Release 470 Registration Keynote GTC ˆ›šš‰ˆš Portal Prelude DOCA Early Ac…

MySQL 搭建双主复制服务 并 通过 HAProxy 负载均衡

一、MySQL 搭建双主复制高可用服务 在数据库管理中&#xff0c;数据的备份和同步是至关重要的环节&#xff0c;而双主复制&#xff08;Dual Master Replication&#xff09;作为一种高可用性和数据同步的解决方案&#xff0c;通过让两个数据库实例同时充当主服务器和从服务器&…

Java 设计模式系列:行为型-状态模式

简介 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;允许一个对象在其内部状态改变时改变其行为。状态模式中类的行为是由状态决定的&#xff0c;在不同的状态下有不同的行为。 状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂…

智能合约语言(eDSL)—— 使用rust实现eDSL的原理

为理解rust变成eDSL的实现原理&#xff0c;我们需要简单了解元编程与宏的概念,元编程被描述成一种计算机程序可以将代码看待成数据的能力&#xff0c;使用元编程技术编写的程序能够像普通程序在运行时更新、替换变量那样操作更新、替换代码。宏在 Rust 语言中是一种功能&#x…

鸿蒙开发实战:【系统服务管理部件】

简介 samgr组件是OpenHarmony的核心组件&#xff0c;提供OpenHarmony系统服务启动、注册、查询等功能。 系统架构 图 1 系统服务管理系统架构图 目录 /foundation/systemabilitymgr ├── samgr │ ├── bundle.json # 部件描述及编译文件 │ ├── frameworks …

多特征变量序列预测(11) 基于Pytorch的TCN-GRU预测模型

往期精彩内容&#xff1a; 时序预测&#xff1a;LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较-CSDN博客 风速预测&#xff08;一&#xff09;数据集介绍和预处理-CSDN博客 风速预测&#xff08;二&#xff09;基于Pytorch的EMD-LSTM模型-CSDN博客 风速预测&#xff…

基于springboot+vue的智慧生活商城系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

Stability AI 3D:开创3D视觉技术新篇章,提升多视角连贯性与生成质量

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

杰发科技AC7801——Flash数据读取

0. 简介 因为需要对Flash做CRC校验&#xff0c;第一步先把flash数据读出来。 1. 代码 代码如下所示 #include "ac780x_eflash.h" #include "string.h" #define TestSize 1024 ///< 4K #define TestAddressStart 0x08000000 uint8_t Data[7000]; int…

【NLP笔记】Transformer

文章目录 基本架构EmbeddingEncoderself-attentionMulti-Attention残差连接LayerNorm DecoderMask&Cross Attention线性层&softmax损失函数 论文链接&#xff1a; Attention Is All You Need 参考文章&#xff1a; 【NLP】《Attention Is All You Need》的阅读笔记 一…

多数据源 - dynamic-datasource | 集成 HikariCP 连接池

文章目录 连接池集成简介HikariCP 连接池默认 HikariCP 配置自定义 HikariCP 配置Druid 连接池BeeCp 连接池DBCP2 连接池JNDI 数据源🗯️ 上节回顾:上一节中,实现了 dynamic-datasource 的快速入门。 👉 本节目标:在上一节的基础上,集成 HikariCP 数据库连接池并介绍原…

es 集群安全认证

参考文档&#xff1a;Configure security for the Elastic Stack | Elasticsearch Guide [7.17] | Elastic ES敏感信息泄露的原因 Elasticsearch在默认安装后&#xff0c;不提供任何形式的安全防护不合理的配置导致公网可以访问ES集群。比如在elasticsearch.yml文件中,server…

【SpringSecurity】十三、基于Session实现授权认证

文章目录 1、基于session的认证2、Demosession实现认证session实现授权 1、基于session的认证 流程&#xff1a; 用户认证成功后&#xff0c;服务端生成用户数据保存在session中服务端返回给客户端session id (sid&#xff09;&#xff0c;被客户端存到自己的cookie中客户端下…

C# 使用OpenCvSharp4将Bitmap合成为MP4视频的环境

环境安装步骤&#xff1a; 在VS中选中项目或者解决方案&#xff0c;鼠标右键&#xff0c;选择“管理Nuget包”&#xff0c;在浏览窗口中搜索OpenCVSharp4 1.搜索OpenCvSharp4,选择4.8.0版本&#xff0c;点击安装 2.搜索OpenCvSharp4.runtime.win,选择4.8.0版本&#xff0c;点…

O2OA红头文件流转与O2OA版式公文编辑器基本使用

O2OA开发平台在流程管理中&#xff0c;提供了符合国家党政机关公文格式标准&#xff08;GB/T 9704—2012&#xff09;的公文编辑组件&#xff0c;可以让用户在包含公文管理的项目实施过程中&#xff0c;轻松地实现标准化公文格式的在线编辑、痕迹保留、手写签批等功能。并且可以…

vue-router(v4.0) 基础3

编程式导航 除了使用 <router-link> 创建 a 标签来定义导航链接&#xff0c;我们还可以借助 router 的实例方法&#xff0c;通过编写代码来实现。导航到不同的位置 示例该方法的参数可以是一个字符串路径&#xff0c;或者一个描述地址的对象。例如&#xff1a; // 字符串…

Panasonic松下PLC如何数据采集?如何实现快速接入IIOT云平台?

在工业自动化领域&#xff0c;数据采集与远程控制是提升生产效率、优化资源配置的关键环节。对于使用Panasonic松下PLC的用户来说&#xff0c;如何实现高效、稳定的数据采集&#xff0c;并快速接入IIOT云平台&#xff0c;是摆在他们面前的重要课题。HiWoo Box工业物联网关以其强…

fs方法举例

fs.readFile() 读取文件 const fs require(node:fs) const path require(node:path) const s path.resolve(__dirname, ./hello.txt) const buf fs.readFileSync(s) console.log(buf.toString())输出的Buffer对象 用toString()方法转字符串之后 fs.appendFile() 创建新…

景联文科技:提供通用多模态数据,助力AI多模态领域实现飞跃式发展

回顾2023年&#xff0c;以ChatGPT为代表的通用人工智能大模型在全球范围内掀起了新一轮人工智能产业发展浪潮&#xff0c;我国人工智能大模型市场呈现百“模”争鸣、日新月异的迅猛发展态势。 根据大模型之家、钛媒体数据&#xff0c;2023年中国大模型市场规模达到147亿人民币&…

CMU 10-414/714: Deep Learning Systems --hw3

实现功能 在ndarray.py文件中完成一些python array操作 我们实现的NDArray底层存储就是一个一维向量&#xff0c;只不过会有一些额外的属性&#xff08;如shape、strides&#xff09;来表明这个flat array在维度上的分布。底层运算&#xff08;如加法、矩阵乘法&#xff09;都…
最新文章