Flutter Overlay 你用上了么

news2024/11/18 18:30:41

Flutter Overlay 你用上了么

Flutter overlay did you use it?

前言

Flutter中的Overlay是一个用于在屏幕上显示浮层的组件。它可以用来在应用程序中创建弹出窗口、提示框、菜单、对话框等等。

Overlay通常用于在用户与应用程序交互时显示临时性的UI元素,例如:用户点击按钮时显示下拉菜单、用户长按屏幕时显示上下文菜单、显示警告或错误消息等等。

Overlay通常包含一个Stack布局,每个浮层都是一个Positioned widget,可以添加到Stack中。这样,可以将多个浮层叠加在一起,并控制它们的层次关系。

Flutter中的Overlay可以让开发者轻松创建复杂的UI,同时还可以保持应用程序的性能和响应度。

本文中提供的代码示例演示了如何在Flutter中使用Overlay制作工作覆盖层。Overlay包括两个基本组件:OverlayState和OverlayEntry。OverlayState管理所有OverlayEntry,OverlayEntry定义覆盖层中的内容。在示例中,OverlayEntry包含一个带有文本和颜色的容器,可以在屏幕上显示。这些条目可以通过OverlayState的insert和remove方法添加和删除。

原文 https://ducafecat.com/blog/flutter-overlay-did-you-use-it

image-20230518102503338

视频

https://www.bilibili.com/video/BV1qX4y1C7rb/

代码

https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_overlay

参考

https://api.flutter.dev/flutter/widgets/Overlay-class.html

https://api.flutter.dev/flutter/widgets/OverlayEntry-class.html

OverlayEntry

Overlay 通过将独立的子窗口小部件插入到重叠的堆栈中,使它们在其他窗口小部件之上“浮动”可视元素。覆盖允许每个小部件使用 OverlayEntry 对象管理它们在覆盖中的参与。

构造函数

OverlayEntry({
  required this.builder,
  bool opaque = false,
  bool maintainState = false,
})
  • builder:此属性用于此条目,并将在条目位置的覆盖中包含此构建器构建的小部件。
  • opaque:此属性用于获取bool值,该值决定此条目是否阻止整个覆盖。如果条目声明为不透明,则为了提高效率,除非它们具有maintainState设置,否则覆盖将跳过在该条目下面构建条目。
  • maintainState:如果您需要在 OverlayEntry中使用有状态的小部件,那么您需要将 maintainState设置为true,以便小部件可以保持其状态并接收生命周期方法。如果您只是在 OverlayEntry中显示一个静态小部件,则可以将 maintainState设置为false,以便小部件可以释放内存和资源,不会对性能产生过多影响。

OverlayEntry方法

只有一个OverlayEntry的方法

  • remove:此方法用于从覆盖层中删除此条目

OverlayState

Overlay的当前状态用于将OverlayEntries插入覆盖层中。

OverlayState方法

  • debugIsVisible:此方法用于检查给定的OverlayEntry是否可见,并返回布尔值。
  • insert:此方法用于将给定的OverlayEntry插入覆盖层中。
  • insertAll:此方法用于获取OverlayEntries的List,并将所有条目插入覆盖层中。您还可以指定上述和下述属性以说明要插入条目的顺序。
  • rearrange:它可以重新排列当前在屏幕上叠加的所有小部件。具体来说, rearrange方法会将最后添加的 OverlayEntry小部件移动到最上面,并将其他小部件移动到下面。这可以用于确保最新的 OverlayEntry始终位于最上面,并且可以接收用户的触摸事件等。

步骤

第一步:成员变量

  /// overlay 状态
  OverlayState? overlayState;

  /// overlay 层集合
  List<OverlayEntry> entriesList = [];
  @override
  void initState() {
    super.initState();
    // 获取 overlay 状态
    overlayState = Overlay.of(context);
  }
  @override
  void dispose() {
    // 销毁
    overlayState?.dispose();
    super.dispose();
  }

