How to draw tail end for container in Flutter using Custom painter

150

Here is the path for your case:

    
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
      ..style = PaintingStyle.fill;

    Path path;
    double triangleHeight = 10.0;
    double triangleWidth = 10.0;
    double roundedBorder = 5.0;

    Path drawBubbleBody() {
      if (!isOwn) {
        path = Path()
          ..lineTo(0, size.height)
          ..lineTo(triangleWidth, size.height - triangleHeight)
          ..lineTo(size.width - roundedBorder, size.height - triangleHeight)
          ..quadraticBezierTo(size.width, size.height - triangleHeight,
              size.width, size.height - triangleHeight - roundedBorder)
          ..lineTo(size.width, roundedBorder)
          ..quadraticBezierTo(size.width, 0, size.width - roundedBorder, 0);
        path.close();
      }
      if (isOwn) {
        path = Path()
          ..moveTo(size.width - roundedBorder, 0)
          ..lineTo(roundedBorder, 0)
          ..quadraticBezierTo(0, 0, 0, roundedBorder)
          ..lineTo(0, size.height - triangleHeight - roundedBorder)
          ..quadraticBezierTo(0, size.height - triangleHeight, roundedBorder,
              size.height - triangleHeight)
          ..lineTo(size.width - triangleWidth, size.height - triangleHeight)
          ..lineTo(size.width, size.height)
          ..lineTo(size.width, roundedBorder)
          ..quadraticBezierTo(size.width, 0, size.width - roundedBorder, 0);
      }
      return path;
    }

    canvas.drawPath(drawBubbleBody(), paint);
  }

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

In case you want to reproduce:

    import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Material App',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Material App Bar'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              CustomPaint(
                painter: CustomChatBubble(isOwn: true, color: Colors.grey),
                child: Container(
                  width: 200,
                  height: 50,
                ),
              ),
              CustomPaint(
                painter: CustomChatBubble(isOwn: false, color: Colors.orange),
                child: Container(
                  width: 200,
                  height: 50,
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Result : enter image description here

Share:
150
Ahalya Raj
Author by

Ahalya Raj

I am a Mobile Application developer. Having little bit experience in React Native, React.js and Flutter

Updated on December 28, 2022

Comments

  • Ahalya Raj
    Ahalya Raj over 1 year

    I have a chat screen which have the chatItem box with tail end like this

    Need to have that tail end in receiver side and sender side as well as I am currently using custom painter to this. And couldn't able to figure this out.

    enter image description here

    `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(0, size.height)
          ..relativeMoveTo(0,5)
          ..quadraticBezierTo(10, size.height , 0, size.height - 5);
      }
      if (isOwn) {
        path = Path()
          ..moveTo(0, size.height)
          ..relativeMoveTo(0,5)
          ..quadraticBezierTo(10, size.height , 0, size.height - 5);
      }
      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;
    }
    }`
    

    This is the class I used to do isOwn is for toggle between sender and receiver

  • Ahalya Raj
    Ahalya Raj about 3 years
    No MediaQuery widget ancestor found.Getting error like this
  • Simon Sot
    Simon Sot about 3 years
    Of course. Path is relative and will adapt to your sizes, only thing you will have to take care is three variables inside - two triangle dimensions and one for rounded corner size.
  • Ahalya Raj
    Ahalya Raj about 3 years
    Great. Can you share me how you did it?
  • Simon Sot
    Simon Sot about 3 years
    To tell the truth with some experience you will make all path in mind, so did I.