flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel

news2024/9/20 16:41:37

flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel
在之前实现了flutter聊天界面的自定义表情的展示,这里记录一下更多操作展开的相机、相册等操作功能实现。

一、查看效果

更多操作展开的相机、相册等操作功能实现。

在这里插入图片描述

二、代码实现

展开的操作按钮可能比较多,一页显示8个、多个可以左右滑动,这里就用到的flutter_swiper插件
这里使用的swpier插件是

  # 轮播图
  flutter_swiper_null_safety: ^1.0.2

Swiper左右滑动的元素为GridView。
GridView网格布局是一种常见的布局类型,GridView 组件正是实现了网格布局的组件,
SliverGridDelegate是一个抽象类,定义了GridView Layout相关接口,子类需要通过实现它们来实现具体的布局算法。Flutter中提供了两个SliverGridDelegate的子类SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent,
每个GridView包括多个相机、相册等操作按钮

按钮如下

// 每个option的大小
class ChatMoreOptionButton extends StatelessWidget {
  const ChatMoreOptionButton({
    Key? key,
    required this.commMoreOption,
    required this.onMoreOptionPressed,
  }) : super(key: key);

  final CommMoreOption commMoreOption;
  final Function(CommMoreOption commMoreOption) onMoreOptionPressed;

  
  Widget build(BuildContext context) {
    EdgeInsets viewPadding = MediaQuery.of(context).viewPadding;
    Size screenSize = MediaQuery.of(context).size;

    double aWidth = (screenSize.width - (kOptionSperate * 5)) / 4;
    double aHeight = (kMorePanelHeight -
            (kOptionSperate * 3) -
            viewPadding.bottom -
            kSwiperPaginationHeight) /
        2;

    double aMin = min(aWidth, aHeight);

    double marginSpace = kOptionSperate / 2;
    return ButtonWidget(
      margin: EdgeInsets.symmetric(
        vertical: marginSpace,
        horizontal: marginSpace,
      ),
      width: aMin,
      height: aMin,
      borderRadius: 6.0,
      bgColor: ColorUtil.hexColor(0xffffff),
      bgHighlightedColor: ColorUtil.hexColor(0xf0f0f0),
      onPressed: () {
        onMoreOptionPressed(commMoreOption);
      },
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          buildButtonIcon(context),
          SizedBox(
            height: 5.0,
          ),
          Text(
            "${commMoreOption.name}",
            textAlign: TextAlign.left,
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: TextStyle(
              fontSize: 12,
              fontWeight: FontWeight.w500,
              fontStyle: FontStyle.normal,
              color: ColorUtil.hexColor(0x333333),
              decoration: TextDecoration.none,
            ),
          ),
        ],
      ),
    );
  }

  Widget buildButtonIcon(BuildContext context) {
    if (CommMoreOptionIconType.commMoreOptionIconFile ==
        commMoreOption.iconType) {
      // 本地图片
      String imageUrl = "${commMoreOption.icon ?? ""}";
      String start = "file://";
      if (imageUrl.startsWith(start)) {
        String imageAssetFile = imageUrl.substring(start.length);

        return ImageHelper.wrapAssetAtImages(
          "icons/${imageAssetFile}",
          fit: BoxFit.cover,
          width: 26.0,
          height: 26.0,
        );
      }
    } else if (CommMoreOptionIconType.commMoreOptionIconUrl ==
        commMoreOption.iconType) {
      // 网络图片
      String imageUrl = "${commMoreOption.icon ?? ""}";
      return ImageHelper.imageNetwork(
        imageUrl: imageUrl,
        fit: BoxFit.cover,
        width: 40.0,
        height: 40.0,
      );
    }

    return Container();
  }
}

GridView实现排列展示

// 一个swiper的容器
class ChatMoreOptionSwiperContainer extends StatefulWidget {
  const ChatMoreOptionSwiperContainer({
    Key? key,
    required this.moreOptions,
    required this.onMoreOptionPressed,
  }) : super(key: key);

