LoginSignup
5
1

More than 1 year has passed since last update.

リボンの作り方

Last updated at Posted at 2023-04-02

リボン形を作成したかったので、記事を漁ってみたのですが見つからなかったので自分で作ってみました。
2023-04-02 21.26.39.png

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

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

サンプルコード

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;
}

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

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1