Flutter : Need help to implement card with image - flutter

I need to implement a design similar to the image displayed below:

Here is a possible solution with a CustomPainter and a Path to generate the drawing. I used the cubicTo and a addRect functions of the Path class. For more information about these functions and path drawing, I refer to this: Paths in Flutter: A Visual Guide
This is the class which extends the CustomPainter
class CardPainter extends CustomPainter {
final Color color;
final double strokeWidth;
CardPainter({this.color = Colors.black, this.strokeWidth = 3});
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = color
..strokeWidth = this.strokeWidth
..style = PaintingStyle.stroke;
canvas.drawPath(getShapePath(size.width, size.height), paint);
}
// This is the path of the shape we want to draw
Path getShapePath(double x, double y) {
return Path()
..moveTo(2 * x / 3, y)
..cubicTo(x / 1.5, y, 0, y / 3, x, 0) // draw cubic curve
..addRect(Rect.fromLTRB(0, 0, x, y)); // draw rectangle
}
#override
bool shouldRepaint(CardPainter oldDelegate) {
return oldDelegate.color != color;
}
}
Here is the implementation of the CustomPainter
class CustomCard extends StatelessWidget {
CustomCard({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Custom Card Demo')),
body: Center(
child: Container(
color: Colors.grey[300],
width: 250,
height: 120,
padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
child: CustomPaint(
painter: CardPainter(color: Colors.blueAccent, strokeWidth: 3), // this is your custom painter
child: Stack(
children: <Widget>[
Positioned(
top: 25,
left: 30,
child: Text('Text1'),
),
Positioned(
top: 55,
left: 150,
child: Text('Text2'),
),
],
),
),
),
),
);
}
}

Related

Moving Circle in customer painter with gesture detector not working

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

Quarter Circle does not fit my initial circle Flutter Painter

I'm trying to make a loading icon for my loading screen using CustomPaint widget. Unfortunally, it doesn't work as expected...
My quarter circle does not fit in my primary circle, here's a screen of it :
I don't understand why it is doing this...
Here's the code:
import 'package:flutter/material.dart';
import 'package:responsive_sizer/responsive_sizer.dart';
import 'dart:math' as math;
class LoadingPage extends StatelessWidget {
const LoadingPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(
height: 25.h,
),
Stack(
alignment: Alignment.center,
children: [
Container(
width: 28.w,
height: 28.w,
decoration: BoxDecoration(
color: Colors.transparent,
shape: BoxShape.circle,
border: Border.all(width: 3.w, color: Colors.white.withOpacity(0.2)),
),
),
CustomPaint(
painter: MyPainter(),
size: Size(28.w, 28.w),
),
]
),
SizedBox(
height: 6.h,
),
Text(
"LOADING",
style: Theme.of(context).textTheme.headline2,
)
],
);
}
}
class MyPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint()
..strokeWidth = 3.w
..style = PaintingStyle.stroke
..color = Colors.blue;
double degToRad(double deg) => deg * (math.pi / 180.0);
final path = Path()
..arcTo(
Rect.fromCircle(
center: Offset(size.width/2, size.height/2),
radius: 28.w/2,
),
degToRad(90),
degToRad(90),
false);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
Does anyone knows why ? :)
Thanks for your answers,
Chris

Flutter custom shape using CustomPaint

