Reputation: 2924
This is the way to make a container with rounded corners:
Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(10),),
But is there a way to make inverted rounded corners like in the picture below? I couldn't find anything online
Upvotes: 9
Views: 10202
Reputation: 367
If you're looking to create a custom ticket where each corner can be customized independently. I've got something.
This is the result :
And here is the code.
import 'package:flutter/material.dart';
class SwTicketBorder extends ShapeBorder {
final double radius;
final Color? fillColor;
final double borderWidth;
final Color? borderColor;
final bool topLeft;
final bool topRight;
final bool bottomLeft;
final bool bottomRight;
const SwTicketBorder({
required this.radius,
this.fillColor,
this.borderWidth = 1.0,
this.borderColor,
this.topLeft = true,
this.topRight = true,
this.bottomLeft = true,
this.bottomRight = true,
});
@override
EdgeInsetsGeometry get dimensions => EdgeInsets.all(borderWidth);
@override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
return Path()
..fillType = PathFillType.evenOdd
..addPath(getOuterPath(rect, textDirection: textDirection), Offset.zero);
}
@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) =>
_createPath(rect);
@override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
if (fillColor == null && borderColor == null) return;
if (fillColor != null) {
final fillPaint = Paint()
..color = fillColor! // Use the provided fillColor
..style = PaintingStyle.fill;
final fillPath = getInnerPath(rect, textDirection: textDirection);
canvas.drawPath(fillPath, fillPaint);
}
if (borderColor != null) {
final borderPaint = Paint()
..color = borderColor!
..style = PaintingStyle.stroke
..strokeWidth = borderWidth;
final borderPath = getOuterPath(rect, textDirection: textDirection);
canvas.drawPath(borderPath, borderPaint);
}
}
Path _createPath(Rect rect) {
// Inset the rect by the pathWidth so the border is inside the rect.
Rect insetRect = rect.deflate(borderWidth);
// The path for the 'ticket' shape
Path path = Path();
// The distance from the corner to start the curve for the inverted corners
double inset = radius;
// Move to the start point
path.moveTo(insetRect.left + inset, insetRect.top);
// Top line and top-right inverted corner
path.lineTo(insetRect.right - inset, insetRect.top);
path.arcToPoint(
Offset(insetRect.right, insetRect.top + inset),
radius: Radius.circular(inset),
clockwise: !topRight,
);
// Right line and bottom-right inverted corner
path.lineTo(insetRect.right, insetRect.bottom - inset);
path.arcToPoint(
Offset(insetRect.right - inset, insetRect.bottom),
radius: Radius.circular(inset),
clockwise: !bottomRight,
);
// Bottom line and bottom-left inverted corner
path.lineTo(insetRect.left + inset, insetRect.bottom);
path.arcToPoint(
Offset(insetRect.left, insetRect.bottom - inset),
radius: Radius.circular(inset),
clockwise: !bottomLeft,
);
// Left line and top-left inverted corner
path.lineTo(insetRect.left, insetRect.top + inset);
path.arcToPoint(
Offset(insetRect.left + inset, insetRect.top),
radius: Radius.circular(inset),
clockwise: !topLeft,
);
// Close the path
path.close();
return path;
}
@override
ShapeBorder scale(double t) {
return SwTicketBorder(
radius: radius * t,
borderWidth: borderWidth * t,
borderColor: borderColor,
topLeft: topLeft,
topRight: topRight,
bottomLeft: bottomLeft,
bottomRight: bottomRight,
);
}
}
width: double.infinity,
decoration: const ShapeDecoration(
shape: SwTicketBorder(
radius: 16,
fillColor: Colors.grey,
borderColor: Colors.yellow,
borderWidth: 4,
bottomLeft: true,
bottomRight: true,
topLeft: false,
topRight: false,
),
),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 24,
),
child: const Column(
children: [
Text(
"This is a ticket",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w600,
),
),
Text(
"And this is its description",
style: TextStyle(
fontSize: 16,
),
),
],
),
)
Upvotes: 0
Reputation:
as an option (based on @pskink tip)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(appBar: AppBar(), body: Demo()));
}
}
class Demo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
Container(
width: 300,
height: 200,
margin: const EdgeInsets.only(top: 24, bottom: 16),
decoration: ShapeDecoration(shape: WeirdBorder(radius: 32), color: Colors.red),
),
Container(
width: 200,
height: 100,
margin: const EdgeInsets.only(bottom: 16),
decoration: ShapeDecoration(shape: WeirdBorder(radius: 16, pathWidth: 8), color: Colors.green),
),
Container(
width: 300,
height: 200,
decoration: ShapeDecoration(shape: WeirdBorder(radius: 12, pathWidth: 2), color: Colors.blue),
),
],
),
);
}
}
class WeirdBorder extends ShapeBorder {
final double radius;
final double pathWidth;
WeirdBorder({@required this.radius, this.pathWidth = 1});
@override
EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
@override
Path getInnerPath(Rect rect, {TextDirection textDirection}) {
return Path()
..fillType = PathFillType.evenOdd
..addPath(getOuterPath(rect, textDirection: textDirection), Offset.zero);
}
@override
Path getOuterPath(Rect rect, {TextDirection textDirection}) => _createPath(rect);
@override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}
@override
ShapeBorder scale(double t) => WeirdBorder(radius: radius);
Path _createPath(Rect rect) {
final innerRadius = radius + pathWidth;
final innerRect = Rect.fromLTRB(rect.left + pathWidth, rect.top + pathWidth, rect.right - pathWidth, rect.bottom - pathWidth);
final outer = Path.combine(PathOperation.difference, Path()..addRect(rect), _createBevels(rect, radius));
final inner = Path.combine(PathOperation.difference, Path()..addRect(innerRect), _createBevels(rect, innerRadius));
return Path.combine(PathOperation.difference, outer, inner);
}
Path _createBevels(Rect rect, double radius) {
return Path()
..addOval(Rect.fromCircle(center: Offset(rect.left, rect.top), radius: radius))
..addOval(Rect.fromCircle(center: Offset(rect.left + rect.width, rect.top), radius: radius))
..addOval(Rect.fromCircle(center: Offset(rect.left, rect.top + rect.height), radius: radius))
..addOval(Rect.fromCircle(center: Offset(rect.left + rect.width, rect.top + rect.height), radius: radius));
}
}
Upvotes: 3