Border at corner only in flutter

566

This can be done with CustomPaint widget with CustomPainter set as foregroundPainter:

    import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'CustomPaint spotlight',
      home: Scaffold(
        appBar: AppBar(
          title: Text('CustomPaint spotlight'),
        ),
        body: Center(
          child: CustomPaint(
            foregroundPainter: BorderPainter(),
            child: Container(
              width: 200,
              height: 100,
              color: Colors.deepOrange[50],
            ),
          ),
        ),
      ),
    );
  }
}

class BorderPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    double sh = size.height; // for convenient shortage
    double sw = size.width; // for convenient shortage
    double cornerSide = sh * 0.1; // desirable value for corners side

    Paint paint = Paint()
      ..color = Colors.black
      ..strokeWidth = 1.5
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    Path path = Path()
      ..moveTo(cornerSide, 0)
      ..quadraticBezierTo(0, 0, 0, cornerSide)
      ..moveTo(0, sh - cornerSide)
      ..quadraticBezierTo(0, sh, cornerSide, sh)
      ..moveTo(sw - cornerSide, sh)
      ..quadraticBezierTo(sw, sh, sw, sh - cornerSide)
      ..moveTo(sw, cornerSide)
      ..quadraticBezierTo(sw, 0, sw - cornerSide, 0);

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(BorderPainter oldDelegate) => false;

  @override
  bool shouldRebuildSemantics(BorderPainter oldDelegate) => false;
}

This produces the result like:

You need to know that CustomPaint does not crop its child corners and in some cases that might be discouraging. You have two ways of solving this:

  • Easy one - to cast rounded borders on its child and make them be visibly equal to CustomPaint borders.

  • Robust one - to wrap child with ClipPath with a path that copies CustomPaint path

    import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'CustomPaint spotlight',
      home: Scaffold(
        appBar: AppBar(
          title: Text('CustomPaint spotlight'),
        ),
        body: Center(
          child: CustomPaint(
            foregroundPainter: BorderPainter(),
            child: ClipPath(
              clipper: BorderClipper(),
              child: Container(
                width: 200,
                height: 100,
                color: Colors.deepOrange[50],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class BorderPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    double sh = size.height; // for convenient shortage
    double sw = size.width; // for convenient shortage
    double cornerSide = sh * 0.1; // desirable value for corners side

    Paint paint = Paint()
      ..color = Colors.black
      ..strokeWidth = 1.5
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    Path path = Path()
      ..moveTo(cornerSide, 0)
      ..quadraticBezierTo(0, 0, 0, cornerSide)
      ..moveTo(0, sh - cornerSide)
      ..quadraticBezierTo(0, sh, cornerSide, sh)
      ..moveTo(sw - cornerSide, sh)
      ..quadraticBezierTo(sw, sh, sw, sh - cornerSide)
      ..moveTo(sw, cornerSide)
      ..quadraticBezierTo(sw, 0, sw - cornerSide, 0);

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(BorderPainter oldDelegate) => false;

  @override
  bool shouldRebuildSemantics(BorderPainter oldDelegate) => false;
}

class BorderClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    double sh = size.height; // for convenient shortage
    double sw = size.width; // for convenient shortage
    double cornerSide = sh * 0.1;

    Path path = Path()
      ..moveTo(cornerSide, 0)
      ..quadraticBezierTo(0, 0, 0, cornerSide)
      ..moveTo(0, sh - cornerSide)
      ..quadraticBezierTo(0, sh, cornerSide, sh)
      ..moveTo(sw - cornerSide, sh)
      ..quadraticBezierTo(sw, sh, sw, sh - cornerSide)
      ..moveTo(sw, cornerSide)
      ..quadraticBezierTo(sw, 0, sw - cornerSide, 0);
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

Share:
566
Farhana Naaz Ansari
Author by

Farhana Naaz Ansari

Farhana... The only way of writing fewer bugs is writing less code.

Updated on December 28, 2022

Comments

  • Farhana Naaz Ansari
    Farhana Naaz Ansari over 1 year

    I trying to make border at corner only in container.

    Container(
      padding: const EdgeInsets.only(right: 8.0, left: 8.0),
      height: 60,
      child: Card(
        elevation: 2,
        shape: RoundedRectangleBorder(
            side: BorderSide(color: appThemeColor.shade200, width: 0.5),
            borderRadius: BorderRadius.circular(5)),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: <Widget>[
            Container(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.only(
                    topRight: Radius.circular(15),
                    bottomRight: Radius.circular(15)),
                color: Colors.deepPurple,
              ),
              width: 5,
            ),
          ],
        ),
      ),
    )
    

    enter image description here

    • Simon Sot
      Simon Sot about 3 years
      Let me clarify a bit. You want painted corners like on image?
    • Farhana Naaz Ansari
      Farhana Naaz Ansari about 3 years
      @SimonSot just need painted corner.
    • Simon Sot
      Simon Sot about 3 years
      I get it. This is easily done with CustomPaint widget. Do you want me to code it for you?
    • Farhana Naaz Ansari
      Farhana Naaz Ansari about 3 years
      @SimonSot yes, i don't have much knowledge of custom paint.
  • Farhana Naaz Ansari
    Farhana Naaz Ansari about 3 years
    Thank you! for your answer.