Flutter Curve Bar - flutter

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:

Related

Flutter draw container with a curve in the center

I'd like to draw the black container shape with flutter.
There are many ways to do this, the first time I thought it was a cut, I was thinking in a ClipPath widget, but now I see you have a Circle widget at the top of your black Container.
This is an example:
class FunnyContainer extends StatelessWidget {
const FunnyContainer({Key? key}) : super(key: key);
Widget _childContainer() {
return Padding(
padding: const EdgeInsets.all(20.0),
child: DecoratedBox(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.yellow,
width: 4,
),
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 40),
child: Container(
height: 400,
decoration: const BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Stack(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 40),
Expanded(child: _childContainer()),
Expanded(child: _childContainer()),
Expanded(child: _childContainer()),
],
),
const Positioned(
left: 0,
right: 0,
top: -50,
child: CircleAvatar(
backgroundColor: Colors.white,
radius: 40,
),
)
],
),
),
),
);
}
}
Result:
UPDATE (Using your updated design)
Now the you updated your post, this could be done using Clippers, the problem is that clip needs Shadow, so I took this code : https://gist.github.com/coman3/e631fd55cd9cdf9bd4efe8ecfdbb85a7
And I used on this example:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: Padding(
padding: const EdgeInsets.all(40.0),
child: ClipShadowPath(
clipper: _MyClipper(100),
shadow: const Shadow(
blurRadius: 15,
color: Colors.grey,
offset: Offset(0, 10),
),
child: SizedBox(
height: 400,
child: Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: const [
Spacer(),
],
),
),
),
),
),
),
);
}
}
class _MyClipper extends CustomClipper<Path> {
final double space;
_MyClipper(this.space);
#override
Path getClip(Size size) {
final path = Path();
final halfWidth = size.width / 2;
final halfSpace = space / 2;
final curve = space / 6;
final height = halfSpace / 1.4;
path.lineTo(halfWidth - halfSpace, 0);
path.cubicTo(halfWidth - halfSpace, 0, halfWidth - halfSpace + curve,
height, halfWidth, height);
path.cubicTo(halfWidth, height, halfWidth + halfSpace - curve, height,
halfWidth + halfSpace, 0);
path.lineTo(size.width, 0);
path.lineTo(size.width, size.height);
path.lineTo(0, size.height);
path.close();
return path;
}
#override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => true;
}
RESULT
You just need to update the Path in order to get the rounded corners of the Container.
You may use Stack which may write draw circle on top of the container like this:
class BlackContainer extends StatelessWidget {
const BlackContainer({
Key? key,
required this.child,
}) : super(key: key);
final Widget child;
#override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
Container(
child: child,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.black,
boxShadow: const [
BoxShadow(
color: Colors.grey,
spreadRadius: 5,
blurRadius: 10,
),
],
),
),
Positioned(
top: -25,
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
width: 40,
height: 40,
),
),
],
);
}
}
And use it like that:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: BlackContainer(
child: Text(
'Content here',
style: Theme.of(context)
.textTheme
.bodyText2!
.copyWith(color: Colors.white),
),
),
),
);
}
}
Position a transparent png on top of your container.
child: Container(
width: 200,
height: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.black,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3),
),
],
),
child: Container(
child: Image.asset('assets/images/half-circle.png', fit: BoxFit.contain, alignment: new Alignment(-1.0, -1.0)),
margin: EdgeInsets.only(left: 50.0, right: 50.0),
),
),
Image:
Live example

How make curved container in flutter

