How to make a cutout in a container using ClipPath? - flutter

I have a page on which the container is located, I need to make borders and a cutout for the buttons in this container. I want to do this using ClipPath when I added this widget, nothing has changed, there are no borders and there is no vyzer that I registered. Tell me why nothing is displayed, what is my mistake?
#override
Widget build(BuildContext context) {
return ClipPath(
clipper: CustomClipperArc(position: 150, holeRadius: 32),
child: Container(
decoration: const BoxDecoration(
color: constants.Colors.greyXDark,
),
padding: const EdgeInsets.only(top: 50),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
constants.Assets.likeBoarding,
height: 50,
),
],
),
],
),
),
);
}
CustomClipperArc
class CustomClipperArc extends CustomClipper<Path> {
CustomClipperArc({this.position, this.holeRadius = 16});
double? position;
final double holeRadius;
#override
Path getClip(Size size) {
final path = Path()
..moveTo(0, 0)
..lineTo(position! - holeRadius, 0.0)
..lineTo(size.width, 0.0)
..lineTo(size.width, size.height)
..lineTo(position!, size.height)
..arcToPoint(
Offset(position! - holeRadius, size.height),
clockwise: false,
radius: const Radius.circular(1),
);
path.lineTo(0.0, size.height);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper oldClipper) => oldClipper != this;
}

Here is your Clip Code... and also use Shape Maker to design such layout and you will get clip code
Your clip container with border
Padding(
padding: const EdgeInsets.all(8.0),
child: Stack(
children: <Widget>[
ClipPath(
clipper: CustomClipPathTopContainer(),
child: Container(
height: 400,
color: Colors.green,
),
),
CustomPaint(
painter: BorderPainter(),
child: Container(
height: 400,
),
),
],
),
),
Clipping Code of Container
import 'package:flutter/cupertino.dart';
class CustomClipPathTopContainer extends CustomClipper<Path> {
#override
Path getClip(Size size) {
double w = size.width;
double h = size.height;
Path path0 = Path();
path0.moveTo(0,size.height);
path0.lineTo(0,0);
path0.lineTo(size.width,0);
path0.lineTo(size.width,size.height);
path0.lineTo(size.width*0.3333333,size.height);
path0.lineTo(size.width*0.3014833,size.height*0.9182714);
path0.quadraticBezierTo(size.width*0.2795000,size.height*0.8617714,size.width*0.2488333,size.height*0.8552714);
path0.quadraticBezierTo(size.width*0.2145667,size.height*0.8643571,size.width*0.1948583,size.height*0.9179714);
path0.lineTo(size.width*0.1653417,size.height);
path0.lineTo(0,size.height);
path0.lineTo(0,size.height);
path0.lineTo(0,size.height);
path0.close();
return path0;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
Making Border Painting
class BorderPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2
..color = Colors.pink;
Path path0 = Path();
path0.moveTo(0,size.height);
path0.lineTo(0,size.height*0.2114714);
path0.quadraticBezierTo(size.width*0.1255833,size.height*0.4277857,size.width*0.2698833,size.height*0.4275143);
path0.cubicTo(size.width*0.4168083,size.height*0.4270286,size.width*0.5467250,size.height*0.0190429,size.width*0.6867167,size.height*0.0189000);
path0.quadraticBezierTo(size.width*0.8293000,size.height*0.0211000,size.width,size.height*0.2121429);
path0.lineTo(size.width,size.height);
path0.lineTo(0,size.height);
path0.lineTo(0,size.height);
path0.lineTo(0,size.height*0.9994143);
path0.close();
canvas.drawPath(path0, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}

Related

How to create a container with a sharp arrow on its edge in flutter

I want to create a Container, which has a sharp triangle type figure or arrow on its edge, exactly like in the picture below. If there is a pre-defined widget for funtions like this, please do tell me.
You can use CustomPainter,try this
Container(
child:Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:CrossAxisAlignment.end,
children:[
Container(
child:CustomPaint(
painter: TrianglePainter(
strokeColor: Colors.red,
strokeWidth: 10,
paintingStyle: PaintingStyle.fill,
),
child: Container(
height: 10,
width: 20,
),
)
),
Container(
color:Colors.red,
height:2,
width:100,
),
Container(
color:Colors.blue,
height:20,
width:100,
)
]
)
)
Triangle Painter Class:
class TrianglePainter extends CustomPainter {
final Color strokeColor;
final PaintingStyle paintingStyle;
final double strokeWidth;
TrianglePainter({this.strokeColor = Colors.black, this.strokeWidth = 3, this.paintingStyle = PaintingStyle.stroke});
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = strokeColor
..strokeWidth = strokeWidth
..style = paintingStyle;
canvas.drawPath(getTrianglePath(size.width, size.height), paint);
}
Path getTrianglePath(double x, double y) {
return Path()
..moveTo(0, y)
..lineTo(x / 2, 0)
..lineTo(x, y)
..lineTo(0, y);
}
#override
bool shouldRepaint(TrianglePainter oldDelegate) {
return oldDelegate.strokeColor != strokeColor ||
oldDelegate.paintingStyle != paintingStyle ||
oldDelegate.strokeWidth != strokeWidth;
}
}

