flutter聊天界面-聊天气泡长按弹出复制、删除按钮菜单

flutter聊天界面-聊天气泡长按弹出复制、删除按钮菜单
在之前实现了flutter聊天界面的富文本展示内容,这里记录一下当长按聊天气泡的时候弹出复制、删除等菜单功能

一、查看效果

当长按聊天气泡的时候弹出复制、删除等菜单,可新增更多按钮

在这里插入图片描述

二、代码实现

实现箭头效果,这里实现自定义的CustomPainter。flutter提供一块2D画布Canvas,Canvas内部封装了一些基本绘制的API,开发者可以通过Canvas绘制各种自定义图形。在Flutter中,提供了一个CustomPaint 组件,它可以结合画笔CustomPainter来实现自定义图形绘制。

绘制箭头效果代码

class ChatBubbleMenuShape extends CustomPainter {final Color bgColor;final double arrowSize;ChatBubbleMenuShape(this.bgColor, this.arrowSize);void paint(Canvas canvas, Size size) {var paint = Paint()..color = bgColor;var path = Path();path.lineTo(-arrowSize, 0);path.lineTo(0, arrowSize);path.lineTo(arrowSize, 0);canvas.drawPath(path, paint);}bool shouldRepaint(CustomPainter oldDelegate) {return false;}
}

// 长按气泡菜单的容器,展示具体的菜单容器