I just want my created shape so that i can stack a widget to achieve the Image below. i am trying to get the transparent shape at the back ground of the X and Love. I Try using the shape maker but my mouse designing is not perfect. here is the code generated from the shape maker
child: CustomPaint(
size: Size(400,(400*0.2857142857142857).toDouble()),
painter: RPSCustomPainter(),
),
class RPSCustomPainter 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(size.width*0.2137714,size.height*0.2524000);
path_0.cubicTo(size.width*0.1736143,size.height*0.4775500,size.width*0.1973000,size.height*0.6711500,size.width*0.2153286,size.height*0.7510000);
path_0.cubicTo(size.width*0.2270429,size.height*0.7777500,size.width*0.2705286,size.height*0.9439500,size.width*0.3556000,size.height*0.7521500);
path_0.cubicTo(size.width*0.3856000,size.height*0.6504000,size.width*0.3970143,size.height*0.6162000,size.width*0.4283571,size.height*0.7526000);
path_0.cubicTo(size.width*0.4669286,size.height*0.8264000,size.width*0.5172429,size.height*0.9022500,size.width*0.5719714,size.height*0.7500000);
path_0.cubicTo(size.width*0.6146429,size.height*0.5440500,size.width*0.5914429,size.height*0.3101000,size.width*0.5713714,size.height*0.2514000);
path_0.cubicTo(size.width*0.5520714,size.height*0.1778000,size.width*0.4875429,size.height*0.0767500,size.width*0.4296571,size.height*0.2527000);
path_0.cubicTo(size.width*0.4023714,size.height*0.3646000,size.width*0.3816857,size.height*0.3850000,size.width*0.3557143,size.height*0.2523000);
path_0.cubicTo(size.width*0.3438571,size.height*0.2086000,size.width*0.2652143,size.height*0.0579000,size.width*0.2137714,size.height*0.2524000);
path_0.close();
canvas.drawPath(path_0, paint_0);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
what i am trying to achieve
my result. the shape is not perfect.
thanks
At first I wanted to describe you the ways you can achieve the shape you want and so on...
But got carried away with this fun programming challenge and ended up creating an actual widget :)
It depends on the font_awesome_flutter package, so don't forget to install it (for the heart icon). font_awesome_flutter
So the widget's source code is:
import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
extension ToRadians on int {
double get toRadians => this * (math.pi / 180.0);
}
enum _ButtonType { like, dislike }
class LikeOrNot extends StatelessWidget {
final VoidCallback onLike;
final VoidCallback onDislike;
// Percents from total widget width, default - 2%
final _gapSizeRatio = 0.02;
final _likeIconColor = const Color(0xffb85076);
final _dislikeIconColor = Colors.white;
const LikeOrNot({
Key? key,
required this.onLike,
required this.onDislike,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 2,
child: LayoutBuilder(
builder: (context, constraints) {
final buttonPaddings = constraints.maxHeight * 0.1;
final halfWidth = constraints.maxWidth / 2;
return Stack(
children: [
Positioned.fill(
child: CustomPaint(
painter: RPSCustomPainter(
gapSizeRatio: _gapSizeRatio,
),
),
),
Positioned(
left: 0,
bottom: 0,
top: 0,
right: halfWidth + constraints.maxWidth * _gapSizeRatio,
child: SizedBox.expand(
child: Padding(
padding: EdgeInsets.all(buttonPaddings),
child: _buildButton(_ButtonType.dislike),
),
),
),
Positioned(
right: 0,
bottom: 0,
top: 0,
left: halfWidth + constraints.maxWidth * _gapSizeRatio,
child: SizedBox.expand(
child: Padding(
padding: EdgeInsets.all(buttonPaddings),
child: _buildButton(_ButtonType.like),
),
),
),
],
);
},
),
);
}
Widget _buildButton(_ButtonType buttonType) {
final isPositiveAction = buttonType == _ButtonType.like;
return ElevatedButton(
style: ElevatedButton.styleFrom(
shape: const CircleBorder(),
primary: isPositiveAction ? _dislikeIconColor : _likeIconColor,
onPrimary: isPositiveAction ? _likeIconColor : _dislikeIconColor,
padding: EdgeInsets.zero,
elevation: 10,
shadowColor: Colors.black54,
),
onPressed: onDislike,
child: FractionallySizedBox(
widthFactor: 0.35,
heightFactor: 0.35,
child: FittedBox(
child: isPositiveAction
? const FaIcon(FontAwesomeIcons.heart)
: const Icon(Icons.close),
),
),
);
}
}
class RPSCustomPainter extends CustomPainter {
final double gapSizeRatio;
RPSCustomPainter({
required this.gapSizeRatio,
});
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.black.withOpacity(0.08)
..style = PaintingStyle.fill
..strokeWidth = 1;
final path = Path();
final gapSize = size.width * gapSizeRatio;
final arcRadius = size.height / 2 - gapSize / 2;
final leftCircleCenter = Offset(
size.width * 0.25 - gapSize / 2,
size.height / 2,
);
final rightCircleCenter = Offset(
size.width * 0.75 + gapSize / 2,
size.height / 2,
);
path.arcTo(
Rect.fromCircle(
center: leftCircleCenter,
radius: arcRadius,
),
45.toRadians,
270.toRadians,
false,
);
final bezierOffset = arcRadius * (105 / 360);
path.quadraticBezierTo(
size.width / 2,
size.height * 0.30,
rightCircleCenter.dx - arcRadius + bezierOffset,
rightCircleCenter.dy - arcRadius + bezierOffset,
);
path.arcTo(
Rect.fromCircle(
center: rightCircleCenter,
radius: arcRadius,
),
225.toRadians,
270.toRadians,
false,
);
path.quadraticBezierTo(
size.width / 2,
size.height * 0.70,
leftCircleCenter.dx + arcRadius - bezierOffset,
leftCircleCenter.dy + arcRadius - bezierOffset,
);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
It's dynamic, the buttons are built-in. You get 2 options to work with - onDislike() and onLike() callbacks.
An example using different sizes.
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xffc0496f), Color(0xffdb6b59)],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
title: const Text('Test'),
backgroundColor: Colors.transparent,
elevation: 0,
),
body: Container(
width: double.infinity,
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
for (final size in List.generate(5, (index) => index++))
FractionallySizedBox(
widthFactor: 1.0 - size * 0.2,
child: LikeOrNot(
onLike: () {},
onDislike: () {},
),
),
],
),
),
),
);
}
}
There's a _gapSize parameter which is the gap between two circles. The one you need is already inside (2% default) but you can get some cool other variations by changing it. For example, here's a gap of 20% total width:

CustomPaint inside custom button flutter

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;
}

Flutter: Remove space between widgets

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