リボンの作り方

リボン形を作成したかったので、記事を漁ってみたのですが見つからなかったので自分で作ってみました。
<img width=25%><img width=50% alt="2023-04-02 21.26.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1248189/362c152d-e51b-fb14-3b4a-f34e121136b3.png">

もっと良い書き方があると思いますが、もしリボンを作りたいと思った方の参考になれば幸いです。

:::note warn
サンプルコードではレスポンシブ対応ができていないので、端末によってUI崩れが起きてしまうと思います。
:::

サンプルコード
```dart
import 'package:flutter/material.dart';

class SampleRibbon extends StatelessWidget {
  const SampleRibbon({Key? key}) : super(key: key);

  static Route<void> route() {
    return MaterialPageRoute(
      builder: (context) => const SampleRibbon(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          Stack(
            children: [
              /// リボンの長方形
              Positioned(
                right: 80,
                left: 80,
                top: 22,
                child: ConstrainedBox(
                  constraints:
                      const BoxConstraints(minHeight: 60, maxWidth: 260),
                  child: DecoratedBox(
                    decoration: BoxDecoration(
                      color: Colors.blue,
                      borderRadius: BorderRadius.circular(6),
                    ),
                    child: Center(
                      child: Column(
                        children: const [
                          Text(
                            'SampleRibbon',
                            style: TextStyle(fontSize: 28, color: Colors.white),
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
              ),

              /// リボン右端
              Padding(
                padding: const EdgeInsets.only(top: 60, left: 280),
                child: Align(
                  child: ClipPath(
                    clipper: RightArcClipper(),
                    child: Container(
                      color: Colors.blue,
                      height: 35,
                      width: 45,
                    ),
                  ),
                ),
              ),

              /// リボン左端
              Padding(
                padding: const EdgeInsets.only(top: 60, right: 285),
                child: Align(
                  child: ClipPath(
                    clipper: LeftArcClipper(),
                    child: Container(
                      color: Colors.blue,
                      height: 35,
                      width: 45,
                    ),
                  ),
                ),
              ),

              /// リボン左端の影
              Padding(
                padding: const EdgeInsets.only(top: 83, right: 250),
                child: Align(
                  child: ClipPath(
                    clipper: LeftTriangleClipper(),
                    child: Container(
                      color: Colors.blueGrey,
                      height: 12,
                      width: 15,
                    ),
                  ),
                ),
              ),

              /// リボン右端の影
              Padding(
                padding: const EdgeInsets.only(top: 83, left: 250),
                child: Align(
                  child: ClipPath(
                    clipper: RightTriangleClipper(),
                    child: Container(
                      color: Colors.blueGrey,
                      height: 12,
                      width: 15,
                    ),
                  ),
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class RightArcClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();

    var leftControlPoint = const Offset(2.0, 7.5);
    var leftPoint = const Offset(0.0, 15.0);
    path.quadraticBezierTo(
        leftControlPoint.dx, leftControlPoint.dy, leftPoint.dx, leftPoint.dy);

    path.lineTo(0.0, size.height);
    path.lineTo(size.width, size.height);
    path.lineTo(size.width - 20, size.height / 2);
    path.lineTo(size.width, 0.0);
    path.close();

    return path;
  }

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

class LeftArcClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();

    var rightControlPoint = const Offset(20.0, 20.0);
    var rightPoint = const Offset(20.0, 20.0);
    path.quadraticBezierTo(rightControlPoint.dx, rightControlPoint.dy,
        rightPoint.dy, rightPoint.dx);

    path.lineTo(0.0, size.height);
    path.lineTo(size.width, size.height);
    path.lineTo(size.width, 0.0);
    path.close();

    return path;
  }

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

class LeftTriangleClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();
    path.lineTo(0.0, 0.0);
    path.lineTo(size.width, size.height);
    path.lineTo(size.width, 0.0);
    path.close();

    return path;
  }

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

class RightTriangleClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();
    path.lineTo(size.width, 0.0);
    path.lineTo(0, size.height);
    path.lineTo(0.0, 0.0);
    path.close();

    return path;
  }

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


参考にさせて頂いたコード↓

https://gist.github.com/agungsb/