title_clip_path.dart 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import 'package:flutter/material.dart';
  2. const double _width = 170;
  3. const double _height = 38;
  4. class TitleClipRect extends StatelessWidget {
  5. const TitleClipRect({
  6. super.key,
  7. this.color = Colors.grey,
  8. required this.title,
  9. required this.arrowHeight,
  10. required this.clickTitle,
  11. });
  12. final Color? color;
  13. final String title;
  14. final double arrowHeight;
  15. final Function clickTitle;
  16. @override
  17. Widget build(BuildContext context) {
  18. return InkWell(
  19. onTap: () {
  20. clickTitle.call();
  21. },
  22. customBorder: HoleShapeBorder(arrowHeight),
  23. child: Card(
  24. margin: const EdgeInsets.all(0),
  25. shape: HoleShapeBorder(arrowHeight),
  26. color: color,
  27. elevation: color == null ? 5 : 0,
  28. shadowColor: Theme.of(context).primaryColor.withOpacity(0.8),
  29. child: ClipPath(
  30. clipper: TitleClipPath(arrowHeight),
  31. child: Container(
  32. width: _width,
  33. height: _height,
  34. padding: const EdgeInsets.symmetric(
  35. horizontal: 15,
  36. ),
  37. decoration: BoxDecoration(
  38. boxShadow: [
  39. BoxShadow(
  40. color: Theme.of(context).primaryColor.withOpacity(1),
  41. ),
  42. ],
  43. color: color,
  44. ),
  45. alignment: Alignment.center,
  46. child: FittedBox(
  47. child: Text(
  48. title,
  49. style: const TextStyle(color: Colors.white, fontSize: 20),
  50. ),
  51. ),
  52. ),
  53. ),
  54. ),
  55. );
  56. }
  57. }
  58. class TitleClipPath extends CustomClipper<Path> {
  59. final double arrowHeight;
  60. TitleClipPath(this.arrowHeight);
  61. @override
  62. Path getClip(Size size) {
  63. final height = size.height;
  64. final arrowBase = height / 2;
  65. // final arrowPLine = math.tan(120 / 180) * arrowBase;
  66. final path = Path();
  67. path.moveTo(0, 0); // 左上角
  68. path.lineTo(size.width - arrowHeight, 0); // 右上角
  69. path.lineTo(size.width, arrowBase); // 右端点
  70. path.lineTo(size.width - arrowHeight, height); // 右下角
  71. path.lineTo(0, height); // 左下角
  72. path.lineTo(arrowHeight, arrowBase); // 左端点
  73. path.lineTo(0, 0); // 左上角
  74. return path;
  75. }
  76. @override
  77. bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
  78. return false;
  79. }
  80. }
  81. class HoleShapeBorder extends ShapeBorder {
  82. final Offset offset;
  83. final double size;
  84. final double arrowHeight;
  85. const HoleShapeBorder(this.arrowHeight,
  86. {this.offset = const Offset(0, 0), this.size = 0});
  87. @override
  88. EdgeInsetsGeometry get dimensions => throw UnimplementedError();
  89. @override
  90. void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}
  91. @override
  92. Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
  93. var path = Path();
  94. final height = rect.size.height;
  95. final arrowBase = height / 2;
  96. path.moveTo(0, 0); // 左上角
  97. path.lineTo(rect.size.width - arrowHeight, 0); // 右上角
  98. path.lineTo(rect.size.width, arrowBase); // 右端点
  99. path.lineTo(rect.size.width - arrowHeight, height); // 右下角
  100. path.lineTo(0, height); // 左下角
  101. path.lineTo(arrowHeight, arrowBase); // 左端点
  102. path.lineTo(0, 0); // 左上角
  103. return path;
  104. }
  105. @override
  106. ShapeBorder scale(double t) {
  107. // TODO: implement scale
  108. throw UnimplementedError();
  109. }
  110. @override
  111. Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
  112. // TODO: implement getInnerPath
  113. throw UnimplementedError();
  114. }
  115. }