I have a custom button that I plan to reuse over and over again.
How can I put a CustomPainter into CustomButtom, with the condition that it also changes its internal state?
class CustomButton extends StatelessWidget {
CustomButton({this.icon, this.text, this.onPressed});
final Icon icon;
final Text text;
final Function onPressed;
#override
Widget build(BuildContext context) {
return RawMaterialButton(
onPressed: onPressed,
elevation: 2.0,
fillColor: Colors.lightGreenAccent,
child: SizedBox(
width: 90,
height: 90,
child: Column(
children: [
icon,
text,
Container(
height: 15,
width: 70,
decoration: new BoxDecoration(
color: Colors.red,
borderRadius: new BorderRadius.all(Radius.elliptical(45, 10)),
),
),
],
),
),
padding: EdgeInsets.all(5.0),
shape: CircleBorder(),
);
}
}
How can I implement a smile animation that should not be a rotation, but a transition from one state to another?
Smooth redrawing of the CustomPaint part when the button is pressed
class SmileyPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final radius = 100.0;
final center = Offset(150, 120);
// Body
final paint = Paint()..color = Colors.yellow;
canvas.drawCircle(center, radius, paint);
// Eyes
canvas.drawOval(
Rect.fromCenter(
center: Offset(center.dx + radius / 3, center.dy - radius / 3),
width: 25,
height: 60),
Paint());
canvas.drawOval(
Rect.fromCenter(
center: Offset(center.dx - radius / 3, center.dy - radius / 3),
width: 25,
height: 60),
Paint());
final paintMouth = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 10;
Path path = Path();
path.moveTo(200, 150);
path.quadraticBezierTo(150, 200, 100, 150);
path.moveTo(200, 150);
path.quadraticBezierTo(150, 220, 100, 150);
canvas.drawPath(path, paintMouth);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Related
Im trying to draw the ticket model using dart canvas.
But I just can't draw those semi-circles. path.arcToPoint seems not to work.
Try this demo:
import 'package:flutter/material.dart';
class Ticket extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.orange
..strokeWidth = 2
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
Path path = Path();
//top
path.moveTo(50, 0);
path.lineTo(50, 150);
path.arcToPoint(const Offset(70, 170), radius: const Radius.circular(20));
path.lineTo(300, 170);
path.arcToPoint(const Offset(320, 150), radius: const Radius.circular(20));
path.lineTo(320, 0);
//bottom
path.moveTo(70, 170);
path.lineTo(300, 190);
path.arcToPoint(const Offset(320, 210), radius: const Radius.circular(20), clockwise: false);
path.lineTo(310, 290);
path.arcToPoint(const Offset(290, 300), radius: const Radius.circular(20));
path.lineTo(60, 280);
path.arcToPoint(const Offset(40, 260), radius: const Radius.circular(20));
path.lineTo(50, 190);
path.arcToPoint(const Offset(70, 170), radius: const Radius.circular(20), clockwise: false);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(Ticket oldDelegate) => false;
#override
bool shouldRebuildSemantics(Ticket oldDelegate) => false;
}
use:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: CustomPaint(
painter: Ticket(),
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
),
),
),
);
}
I try to create a border around a container, that is not that difficult ofcourse, but i need also a text IN the border with space around it. Like horizontal divder but i need a complete border.
Attached what i like to achieve.
Any one who can help me how to approach this?
Thanks!
Tried to use the horizontal and vertical divider packages, but then the border is not in full.
You can use CustomPainter like this:
class CustomDraw extends CustomPainter {
late Paint painter;
late double radius;
late double textWidth;
CustomDraw(Color color, this.textWidth, {this.radius = 0}) {
painter = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2
..color = color;
}
#override
void paint(Canvas canvas, Size size) {
var path = Path();
path.moveTo(size.width - ((size.width - textWidth) / 2), 0);
path.lineTo(size.width - radius, 0);
path.cubicTo(size.width - radius, 0, size.width, 0, size.width, radius);
path.lineTo(size.width, size.height - radius);
path.cubicTo(size.width, size.height - radius, size.width, size.height,
size.width - radius, size.height);
path.lineTo(radius, size.height);
path.cubicTo(radius, size.height, 0, size.height, 0, size.height - radius);
path.lineTo(0, radius);
path.cubicTo(0, radius, 0, 0, radius, 0);
path.lineTo(((size.width - textWidth) / 2), 0);
canvas.drawPath(path, painter);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
and this widget:
class CustomTitleWidget extends StatefulWidget {
final double height;
final double width;
final double? radius;
final String title;
const CustomTitleWidget(
{Key? key,
required this.height,
required this.width,
required this.title,
this.radius})
: super(key: key);
#override
State<CustomTitleWidget> createState() => _CustomTitleWidgetState();
}
class _CustomTitleWidgetState extends State<CustomTitleWidget> {
GlobalKey textKey = GlobalKey();
double textHeight = 0.0;
double textWidth = 0.0;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
final textKeyContext = textKey.currentContext;
if (textKeyContext != null) {
final box = textKeyContext.findRenderObject() as RenderBox;
textHeight = box.size.height;
textWidth = box.size.width;
}
});
});
}
#override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
alignment: Alignment.topCenter,
children: [
CustomPaint(
child: Container(
height: widget.height,
width: widget.width,
),
painter: CustomDraw(
Colors.red,
textWidth,
radius: widget.radius ?? 0,
),
),
Positioned(
top: -textHeight / 2,
child: Padding(
key: textKey,
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
widget.title,
),
),
)
],
);
}
}
use like this:
CustomTitleWidget(
height: 200,
width: double.infinity,
title: 'asdasdasdasdasd',
radius: 16),
#Maenda, We can implement such kind of structure using Stack, take one container with a border & put the other container over the first one.
Here is an example:
Stack(
children: [
Positioned(
top: 12,
left: 0,
right: 0,
child: Container(
height: 120,
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.green,
border: Border.all(width: 1.2),
borderRadius: BorderRadius.circular(3)),
)),
Container(
margin: EdgeInsets.symmetric(
horizontal: 21,
),
child: Column(
children: [
Container(
width: 100,
margin: EdgeInsets.only(top: 12, bottom: 5),
alignment: Alignment.center,
color: Colors.blue,
child: Text(
"Any Text",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
Text(
"Nature, in the broadest sense, is the physical world or universe. Nature can refer to the phenomena of the physical world, and also to life in general.",
textAlign: TextAlign.center,
),
SizedBox(
height: 8,
)
],
),
),
],
);
Hope this works! You can customize as per the design.
I am trying to use gesture detector to move the red point that i draw in custom painter around the xy plot that i draw in a seperate custom painter but i cant seem to get it to work. I would like to thank any help give in advance.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isDown = false;
var xPos = 5.0;
var yPos = 5.0;
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Builder(builder: (BuildContext context) {
return Scaffold(
body: Center(
child: Container(
alignment: Alignment.center,
height: MediaQuery.of(context).size.height * 0.3,
width: MediaQuery.of(context).size.width,
color: Colors.grey,
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.3,
),
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: LayoutBuilder(
builder: (_, constraints) => Container(
width: constraints.widthConstraints().maxWidth,
height: constraints.widthConstraints().maxHeight,
color: Colors.white,
child: Stack(children: [
// plotting X Y axis
SizedBox(
width: constraints.widthConstraints().maxWidth,
height:
constraints.widthConstraints().maxHeight,
child: CustomPaint(painter: PlotPainter())),
GestureDetector(
onPanUpdate: (details) {
final tapPos = details.localPosition;
if (tapPos.dx > 5 &&
tapPos.dx <
constraints
.widthConstraints()
.maxWidth) {
setState(() {
xPos = tapPos.dx - 5;
yPos = tapPos.dy - 5;
});
}
if (tapPos.dy > 5 &&
tapPos.dy <
constraints
.widthConstraints()
.maxHeight) {
setState(() {
yPos = tapPos.dy - 5;
});
}
},
child: SizedBox(
width:
constraints.widthConstraints().maxWidth,
height:
constraints.widthConstraints().maxHeight,
child: CustomPaint(
painter: PointsPainter(xPos, yPos))),
),
// plotting points
]),
)),
),
),
);
}));
}
}
class PointsPainter extends CustomPainter {
PointsPainter(this.xPos, this.yPos);
double xPos;
double yPos;
#override
void paint(Canvas canvas, Size size) {
// TODO: implement points
final midY = size.height / 2;
final midX = size.width / 2;
final Circlepaint = Paint()
..style = PaintingStyle.fill
..color = Colors.red;
canvas.drawCircle(Offset(xPos, yPos), 5, Circlepaint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}
// drawing the x y axis
class PlotPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final midY = size.height / 2;
final midX = size.width / 2;
final paint = Paint()
..style = PaintingStyle.fill
..color = Colors.black
..strokeWidth = 1.0;
final textPainterx = TextPainter(
text: const TextSpan(
text: 'x',
style: TextStyle(
color: Colors.black,
fontSize: 15,
),
),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center);
final textPaintery = TextPainter(
text: const TextSpan(
text: 'y',
style: TextStyle(
color: Colors.black,
fontSize: 13,
),
),
textDirection: TextDirection.ltr,
textAlign: TextAlign.center);
// X axis
canvas.drawLine(Offset(0, midY), Offset(size.width, midY), paint);
//y Axis
canvas.drawLine(Offset(midX, 0), Offset(midX, size.height), paint);
//arrow head of the X-Y Axis
canvas.drawLine(
Offset(size.width, midY), Offset(size.width - 5, midY - 3), paint);
canvas.drawLine(
Offset(size.width, midY), Offset(size.width - 5, midY + 3), paint);
canvas.drawLine(Offset(midX, 0), Offset(midX - 3, 5), paint);
canvas.drawLine(Offset(midX, 0), Offset(midX + 3, 5), paint);
textPainterx.layout();
textPaintery.layout();
// Draw the text X at the X axis
textPainterx.paint(canvas, Offset(size.width - 7, midY + 1));
// Draw the text y at the Y axis
textPaintery.paint(canvas, Offset(midX + 5, 0));
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
The Problem was that you did not wrap the custom painter inside gesture detector now that is working fine
now you only have to add condition that it will not go more up and down on given height
I have to design screen like above image:
I want to draw white background portion in the above image. I have try by using custom paint with clip path but I can't achieve like the image.
Here is my code:
class CommomContainer extends CustomClipper<Path> {
CommomContainer({#required this.holeRadius});
final double holeRadius;
#override
Path getClip(Size size) {
final path = Path()
..moveTo(0, 0)
..lineTo(size.width / 2 - holeRadius - 10, 0.0)
..quadraticBezierTo(
size.width / 2 - holeRadius, 0.0, size.width / 2 - holeRadius, 10.0)
..arcToPoint(
Offset(size.width / 2 + holeRadius, 0.0),
clockwise: false,
radius: Radius.circular(2),
)
..lineTo(size.width, 0.0)
..lineTo(size.width, size.height);
path.lineTo(0.0, size.height);
path.close();
return path;
}
#override
bool shouldReclip(CommomContainer oldClipper) => true;
}
Any help is appreciated.
Here you can use Shape Maker and I used that too. You can change stroke style from PaintingStyle.stroke to PaintingStyle.fill to fill the desired color.
class MyCustomPainter extends CustomPainter{
#override
void paint(Canvas canvas, Size size) {
Paint paint_0 = new Paint()
..color = Color.fromARGB(255, 33, 150, 243)
..style = PaintingStyle.stroke
..strokeWidth = 1;
Path path_0 = Path();
path_0.moveTo(0,0);
path_0.lineTo(size.width,0);
path_0.lineTo(size.width,size.height);
path_0.lineTo(0,size.height);
path_0.lineTo(0,0);
path_0.close();
canvas.drawPath(path_0, paint_0);
Paint paint_1 = new Paint()
..color = Color.fromARGB(255, 33, 150, 243)
..style = PaintingStyle.stroke
..strokeWidth = 1;
Path path_1 = Path();
path_1.moveTo(size.width*0.3071375,size.height*0.3273600);
path_1.cubicTo(size.width*0.3067750,size.height*0.1999400,size.width*0.3815000,size.height*0.1964000,size.width*0.4125000,size.height*0.2000000);
path_1.cubicTo(size.width*0.4514375,size.height*0.3614200,size.width*0.5514000,size.height*0.3594200,size.width*0.5875000,size.height*0.2000000);
path_1.quadraticBezierTo(size.width*0.6656000,size.height*0.1954200,size.width*0.6943500,size.height*0.3273600);
path_1.quadraticBezierTo(size.width*0.6943500,size.height*0.6060200,size.width*0.6943500,size.height*0.6990200);
path_1.quadraticBezierTo(size.width*0.6943500,size.height*0.8229400,size.width*0.6169000,size.height*0.8229400);
path_1.quadraticBezierTo(size.width*0.4427250,size.height*0.8229400,size.width*0.3846250,size.height*0.8229400);
path_1.quadraticBezierTo(size.width*0.3049125,size.height*0.8229600,size.width*0.3071375,size.height*0.6990200);
path_1.quadraticBezierTo(size.width*0.3071375,size.height*0.6060200,size.width*0.3071375,size.height*0.3273600);
path_1.close();
canvas.drawPath(path_1, paint_1);
Paint paint_2 = new Paint()
..color = Color.fromARGB(255, 33, 150, 243)
..style = PaintingStyle.stroke
..strokeWidth = 1;
Path path_2 = Path();
path_2.moveTo(size.width*0.5000002,size.height*0.0804608);
path_2.cubicTo(size.width*0.5273750,size.height*0.0804200,size.width*0.5684875,size.height*0.1111200,size.width*0.5684620,size.height*0.1899997);
path_2.cubicTo(size.width*0.5684875,size.height*0.2338200,size.width*0.5479250,size.height*0.2995600,size.width*0.5000002,size.height*0.2995385);
path_2.cubicTo(size.width*0.4726125,size.height*0.2995600,size.width*0.4315250,size.height*0.2667000,size.width*0.4315384,size.height*0.1899997);
path_2.cubicTo(size.width*0.4315250,size.height*0.1462000,size.width*0.4520625,size.height*0.0804200,size.width*0.5000002,size.height*0.0804608);
path_2.close();
canvas.drawPath(path_2, paint_2);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
Try this, I have used stack and then I have put 2 concentric circle on top of rectangle.
Scaffold(
backgroundColor: Colors.yellow,
body: Center(
child: Stack(
alignment: Alignment.topCenter,
clipBehavior: Clip.none,
children: [
Card(
color: Colors.white,
child: Container(
height: 300,
width: 300,
),
),
Positioned(
top: -50,
child: Container(
height: 100,
width: 100,
// color: Colors.yellow,
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.yellow),
),
),
Positioned(
top: -40,
child: Container(
height: 80,
width: 80,
// color: Colors.yellow,
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.white),
),
)
],
),));
By using CustomPainter i have created a half circle.Now i have this widget tree:
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
// card view
height: 100,
alignment: Alignment.center,
margin: EdgeInsets.only(
top: 20.0, bottom: 0.0, left: 50.0, right: 50.0),
decoration: BoxDecoration(
boxShadow: ([
BoxShadow(
color: color_transparent_black,
spreadRadius: 5,
blurRadius: 3.0,
offset: Offset(2, 3),
),
]),
borderRadius: BorderRadius.circular(14.0),
),),
MyArc(diameter: 200),
This is MyArc:
class MyArc extends StatelessWidget {
final double diameter;
const MyArc({Key key, this.diameter = 200}) : super(key: key);
#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, 0),
height: size.height,
width: size.width,
),
6.4,
2.9,
false,
paint,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
But i got this problem:
I don't want any space between gray and blue shape!!!
I finally figured it out...
import 'dart:math' as math;
//...
class MyPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint blue = Paint()..color = Colors.blue;
canvas.translate(0, -size.height / 2);
canvas.drawArc(
Offset.zero & size,
-math.pi,
-math.pi,
false,
blue,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
If you want to make your canvas fit nicely also you can make it rectangular like so...
CustomPaint(
painter: MyPainter(),
size: Size(diamieter, diameter / 2),
)
Then have you custom painter as...
class MyPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint blue = Paint()..color = Colors.blue;
Paint red = Paint()..color = Colors.red;
canvas.drawRect(Offset.zero & size, red);
canvas.translate(0, -size.height);
canvas.drawArc(
Offset.zero & Size(size.width, size.height * 2),
-math.pi,
-math.pi,
false,
blue,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
It'll fit snug this way...
try changing the spreadRadius to 3.0
Its possible that it's creating a boundary greater than the container's Blur Radius boundary.
BoxShadow(
color: color_transparent_black,
spreadRadius: 5,
blurRadius: 3.0,
offset: Offset(2, 3),
),