// 长按气泡菜单的容器
class ChatBubbleMenuContainer extends StatefulWidget {const ChatBubbleMenuContainer({Key? key,required this.chatMessage,required this.bubbleOffset,required this.bubbleSize,required this.onBubbleMenuButtonPressed,}) : super(key: key);final CommonChatMessage chatMessage;final Offset bubbleOffset;final Size bubbleSize;final Function(int index) onBubbleMenuButtonPressed;State<ChatBubbleMenuContainer> createState() =>_ChatBubbleMenuContainerState();
}class _ChatBubbleMenuContainerState extends State<ChatBubbleMenuContainer> {Widget build(BuildContext context) {double itemWidth = 60;double itemHeight = 40;double menuWidth = itemWidth * 2;double menuHeight = itemHeight * 2;double dx =widget.bubbleOffset.dx + (widget.bubbleSize.width - menuWidth) / 2.0;double dy = widget.bubbleOffset.dy;print("widget.bubbleOffset:${widget.bubbleOffset}");LoggerManager().debug("chatBubbleFrame offset:${widget.bubbleOffset},""size:${widget.bubbleSize}");double arrowSize = 10.0;return Stack(children: [Positioned(left: dx - arrowSize / 2.0,top: dy - menuHeight / 2.0,child: buildMenu(context,Size(itemWidth, itemHeight),),),Positioned(left: dx + menuWidth / 2 + arrowSize / 2.0,top: dy - menuHeight / 2.0 + itemHeight + arrowSize - 2.0,child: CustomPaint(painter:ChatBubbleMenuShape(ColorUtil.hexColor(0x454545), arrowSize),),),],);}Widget buildMenu(BuildContext context, Size itemSize) {return Container(padding: const EdgeInsets.all(5.0),decoration: BoxDecoration(color: ColorUtil.hexColor(0x454545),borderRadius: const BorderRadius.only(topRight: Radius.circular(3),topLeft: Radius.circular(3),bottomLeft: Radius.circular(3),bottomRight: Radius.circular(3),),),child: Wrap(spacing: 8.0, // 主轴(水平)方向间距runSpacing: 4.0, // 纵轴(垂直)方向间距alignment: WrapAlignment.center, //沿主轴方向居中children: [ChatBubbleMenuButton(width: itemSize.width,height: itemSize.height,icon: "file://ic_post_unlike.png",name: "复制",onBubbleMenuButtonPressed: () {widget.onBubbleMenuButtonPressed(0);},),ChatBubbleMenuButton(width: itemSize.width,height: itemSize.height,icon: "file://ic_post_unlike.png",name: "删除",onBubbleMenuButtonPressed: () {widget.onBubbleMenuButtonPressed(1);},),],),);}
}// 显示气泡菜单
class ChatBubbleMenuButton extends StatelessWidget {const ChatBubbleMenuButton({Key? key,required this.icon,required this.name,required this.onBubbleMenuButtonPressed,required this.width,required this.height,}) : super(key: key);final String icon;final String name;final Function onBubbleMenuButtonPressed;final double width;final double height;Widget build(BuildContext context) {return ButtonWidget(width: width,height: height,borderRadius: 6.0,onPressed: () {onBubbleMenuButtonPressed();},child: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [buildButtonIcon(context),SizedBox(height: 2.0,),Text("${name}",textAlign: TextAlign.left,maxLines: 1,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 11,fontWeight: FontWeight.w500,fontStyle: FontStyle.normal,color: ColorUtil.hexColor(0xffffff),decoration: TextDecoration.none,),),],),);}Widget buildButtonIcon(BuildContext context) {// 本地图片String imageUrl = "${icon ?? ""}";String start = "file://";if (imageUrl.startsWith(start)) {String imageAssetFile = imageUrl.substring(start.length);return ImageHelper.wrapAssetAtImages("icons/${imageAssetFile}",width: 18.0,height: 18.0,);}// 网络图片return ImageHelper.imageNetwork(imageUrl: imageUrl,width: 18.0,height: 18.0,errorHolder: Container(),);}
}

我们需要在聊天气泡上使用Gesture实现长按获取到获取气泡的位置及大小

GestureDetector(onTap: () {if (widget.onBubbleTapPressed != null) {}},onDoubleTap: () {if (widget.onBubbleDoubleTapPressed != null) {}},onLongPressStart: (LongPressStartDetails details) {
// 获取到获取气泡的位置及大小},child: Container(),);

获取大小代码

if (bubbleKey.currentContext == null) return null;// 获取输入框的位置final renderObject =bubbleKey.currentContext!.findRenderObject() as RenderBox;if (renderObject == null) return null;// offset.dx , offset.dy 就是控件的左上角坐标Offset offset = renderObject.localToGlobal(Offset.zero);//获取sizeSize size = renderObject.size;

三、实现弹窗功能

showGeneralDialog:用于自定义提示框

// 气泡长按操作static void elemBubbleLongPress(BuildContext context, CommonChatMessage chatMessage,{Map<String, dynamic>? additionalArguments,required LongPressStartDetails details,ChatBubbleFrame? chatBubbleFrame}) {if (ChatBubbleFrame == null) {// 没有气泡大小的时候return;}Offset bubbleOffset = chatBubbleFrame!.offset;Size bubbleSize = chatBubbleFrame!.size;LoggerManager().debug("chatBubbleFrame offset:${chatBubbleFrame.offset},""size:${chatBubbleFrame.size}");// 气泡长按弹出菜单showGeneralDialog(context: context,barrierLabel: '',barrierColor: Colors.black.withOpacity(0.0),transitionDuration: const Duration(milliseconds: 200),barrierDismissible: true,pageBuilder: (BuildContext dialogContext, Animation animation,Animation secondaryAnimation) {return GestureDetector(child: ChatBubbleMenuContainer(chatMessage: chatMessage,bubbleOffset: bubbleOffset,bubbleSize: bubbleSize,onBubbleMenuButtonPressed: (int index) {Navigator.of(dialogContext).pop();},),onTapDown: (TapDownDetails details) {Navigator.of(dialogContext).pop();},);},transitionBuilder: (_, anim, __, child) {return FadeTransition(opacity: anim,child: child,);},);

四、小结

flutter聊天界面-聊天气泡长按弹出复制、删除按钮菜单,主要实现Canvas结合画笔CustomPainter绘制,根据GestureDetector获取位置,通过findRenderObject、localToGlobal获取当前气泡的大小及位置,最后使用showGeneralDialog弹出。

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode 222. 完全二叉树的节点个数

2023.7.3 用层序遍历遍历一遍二叉树&#xff0c;然后遍历的每个节点都进行一次计数&#xff0c;直接上代码&#xff1a; class Solution { public:int countNodes(TreeNode* root) {queue<TreeNode*> que;int ans 0;if(root nullptr) return ans;que.push(root);while…

风力发电系统的随机调度研究(matlab代码实现)

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

面向订单交付的ETO项目管理数字化解决方案︱高远科技PMO副总经理董方好

北京高远华信科技有限公司PMO副总经理董方好先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;面向订单交付的ETO项目管理数字化解决方案。大会将于8月12-13日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1a; 订单…

基于matlab开发和评估停车场场景中的视觉定位算法(附源码)

一、前言 本示例展示了如何使用虚幻引擎模拟环境中的合成图像数据开发视觉定位系统。 获取基本事实以评估定位算法在不同条件下的性能是一项具有挑战性的任务。与使用高精度惯性导航系统或差分GPS等更昂贵的方法相比&#xff0c;不同场景下的虚拟仿真是一种经济高效的方法来获…

电脑免费简单又好用的记事本app软件推荐

很多职场人士在办公时都需要用到电脑&#xff0c;在电脑上有很多好用的工具软件可以用来辅助工作的展开&#xff0c;其中记事本类的App就有不少优质软件存在。那电脑免费简单又好用的记事本app软件推荐哪些呢&#xff1f;这里小编就以自己的Windows10电脑为例&#xff0c;为大家…

MCU是否会从ADAS域控制器消失

摘要&#xff1a; ADAS架构及MCU功能概览、未来ADAS架构的两种方案、MCU是否从ADAS域消失的一点思考 ADAS的装机量和渗透率再提升&#xff0c;尤其L2及L2级&#xff1b;那么随着ADAS域控制器主控芯片的增强&#xff0c;未来&#xff0c;MCU是否还会存在&#xff1f; 转自佐思汽…

网络编程4——传输层TCP协议的三大安全机制:三次握手四次挥手+确认应答机制+超时重传机制

文章目录 前言一、TCP协议段与机制TCP协议的特点TCP报头结构TCP协议的机制与特性 二、TCP协议的 连接管理机制 TCP建立连接&#xff1a;三次握手 TCP断开连接&#xff1a;四次挥手 三、TCP协议的 确认应答机制 四、TCP协议的 超时重传机制 总结 前言 本人是一个刚刚上路的I…

基础算法-子矩阵的和

1 课堂笔记 2 代码 3.代码解析 #include<iostream> using namespace std; const int maxn1010; int a[maxn][maxn],s[maxn][maxn]; int n,m,q,x1,y1,x2,y2;int main(){scanf("%d%d%d",&n,&m,&q);//为数组赋值for(int i1;i<n;i){for(int j1;j&…

《黑马头条》 内容安全 自动审核 feign 延迟任务精准发布 kafka

04自媒体文章-自动审核 1)自媒体文章自动审核流程 1 自媒体端发布文章后&#xff0c;开始审核文章 2 审核的主要是审核文章的 内容&#xff08;文本内容和图片&#xff09; 3 借助 第三方提供的接口审核文本 4 借助第三方提供的接口审核图片&#xff0c;由于图片存储到minIO中&…

Hadoop/Hive/Spark小文件处理

什么是小文件&#xff1f; 小文件指的是文件size比HDFS的block size小很多的文件。Hadoop适合处理少量的大文件&#xff0c;而不是大量的小文件。 hadoop小文件常规的处理方式 1、小文件导致的问题 首先&#xff0c;在HDFS中&#xff0c;任何block&#xff0c;文件或者目录…

E2. Rudolf and Snowflakes (hard version) codeforces1846E2

Problem - E2 - Codeforces 题目大意&#xff1a;在无向图中&#xff0c;初始有一个点&#xff0c;然后将k个点连接到1号点上&#xff0c;之后每次操作分别将k歌点连接到之前新加的点上&#xff0c;这样的操作至少有1次&#xff0c;t次询问&#xff0c;每次询问给出一个数n&am…

Mockplus Cloud - June 2023crack

Mockplus Cloud - June 2023crack 添加便签以澄清情节提要上的任何设计概念。 新的流程图工具直接在情节提要上可视化任何设计流程和过程。 添加了在发布到Mockplus Cloud时删除RP页面的功能。 添加设计注释时包括图像和链接。 添加了一个新的提示&#xff0c;用于在断开互联网…