  final List<CommMoreOption> moreOptions;
  final Function(CommMoreOption commMoreOption) onMoreOptionPressed;

  
  State<ChatMoreOptionSwiperContainer> createState() =>
      _ChatMoreOptionSwiperContainerState();
}

class _ChatMoreOptionSwiperContainerState
    extends State<ChatMoreOptionSwiperContainer> {
  
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 10.0),
      child: GridView.builder(
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: kGridCrossAxisCount, //每行三列
          childAspectRatio: 1.0, //显示区域宽高相等
        ),
        itemCount: widget.moreOptions.length,
        itemBuilder: (context, index) {
          CommMoreOption commMoreOption = widget.moreOptions[index];
          return ChatMoreOptionButton(
            onMoreOptionPressed: widget.onMoreOptionPressed,
            commMoreOption: commMoreOption,
          );
        },
      ),
    );
  }
}

最后使用Swiper实现左右滑动的效果,代码如下

// 中间间隔
const double kOptionSperate = 15.0;
const double kSwiperPaginationHeight = 10.0;

const int kGridCrossAxisCount = 4;
const int kGridCrossAxisRow = 2;

// 更多操作
class ChatMoreOptionPanel extends StatefulWidget {
  const ChatMoreOptionPanel({
    Key? key,
    required this.morePanelHeight,
    required this.chatInputBarController,
    required this.moreOptionEntries,
  }) : super(key: key);

  final double morePanelHeight;
  final ChatInputBarController chatInputBarController;
  final List<CommMoreOption> moreOptionEntries;

  
  State<ChatMoreOptionPanel> createState() => _ChatMoreOptionPanelState();
}

class _ChatMoreOptionPanelState extends State<ChatMoreOptionPanel> {
  List<CommMoreOption> allOptionList = [];

  List<List<CommMoreOption>> optionSwiperList = [];

  
  void initState() {
    // TODO: implement initState
    super.initState();

    CommMoreOption commMoreOption = CommMoreOption();
    commMoreOption.icon = "file://ic_toolbar_camera.png";
    commMoreOption.name = "相机";
    commMoreOption.iconType = CommMoreOptionIconType.commMoreOptionIconFile;
    commMoreOption.type = 0;
    commMoreOption.linkUrl = kOptionCamera;
    allOptionList.add(commMoreOption);

    CommMoreOption commMoreOption1 = CommMoreOption();
    commMoreOption1.icon = "file://ic_toolbar_ablum.png";
    commMoreOption1.name = "相册";
    commMoreOption1.iconType = CommMoreOptionIconType.commMoreOptionIconFile;
    commMoreOption1.type = 0;
    commMoreOption1.linkUrl = kOptionAlbum;
    allOptionList.add(commMoreOption1);

    CommMoreOption commMoreOption2 = CommMoreOption();
    commMoreOption2.icon = "file://ic_toolbar_coupon.png";
    commMoreOption2.name = "卡券";
    commMoreOption2.iconType = CommMoreOptionIconType.commMoreOptionIconFile;
    commMoreOption2.type = 0;
    commMoreOption2.linkUrl = kOptionCoupon;
    allOptionList.add(commMoreOption2);

    if (widget.moreOptionEntries.isNotEmpty) {
      allOptionList.addAll(widget.moreOptionEntries);
    }

    handlerSwiperList();
  }

  void handlerSwiperList() {
    List<List<CommMoreOption>> tmpOptionSwiperList = [];
    int aPageNum = kGridCrossAxisCount * kGridCrossAxisRow;
    int swiperCount = (allOptionList.length % aPageNum == 0
            ? allOptionList.length / aPageNum
            : (allOptionList.length / aPageNum + 1))
        .toInt();
    for (int i = 0; i < swiperCount; i++) {
      int location = 0;
      int length = 0;

      location = i * aPageNum;
      if (i == 0) {
        length =
            aPageNum > allOptionList.length ? allOptionList.length : aPageNum;
      } else {
        length = (1 + i) * aPageNum > allOptionList.length
            ? (allOptionList.length - i * aPageNum)
            : aPageNum;
      }

      List<CommMoreOption> swiperItems =
          allOptionList.sublist(location, (location + length));
      tmpOptionSwiperList.add(swiperItems);
    }
    optionSwiperList = tmpOptionSwiperList;
    setState(() {});
  }

  
  void dispose() {
    // TODO: implement dispose
    super.dispose();
  }