I need make container with angle for chat messaging application.
enter image description here
return Container(
margin: EdgeInsets.only(left: 30, top: 100, right: 30, bottom: 50),
height: double.infinity,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)
),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3), // changes position of shadow
),
],
),
Please help, how make it?
Here you can go, make a widget and call that widget for use.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Positioned(
top: 20,
left: 20,
child: Container(
height: 50,
width: 300,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3), // changes position of shadow
),
],
),
),
),
Positioned(
top: 70,
left: 300,
child: CustomPaint(painter: triangle()),
)
],
));
}
The custom paint, you can call the below widget.
class triangle extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()..color = Colors.blue;
var path = Path();
path.lineTo(-10, 0);
path.lineTo(0, 10);
path.lineTo(10, 0);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
Now just edit the path as you like to do.
Changed my answer and adapted from Flutter - ClipPath
Container(
margin:
EdgeInsets.only(left: 30, top: 100, right: 30, bottom: 50),
height: 100,
width: double.infinity,
decoration: ShapeDecoration(
color: Colors.white,
shape: MessageBorder(),
shadows: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: Offset(0, 3), // changes position of shadow
),
],
),
),
class MessageBorder extends ShapeBorder {
final bool usePadding;
MessageBorder({this.usePadding = true});
#override
EdgeInsetsGeometry get dimensions =>
EdgeInsets.only(bottom: usePadding ? 20 : 0);
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) => null;
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
rect = Rect.fromPoints(rect.topLeft, rect.bottomRight - Offset(0, 20));
return Path()
..addRRect(RRect.fromLTRBAndCorners(
rect.left, rect.top, rect.right, rect.bottom,
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
bottomRight: Radius.circular(10)))
..moveTo(30, rect.bottomCenter.dy)
..relativeLineTo(0, 20)
..relativeLineTo(20, -20)
..close();
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}
#override
ShapeBorder scale(double t) => this;
}

How to add shadow to ClipOval in flutter?

I have been trying to make a new app being a beginner. So, adding shadows to things is completely new to me.
So, Following is my code:
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
ClipOval(
child: Material(
color: Colors.white, // button color
child: InkWell(
// splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 46, height: 46, child: Icon(Icons.menu,color: Colors.red,),),
onTap: () {},
),
),
),
],
),
),
Following is the mock:
Adding shadow to ClipOval:
Center(
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.green,
blurRadius: 50.0,
spreadRadius: 10.0,
)
],
),
child: ClipOval(
child: Image.network(
'https://i.picsum.photos/id/384/536/354.jpg?hmac=MCKw0mm4RrI3IrF4QicN8divENQ0QthnQp9PVjCGblo',
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
),
),
Output:
You can create your own CustomClipper
class CustomClipperOval extends CustomClipper<Rect> {
#override
Rect getClip(Size size) {
return Rect.fromCircle(
center: new Offset(size.width / 2, size.width / 2),
radius: size.width / 2 + 3);
}
#override
bool shouldReclip(CustomClipper<Rect> oldClipper) {
return false;
}
}
class ClipOvalShadow extends StatelessWidget {
final Shadow shadow;
final CustomClipper<Rect> clipper;
final Widget child;
ClipOvalShadow({
#required this.shadow,
#required this.clipper,
#required this.child,
});
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: _ClipOvalShadowPainter(
clipper: this.clipper,
shadow: this.shadow,
),
child: ClipRect(child: child, clipper: this.clipper),
);
}
}
class _ClipOvalShadowPainter extends CustomPainter {
final Shadow shadow;
final CustomClipper<Rect> clipper;
_ClipOvalShadowPainter({#required this.shadow, #required this.clipper});
#override
void paint(Canvas canvas, Size size) {
var paint = shadow.toPaint();
var clipRect = clipper.getClip(size).shift(Offset(0, 0));
canvas.drawOval(clipRect, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
And then to use it
ClipOvalShadow(
shadow: Shadow(
color: Colors.amber,
offset: Offset(1.0, 1.0),
blurRadius: 2,
),
clipper: CustomClipperOval(),
child: ClipOval(
child: Material(
color: Colors.white, // button color
child: InkWell(
// splashColor: Colors.red, // inkwell color
child: Container(
width: 46,
height: 46,
child: Icon(
Icons.menu,
color: Colors.black,
),
),
onTap: () {},
),
),
),
),
The Result will be

Flutter how to draw semicircle (half circle)

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),
))),

Outlined transparent button with gradient border in flutter

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