How to create a border like this in Flutter?

How to create a border like this:
You can achieve a similar clipped border using CustomClipper. Here is a simple CustomClipper I have created for you.
First Create a custom clipper.
class MyClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var smallLineLength = size.width / 20;
const smallLineHeight = 20;
var path = Path();
path.lineTo(0, size.height);
for (int i = 1; i <= 20; i++) {
if (i % 2 == 0) {
path.lineTo(smallLineLength * i, size.height);
} else {
path.lineTo(smallLineLength * i, size.height - smallLineHeight);
}
}
path.lineTo(size.width, 0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper old) => false;
}
And wrap the created CustomClipper with ClipPath.
SizedBox(
height: 200,
width: 500,
child: ClipPath(
clipper: MyClipper(),
child: Container(
height: 200,
width: 500,
alignment: Alignment.center,
color: Colors.red,
child: const Text("abc"),
)),
),
Full Code:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: MyWidget(),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
SizedBox(
height: 200,
width: 500,
child: ClipPath(
clipper: MyClipper(),
child: Container(
height: 200,
width: 500,
alignment: Alignment.center,
color: Colors.red,
child: const Text("abc"),
)),
),
]),
);
}
}
class MyClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var smallLineLength = size.width / 20;
const smallLineHeight = 20;
var path = Path();
path.lineTo(0, size.height);
for (int i = 1; i <= 20; i++) {
if (i % 2 == 0) {
path.lineTo(smallLineLength * i, size.height);
} else {
path.lineTo(smallLineLength * i, size.height - smallLineHeight);
}
}
path.lineTo(size.width, 0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper old) => false;
}
You can run this code by copying/pasting in dartpad.
You can learn more about CustomClipper from here, medium article
Try below code hope its helpful to you. Used flutter_custom_clippers package here and used PointsClipper() for your expected design In this package you used more shapes see documentation in given link. add flutter_custom_clippers: ^2.0.0 dependency in your pubspec.yaml file
ClipPath(
clipper: PointsClipper(),
child: Container(
height: 100,
color: Colors.grey[300],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'Total',
style: TextStyle(
color: Colors.pink,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(
'QAR 130.00',
style: TextStyle(
color: Colors.pink,
fontSize: 15,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
),
Your result screen->
import 'package:flutter/material.dart';
import 'a.dart';
void main() => runApp(VideoPlayerApp());
class VideoPlayerApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Zigzag',
home: SafeArea(
child: Scaffold(
body: ZigzagApp(),
),
),
);
}
}
class ZigzagApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: 200,
color: Colors.pinkAccent,
child: CustomPaint(
size: MediaQuery.of(context).size,
painter: MyPainter(),
),
);
}
}
//paint widget for zigzag
import 'package:flutter/material.dart';
import 'dart:math' as math;
class MyPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint();
paint.color = Colors.white;
paint.style = PaintingStyle.fill;
paintZigZag(canvas, paint, Offset(0, 200), Offset(400, 200), 35, 10);
}
void paintZigZag(Canvas canvas, Paint paint, Offset start, Offset end,
int zigs, double width) {
assert(zigs.isFinite);
assert(zigs > 0);
canvas.save();
canvas.translate(start.dx, start.dy);
end = end - start;
canvas.rotate(math.atan2(end.dy, end.dx));
final double length = end.distance;
final double spacing = length / (zigs * 2.0);
final Path path = Path()..moveTo(0.0, 0.0);
for (int index = 0; index < zigs; index += 1) {
final double x = (index * 2.0 + 1.0) * spacing;
final double y = width * ((index % 2.0) * 2.0 - 1.0);
path.lineTo(x, y);
}
path.lineTo(length, 0.0);
canvas.drawPath(path, paint);
canvas.restore();
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

Flutter: Making clipped area transparent for scrolling ListView

I have a ListView that I would like to "disappear" when it hits the clipping of another widget.
Here is my code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
TopWidget(),
Expanded(
child: ListView(
itemExtent: 100,
children: <Widget>[
Card(color: Colors.green,),
],
),
),
],
),
);
}
}
class TopWidget extends StatelessWidget {
TopWidget();
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: ShadowPainter(),
child: ClipPath(
clipper: TopWidgetClipper(),
child: Container(
height: 370,
color: Colors.blue,
),
),
);
}
}
class TopWidgetClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
Offset controllPoint1 = Offset(0, size.height - 100);
Offset endPoint1 = Offset(100, size.height - 100);
Offset controllPoint2 = Offset(size.width, size.height - 100);
Offset endPoint2 = Offset(size.width, size.height - 200);
Path path = Path()
..lineTo(0, size.height)
..quadraticBezierTo(
controllPoint1.dx, controllPoint1.dy, endPoint1.dx, endPoint1.dy)
..lineTo(size.width - 100, size.height - 100)
..quadraticBezierTo(
controllPoint2.dx, controllPoint2.dy, endPoint2.dx, endPoint2.dy)
..lineTo(size.width, 0);
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return true;
}
}
class ShadowPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Offset controllPoint1 = Offset(0, size.height - 100);
Offset endPoint1 = Offset(100, size.height - 100);
Offset controllPoint2 = Offset(size.width, size.height - 100);
Offset endPoint2 = Offset(size.width, size.height - 200);
Path path = Path()
..lineTo(0, size.height)
..quadraticBezierTo(
controllPoint1.dx, controllPoint1.dy, endPoint1.dx, endPoint1.dy)
..lineTo(size.width - 100, size.height - 100)
..quadraticBezierTo(
controllPoint2.dx, controllPoint2.dy, endPoint2.dx, endPoint2.dy)
..lineTo(size.width, 0);
canvas.drawShadow(path, Colors.grey[50], 3.0, false);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
So far, when I am scrolling down, the list (green box) goes unseen when it reaches the bottom of the container (yellow border) of my clipped TopWidget. But I would like the list to smoothly disappear only when it reaches the edges of my clipping (i.e. the blue area - like in the second screenshot).
Any ideas how I could accomplish this? Thank you!
As I have learnt it from #pskink (thanks for that) in use cases like this where you need the widget to actually adjust its boundaries (spoiler: shape) you should make use of the shape property of different widgets and use the Path you used for this example in a custom class which extends ShapeBorder. Easiest approach would be:
Container(
height: 370,
decoration: ShapeDecoration(
color: Colors.blue,
shape: AppBarBorder(),
/// You can also specify some neat shadows to cast on widgets scrolling under this one
shadows: [
BoxShadow(
color: Colors.black.withOpacity(0.7),
blurRadius: 18.0,
spreadRadius: 2.0,
),
],
),
),
And the custom class:
class AppBarBorder extends ShapeBorder {
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
Offset controllPoint1 = Offset(0, rect.size.height - 100);
Offset endPoint1 = Offset(100, rect.size.height - 100);
Offset controllPoint2 = Offset(rect.size.width, rect.size.height - 100);
Offset endPoint2 = Offset(rect.size.width, rect.size.height - 200);
return Path()
..lineTo(0, rect.size.height)
..quadraticBezierTo(
controllPoint1.dx, controllPoint1.dy, endPoint1.dx, endPoint1.dy)
..lineTo(rect.size.width - 100, rect.size.height - 100)
..quadraticBezierTo(
controllPoint2.dx, controllPoint2.dy, endPoint2.dx, endPoint2.dy)
..lineTo(rect.size.width, 0);
}
#override
EdgeInsetsGeometry get dimensions => EdgeInsets.only(bottom: 0);
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) => null;
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}
#override
ShapeBorder scale(double t) => this;
}
Pretty much the same approach how you would declare a CustomClipper or CustomPainter since you don't need to implement most of those methods and essentially only need to care about getOuterPath.
At the end we need to restructure the layout itself, since currently you have a Column with this custom Container shape and the ListView beneath that. Since the Container is not part of the ListView it can't be scrolled under or something. Easiest approach would be using a Stack:
Stack(
children: [
Expanded(
child: ListView(
padding: EdgeInsets.only(top: 370.0),
itemExtent: 100,
children: <Widget>[
Card(
color: Colors.green,
),
],
),
),
Container(
height: 370,
decoration: ShapeDecoration(
color: Colors.blue,
shape: AppBarBorder(),
shadows: [
BoxShadow(
color: Colors.black.withOpacity(0.7),
blurRadius: 18.0,
spreadRadius: 2.0,
),
],
),
),
],
),