  // 点击不同的操作Option
  void onMoreOptionPressed(CommMoreOption commMoreOption) {
    // 点击不同的操作Option
    // 发送eventBus事件
    CommEventBusModel eventBusModel = CommEventBusModel(
      commEventBusType: CommEventBusType.commEventBusTypeMoreOption,
      data: commMoreOption,
    );
    kCommEventBus.fire(eventBusModel);
  }

  
  Widget build(BuildContext context) {
    Size screenSize = MediaQuery.of(context).size;
    return Container(
      width: screenSize.width,
      height: widget.morePanelHeight,
      decoration: BoxDecoration(
        color: ColorUtil.hexColor(0xf7f7f7),
      ),
      padding: EdgeInsets.only(
        left: 0.0,
        right: 0.0,
        bottom: 0.0,
        top: 0.0,
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Expanded(
            child: Swiper(
              // 横向
              scrollDirection: Axis.horizontal,
              // 布局构建
              itemBuilder: (BuildContext context, int index) {
                List<CommMoreOption> optionList = optionSwiperList[index];
                return ChatMoreOptionSwiperContainer(
                  moreOptions: optionList,
                  onMoreOptionPressed: (CommMoreOption commMoreOption) {
                    onMoreOptionPressed(commMoreOption);
                  },
                );
              },
              //条目个数
              itemCount: optionSwiperList.length,
              // 自动翻页
              autoplay: false,
              // 分页指示
              pagination: SwiperPagination(
                //指示器显示的位置 Alignment.bottomCenter 底部中间
                alignment: Alignment.bottomCenter,
                // 距离调整
                margin: const EdgeInsets.only(bottom: 0.0),
                // 指示器构建
                builder: DotSwiperPaginationBuilder(
                  // 点之间的间隔
                  space: 3,
                  // 没选中时的大小
                  size: 6,
                  // 选中时的大小
                  activeSize: 6,
                  // 没选中时的颜色
                  color: ColorUtil.hexColor(0xDCDCDC),
                  //选中时的颜色
                  activeColor: ColorUtil.hexColor(0xff462e),
                ),
              ),
              // pagination: _buildSwiperPagination(),
              // pagination: _buildNumSwiperPagination(),
              //点击事件
              onTap: (index) {
                print(" 点击 " + index.toString());
              },
              // 相邻子条目视窗比例
              viewportFraction: 1,
              // 用户进行操作时停止自动翻页
              autoplayDisableOnInteraction: true,
              // 无限轮播
              loop: false,
              //当前条目的缩放比例
              scale: 1,
            ),
          ),
          buildAreaBottom(context),
        ],
      ),
    );
  }

  Widget buildAreaBottom(BuildContext context) {
    EdgeInsets viewPadding = MediaQuery.of(context).viewPadding;
    Size screenSize = MediaQuery.of(context).size;
    return Container(
      decoration: BoxDecoration(
        color: ColorUtil.hexColor(0xf7f7f7),
      ),
      height: viewPadding.bottom,
      width: screenSize.width,
    );
  }
}

三、小结

flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel菜单,主要Swiper和GridView排列相机、相册等按钮,点击按钮时候发送事件,使用EventBus来处理后续的逻辑实现。

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

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

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

相关文章

前端(四)——vue.js、vue、vue2、vue3

&#x1f60a;博主&#xff1a;小猫娃来啦 &#x1f60a;文章核心&#xff1a;vue.js、vue、vue2、vue3从全局到局部 文章目录 vue.js、vue、vue2、vue3是什么关系?Vue.js简介发展历程特点与优势生态系统Vue.js基础知识安装与配置 基本语法Vue.js主要版本解析Vue.js 2.x vue2…

