Flutter Draw Ring - flutter

Please I'm trying to draw a ring using custom paint in flutter, is there any idea on how to implement it?
As I tried to implement it like this but it's not accepted as I need the shadow to be more professional:
Stack(
alignment: Alignment.center,
children: [
Container(
height: 180,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 2,
spreadRadius: 1,
offset: Offset(0, 2)),
],
),
),
Container(
height: 150,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 2,
spreadRadius: 1,
offset: Offset(0, 2)),
],
),
),
],
),

Yes it could be done easily using Custom Paint.
class MyHome extends StatelessWidget{
#override
Widget build(BuildContext context){
return Scaffold(
body: Center(
child: Container(
height:200,
width:200,
child:CustomPaint(
painter:RingPainter(),
),
),
),
);
}
}
class RingPainter extends CustomPainter{
#override
void paint(Canvas canvas, Size size) {
double height=size.height;
double width=size.width;
//Paint to draw ring shape
Paint paint=Paint()..color=Colors.green..strokeWidth=16.0
..style=PaintingStyle.stroke..strokeCap=StrokeCap.round;
//defining Center of Box
Offset center=Offset(width/2,height/2);
//defining radius
double radius=min(width/2,height/2);
canvas.drawCircle(center,radius,paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}

Related

How can I create a circle outline with a shadow in Flutter?

I need a circular OutlinedButton with an elevation but that property is not available for that widget. I'm trying to create a circle outline with a shadow and wrap it in a GestureDetector to achieve the same behavior but all options that I have tried have the shadow all the way to the center of the circle or don't have any shadow in the inner part of the circle.
This is the output that I'm looking for:
How can I achieve this?
Try This Code:
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(20),
child: IconButton(
onPressed: () {},
iconSize: 200,
icon: const Icon(
Icons.circle_outlined,
shadows: [Shadow(blurRadius: 70)],
),
color: Colors.blue,
),
),
);
}
you can use a standard OutlinedButton widget with a modified shape property:
class RingBorder extends CircleBorder {
const RingBorder({
this.thickness = 12,
this.color = Colors.blue,
this.shadowColor = Colors.black,
this.shadowRadius = 6,
this.paintShadowCounter = 1,
});
final double thickness;
final Color color;
final Color shadowColor;
final double shadowRadius;
final int paintShadowCounter;
#override
CircleBorder copyWith({BorderSide? side, double? eccentricity}) => this;
#override
void paint(ui.Canvas canvas, ui.Rect rect, {ui.TextDirection? textDirection}) {
final path = Path()
..fillType = PathFillType.evenOdd
..addOval(Rect.fromCircle(center: rect.center, radius: rect.shortestSide / 2 - shadowRadius))
..addOval(Rect.fromCircle(center: rect.center, radius: rect.shortestSide / 2 - shadowRadius - thickness));
final paint = Paint()..color = color;
canvas.drawPath(path, paint);
paint
..color = shadowColor
..maskFilter = MaskFilter.blur(BlurStyle.outer, shadowRadius);
for (int i = 0; i < paintShadowCounter; i++) {
canvas.drawPath(path, paint);
}
}
}
now you can use it as follows:
OutlinedButton(
style: ButtonStyle(
shape: MaterialStatePropertyAll(RingBorder(
color: Colors.orange,
paintShadowCounter: 4,
shadowRadius: 12,
)),
),
onPressed: () => print('pressed'),
child: Container(),
);
notice that all params are optional and contain some default values
you can take IconButton and take circle_outlined as icon and give shadow as per your need:
IconButton(
onPressed: () {},
iconSize: 50,
icon: const Icon(
Icons.circle_outlined,
shadows: [Shadow(blurRadius: 20)],
),
color: Colors.blue,
),
Try below code hope its help to you. I have try same like as your shared image
Container(
alignment: Alignment.topCenter,
padding: const EdgeInsets.all(20),
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
color: Colors.transparent,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.9),
spreadRadius: 6, //change on your need
blurRadius: 10,
offset: const Offset(0, 3),
)
],
),
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
border: Border.all(color: Colors.blue, width: 5),
boxShadow: const [
BoxShadow(
color: Colors.black26,
),
BoxShadow(
color: Colors.white,
spreadRadius: -10.0, //change on your need
blurRadius: 12.0,
),
],
),
),
),
),
Result->
wrap the OutlinedButton in a Container and apply a BoxShadow to the Container.
GestureDetector(
onTap: () {},
child: Container(
width: 100.0,
height: 100.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10.0,
offset: Offset(0, 10),
),
],
),
child: OutlinedButton(
onPressed: () {},
child: Text("Button"),
),
),
)
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: InkWell(
onTap: () {},
child: Container(
height: 150,
width: 150,
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: const BorderRadius.all(
Radius.circular(100),
),
border: Border.all(
color: Color(0xff00AEEF),
width: 3.w),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.9),
spreadRadius: 4,
blurRadius: 10,
offset: Offset(0, 3),
)
]),
child: OutlinedButton(
onPressed: () {},
child: Text("You can add Text and icon"),
),
),
),
);
}

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

