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

这篇具有很好参考价值的文章主要介绍了flutter聊天界面-聊天气泡长按弹出复制、删除按钮菜单。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

一、查看效果

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

flutter聊天界面-聊天气泡长按弹出复制、删除按钮菜单,flutter开发实战,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);
    //获取size
    Size 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弹出。

学习记录,每天不停进步。文章来源地址https://www.toymoban.com/news/detail-541785.html

到了这里,关于flutter聊天界面-聊天气泡长按弹出复制、删除按钮菜单的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Flutter GetX 实现 ChatGPT 简单聊天界面

    Flutter 是一款跨平台的移动应用开发框架,而 GetX 是 Flutter 中一种简单易用的状态管理和路由管理工具。本篇我们将使用 Flutter 和 GetX 实现一个简单的聊天界面,以与 ChatGPT 进行交互。 我们需要在 Flutter 项目中引入 GetX 库。在 pubspec.yaml 文件中添加以下依赖: 在 main 函数中添

    2023年04月17日
    浏览(67)
  • flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel

    flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel 在之前实现了flutter聊天界面的自定义表情的展示,这里记录一下更多操作展开的相机、相册等操作功能实现。 更多操作展开的相机、相册等操作功能实现。 展开的操作按钮可能比较多,一页显示8个、多个可以左右滑

    2024年02月13日
    浏览(33)
  • flutter聊天界面-TextField输入框buildTextSpan实现@功能展示高亮功能

    flutter聊天界面-TextField输入框buildTextSpan实现@功能展示高亮功能 最近有位朋友讨论的时候,提到了输入框的高亮展示。在flutter TextField中需要插入特殊样式的标签,比如:“请 @张三 回答一下”,这一串字符在TextField中输入,当输入@时 弹出好友列表选择,然后将 “@张三”高

    2024年02月07日
    浏览(42)
  • flutter聊天界面-Text富文本表情emoji、url、号码展示

    flutter聊天界面-Text富文本表情emoji、url、号码展示 Text富文本表情emoji展示,主要通过实现Text.rich展示文本、emoji、自定义表情、URL等 Text用于显示简单样式文本 TextSpan它代表文本的一个“片段”,不同“片段”可按照不同的样式显示。 示例片段 Text富文本表情emoji展示主要通过

    2024年02月12日
    浏览(29)
  • 微信小程序实现气泡弹出框

    这个组件可以实现 引用别人的组件,上面github可以很好的实现气泡弹窗效果 show:function(){ //如果show值为true,则更改为false 反之设置true if(this.data.show){ this.setData({ show:false }) }else{ this.setData({ show:true }) } }, he(){ console.log(“sadasdd”) this.setData({ show:!this.show }) wx.navigateTo({ url: ‘/p

    2024年02月09日
    浏览(36)
  • Android 使用.9图 NinePatchDrawable实现动态聊天气泡

    最近一段时间,在做一个需求,需要实现一个聊天气泡的动画效果,如下图所示: GitHub源码demo ,建议下载demo,运行查看。 动态聊天气泡动画 静态聊天气泡 经过一段时间调研,实现方案如下: 实现方案 从服务端下载zip文件,文件中包含配置文件和多张png图片,配置文件定义

    2024年04月23日
    浏览(23)
  • 小程序的点击复制功能和长按复制功能

    前言:         在小程序中实现点击复制功能和长按复制功能,主要使用wx.setClipboardData 小程序的复制功能。 wx.setClipboardData 小程序的复制功能 功能描述 设置系统剪贴板的内容。调用成功后,会弹出 toast 提示\\\"内容已复制\\\",持续 1.5s 参数 Object object 属性 类型 默认值 必填 说明

    2024年02月11日
    浏览(60)
  • 微信小程序实现文字长按复制、一键复制功能

    长按复制 可利用 bindlongtap 方法,手指长按 500ms 之后触发事件。 一键复制 可利用 bindtap 方法,点击立即触发事件。 注:样式可自行添加适合的样式 select-text 可选文本组件。该组件有两种使用模式:长按出现选区,与浏览器默认效果一致;长按出现复制按钮,点击复制拷贝全

    2024年02月02日
    浏览(39)
  • android 12.0长按Power弹出关机对话框去掉屏幕截图和紧急呼救功能

    在12.0的系统长按关机键,会弹出关机的对话框,关机对话框里面由关机重启截图和紧急呼叫等功能,而由于开发功能需求要求去掉屏幕截图和紧急呼叫等功能,所以就要先找到关机对框的代码 然后实现功能 功能分析: 长按电源键弹出关机对话框,通过adb shell命令发现 就是

    2024年02月06日
    浏览(40)
  • uniapp长按事件,长按删除

    longtap template部分 script部分 k

    2024年02月13日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包