Flutter 颤振:如何使聊天信息中的电报式气泡

Flutter 颤振:如何使聊天信息中的电报式气泡,flutter,dart,flutter-layout,Flutter,Dart,Flutter Layout,电流输出: 预期产出: Align( alignment: Alignment.topRight, child: Container( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20), margin: EdgeInsets.only(right: 12, top: 8), decoration: BoxDecoration( color: Color(0xFF48699

电流输出

预期产出

Align(
  alignment: Alignment.topRight,
  child: Container(
    padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
    margin: EdgeInsets.only(right: 12, top: 8),
    decoration: BoxDecoration(
      color: Color(0xFF486993),
      borderRadius: BorderRadius.all(Radius.circular(20)),
    ),
    child: Text("This is my message"),
  ),
)


代码

Align(
  alignment: Alignment.topRight,
  child: Container(
    padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
    margin: EdgeInsets.only(right: 12, top: 8),
    decoration: BoxDecoration(
      color: Color(0xFF486993),
      borderRadius: BorderRadius.all(Radius.circular(20)),
    ),
    child: Text("This is my message"),
  ),
)
给你:

import 'package:flutter/material.dart';

void main() => runApp(
    MyApp()
);

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Padding(
        padding: EdgeInsets.all(7),
        child: Align(
          alignment: Alignment.centerRight,
          child: Stack(
            children: [
              Container(
                padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
                decoration: BoxDecoration(
                  color: Color(0xFF486993),
                  borderRadius: BorderRadius.all(Radius.circular(20)),
                ),
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    RichText(
                      text: TextSpan(
                        children: <TextSpan>[
                          TextSpan(
                            text: 'This is telegram message   ',
                            style: TextStyle(
                                color: Colors.white,
                                fontSize: 14.0
                            ),
                          ),
                          TextSpan(
                            text: '3:16 PM',
                            style: TextStyle(
                                color: Colors.grey,
                                fontSize: 12.0,
                                fontStyle: FontStyle.italic
                            ),
                          ),
                        ],
                      ),
                    ),
                    Icon(Icons.check, color: Color(0xFF7ABAF4), size: 16,)
                  ]
                ),
              ),
              Positioned(
                bottom: 0,
                right: 0,
                child: CustomPaint(
                  painter: ChatBubbleTriangle(),
                )
              )
            ]
          )
        ),
      ),
    );
  }
}

class ChatBubbleTriangle extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var paint = Paint()..color = Color(0xFF486993);

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

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

导入“包装:颤振/材料.省道”;
void main()=>runApp(
MyApp()
);
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
主页:填充(
填充:边缘设置。全部(7),
子对象:对齐(
对齐:alignment.centerRight,
子:堆栈(
儿童:[
容器(
填充:常量边集。对称(垂直:8,水平:20),
装饰:盒子装饰(
颜色:颜色(0xFF486993),
borderRadius:borderRadius.all(半径圆形(20)),
),
孩子:排(
mainAxisSize:mainAxisSize.min,
儿童:[
RichText(
text:TextSpan(
儿童:[
TextSpan(
文字:“这是电报信息”,
样式:TextStyle(
颜色:颜色,白色,
字体大小:14.0
),
),
TextSpan(
文本:“下午3:16”,
样式:TextStyle(
颜色:颜色。灰色,
字体大小:12.0,
fontStyle:fontStyle.italic
),
),
],
),
),
图标(Icons.check,颜色:颜色(0xFF7ABAF4),大小:16,)
]
),
),
定位(
底部:0,
右:0,,
孩子:定制油漆(
画师:ChatBubbletTriangle(),
)
)
]
)
),
),
);
}
}
类ChatBubbletTriangle扩展了CustomPainter{
@凌驾
空心油漆(帆布,尺寸){
var paint=paint()…color=color(0xFF486993);
var path=path();
path.lineTo(-15,0);
行至(0,-15)的路径;
lineTo(0,0);
画布.绘制路径(路径,绘制);
}
@凌驾
bool应重新绘制(自定义代理){
返回true;
}
}
您必须更改
paint
方法,因为现在它只是一个三角形


否则,它看起来就像你要求的那样。

我自己试图重新创建电报布局,我想出了这个来制作形状

enum _TailDirection { right, left }

class ChatBubblePainter extends CustomPainter {
  ChatBubblePainter({this.color, this.isSentByMe});

  final Color color;
  final bool isSentByMe;

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = color;

    Path paintBubbleTail(_TailDirection direction) {
      double startingPoint;
      double point;
      double endPoint;
      double curvePoint;
      if (direction == _TailDirection.right) {
        startingPoint = size.width - 5;
        point = size.width + 10;
        endPoint = size.width + 3;
        curvePoint = size.width;
      }
      if (direction == _TailDirection.left) {
        startingPoint = 5;
        point = -10;
        endPoint = -3;
        curvePoint = 0;
      }
      return Path()
        ..moveTo(startingPoint, size.height)
        ..lineTo(point, size.height)
        ..quadraticBezierTo(endPoint, size.height, curvePoint, size.height - 10);
    }

    final RRect bubbleBody = RRect.fromRectAndRadius(
        Rect.fromLTWH(0, 0, size.width, size.height), Radius.circular(5.0));
    final Path bubbleTail = isSentByMe
        ? paintBubbleTail(_TailDirection.right)
        : paintBubbleTail(_TailDirection.left);

    canvas.drawRRect(bubbleBody, paint);
    canvas.drawPath(bubbleTail, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }
}

下面是在flatter中创建整洁的语音泡泡所需要的