第二步:随机层显示

  // 随机位置显示层
  void showRandomOverlay(BuildContext context) {
    // 随机颜色
    final bgColor = Color.fromARGB(
      255,
      1 + Random().nextInt(254),
      1 + Random().nextInt(254),
      1 + Random().nextInt(254),
    );

    // 屏幕宽度
    final screenWidth = MediaQuery.of(context).size.width;

    // 随机屏幕高度
    final randomHeight =
        MediaQuery.of(context).size.height * Random().nextDouble();

    OverlayEntry? overlayEntry;
    overlayEntry = OverlayEntry(builder: (context) {
      return Positioned(
        // 指定位置
        left: 0,
        top: randomHeight,
        child: GestureDetector(
          // 点击删除
          onTap: () {
            overlayEntry?.remove();
            entriesList.remove(overlayEntry);
          },
          // 背景随机色
          child: Container(
            width: screenWidth,
            height: 100,
            color: bgColor,
            child: Center(
              // 提示文字
              child: Text(
                "这是一个 overlay ${Random().nextInt(100)} 层, 点击关闭",
                style: const TextStyle(
                  color: Colors.white,
                  fontSize: 20,
                  decoration: TextDecoration.none,
                ),
              ),
            ),
          ),
        ),
      );
    });
    overlayState?.insert(overlayEntry);
    entriesList.add(overlayEntry);
  }

第三步:控制按钮

  // 控制按钮
  Widget _buildBtns() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        // 随机新增
        ElevatedButton(
          onPressed: () => showRandomOverlay(context),
          child: const Text("随机新增"),
        ),

        // 关闭所有
        ElevatedButton(
          onPressed: () {
            for (final entry in entriesList) {
              entry.remove();
            }
            entriesList = [];
          },
          child: const Text("关闭所有"),
        ),

        // 随机排序
        ElevatedButton(
          onPressed: () {
            // 从屏幕上移除
            for (final entry in entriesList) {
              entry.remove();
            }

            // 使用Random类创建随机数生成器
            Random random = Random();

            // 使用List的sublist()方法创建一个新列表
            List<OverlayEntry> shuffledEntries = entriesList.sublist(0);

            // 调用List的shuffle()方法,传入一个随机数生成器
            shuffledEntries.shuffle(random);

            // 插入界面
            overlayState?.insertAll(shuffledEntries);
          },
          child: const Text("随机排序"),
        ),
      ],
    );
  }

最后:主视图显示

  // 主视图
  Widget _mainView() {
    return Padding(
      padding: const EdgeInsets.only(top: 100),
      child: Align(
        alignment: Alignment.topCenter,
        child: _buildBtns(),
      ),
    );
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _mainView(),
    );
  }

完整代码

lib/overlay_view.dart

import 'dart:math';

import 'package:flutter/material.dart';

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

  @override
  State<OverlayUsePage> createState() => _OverlayUsePageState();
}

class _OverlayUsePageState extends State<OverlayUsePage{
  /// overlay 状态
  OverlayState? overlayState;

  /// overlay 层集合
  List<OverlayEntry> entriesList = [];

  // 随机位置显示层
  void showRandomOverlay(BuildContext context) {
    // 随机颜色
    final bgColor = Color.fromARGB(
      255,
      1 + Random().nextInt(254),
      1 + Random().nextInt(254),
      1 + Random().nextInt(254),
    );

    // 屏幕宽度
    final screenWidth = MediaQuery.of(context).size.width;

    // 随机屏幕高度
    final randomHeight =
        MediaQuery.of(context).size.height * Random().nextDouble();

    OverlayEntry? overlayEntry;
    overlayEntry = OverlayEntry(builder: (context) {
      return Positioned(
        // 指定位置
        left: 0,
        top: randomHeight,
        child: GestureDetector(
          // 点击删除
          onTap: () {
            overlayEntry?.remove();
            entriesList.remove(overlayEntry);
          },
          // 背景随机色
          child: Container(
            width: screenWidth,
            height: 100,
            color: bgColor,
            child: Center(
              // 提示文字
              child: Text(
                "这是一个 overlay ${Random().nextInt(100)} 层, 点击关闭",
                style: const TextStyle(
                  color: Colors.white,
                  fontSize: 20,
                  decoration: TextDecoration.none,
                ),
              ),
            ),
          ),
        ),
      );
    });
    overlayState?.insert(overlayEntry);
    entriesList.add(overlayEntry);
  }

  // 控制按钮
  Widget _buildBtns() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        // 随机新增
        ElevatedButton(
          onPressed: () => showRandomOverlay(context),
          child: const Text("随机新增"),
        ),

        // 关闭所有
        ElevatedButton(
          onPressed: () {
            for (final entry in entriesList) {
              entry.remove();
            }
            entriesList = [];
          },
          child: const Text("关闭所有"),
        ),

