リボン形を作成したかったので、記事を漁ってみたのですが見つからなかったので自分で作ってみました。
もっと良い書き方があると思いますが、もしリボンを作りたいと思った方の参考になれば幸いです。
サンプルコードではレスポンシブ対応ができていないので、端末によって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;
}
参考にさせて頂いたコード↓