首先创建一个扩展painter类的自定义painter类

class CustomChatBubble extends CustomPainter {
  CustomChatBubble({this.color, @required this.isOwn});

  final Color color;
  final bool isOwn;

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = color ?? Colors.blue;

    Path paintBubbleTail() {
      Path path;
      if (!isOwn) {
        path = Path()
          ..moveTo(5, size.height - 5)
          ..quadraticBezierTo(-5, size.height, -16, size.height - 4)
          ..quadraticBezierTo(-5, size.height - 5, 0, size.height - 17);
      }
      if (isOwn) {
        path = Path()
          ..moveTo(size.width - 6, size.height - 4)
          ..quadraticBezierTo(
              size.width + 5, size.height, size.width + 16, size.height - 4)
          ..quadraticBezierTo(
              size.width + 5, size.height - 5, size.width, size.height - 17);
      }
      return path;
    }

    final RRect bubbleBody = RRect.fromRectAndRadius(
        Rect.fromLTWH(0, 0, size.width, size.height), Radius.circular(16));
    final Path bubbleTail = paintBubbleTail();

    canvas.drawRRect(bubbleBody, paint);
    canvas.drawPath(bubbleTail, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }
}
然后在聊天屏幕中使用它。请注意,此屏幕仅用于演示目的,因此您可以根据需要对其进行修改

class ChatSampleWidget extends StatefulWidget {
  @override
  _ChatSampleWidgetState createState() => _ChatSampleWidgetState();
}

class _ChatSampleWidgetState extends State<ChatSampleWidget> {
  TextEditingController _editingController = TextEditingController();
  FocusNode _focusNode = FocusNode();
  final TextStyle textStyle = TextStyle(color: Colors.white);
  @override
  void initState() {
    super.initState();
  }

  @override
  dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Chatter'),
      ),
      body: SafeArea(
        child: Stack(
          children: <Widget>[
            Container(
              padding: EdgeInsets.all(20),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.start,
               
                children: <Widget>[
                  Row(
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: <Widget>[
                      CustomPaint(
                          painter: CustomChatBubble(isOwn: false),
                          child: Container(
                              padding: EdgeInsets.all(8),
                              child: Text(
                                'Message from someone else \n Says sometihngs',
                                style: textStyle,
                              ))),
                    ],
                  ),
                  SizedBox(
                    height: 5,
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: <Widget>[
                      CustomPaint(
                          painter:
                              CustomChatBubble(color: Colors.grey, isOwn: false),
                          child: Container(
                              padding: EdgeInsets.all(10),
                              child: FlutterLogo())),
                    ],
                  ),
                  SizedBox(
                    height: 5,
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.end,
                    children: <Widget>[
                      CustomPaint(
                          painter:
                              CustomChatBubble(color: Colors.green, isOwn: true),
                          child: Container(
                              padding: EdgeInsets.all(8),
                              child: Text(
                                'Message from me',
                                style: textStyle,
                              ))),
                    ],
                  )
                ],
              ),
            ),
            Positioned(
                bottom: 0,
                child: Container(
                  padding: EdgeInsets.all(8),
                  width: MediaQuery.of(context).size.width,
                  color: Colors.grey.withOpacity(0.1),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                    children: <Widget>[
                      Flexible(
                        child: TextField(
                          controller: _editingController,
                          focusNode: _focusNode,
                          decoration:
                              InputDecoration(hintText: 'Say something...'),
                        ),
                      ),
                      IconButton(
                          icon: Icon(
                            Icons.send,
                            size: 30,
                          ),
                          onPressed: () {
                            print(_editingController.text);
                          })
                    ],
                  ),
                ))
          ],
        ),
      ),
    );
  }
}
class ChatSampleWidget扩展了StatefulWidget{
@凌驾
_ChatSampleWidgetState createState()=>\u ChatSampleWidgetState();
}
类ChatSampleWidgetState扩展状态{
TextEditingController _editingController=TextEditingController();
FocusNode_FocusNode=FocusNode();
最终文本样式文本样式=文本样式(颜色:Colors.white);
@凌驾
void initState(){
super.initState();
}
@凌驾
处置{
super.dispose();
}
@凌驾
小部件构建(构建上下文){
返回脚手架(
appBar:appBar(
标题:文本(“聊天”),
),
正文:安全区(
子:堆栈(
儿童:[
容器(
填充:边缘设置。全部(20),
子:列(
mainAxisAlignment:mainAxisAlignment.start,
儿童:[
划船(
mainAxisAlignment:mainAxisAlignment.start,
儿童:[
定制油漆(
油漆工:定制泡沫(isOwn:false),
子:容器(
填充:边缘设置。全部(8),
子:文本(
'来自其他人的消息\n说了些什么',
风格:textStyle,
))),
],
),
大小盒子(
身高:5,,
),
划船(
mainAxisAlignment:mainAxisAlignment.start,
儿童:[
定制油漆(
画家:
CustomChatBubble(颜色:Colors.grey,isOwn:false),
子:容器(
填充:边缘设置。全部(10),
child:flatterlogo()),
],
),
大小盒子(
身高:5,,
),
划船(
mainAxisAlignment:mainAxisAlignment.end,
儿童:[
定制油漆(
画家:
CustomChatBubble(颜色:Colors.green,isOwn:true),
子:容器(
填充:边缘设置。全部(8),
子:文本(
“我的消息”,
风格:textStyle,
))),
],
)
],
),
),
定位(
底部:0,
子:容器(
填充:边缘设置。全部(8),
宽度:MediaQuery.of(context).size.width,