python 第十一章 文件操作

系列文章目录 第一章 初识python 第二章 变量 第三章 基础语句 第四章 字符串str 第五章 列表list [] 第六章 元组tuple ( ) 第七章 字典dict {} 第八章 集合set {} 第九章 常用操作 第十章 函数 文章目录 系列文章目录11.1文件操作的作用11.2文件的基本操作打开打开文件模式 …

string底层是如何实现的

前言 在我们学习的时候总是会用到string&#xff0c;知道它具备各种功能&#xff0c;它也是一种很强大的模板&#xff0c;那么有没有想过&#xff0c;我们天天都在使用的它&#xff0c;它的底层又是怎么样的&#xff0c;它又是如何实现的呢。这里讲挑选几个比较常用的一个功能…

SGD原理及Pytorch实现

&#x1f38f;目录 &#x1f388;1 SGD       &#x1f384;1.1 原理       &#x1f384;1.2 构造       &#x1f384;1.3 参数详解——momentum ✨1 SGD &#x1f95a;2.1 原理 SGD为随机梯度下降&#xff0c;原理可看刘建平老师博客。 &#x1f383;…

Blender--原理化体积

“原理化体积 着色器将所有体积着色组件组合到一个易于使用的节点中。该节点含有散射&#xff0c;吸收和黑体辐射属性&#xff0c;因此&#xff0c;可以仅仅使用该着色器节点对烟雾和火焰等进行渲染。” 官方文档介绍&#xff1a;原理化体积 — Blender Manual 可以用于实现丁…

Rainbond开源

Rainbond的 Gateway API 插件制作实践 Gateway API 作为新一代的流量管理标准&#xff0c;对原有 Ingress 的扩展不规范、移植性差等问题做出了改进。从兼容K8s生态和优化网关体验出发&#xff0c;Rainbond 支持以插件的形式扩展平台网关能力&#xff0c;目前已经有多家社区提供…

启动项目报错,如何分析pom

报错信息如下 Caused by: java.lang.NoClassDefFoundError: org/springframework/core/metrics/ApplicationStartup 报错说找不到ApplicationStartup这个类&#xff0c;到项目中查看pom的dependency 我的项目引入了两个依赖 <parent><groupId>org.springframewor…

vue使用富文本编辑器 Wangeditor 可显示编辑新增回显禁用

1.效果图 2.安装依赖 npm install wangeditor 3.在main.js 全局引入 富文本组件 import editorBar from "/components/editor/editor.vue";Vue.component(editorBar, editorBar) 全局引入页面使用 <editor-bar v-model"form.nr" :flag"false&quo…

【高并发网络通信架构】1.Linux下实现单客户连接的tcp服务端

目录 一&#xff0c;函数清单 1.socket 方法 2.bind 方法 3.listen 方法 4.accept 方法&#xff08;阻塞函数&#xff09; 5.recv 方法&#xff08;阻塞函数&#xff09; 6.send 方法 7.close 方法 8.htonl 方法 9.htons 方法 10.fcntl 方法 二&#xff0c;代码实现…

0126 线性表

目录 2.线性表 2.1线性表的定义和基本概念 2.1部分习题 2.2线性表的顺序表示 2.2部分习题 2.3线性表的链式表示 2.3部分习题 2.线性表 2.1线性表的定义和基本概念 2.1部分习题 1.线性表是具有n个&#xff08;&#xff09;的有限序列 A.数据表 B.字符 C.…

FPGA纯verilog实现UDP协议栈,sgmii接口SFP光口收发,提供工程源码和技术支持

目录 1、前言2、我这里已有的UDP方案3、该UDP协议栈性能4、详细设计方案SFPGMII AXIS接口模块AXIS FIFOUDP协议栈1G/2.5G Ethernet PCS/PMA or SGMII 5、vivado工程1-->B50610 工程6、上板调试验证并演示准备工作查看ARPUDP数据回环测试 7、福利&#xff1a;工程代码的获取 …