Draw Bezier curve with rectangle in Flutter

I'm starting with Flutter and have to design a UI that looks like
But with icon button at the center of the Bezier curve.
What I tried is
class HeaderPainter extends CustomPainter {
HeaderPainter({
#required this.color,
#required this.avatarRadius
});
final Color color;
final double avatarRadius;
#override
void paint(Canvas canvas, Size size) {
final shapeBounds = Rect.fromLTRB(0, 0, size.width, size.height - avatarRadius);
final centerAvatar = Offset(shapeBounds.center.dx, shapeBounds.bottom);
final avatarBounds = Rect.fromCircle(center: centerAvatar, radius: avatarRadius).inflate(3);
_drawBackground(canvas, shapeBounds, avatarBounds);
}
#override
bool shouldRepaint(HeaderPainter oldDelegate) {
return color != oldDelegate.color;
}
void _drawBackground(Canvas canvas, Rect shapeBounds, Rect avatarBounds) {
final paint = Paint()..color = color;
final backgroundPath = Path()
..moveTo(shapeBounds.left, shapeBounds.top)
..lineTo(shapeBounds.bottomLeft.dx, shapeBounds.bottomLeft.dy)
..arcTo(avatarBounds, -pi, pi, false)
..lineTo(shapeBounds.bottomRight.dx, shapeBounds.bottomRight.dy)
..lineTo(shapeBounds.topRight.dx, shapeBounds.topRight.dy)
..lineTo(0.0, shapeBounds.height - 100)
..quadraticBezierTo(
shapeBounds.width / 4, shapeBounds.height,
shapeBounds.width / 2, shapeBounds.height
)
..quadraticBezierTo(
shapeBounds.width - shapeBounds.width / 4, shapeBounds.height,
shapeBounds.width, shapeBounds.height - 100
)
..lineTo(shapeBounds.width, 0.0)
..close();
canvas.drawPath(backgroundPath, paint);
}
}
And the outcome is
How can I get the bezier curve with the rectangle?
Edit 2: The HeaderPainter is used like
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Container(
child: CustomPaint(
size: Size.fromHeight(400.0),
painter: HeaderPainter(
color: Colors.red,
avatarRadius: avatarRadius
),
),
),
Positioned(
left: 0,
right: 0,
bottom: titleBottomMargin,
child: Column(
children: <Widget>[
Text('Hello World', style: TextStyle(fontWeight: FontWeight.bold),),
],
),
),
Align(
alignment: Alignment.bottomCenter,
child: CircleAvatar(
radius: avatarRadius,
backgroundColor: Colors.green,
child: IconButton(icon: Icon(Icons.message), onPressed: _onAddMessageButtonClick,),
),
)
],
);
}
Got it solved using ClipPath and CustomClipper.
The updated build() method is
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Container(
child: ClipPath(
clipper: HeaderClipper(avatarRadius: avatarRadius),
child: CustomPaint(
size: Size.fromHeight(400.0),
painter: HeaderPainter(
color: Colors.green,
avatarRadius: avatarRadius
),
),
),
),
...
and HeaderClipper
class HeaderClipper extends CustomClipper<Path> {
HeaderClipper({
#required this.avatarRadius
});
final avatarRadius;
#override
getClip(Size size) {
final path = Path()
..lineTo(0.0, size.height - 100)
..quadraticBezierTo(
size.width / 4, (size.height - avatarRadius),
size.width / 2, (size.height - avatarRadius)
)
..quadraticBezierTo(
size.width - (size.width / 4), (size.height - avatarRadius),
size.width, size.height - 100
)
..lineTo(size.width, 0.0)
..close();
return path;
}
#override
bool shouldReclip(CustomClipper oldClipper) {
return false;
}
}
The final output is