        // 随机排序
        ElevatedButton(
          onPressed: () {
            // 从屏幕上移除
            for (final entry in entriesList) {
              entry.remove();
            }

            // 使用Random类创建随机数生成器
            Random random = Random();

            // 使用List的sublist()方法创建一个新列表
            List<OverlayEntry> shuffledEntries = entriesList.sublist(0);

            // 调用List的shuffle()方法,传入一个随机数生成器
            shuffledEntries.shuffle(random);

            // 插入界面
            overlayState?.insertAll(shuffledEntries);
          },
          child: const Text("随机排序"),
        ),
      ],
    );
  }

  // 主视图
  Widget _mainView() {
    return Padding(
      padding: const EdgeInsets.only(top: 100),
      child: Align(
        alignment: Alignment.topCenter,
        child: _buildBtns(),
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    // 获取 overlay 状态
    overlayState = Overlay.of(context);
  }

  @override
  void dispose() {
    // 销毁
    overlayState?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: _mainView(),
    );
  }
}

小结

本文提供了一个很好的介绍Flutter Overlay的概念和使用方法。无论是初学者还是有经验的开发人员,都可以从本文中获得新的知识和见解。如果你想在你的Flutter项目中实现工作覆盖层,本文是一个很好的起点。


© 猫哥 ducafecat.com

end

本文由 mdnice 多平台发布

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

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

相关文章

3dMax一键窗户可入库插件使用方法详解

3dMax一键窗户(可入库)插件使用教程 3dMax一键窗户(可入库)插件,支持在选中的多边形上创建窗户模型,并可以自定义窗户形状,保存到库里下次使用。 【安装方法】 1.复制“窗户样本”文件夹到D盘根目录(D:\窗户样本) 2.拖动插件脚本到3dmax视口中打开即可。 【创建窗户】…

SolidWorks装配体中让弹簧随装配体运动的方法

弹簧是我们日常设计中最常用的几种零部件之一&#xff0c;但是弹簧不跟螺栓一样装好之后是相对静止的&#xff0c;弹簧在装配好后需要进行运动&#xff0c;在SolidWorks装配体中可以让弹簧跟随其他物体运动&#xff0c;操作分为三大步&#xff1a; 一、创建弹簧&#xff08;使…

微服务: Seata AT 分布式事务配置出现异常解决(相当全面)(下篇)

目录 1. 文章传送门 -> 上篇传送门: 微服务: Seata AT 分布式事务以及配置方式(上篇) -> 中篇传送门: 微服务: Seata AT springCloud整合分布式事务以配置方式(中篇) 2. 异常总结分类: 3. 解决上述问题: -> 解决上述问题一: 1. no available service null f…

面了一个测试工程师要求月薪23K,总感觉他藏了很多面试题...

最近有朋友去华为面试&#xff0c;面试前后进行了20天左右&#xff0c;包含4轮电话面试、1轮笔试、1轮主管视频面试、1轮hr视频面试。 据他所说&#xff0c;80%的人都会栽在第一轮面试&#xff0c;要不是他面试前做足准备&#xff0c;估计都坚持不完后面几轮面试。 其实&…

七人拼团系统开发模式,如何做到短短几个月就销售额上亿?

随着经济的迅速发展&#xff0c;市场上的商业模式也是层出不穷&#xff0c;而且各具特色&#xff0c;看得人眼花缭乱。最近又新出了一个七人拼团商业模式&#xff0c;不仅能够助力企业快速裂变获客&#xff0c;还能迅速提升产品销量&#xff0c;达到短短几个月就销售额上亿的“…

MariaDB 主从同步配置

1 服务器结构 角色ip地址安装教程主节点192.168.31.102CentOS-7 安装 MariaDB-10.8从节点192.168.31.103 2 原理&#xff1a; 原理&#xff1a; &#xff08;1&#xff09;master服务器将数据的改变记录到二进制binlog日志&#xff0c;当master上的数据发生改变时&#xff0c…

【连续介质力学】张量场

张量场 张量场表示张量 T ( x ⃗ , t ) T(\vec x, t) T(x ,t)在空间 x ⃗ \vec x x 和时间 t t t中如何变化&#xff0c;将张量场视为可微函数 如果一个张量场不依赖于时间&#xff0c;则此张量场称为定常场&#xff0c;例如 T T ( x ⃗ ) T T(\vec x) TT(x )&#xff1b;相…

如此优秀的低代码平台,佬们一起来体验一把!

前言&#xff1a;低代码平台是一种新兴的应用开发技术&#xff0c;将可视化建模、自动生成代码和开发者编写的代码结合在一起&#xff0c;使应用程序的开发变得更加快速、简单且高效。低代码平台的基本思想是通过消除繁琐的手动编码工作&#xff0c;来让开发者更好地专注于业务…

基于 Spring Boot + MyBatis Plus + Vue Element 实现的后台管理系统 + 微信小程序

