flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel
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 _ChatMoreOptionSwiperContainerStateextends 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 initStatesuper.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 disposesuper.dispose();}// 点击不同的操作Optionvoid 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来处理后续的逻辑实现。
学习记录,每天不停进步。
相关文章:
flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel
flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel 在之前实现了flutter聊天界面的自定义表情的展示,这里记录一下更多操作展开的相机、相册等操作功能实现。 一、查看效果 更多操作展开的相机、相册等操作功能实现。 二、代码实现 展开的操作按钮可能比…...
浅析泵站自动化技术的发展趋势
摘要:基于泵站对我国水利及水务事业的重要性,文章以城市供水行业大型泵站为对象,分析了泵站自动化技术 发展现状,结合泵站自动化技术的发展需求,从管控一体化、系统自诊断、运行信息实时化管理等方面展望了泵 站自动化…...
5.4.1 虚拟专用网VPN
5.4.1 虚拟专用网VPN 我们已经学习了因特网的路由协议(5.3.1 因特网的路由协议(一)、5.3.2 因特网的路由协议(二)基于距离向量算法的RIP协议、5.3.3 因特网的路由协议(三)OSPF协议、5.3.4 因特…...
第42节:cesium 火焰效果(含源码+视频)
结果示例: 完整源码: <template><div class="viewer"><!-- :shouldAnimate="true" 添加动画 --><vc-viewer @ready...
MySQL基础篇第5章(排序与分页)
文章目录 1、排序1.1 排序规则1.2 单列排序1.3 多列排序 2、分页2.1 背景2.2 实现规则2.3 拓展 1、排序 1.1 排序规则 使用 ORDER BY 子句排序 ASC(ascend): 升序DESC(descend):降序 ORDER BY 子句在SELECT语句的结尾。 1.2 …...
LeetCode解法汇总2679. 矩阵中的和
目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:力扣 描述: 给你一个下标从 0 开始的二维整数数组 nums 。一开始你的分数为 0 。你需要执行…...
flask-apscheduler实现定时任务
秋风阁-北溪入江流 flask-apscheduler是一个支持apscheduler的flask插件,通过flask-apscheduler可以很方便的定义定时任务。 安装flask-apscheduler pip install flask-apschedulerflask-apscheduler组件 apscheduler库包含有组件: triggers…...
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工程详解6、上板调试验证并演示准备工作查看ARPUDP数据回环测试 7、福利:工程代码的获取 1、前言 目前网…...
【Python入门系列】第十五篇:Python数据可视化和图表绘制
文章目录 前言一、可视化与绘图常用库二、Matplotlib1、折线图2、散点图3、柱状图: 三、Seaborn1、散点图2、箱线图3、小提琴图4、热力图 四、Plotly1、折线图2、散点图3、条形图 总结 前言 数据可视化是数据分析和数据科学中非常重要的一部分。通过可视化…...
isBlank函数和isEmpty函数的区别
StrUtil.isBlank(CharSequence):该方法用于判断字符串是否为空或仅包含空白字符。如果传入的字符串为null、空字符串(“”)或仅包含空白字符(如空格、制表符、换行符等),则返回true;否则返回false。 StrUtil.isEmpty(…...
「SQL面试题库」 No_121 The Most Recent Three Orders
🍅 1、专栏介绍 「SQL面试题库」是由 不是西红柿 发起,全员免费参与的SQL学习活动。我每天发布1道SQL面试真题,从简单到困难,涵盖所有SQL知识点,我敢保证只要做完这100道题,不仅能轻松搞定面试࿰…...
【计算机视觉 | 目标检测 | 图像分割】arxiv 计算机视觉关于目标检测和图像分割的学术速递(7 月 7 日论文合集)
文章目录 一、检测相关(5篇)1.1 Contextual Affinity Distillation for Image Anomaly Detection1.2 Noise-to-Norm Reconstruction for Industrial Anomaly Detection and Localization1.3 MMNet: Multi-Collaboration and Multi-Supervision Network for Sequential Deepfake…...
直流运算放大器-----仪表放大器(三)
详见 模拟电子技术基础 6.4.1 电路图 计算公式 仿真 电流流向...
【Zookeeper】终端操作常用命令
文章目录 服务端常用命令客户端常用命令 zookeeper版本3.7.1 Zookeeper是一个开源的分布式协调服务。 Zookeeper是Apache软件基金会的一个项目,它提供了分布式应用程序协调的通用服务,如分布式同步、命名服务、集群维护等,以简化分布式应用协…...
leetcode 1110. 删点成林
给出二叉树的根节点 root,树上每个节点都有一个不同的值。 如果节点值在 to_delete 中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。 返回森林中的每棵树。你可以按任意顺序组织答案…...
华为Harmony应用开发初探
HarmonyOS是一款面向万物互联时代的、全新的分布式操作系统。在传统的单设备系统能力基础上,HarmonyOS提出了基于同一套系统能力、适配多种终端形态的分布式理念,能够支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备,提供全场景(移动办公、运动健康、社交通信、媒体…...
电脑应用程序发生异常怎么办?
有时候我们打开电脑上面的某个软件时,会打不开,并且会弹出如下的错误提示“应用程序发生异常 未知的软件异常(xxx),位置为xx”。相信大多数的人在使用电脑的时…...
【JAVA】准备工作------Java开发环境搭建,IDEA的基础设置与操作
🍉内容专栏:【JAVA】 🍉本文脉络:JAVA的准备工作,IDEA的安装以及基础设置和快捷操作 🍉本文作者:Melon西西 🍉发布时间 :2023.7.11 目录 1. 概述JDK、JRE 2. 第一个程序——创建…...
操作系统真象还原——第5章 保护模式进阶,向内核迈进
第5章 保护模式进阶,向内核迈进 BIOS中断利用0x15子功能0xe802获取内存 汇编语言子功能的调用 填写调用前相关寄存器进行int中断调用获取返回结果输出到对应寄存器的值 80286 拥有24 位地址线,其寻址空间是16MB 。有一些ISA 只使用15MB,剩下…...
设计一款助听器可能需要用到以下音频算法
设计一款助听器可能需要用到以下音频算法: 1 响度补偿算法:助听器可能需要根据用户的听力损失情况调整不同频率范围内的增益,以提供个性化的听力补偿。这可以通过基于用户配置或自适应算法的频率响应调整来实现。 2 噪声抑制:用于…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