How to make a container with inverted rounded corners in Flutter?

This is the way to make a container with rounded corners:
Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(10),),
But is there a way to make inverted rounded corners like in the picture below? I couldn't find anything online
as an option (based on #pskink tip)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(appBar: AppBar(), body: Demo()));
}
}
class Demo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
Container(
width: 300,
height: 200,
margin: const EdgeInsets.only(top: 24, bottom: 16),
decoration: ShapeDecoration(shape: WeirdBorder(radius: 32), color: Colors.red),
),
Container(
width: 200,
height: 100,
margin: const EdgeInsets.only(bottom: 16),
decoration: ShapeDecoration(shape: WeirdBorder(radius: 16, pathWidth: 8), color: Colors.green),
),
Container(
width: 300,
height: 200,
decoration: ShapeDecoration(shape: WeirdBorder(radius: 12, pathWidth: 2), color: Colors.blue),
),
],
),
);
}
}
class WeirdBorder extends ShapeBorder {
final double radius;
final double pathWidth;
WeirdBorder({#required this.radius, this.pathWidth = 1});
#override
EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) {
return Path()
..fillType = PathFillType.evenOdd
..addPath(getOuterPath(rect, textDirection: textDirection), Offset.zero);
}
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) => _createPath(rect);
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}
#override
ShapeBorder scale(double t) => WeirdBorder(radius: radius);
Path _createPath(Rect rect) {
final innerRadius = radius + pathWidth;
final innerRect = Rect.fromLTRB(rect.left + pathWidth, rect.top + pathWidth, rect.right - pathWidth, rect.bottom - pathWidth);
final outer = Path.combine(PathOperation.difference, Path()..addRect(rect), _createBevels(rect, radius));
final inner = Path.combine(PathOperation.difference, Path()..addRect(innerRect), _createBevels(rect, innerRadius));
return Path.combine(PathOperation.difference, outer, inner);
}
Path _createBevels(Rect rect, double radius) {
return Path()
..addOval(Rect.fromCircle(center: Offset(rect.left, rect.top), radius: radius))
..addOval(Rect.fromCircle(center: Offset(rect.left + rect.width, rect.top), radius: radius))
..addOval(Rect.fromCircle(center: Offset(rect.left, rect.top + rect.height), radius: radius))
..addOval(Rect.fromCircle(center: Offset(rect.left + rect.width, rect.top + rect.height), radius: radius));
}
}
Also you can use fw_ticket package.