管理后台的 Vue3 版本采用 vue-element-plus-admin &#xff0c;Vue2 版本采用 vue-element-admin 管理后台的移动端采用 uni-app 方案&#xff0c;一份代码多终端适配&#xff0c;同时支持 APP、小程序、H5&#xff01; 后端采用 Spring Boot、MySQL MyBatis Plus、Redis …

UnityVR--组件3--Line Renderer--线性渲染

目录 前言 Line Renderer组件介绍 Trail Renderer组件介绍 使用Line Renderer绘制线段 使用系统工具或自定义工具绘制线段 Trail Renderer简单制作子弹拖尾效果 前言 Line Renderer线性渲染组件用于在3D中渲染线段&#xff0c;如之前在小游戏中做过的激光门伤害&#xff0…

Axure设计—动态条形图(中继器)

本文将教大家如何用AXURE中的中继器动态条形图。 一、效果介绍 如图&#xff1a; 预览地址&#xff1a;https://i7x7i9.axshare.com 下载地址&#xff1a;https://download.csdn.net/download/weixin_43516258/87807039?spm1001.2014.3001.5503 二、功能介绍 简单填写中继…

PyQt5桌面应用开发(17):类结构+QWebEngineView

本文目录 PyQt5桌面应用系列PyQt5学习PyQt5类结构和帮助速查实现与解释最终界面和完整源代码界面完整的代码 总结 PyQt5桌面应用系列 PyQt5桌面应用开发&#xff08;1&#xff09;&#xff1a;需求分析 PyQt5桌面应用开发&#xff08;2&#xff09;&#xff1a;事件循环 PyQt5桌…

Spring 如何处理请求参数和表单数据

当我们开发 Web 应用程序时&#xff0c;处理请求参数和表单数据是必不可少的。Spring MVC 是一个流行的 Java Web 框架&#xff0c;提供了多种方式来处理请求参数和表单数据。本文将介绍 Spring MVC 中处理请求参数和表单数据的常用方式&#xff0c;并提供相应的代码示例。 处…

Redis数据结构-SDS

一、SDS&#xff08;Simple Dynamic String&#xff0c;简单动态字符串&#xff09; Redis没有使用C语言传统的字符串表示方式&#xff08;以’\0’结尾的字符数组&#xff09;&#xff0c;而是自己实现了sds的抽象类型&#xff0c;Redis默认使用sds作为字符串的表示。 set ms…

新视野(2023.5.5-2023.5.12)

一、知识&#xff1a; 媒体账号买卖网站 A5新媒体交易&#xff1a;https://xmt.a5.net/ 新媒虎:https://www.xinmeihu.com/ 二、资讯&#xff1a; GPT最大的竞争对手Claude宣布支持100K的上下文&#xff01;基本上一本中篇小说都能塞进去了&#xff0c;你再不需要ChatPDF这种…

【框架源码】手写Spring框架IOC容器核心流程

要是想要了解Spring IOC底层&#xff0c;首先我们就得先了解什么是IOC。 IOC就是控制反转&#xff0c;把对象创建和对象之间的调用过程交给Spring进行管理。 使用IOC目的就是之前我们创建对象的方式都是用new的方式创建&#xff0c;这个方式有个缺点&#xff0c;被创建对象的…

【halcon资料】取出区域的轮廓上所有转折点

一、说明 在区域运算的时候&#xff0c;有时候需要用图形的顶点来描述&#xff0c;比如&#xff0c;两个图中对象需要对齐&#xff0c;或者仿射变换&#xff0c;于是特征点是需要提取的。本文给出一个提取顶点的示例。 二、算子 1.1 get_region_polygon算子 &#xff08;1&a…

2023互联网Java面试真题1000道(附答案)

前言 2023 跳槽不迷茫&#xff0c;大家可以先收藏再看&#xff0c;后续跳槽都能用上的&#xff01; Java程序员绝大部分工作的时间都是增删改查&#xff0c;很多人觉得这项工作没什么技术含量&#xff0c;任何一件事情都要站在不同的角度去考虑&#xff0c;对于大部分的java程序…

拼多多新阶段,透露出不寻常

一个企业的特质&#xff0c;往往由这个企业的领导人所决定。 企业文化本质上就是领导人文化&#xff0c;领导人的风格会决定这个企业当下的现状。一个成功的大企业&#xff0c;往往需要不同的领导人来接替完成其发展使命。 在创业期&#xff0c;企业领导人需要的是勇气、执行…

统计一个数的二进制中1的个数(三种方法)

那么好了好了&#xff0c;宝子们&#xff0c;今天给大家分享一篇经典例题的三种实现方法&#xff0c;来吧&#xff0c;开始整活&#xff01;⛳️ 一、基础法 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int number_of_one(int n) {int count 0;while(n){if…