Change the fill color of the container from left to right in Flutter

I am trying to change the color of a container from say white to red. But I want to do it from left to right that is, from the left of the container the color red starts filling up and expands all the way to the right in some duration. I know animated container will change the color but it seems to change the color of the entire container and not how I want it to
Like the 'Next Episode' of Netflix
You could use the Stack Widget and use an AnimatedContainer in between the background container (grey) and the foreground container (displaying icon & text):
class NetflixCustomButton extends StatefulWidget {
final Duration animationDuration;
final double height;
final double width;
final double borderRadius;
const NetflixCustomButton({
this.animationDuration = const Duration(milliseconds: 800),
this.height = 30,
this.width = 130.0,
this.borderRadius = 10.0,
});
#override
_NetflixCustomButtonState createState() =>
_NetflixCustomButtonState();
}
class _NetflixCustomButtonState
extends State<NetflixCustomButton> {
double _animatedWidth = 0.0;
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: widget.height,
width: widget.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
widget.borderRadius,
),
color: Colors.grey,
),
),
AnimatedContainer(
duration: widget.animationDuration,
height: widget.height,
width: _animatedWidth,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
widget.borderRadius,
),
color: Colors.white,
),
),
InkWell(
child: Container(
height: widget.height,
width: widget.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
widget.borderRadius,
),
color: Colors.transparent,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.play_arrow,
color: Colors.black87,
),
Text(
'Next Episode',
style: TextStyle(
color: Colors.black87,
),
),
],
),
),
onTap: () {
setState(() {
_animatedWidth = widget.width;
});
},
),
],
);
}
}

Adding text to custom button

I am able to create custom button widget and now i want add text to this. I tried several option adding it under return container under Flatbutton but it does't work.
Can someone please guide me where exactly i can add text which i can pass to this widget along with icon and will be displayed below icon.
I can add either text or icon right now and I am looking for text below icon.
import 'package:tirthankar/core/const.dart';
import 'package:flutter/material.dart';
class CustomButtonWidget extends StatelessWidget {
final Widget child;
final double size;
final double borderWidth;
final String image;
final bool isActive;
final VoidCallback onTap;
CustomButtonWidget({
this.child,
#required this.size,
#required this.onTap,
this.borderWidth = 2,
this.image,
this.isActive = false
});
#override
Widget build(BuildContext context) {
var boxDecoration = BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(200),
),
border: Border.all(
width: borderWidth,
color: isActive ? AppColors.darkBlue : AppColors.mainColor,
),
boxShadow: [
BoxShadow(
color: AppColors.lightBlueShadow,
blurRadius: 10,
offset: Offset(5,5),
spreadRadius: 3,
),
BoxShadow(
color: Colors.white60,
blurRadius: 10,
offset: Offset(-5,-5),
spreadRadius: 3,
)
],
);
if (image != null){
boxDecoration = boxDecoration.copyWith(
image: DecorationImage(
image: ExactAssetImage(image),
fit: BoxFit.cover
),
);
}
if (isActive){
boxDecoration = boxDecoration.copyWith(
gradient: RadialGradient(
colors: [
AppColors.lightBlue,
AppColors.darkBlue,
]
),
);
} else {
boxDecoration = boxDecoration.copyWith(
gradient: RadialGradient(
colors: [
AppColors.mainColor,
AppColors.mainColor,
AppColors.mainColor,
Colors.white
]
),
);
}
return Container(
width: size,
height: size,
decoration: boxDecoration,
child: FlatButton(
padding: EdgeInsets.all(0),
onPressed: onTap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(200),)
),
child: child ?? Container(),
)
);
}
}
you can replace your child ?? Container() with Column like below :
return Container(
width: size,
height: size,
decoration: BoxDecoration(),
child: FlatButton(
padding: EdgeInsets.all(0),
onPressed: onTap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(200),
)),
child: Column(
children: <Widget>[
iconWidget, // child ?? Container()
textWidget,
],
),
),
);
also you can handle icon only or text only button with wrapping each iconWidget or textWidget with Visibility widget ...

Flutter Curve Bar

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: