flutterdart

Responsive Design in Custom Painter and Stack in Flutter


I have been trying to create something like this in Flutter,

enter image description here

I tried using CustomPaint for it because of the bottom right sharp thing. and I use stack to set texts on it.

The problem is that this is not responsive; on some devices, texts are not where they should be. how can I fix this?

The widget

Stack(
    alignment: widget.alignment,
    children: [
      CustomPaint(
        size: Size(context.width, ((context.width) * 0.58).toDouble()),
        painter: widget.painterWidget ,
      ),
    Padding(
          padding: EdgeInsets.only(
            right: 8.0,
            left: 8.0,
          ),
          child: SizedBox(
            width: context.width,
            child: Column(
....

The CustomPaint

class NotificationCustomPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// Layer 1

Paint paint_fill_0 = Paint()
  ..color = const Color.fromARGB(255, 255, 255, 255)
  ..style = PaintingStyle.fill
  ..strokeWidth = size.width * 0.00
  ..strokeCap = StrokeCap.butt
  ..strokeJoin = StrokeJoin.miter;

Path path_0 = Path();
path_0.moveTo(size.width * 0.9608333, size.height * 0.0714286);
path_0.quadraticBezierTo(
    size.width * 0.9980167, size.height * 0.0702571, size.width * 1.0025000, size.height * 0.1414286);
path_0.cubicTo(size.width * 1.0020833, size.height * 0.2489286, size.width * 1.0017917,
    size.height * 0.5629000, size.width * 1.0013750, size.height * 0.6704000);
path_0.quadraticBezierTo(
    size.width * 1.0005583, size.height * 0.7403286, size.width * 0.9597083, size.height * 0.7454000);
path_0.quadraticBezierTo(
    size.width * 0.2732333, size.height * 0.7409286, size.width * 0.0432333, size.height * 0.7409286);
path_0.quadraticBezierTo(
    size.width * 0.0030750, size.height * 0.7389714, size.width * -0.0011833, size.height * 0.6743571);
path_0.quadraticBezierTo(
    size.width * 0.0008833, size.height * 0.2457071, size.width * 0.0009000, size.height * 0.1366571);
path_0.quadraticBezierTo(
    size.width * -0.0007333, size.height * 0.0698000, size.width * 0.0441667, size.height * 0.0700000);
path_0.lineTo(size.width * 0.0516167, size.height * 0.0029429);
path_0.lineTo(size.width * 0.0800000, size.height * 0.0677000);

canvas.drawPath(path_0, paint_fill_0);
}

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

Solution

  • You can use ShapeBorder for this. Play on gist or dartPad

    /// Create a pointy shape like toolTip
    ///
    /// ```dart
    ///  Container(
    ///    decoration: ShapeDecoration(
    ///      shape: PointyShape(
    ///        padding: const EdgeInsets.all(12),
    ///        pointOffsetX: pointX,
    ///        pointyHeight: 24,
    ///        radiusValue: 16,
    ///        isTop : true,
    ///      ),
    ///      color: Colors.white,
    ///    ),
    ///```
    ///
    class PointyShape extends OutlinedBorder {
      const PointyShape({
        this.pointOffsetX = .85,
        this.radiusValue = 16,
        this.pointyHeight = 24,
        this.padding = const EdgeInsets.all(12),
        this.isTop = true,
      }) : assert(pointOffsetX >= 0 && pointOffsetX <= 1, "offset should be within 0,1");
    
      final double pointOffsetX;
      final double radiusValue;
      final double pointyHeight;
      final EdgeInsets padding;
      final bool isTop;
    
      @override
      OutlinedBorder copyWith({BorderSide? side}) => this;
    
      @override
      EdgeInsetsGeometry get dimensions => padding.copyWith(
            bottom: padding.bottom + (isTop ? 0 : pointyHeight),
            top: padding.top + (isTop ? pointyHeight : 0),
          );
    
      @override
      Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
        return getOuterPath(rect, textDirection: textDirection);
      }
    
      @override
      Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
        final rRect = RRect.fromRectAndRadius(
          Rect.fromLTRB(
            rect.left,
            rect.top + (isTop ? pointyHeight : 0),
            rect.right,
            rect.bottom - (isTop ? 0 : pointyHeight),
          ),
          Radius.circular(radiusValue),
        );
    
        final pointyStartX = rect.right - (rect.width * (1 - pointOffsetX)) - pointyHeight / 2;
    
        final toolTip = isTop
            ? (Path()
              ..moveTo(pointyStartX + pointyHeight / 2, rect.top)
              ..lineTo(pointyStartX, rect.top + pointyHeight)
              ..lineTo(pointyStartX + (pointyHeight), rect.top + pointyHeight)
              ..lineTo(pointyStartX + pointyHeight / 2, rect.top))
            : Path()
          ..moveTo(
            pointyStartX,
            rect.bottom - pointyHeight,
          )
          ..lineTo(pointyStartX + (pointyHeight / 2), rect.bottom)
          ..lineTo(pointyStartX + (pointyHeight), rect.bottom - pointyHeight)
          ..lineTo(pointyStartX, rect.bottom - pointyHeight);
    
        return Path.combine(PathOperation.union, Path()..addRRect(rRect), toolTip);
      }
    
      @override
      void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}
    
      @override
      ShapeBorder scale(double t) => this;
    }