import 'dart:math'; import 'package:flutter/material.dart'; class FirstClipper extends CustomClipper { FirstClipper(); @override Path getClip(Size size) { final path = Path(); final width = size.width; final height = size.height; final halfHeight = height / 2; path.moveTo(0, 0); path.lineTo(width - halfHeight, 0); path.lineTo(width, height / 2); path.lineTo(width - halfHeight, height); path.lineTo(0, height); path.close(); return path; } @override bool shouldReclip(CustomClipper oldClipper) { return false; } } class SecondClipper extends CustomClipper { SecondClipper(); @override Path getClip(Size size) { final path = Path(); final width = size.width; final height = size.height; final halfHeight = height / 2; path.moveTo(0, 0); path.lineTo(width - halfHeight, 0); path.lineTo(width, height / 2); path.lineTo(width - halfHeight, height); path.lineTo(0, height); path.lineTo(halfHeight, height / 2); path.close(); return path; } @override bool shouldReclip(CustomClipper oldClipper) { return false; } } class ThirdClipper extends CustomClipper { ThirdClipper(); @override Path getClip(Size size) { final path = Path(); final width = size.width; final height = size.height; final halfHeight = height / 2; path.moveTo(0, 0); path.lineTo(width - halfHeight, 0); path.arcToPoint( Offset(width, height / 2), radius: Radius.circular(height / 2), ); path.arcToPoint( Offset(width - halfHeight, height), radius: Radius.circular(height / 2), ); path.lineTo(0, height); path.lineTo(halfHeight, height / 2); path.close(); return path; } @override bool shouldReclip(CustomClipper oldClipper) { return false; } } class CircleClipper1 extends CustomClipper { CircleClipper1({required this.thickness}); final double thickness; @override Path getClip(Size size) { final height = size.height; final width = size.width; final innerCircle = width - thickness; final path = Path(); path.moveTo(0, height); path.lineTo(thickness, height); path.arcToPoint( Offset(width, thickness), radius: Radius.circular(innerCircle), ); path.lineTo(width, 0); path.arcToPoint( Offset(0, height), radius: Radius.circular(width), clockwise: false, ); path.close(); return path; } @override bool shouldReclip(CustomClipper oldClipper) { return false; } } class CircleClipper2 extends CustomClipper { CircleClipper2({required this.thickness}); final double thickness; @override Path getClip(Size size) { final height = size.height; final width = size.width; final innerCircle = width - thickness; const endingDegree = 305.0; final path = Path(); final center = Point(width, 0); path.moveTo(0, 0); path.lineTo(thickness, 0); path.arcToPoint( getOffsetOnCircle(center, innerCircle, 295), radius: Radius.circular(innerCircle), clockwise: false, ); path.arcToPoint( getOffsetOnCircle(center, innerCircle + 15, endingDegree), radius: const Radius.circular(15), ); path.lineTo( getOffsetOnCircle(center, width, endingDegree).dx, getOffsetOnCircle(center, width, endingDegree).dy, ); path.arcToPoint( const Offset(0, 0), radius: Radius.circular(width), ); path.close(); return path; } Offset getOffsetOnCircle(Point c, double r, double deg) { final x = c.x + r * sin(deg * pi / 180); final y = c.y + r * cos(deg * pi / 180); return Offset(x, y); } @override bool shouldReclip(CustomClipper oldClipper) { return false; } } void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), home: const MyHomePage(), ); } } class MyHomePage extends StatelessWidget { const MyHomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: Padding( padding: const EdgeInsets.all(8.0), child: LayoutBuilder(builder: (context, constraints) { const numberOfSections = 5; const height = 70.0; const spaceBetween = 10; const overlapDistance = (height / 2) - spaceBetween; final width = (constraints.maxWidth / numberOfSections) + overlapDistance - spaceBetween; return Column( children: [ SizedBox( height: height, child: Stack( children: [ Positioned( left: 0, child: SizedBox( width: width, child: ClipPath( clipper: FirstClipper(), child: Container( height: height, color: Colors.blue, child: const Center(child: Text("Some Text")), ), ), ), ), Positioned( left: width - overlapDistance, child: SizedBox( width: width, child: ClipPath( clipper: SecondClipper(), child: Container( height: height, color: Colors.green, child: const Center(child: Text("Some Text")), ), ), ), ), Positioned( left: (width - overlapDistance) * 2, child: SizedBox( width: width * 2, child: ClipPath( clipper: SecondClipper(), child: Container( height: height, color: Colors.green, child: const Center(child: Text("Some Text")), ), ), ), ), Positioned( left: (width - overlapDistance) * 4 + overlapDistance, child: SizedBox( width: width, child: ClipPath( clipper: ThirdClipper(), child: Container( height: height, color: Colors.green, child: const Center(child: Text("Some Text")), ), ), ), ), ], ), ), const SizedBox(height: 5), Stack( children: [ Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ ClipPath( clipper: CircleClipper1(thickness: 35), child: Container( height: 100, width: 100, color: Colors.green, ), ), const SizedBox(width: 5), Transform.scale( scaleX: -1, child: ClipPath( clipper: CircleClipper1(thickness: 35), child: Container( height: 100, width: 100, color: Colors.green, ), ), ), ], ), const SizedBox(height: 5), Row( mainAxisSize: MainAxisSize.min, children: [ ClipPath( clipper: CircleClipper2(thickness: 35), child: Container( height: 100, width: 100, color: Colors.blue, ), ), const SizedBox(width: 5), Transform.scale( scaleX: -1, child: ClipPath( clipper: CircleClipper2(thickness: 35), child: Container( height: 100, width: 100, color: Colors.green, ), ), ), ], ), ], ), const Positioned( right: 0, left: 0, top: 0, bottom: 0, child: Icon( Icons.check_circle, size: 56, color: Colors.green, ), ), ], ), ], ); }), ), ); } }