How can I draw semicircle like this?
Code:
class DrawHalfCircleClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
final Path path = new Path();
...
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return true;
}
Create a StatelessWidget say MyArc which accepts a diameter.
import 'dart:math' as math;
class MyArc extends StatelessWidget {
final double diameter;
const MyArc({super.key, this.diameter = 200});
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: MyPainter(),
size: Size(diameter, diameter),
);
}
}
// This is the Painter class
class MyPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()..color = Colors.blue;
canvas.drawArc(
Rect.fromCenter(
center: Offset(size.height / 2, size.width / 2),
height: size.height,
width: size.width,
),
math.pi,
math.pi,
false,
paint,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
Usage:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: MyArc(diameter: 300),
);
}
Container(
decoration: const BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(100),
bottomRight: Radius.circular(100),
),
With this code you can make a half circle.
create a class that extends from CustomClipper and use the arcToPoint method to draw the circle and use the ClipPath widget, here is the code to implement that
ClipPath(
clipper: CustomClip(),
child: Container(
width: 200,
height: 100,
color: Colors.blue,
),
),
class CustomClip extends CustomClipper<Path> {
#override
Path getClip(Size size) {
double radius = 100;
Path path = Path();
path
..moveTo(size.width / 2, 0)
..arcToPoint(Offset(size.width, size.height),
radius: Radius.circular(radius))
..lineTo(0, size.height)
..arcToPoint(
Offset(size.width / 2, 0),
radius: Radius.circular(radius),
)
..close();
return path;
}
#override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
return true;
}
}
with a simple implementation( not the best)
you can draw 2 container both have same width and height inside a row
and provide a BoxDectoration for each container => BorderRadius
as the following code,
this is not the best implementation , it just works
Row(children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topRight: Radius.circular(200),),
color: Colors.blue[300],
),
width: 200,
height: 200,
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(200),),
color: Colors.blue[300],
),
width: 200,
height: 200,
)
],
),
UPDATE: You only need a Container, EASY PEASY:
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(100),
topLeft: Radius.circular(100)),
color: Colors.red,
shape: BoxShape.rectangle,
),
height: 35,
width: 35,
),
Here is a simple code using Stack. You can easily generate a semicircle using a rectangle and a circle. Reshape the containers with BoxDecoration(shape:)
Stack(
children: [
Align(
alignment: Alignment.center,
child: Container(
height: 35,
width: 35,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
),
Align(
alignment: Alignment.centerLeft,
child: Container(
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.rectangle,
),
height: 35,
width: 35,
),
),
],
),
This will work, you can change the dimensions, but make sure the height is half the borderRadius and the width is equal to the borderRadius.
Container(
height: 50,
width:100,
decoration: const BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(100),
bottomRight: Radius.circular(100),
))),
Related
I currently have this code where I am trying to apply a specific type of corner to a container.
Eg.
class TestCode extends StatelessWidget {
const TestCode({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
width: 358,
height: 177,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(30)),
color: Color(0x6b000000),
shape: BoxShape.rectangle,
),
),
),
);
}
}
What I want to do is not to have rounded corners but cut off corners in a container.
You can use CustomPainter, first crate this class:
class CustomDraw extends CustomPainter {
late Paint painter;
CustomDraw(BuildContext buildContext, Color color) {
painter = Paint()
..color = color
..style = PaintingStyle.fill;
}
#override
void paint(Canvas canvas, Size size) {
var path = Path();
path.moveTo(0, size.height * 0.2);
path.lineTo(size.height * 0.2, 0);
path.lineTo(size.width, 0);
path.lineTo(size.width, size.height * 0.8);
path.lineTo(size.width - size.height * 0.2, size.height);
path.lineTo(0, size.height);
path.close();
canvas.drawPath(path, painter);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
then use it like this:
Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(),
child: CustomPaint(
size: Size(300, 100),
painter: CustomDraw(context, Colors.red),
),
),
here is the easiest way:
Material(
clipBehavior: Clip.antiAlias,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
bottomRight: Radius.circular(20.0))),
child: Container(
height: 100,
width: 100,
decoration: const BoxDecoration(
color: Colors.grey,
),
),
),
Output:
I wan to create above shape in flutter. I was using clipPath for getting the sides right but i couldn't get the rounded corners.
You must use for this shape flutter_custom_clippers package from here.
Add this dependency in your pubspec.yaml file
Try below code hope its help to you
Import package in your file
import 'package:flutter_custom_clippers/flutter_custom_clippers.dart';
Your Widget:
ClipPath(
clipper: RoundedDiagonalPathClipper(),
child: Container(
height: 320,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(
50.0,
),
),
color: Colors.grey[300],
),
child: Center(
child: Text("Your Shape"),
),
),
),
Your result screen->
You can do this with CustomClipper
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Rounded corner'),
),
body: Container(
decoration: BoxDecoration(
color: Color(0xff240046), borderRadius: BorderRadius.circular(15)),
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 18),
margin: EdgeInsets.symmetric(vertical: 8),
child: ClipPath(
clipper: CustomRectClipper(),
child: Container(
height: 500,
width: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(50.0)),
color: Colors.grey,
),
child: Center(child: Text("RoundedDiagonalPathClipper()")),
),
),
),
);
}
}
class CustomRectClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
Path path = new Path()
..lineTo(0.0, size.height)
..lineTo(size.width, size.height)
..lineTo(size.width, 0.0)
..quadraticBezierTo(size.width-50, 0.0, size.width - 60.0, -5.0)
..lineTo(40.0, 150.0) // here you adjust the value as much as you nee
..quadraticBezierTo(0.0, 180.0, 0.0, 220.0);
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return false;
}
}
Output:
How to create curve in container like this
If you need to create a curve for the container that the profile picture lives on top of, your best bet would be to use a ClipPath with a custom clipper.
Something like this would do the trick:
ClipPath(
clipper: CurveClipper(),
child: Container(
color: Colors.red,
height: 200.0,
),
);
Our custom CurveClipper requires us to draw a path that includes a bézier curve, to get that curve shape at the bottom of our container:
class CurveClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
int curveHeight = 40;
Offset controlPoint = Offset(size.width / 2, size.height + curveHeight);
Offset endPoint = Offset(size.width, size.height - curveHeight);
Path path = Path()
..lineTo(0, size.height - curveHeight)
..quadraticBezierTo(controlPoint.dx, controlPoint.dy, endPoint.dx, endPoint.dy)
..lineTo(size.width, 0)
..close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
It's rounded Appbar actually in this picture. To achive this:
AppBar(
title: Text('Anything'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
bottom: Radius.circular(30),
),
),
),
If you want container with this shape:
Container(
height: 200.0,
decoration: new BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.vertical(
bottom: Radius.elliptical(
MediaQuery.of(context).size.width, 100.0)),
),
),
I used CustomPainter to draw the required container and placed it at the bottom of the stack. The rest of the widgets can be aligned on top as required. Complete the rest of the screen by populating the Column widget.
Image of the output is as shown: Output Image for the design
Code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ProfileScreen(),
);
}
}
// class to draw the profile screen
class ProfileScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: const Color(0xffea5d49),
leading: Icon(
Icons.menu,
color: Colors.white,
),
),
body: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
),
painter: HeaderCurvedContainer(),
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(20.0),
child: Text(
'Profile',
style: TextStyle(
fontSize: 35.0,
letterSpacing: 1.5,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
Container(
width: MediaQuery.of(context).size.width / 2,
height: MediaQuery.of(context).size.width / 2,
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
// image: DecorationImage(
// image: AssetImage(null),
// fit: BoxFit.cover,
// ),
),
),
],
),
],
),
),
);
}
}
// CustomPainter class to for the header curved-container
class HeaderCurvedContainer extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()..color = const Color(0xffea5d49);
Path path = Path()
..relativeLineTo(0, 150)
..quadraticBezierTo(size.width / 2, 250.0, size.width, 150)
..relativeLineTo(0, -150)
..close();
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
I wonder if there is a better solution for making a curved bar like the following image.
Here is my flutter code:
import 'package:flutter_web/material.dart';
class CurvedBar extends StatelessWidget {
const CurvedBar({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(height: 50,
color: Colors.orange,
child: Column(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20.0),
bottomRight: Radius.circular(20.0)),
child: Container(
height: 20.0,
width: double.infinity,
color: Colors.white,
),
),
Container(
color: Colors.white,
child: Row(
children: <Widget>[
Flexible(
flex: 1,
child: ClipRRect(
borderRadius:
BorderRadius.only(topRight: Radius.circular(20.0)),
child: Container(
height: 20.0,
color: Colors.orange,
),
)),
Flexible(
flex: 1,
child: ClipRRect(
borderRadius:
BorderRadius.only(topLeft: Radius.circular(20.0)),
child: Container(
height: 20.0,
color: Colors.orange,
),
))
],
))
],
));
}
}
make a custom ShapeBorder class like this one (the key method is _getPath that returns your shape's Path):
class CustomShapeBorder extends ShapeBorder {
const CustomShapeBorder();
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) => _getPath(rect);
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) => _getPath(rect);
_getPath(Rect rect) {
final r = rect.height / 2;
final radius = Radius.circular(r);
final offset = Rect.fromCircle(center: Offset.zero, radius: r);
return Path()
..moveTo(rect.topLeft.dx, rect.topLeft.dy)
..relativeArcToPoint(offset.bottomRight, clockwise: false, radius: radius)
..lineTo(rect.center.dx - r, rect.center.dy)
..relativeArcToPoint(offset.bottomRight, clockwise: true, radius: radius)
..relativeArcToPoint(offset.topRight, clockwise: true, radius: radius)
..lineTo(rect.centerRight.dx - r, rect.centerRight.dy)
..relativeArcToPoint(offset.topRight, clockwise: false, radius: radius)
..addRect(rect);
}
#override
EdgeInsetsGeometry get dimensions {
return EdgeInsets.all(0);
}
#override
ShapeBorder scale(double t) {
return CustomShapeBorder();
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
}
}
now you can use it like:
Container(
margin: EdgeInsets.only(top: 80),
height: 50,
width: double.infinity,
decoration: ShapeDecoration(
shape: CustomShapeBorder(),
//color: Colors.orange,
gradient:
LinearGradient(colors: [Colors.blue, Colors.orange]),
shadows: [
BoxShadow(
color: Colors.black, offset: Offset(3, -3), blurRadius: 3),
],
),
),
Result:
Is it possible to create an outlined(transparent) button with gradient border in flutter? I tried to use LinearGradient in BorderSide style but it's not allowed.
I spent about two hours on it :)
how to use:
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
UnicornOutlineButton(
strokeWidth: 2,
radius: 24,
gradient: LinearGradient(colors: [Colors.black, Colors.redAccent]),
child: Text('OMG', style: TextStyle(fontSize: 16)),
onPressed: () {},
),
SizedBox(width: 0, height: 24),
UnicornOutlineButton(
strokeWidth: 4,
radius: 16,
gradient: LinearGradient(
colors: [Colors.blue, Colors.yellow],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
child: Text('Wow', style: TextStyle(fontSize: 16)),
onPressed: () {},
),
],
),
),
),
),
);
}
}
and the class itself:
class UnicornOutlineButton extends StatelessWidget {
final _GradientPainter _painter;
final Widget _child;
final VoidCallback _callback;
final double _radius;
UnicornOutlineButton({
#required double strokeWidth,
#required double radius,
#required Gradient gradient,
#required Widget child,
#required VoidCallback onPressed,
}) : this._painter = _GradientPainter(strokeWidth: strokeWidth, radius: radius, gradient: gradient),
this._child = child,
this._callback = onPressed,
this._radius = radius;
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: _painter,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: _callback,
child: InkWell(
borderRadius: BorderRadius.circular(_radius),
onTap: _callback,
child: Container(
constraints: BoxConstraints(minWidth: 88, minHeight: 48),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_child,
],
),
),
),
),
);
}
}
class _GradientPainter extends CustomPainter {
final Paint _paint = Paint();
final double radius;
final double strokeWidth;
final Gradient gradient;
_GradientPainter({#required double strokeWidth, #required double radius, #required Gradient gradient})
: this.strokeWidth = strokeWidth,
this.radius = radius,
this.gradient = gradient;
#override
void paint(Canvas canvas, Size size) {
// create outer rectangle equals size
Rect outerRect = Offset.zero & size;
var outerRRect = RRect.fromRectAndRadius(outerRect, Radius.circular(radius));
// create inner rectangle smaller by strokeWidth
Rect innerRect = Rect.fromLTWH(strokeWidth, strokeWidth, size.width - strokeWidth * 2, size.height - strokeWidth * 2);
var innerRRect = RRect.fromRectAndRadius(innerRect, Radius.circular(radius - strokeWidth));
// apply gradient shader
_paint.shader = gradient.createShader(outerRect);
// create difference between outer and inner paths and draw it
Path path1 = Path()..addRRect(outerRRect);
Path path2 = Path()..addRRect(innerRRect);
var path = Path.combine(PathOperation.difference, path1, path2);
canvas.drawPath(path, _paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => oldDelegate != this;
}
You can achieve this by doing just a simple trick
You have to define two Containers.
First outer container with a gradient background and the second inner container with white background. and as a child of the inner container, you can place anything e.g. TextField, Text, another button, etc.
final kInnerDecoration = BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(32),
);
final kGradientBoxDecoration = BoxDecoration(
gradient: LinearGradient(colors: [Colors.black, Colors.redAccent]),
border: Border.all(
color: kHintColor,
),
borderRadius: BorderRadius.circular(32),
);
Now this is your View
Container(
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
child:Text("Button Title with your style"),
decoration: kInnerDecoration,
),
),
height: 66.0,
decoration: kGradientBoxDecoration,
),
Done
Use OutlinedButton (Recommended)
Create this class (null-safe code)
class MyOutlinedButton extends StatelessWidget {
final VoidCallback onPressed;
final Widget child;
final ButtonStyle? style;
final Gradient? gradient;
final double thickness;
const MyOutlinedButton({
Key? key,
required this.onPressed,
required this.child,
this.style,
this.gradient,
this.thickness = 2,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(gradient: gradient),
child: Container(
color: Colors.white,
margin: EdgeInsets.all(thickness),
child: OutlinedButton(
onPressed: onPressed,
style: style,
child: child,
),
),
);
}
}
Usage:
MyOutlinedButton(
onPressed: () {},
gradient: LinearGradient(colors: [Colors.indigo, Colors.pink]),
child: Text('OutlinedButton'),
)
To change the size, you can insert a Container:
OutlineGradientButton(
child: Container(
constraints: BoxConstraints(maxWidth: 300, maxHeight: 50),
height: 50,
alignment: Alignment.center,
child: Text(
'Text',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white, fontSize: 20, fontWeight: FontWeight.w500),
),
),
gradient: LinearGradient(
colors: [Color(0xfff3628b), Color(0xffec3470)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
strokeWidth: 3,
radius: Radius.circular(25),
),
Now there is a easier way provided. Flutter now has a package which does the job perfectly. I am leaving a link to the documentation for further use
https://pub.dev/packages/gradient_borders.
You can use a structure like below. You can also use it as a flat button by removing BorderRadius.
InkWell(
onTap: () {
print("TAP");
},
child: Container(
height: 85,
width: 85,
padding: EdgeInsets.all(6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
gradient: LinearGradient(
colors: [Colors.blue, Colors.black],
begin: Alignment(-1, -1),
end: Alignment(2, 2),
),
),
child: Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(100),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
image: DecorationImage(
image: Image.network("https://images.unsplash.com/photo-1612151855475-877969f4a6cc?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8aGQlMjBpbWFnZXxlbnwwfHwwfHw%3D&w=400&q=80").image,
fit: BoxFit.fitHeight,
), //By deleting the image here, you can only use it text.
color: Colors.white,
border: Border.all(
color: Colors.white,
width: 4,
),
),
child: Center(child: Text("sssss")), //By deleting the text here, you can only use it visually.
width: 75,
height: 75,
),
),
),
),
)
Wrap your widget width CustomPaint widget and use this _CustomGradientBorder that extends CustomPainter.
CustomPaint(painter: const _CustomGradientBorder(thickness: 1,
colors: [Colors.red, Colors.green, Colors.blue, Colors.tealAccent],
radius: 8), child: //widget here)
class _CustomGradientBorder extends CustomPainter{
final double thickness;
final List<Color> colors;
final double radius;
const _CustomGradientBorder({required this.thickness, required this.colors, required this.radius});
#override
void paint(Canvas canvas, Size size) {
final Path path = Path();
path.moveTo(0, size.height/2);
path.lineTo(0, radius);
path.quadraticBezierTo(0, 0, radius, 0);
path.lineTo(size.width-radius, 0);
path.quadraticBezierTo(size.width, 0, size.width, radius);
path.lineTo(size.width, size.height-radius);
path.quadraticBezierTo(size.width, size.height, size.width-radius, size.height);
path.lineTo(radius, size.height);
path.quadraticBezierTo(0, size.height, 0, size.height-radius);
path.close();
final Paint paint = Paint()
..style = PaintingStyle.stroke
..shader = LinearGradient(colors: colors).createShader(Rect.fromCenter(center: Offset(size.width/2, size.height/2), width: size.width, height: size.height))
..strokeWidth = thickness;
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class GradientBorderWidget extends StatelessWidget {
final Widget child;
const GradientBorderWidget({super.key, required this.child});
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 1.5, vertical: 1.5),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: const LinearGradient(
colors: [color1, color2, color3],
begin: Alignment.centerLeft,
end: Alignment.centerRight)),
alignment: Alignment.center,
child: ClipRRect(borderRadius: BorderRadius.circular(15), child:
child),
);
}
}
I tried many ways to do that, but all of them had their limitations, then I found a package that worked how I expected: https://pub.dev/packages/outline_gradient_button