c++日程管理系统

一.需求分析 多功能日历&#xff08;要求有简单ui&#xff09; 要求&#xff1a; 1.使用c代码用visual stdio运行 2.用户登录注册 3.登录后给用户三大功能选择&#xff1a; &#xff08;1&#xff09;基本日历&#xff1a;显示日历,默认六月&#xff0c;每日有对应的日程 &…

开源防病毒引擎ClamAV

本文软件是应网友 Windows 的要求折腾的&#xff1b; 什么是 ClamAV &#xff1f; ClamAV 是一个开源 ( GPLv2 ) 反病毒工具包&#xff0c;专为邮件网关上的电子邮件扫描而设计。它提供了许多实用程序&#xff0c;包括灵活且可扩展的多线程守护程序、命令行扫描程序和用于自动数…

尚硅谷Docker实战教程-笔记09【高级篇,DockerFile解析】

尚硅谷大数据技术-教程-学习路线-笔记汇总表【课程资料下载】视频地址&#xff1a;尚硅谷Docker实战教程&#xff08;docker教程天花板&#xff09;_哔哩哔哩_bilibili 尚硅谷Docker实战教程-笔记01【基础篇&#xff0c;Docker理念简介、官网介绍、平台入门图解、平台架构图解】…

LSTM介绍

LSTM ft表示 遗忘门。LSTM对于每一次的输入,首先决定遗忘掉之前的哪些记忆。将ft和t-1时刻的细胞状态相乘,得出将遗忘哪些记忆。 记忆门 是用来控制是否将t时刻的数据并入细胞状态。tanh函数可以将此刻向量中的有效信息提取出来,为gt。sigmoid函数用于控制此刻多少记忆进入…

阿里云ACE认证和华为云HCIE认证对比分析

最近有不少小伙伴私信问我&#xff0c;阿里云、腾讯云、华为云这几大厂商的云认证&#xff0c;哪一个更好&#xff0c;今天有空来跟大家认真地分析一下。 01-从云计算市场占有率来看 作为国内云计算市场的探路人&#xff0c;阿里虽然一路走来有过很多坎坷&#xff0c;但也不负…

Linux系统优化策略

Linux系统优化策略 1.禁用不需要的服务 在Linux系统中禁用不需要的服务可以通过以下步骤进行&#xff1a; 使用以下命令查看当前正在运行的服务&#xff1a; systemctl list-unit-files --typeservice根据需要禁用特定的服务&#xff0c;使用以下命令&#xff1a; systemctl …

【LeetCode周赛】2022上半年题目精选集——双指针

文章目录 2271. 毯子覆盖的最多白色砖块数思路代码 2302. 统计得分小于 K 的子数组数目代码1——前缀和滑动窗口代码2——双指针 O ( 1 ) O(1) O(1)空间 &#xff08;代码1的优化&#xff09; https://leetcode.cn/circle/discuss/G0n5iY/ 2271. 毯子覆盖的最多白色砖块数 22…

Win10安全中心怎么关闭?Win10安全中心关闭方法

Win10安全中心怎么关闭&#xff1f;关闭Win10的安全中心可以帮助用户自定义系统的安全和防护设置&#xff0c;但有些用户不知道怎么操作才能关闭安全中心&#xff0c;首先用户需要打开Win10电脑的设置选项&#xff0c;接着打开安全中心&#xff0c;然后关掉安全中心的实时保护、…

STM32CubeMX使用示例---生成LED闪烁工程

这里记录一下通过STM32CubeMX配置STM32G030C8T6&#xff0c;生成LED闪烁的示例工程的过程&#xff0c;用来学习使用STM32CubeMX&#xff1b; 以STM32G030C8T6 PB4 脚做LED 输出闪烁为例&#xff0c;外部8MHZ 晶振,系统主频64MHZ,LED 灯1 秒钟闪烁一次做一个示例程序。 文章目录…