如何在Flutter中实现聊天气泡形状的小部件

5
我想设计一个聊天气泡形状的小部件,其中一个角被固定,其高度应根据文本行数调整。目前我正在使用带有一些borderRadius的ClipRRect小部件。但是我想要一个角固定。有什么建议吗? 聊天气泡形状 更新 我知道可以使用stack来完成这个任务,但我正在寻找更好的解决方案,因为我需要在单个视图中多次使用它,并且使用许多stack可能会影响性能。(如果我错了,请纠正我)

关于更新。没什么大不了的。你可以将其最小化。你不需要每次都绘制三角形。只需渲染一次三角形并将其保存在变量中即可。即使你使用9patch图像,恐怕你仍需要采用与纯背景图像相同的方法,因为在这种情况下,纯背景图像也无法帮助你。 - shb
请检查这个,我使用了https://pub.dev/packages/flutter_superchat。 - nikhil
我为实现聊天气泡制作了一个自定义解决方案。请查看此链接:https://arkapp.medium.com/chat-bubble-widget-for-flutter-95d3bb82ddd8 - abdul rehman
4个回答

5

如果您想使用库完成此操作,您可以从 pub.dev 添加 bubble: ^1.1.9+1 (选择最新版本)包,并在 Bubble 中包装您的消息。

Bubble(
style: right ? styleMe : styleSomebody,
//Your message content child here...
)

这里right是一个布尔值,用于告诉气泡位于右侧还是左侧,请编写您的逻辑代码,并在您的小部件内添加styleMestyleSomebody样式属性,如下所示。根据您的主题更改样式。

double pixelRatio = MediaQuery.of(context).devicePixelRatio;
double px = 1 / pixelRatio;

 BubbleStyle styleSomebody = BubbleStyle(
      nip: BubbleNip.leftTop,
      color: Colors.white,
      elevation: 1 * px,
      margin: BubbleEdges.only(top: 8.0, right: 50.0),
      alignment: Alignment.topLeft,
    );

    BubbleStyle styleMe = BubbleStyle(
      nip: BubbleNip.rightTop,
      color: Colors.grey,
      elevation: 1 * px,
      margin: BubbleEdges.only(top: 8.0, left: 50.0),
      alignment: Alignment.topRight,
    );

查看此帖子以获取简单的实现方法 - https://arkapp.medium.com/chat-bubble-widget-for-flutter-95d3bb82ddd8 - abdul rehman

4

大家好,我也在寻找一个聊天气泡形状的小部件,最终我自己制作了一个。 我是通过自定义Painter实现的,但我并不是很擅长。 在此输入图片描述

import 'package:flutter/material.dart';

class ChatBubble extends CustomPainter {
  final Color color;
  final Alignment alignment;

  ChatBubble({
    @required this.color,
    this.alignment,
  });

  var _radius = 10.0;
  var _x = 10.0;




  @override
  void paint(Canvas canvas, Size size) {
    if (alignment == Alignment.topRight) {
      canvas.drawRRect(
          RRect.fromLTRBAndCorners(
            0,
            0,
            size.width - 8,
            size.height,
            bottomLeft: Radius.circular(_radius),
            topRight: Radius.circular(_radius),
            topLeft: Radius.circular(_radius),
          ),
          Paint()
            ..color = this.color
            ..style = PaintingStyle.fill);
      var path = new Path();
      path.moveTo(size.width - _x, size.height - 20);
      path.lineTo(size.width - _x, size.height);
      path.lineTo(size.width, size.height);
      canvas.clipPath(path);
      canvas.drawRRect(
          RRect.fromLTRBAndCorners(
            size.width - _x,
            0.0,
            size.width,
            size.height,
            topRight: Radius.circular(_radius),
          ),
          Paint()
            ..color = this.color
            ..style = PaintingStyle.fill);
    } else {
      canvas.drawRRect(
          RRect.fromLTRBAndCorners(
            _x,
            0,
            size.width,
            size.height,
            bottomRight: Radius.circular(_radius),
            topRight: Radius.circular(_radius),
            topLeft: Radius.circular(_radius),
          ),
          Paint()
            ..color = this.color
            ..style = PaintingStyle.fill);
      var path = new Path();
      path.moveTo(0, size.height);
      path.lineTo(_x, size.height);
      path.lineTo(_x, size.height-20);
      canvas.clipPath(path);
      canvas.drawRRect(
          RRect.fromLTRBAndCorners(
            0,
            0.0,
            _x,
            size.height,
            topRight: Radius.circular(_radius),
          ),
          Paint()
            ..color = this.color
            ..style = PaintingStyle.fill);
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

复制并粘贴上面的代码到你的项目中。

Align(
      alignment: alignment, //Change this to Alignment.topRight or Alignment.topLeft
      child: CustomPaint(
        painter: ChatBubble(color: Colors.blue, alignment: alignment),
        child: Container(
          margin: EdgeInsets.all(10),
          child: Stack(
            children: <Widget>[
              TextView("Hello World"),
            ],
          ),
        ),
      ),
    )

将此代码粘贴到需要显示ChatBubble Widget的位置。 我还在Bitbucket上传了这段代码 ChatBubble Widget。如果您有任何贡献,请随意。


2
抱歉,我无法向您展示它的代码,但我可以提出一个想法,如果您正确实现它可能会有用。假设您使用ClipRect创建的小部件称为MyChatBubbleRect。现在,使用CustomPainter绘制一个三角形,让我们称之为MyChatBubbleTriangle,当然要用与聊天气泡相同的颜色进行填充,但是您可以最初使用不同的颜色进行调试。现在我们有了两个小部件,我们可以将它们堆叠在一起,并使用Positioned小部件覆盖在MyChatBubbleTriangle上方。像这样:
Stack(
  children : [
     MyChatBubbleRect(), // Maybe decrease the width a bit
     Positioned(
        top: 0,
        right: 0,
        child: MyChatBubbleTriangle()
     )
  ]
)

我认为你可以追求这个想法。很抱歉我没有提供正确的源代码。

最初的回答:

我认为您可以尝试这个想法。很抱歉,我无法提供正确的源代码。


谢谢你的回答。我也有这个想法,但感觉不是正确的方法。我认为可以使用自定义边框之类的东西来实现。让我们看看其他人是否有不同的答案。 - Praneeth Dhanushka Fernando
是的,你应该能够使用CustomPainter硬编码并使其工作。 - Shababb Karim

2

Chat Body

DecoratedBox(
    decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(8.0),
   ),
   child: Text("your message goes here"),
);

制作自定义三角形
class ChatBubbleTriangle extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()..color = Colors.blue;

    var path = Path();
    path.lineTo(-10, 0);
    path.lineTo(0, 10);
    path.lineTo(10, 0);
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

在一个堆栈中使用它们,将ChatBubbleTrianglePositioned()小部件一起包装。最初的回答。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接