coupon_clipper.dart 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import 'package:flutter/material.dart';
  2. /// Clips a widget to the form of a coupon card shape
  3. class CouponClipper extends CustomClipper<Path> {
  4. /// Paints a coupon shape around any widget.
  5. ///
  6. /// Usage:
  7. /// ```dart
  8. /// ClipPath(
  9. /// clipper: CouponClipper(
  10. /// // optional (defaults to TextDirection.ltr), works when
  11. /// // curveAxis set to Axis.vertical
  12. /// direction: Directionality.of(context),
  13. /// ),
  14. /// // width and height are important depending on the type
  15. /// // of the text direction
  16. /// child: Container(
  17. /// width: 350,
  18. /// height: 400,
  19. /// color: Colors.purple,
  20. /// ),
  21. /// ),
  22. /// ```
  23. const CouponClipper({
  24. this.borderRadius = 8,
  25. this.curveRadius = 20,
  26. this.curvePosition = 100,
  27. this.curveAxis = Axis.horizontal,
  28. this.direction = TextDirection.ltr,
  29. this.clockwise = false,
  30. }) : assert(
  31. curvePosition > borderRadius,
  32. "'curvePosition' should be greater than the 'borderRadius'",
  33. );
  34. /// Border radius value.
  35. final double borderRadius;
  36. /// The curve radius value, which specifies its size.
  37. final double curveRadius;
  38. /// The curve position, which specifies the curve position depending
  39. /// on the its child's size.
  40. final double curvePosition;
  41. /// The direction of the curve, whether it's set vertically or
  42. /// horizontally.
  43. final Axis curveAxis;
  44. /// If `curveAxis` set to `Axis.vertical`, then direction is useful
  45. /// for languages like (Ar, Fa, ...).
  46. final TextDirection direction;
  47. /// If `false` (by default), then the border radius will be drawn
  48. /// inside the child, otherwise outside.
  49. final bool clockwise;
  50. @override
  51. Path getClip(Size size) {
  52. double directionalCurvePosition = curvePosition;
  53. if (curveAxis == Axis.vertical && direction == TextDirection.rtl) {
  54. directionalCurvePosition = size.width - curvePosition - curveRadius;
  55. }
  56. final Radius arcRadius = Radius.circular(borderRadius);
  57. // border radius arc points
  58. final Offset bottomLeftArc = Offset(borderRadius, size.height);
  59. final Offset bottomRightArc =
  60. Offset(size.width, size.height - borderRadius);
  61. final Offset topRightArc = Offset(size.width - borderRadius, 0);
  62. final Offset topLeftArc = Offset(0, borderRadius);
  63. final Path path = Path();
  64. // starting point
  65. path.moveTo(0, borderRadius);
  66. // left curve
  67. if (curveAxis == Axis.horizontal) {
  68. path.lineTo(0, directionalCurvePosition);
  69. path.quadraticBezierTo(
  70. curveRadius / 1.5,
  71. directionalCurvePosition + (curveRadius / 2),
  72. 0,
  73. directionalCurvePosition + curveRadius,
  74. );
  75. }
  76. // left line, bottom left arc
  77. path.lineTo(0, size.height - borderRadius);
  78. path.arcToPoint(bottomLeftArc, radius: arcRadius, clockwise: clockwise);
  79. // bottom cuve
  80. if (curveAxis == Axis.vertical) {
  81. path.lineTo(directionalCurvePosition, size.height);
  82. path.quadraticBezierTo(
  83. directionalCurvePosition + (curveRadius / 2),
  84. size.height - (curveRadius / 1.5),
  85. directionalCurvePosition + curveRadius,
  86. size.height,
  87. );
  88. }
  89. // bottom line, bottom right arc
  90. path.lineTo(size.width - borderRadius, size.height);
  91. path.arcToPoint(bottomRightArc, radius: arcRadius, clockwise: clockwise);
  92. // right curve
  93. if (curveAxis == Axis.horizontal) {
  94. path.lineTo(size.width, directionalCurvePosition + curveRadius);
  95. path.quadraticBezierTo(
  96. size.width - (curveRadius / 1.5),
  97. directionalCurvePosition + (curveRadius / 2),
  98. size.width,
  99. directionalCurvePosition,
  100. );
  101. }
  102. // right line, top right arc
  103. path.lineTo(size.width, borderRadius);
  104. path.arcToPoint(topRightArc, radius: arcRadius, clockwise: clockwise);
  105. // top curve
  106. if (curveAxis == Axis.vertical) {
  107. path.lineTo(directionalCurvePosition + curveRadius, 0);
  108. path.quadraticBezierTo(
  109. (directionalCurvePosition - (curveRadius / 2)) + curveRadius,
  110. curveRadius / 1.5,
  111. directionalCurvePosition - curveRadius + curveRadius,
  112. 0,
  113. );
  114. }
  115. // top line, top left arc
  116. path.lineTo(borderRadius, 0);
  117. path.arcToPoint(topLeftArc, radius: arcRadius, clockwise: clockwise);
  118. return path;
  119. }
  120. @override
  121. bool shouldReclip(CustomClipper<Path> oldClipper) => oldClipper != this;
  122. }