I want to get with custom painter something just like this. How I can draw it? For me not problem borders, gradients. I couldn't draw left part of my assets card. Please, help!
You can use shader on Paint to have gradient effect.
Paint paint = Paint()
..shader = const LinearGradient(
colors: [
Color.fromARGB(255, 141, 23, 15),
Colors.red,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
).createShader(
Rect.fromLTRB(0, 0, size.width, size.height),
)
Run on dartPad
The shape class
class EatenShape extends CustomPainter {
final double gap = 4.0;
final double radius;
final Radius _border;
final Color canvasColor;
EatenShape({
this.radius = 40,
required this.canvasColor,
}) : _border = Radius.circular(radius);
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..shader = const LinearGradient(
colors: [
Color.fromARGB(255, 141, 23, 15),
Colors.red,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
).createShader(
Rect.fromLTRB(0, 0, size.width, size.height),
)
..style = PaintingStyle.fill;
final _rect = Rect.fromLTRB(
0,
gap,
size.height - gap * 2,
size.height - gap,
);
///left Circle
Path fullPath = Path()
..addOval(_rect)
///eaten shape
..addRRect(
RRect.fromLTRBAndCorners(
size.height * .5,
0,
size.width,
size.height,
bottomLeft: _border,
topLeft: _border,
bottomRight: _border,
topRight: _border,
),
);
Path drawPath = Path()..addPath(fullPath, Offset.zero);
canvas.drawPath(drawPath, paint);
Paint holoPaint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = gap
..color = canvasColor;
canvas.drawOval(_rect, holoPaint);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
I've tried with Path's fillType, not sure about the issue I've faced there. Another thing can be done just using Container with decoration without using CustomPaint.
You can use this paint as
SizedBox(
width: 300,
height: 70,
child: Stack(
children: [
Positioned.fill(
child: CustomPaint(
painter: EatenShape(
canvasColor: Theme.of(context).scaffoldBackgroundColor,
),
),
),
],
),
),
Related
In Flutter I try to make a segmented ring (or radial gauge) around a number to indicate its value. Like here
The example of in the link is already a solution itself, however, I read syncfusion widgets are not free. (I am using it commercially)
Any alternatives or ideas how to set this into practice?
You can use CustomPainter to achieve the same,
class RingPainter extends CustomPainter {
const RingPainter({
required this.percentage,
this.startColor,
this.endColor,
this.width,
}) : assert(percentage >= 0 && percentage <= 100,
"Percentage must be in the range 0-100!");
final double percentage;
final Color? startColor;
final Color? endColor;
final double? width;
double get progress => percentage / 100;
#override
void paint(Canvas canvas, Size size) {
var angle = math.pi / 180 * 230;
canvas.rotateAroundCenter(size, angle);
canvas.drawRing(
size,
1,
startColor: Colors.black12,
endColor: Colors.black12,
width: width,
);
canvas.drawRing(
size,
progress,
startColor: startColor,
endColor: endColor,
width: width,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
extension on Canvas {
rotateAroundCenter(Size size, double angleInRadians) {
final double r =
math.sqrt(size.width * size.width + size.height * size.height) / 2;
final alpha = math.atan(size.height / size.width);
final beta = alpha + angleInRadians;
final shiftY = r * math.sin(beta);
final shiftX = r * math.cos(beta);
final translateX = size.width / 2 - shiftX;
final translateY = size.height / 2 - shiftY;
translate(translateX, translateY);
rotate(angleInRadians);
}
drawRing(
Size size,
double value, {
Color? startColor,
Color? endColor,
double? width,
}) {
final rect = Rect.fromLTWH(-15, 0.0, size.width, size.height);
final gradient = SweepGradient(
startAngle: 3 * math.pi / 2,
endAngle: 7 * math.pi / 2,
tileMode: TileMode.repeated,
colors: [
startColor ?? Colors.pink,
endColor ?? Colors.blueAccent,
],
);
final paint = Paint()
..shader = gradient.createShader(rect)
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = width ?? 24;
final center = Offset(size.width / 2, size.height / 2);
final radius =
math.min(size.width / 2, size.height / 2) - ((width ?? 24) / 2);
const startAngle = -math.pi / 2;
final sweepAngle = 2 * math.pi * value * 0.723;
drawArc(
Rect.fromCircle(center: center, radius: radius),
startAngle,
sweepAngle,
false,
paint,
);
}
}
Use it with CustomPaint widget,
CustomPaint(
painter: RingPainter(
percentage: 70,
width: 20,
),
)
In case, you want text inside ring,
Center(
child: SizedBox(
width: 200,
height: 200,
child: Stack(
fit: StackFit.expand,
children: [
const Center(
child: Text(
"70%",
style: TextStyle(
fontSize: 48,
fontWeight: FontWeight.w900,
color: Colors.purple,
),
),
),
CustomPaint(
painter: RingPainter(
percentage: 70,
width: 20,
),
),
],
),
),
)
Idea is to make a custom shaped container by giving it ShapeDecoration().
I used this thread as a reference.
Eventialy I achieved the shape I wanted, but once I wrap this custom shaped container with margin\padding\SizedBoxes around it - it's content is being pushed out of the bounds of the container. Check out screenshots.
Normaly it should be smth like this:
but with paddings between elements.
So the problem is that my customized container behavers realy wierd once it has any margin\padding arround it.
return Padding(
padding: const EdgeInsets.symmetric(vertical: 40),
child: Container(
padding: const EdgeInsets.all(8),
height: 88,
alignment: Alignment.center,
decoration: ShapeDecoration(
shape: UnitListItemBorderShape(
color: _isCurrent ? theme.listItemBorderColor : theme.listItemBackgroundColor,
),
color: theme.listItemBackgroundColor,
),
child: Stack(
children: [
//custom container's ineer content
] ,
),
);
UnitListItemBorderShape class:
class UnitListItemBorderShape extends ShapeBorder {
const UnitListItemBorderShape({color}) : _color = color;
final Color _color;
#override
EdgeInsetsGeometry get dimensions => const EdgeInsets.all(0);
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) => null;
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) => UnitListItemShape.getShape(
rect.width,
rect.height,
const Radius.circular(16),
const Radius.circular(20),
);
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
final Paint borderPaint = Paint()
..color = _color
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.drawPath(getOuterPath(rect), borderPaint);
}
#override
ShapeBorder scale(double t) => null;
}
UnitListItemShape class:
class UnitListItemShape {
static Path getShape(double width, double height, Radius borderRadius, Radius circleRadius) {
final double rightOffset = circleRadius.x;
final Rect rect = Rect.fromCenter(center: Offset(width - rightOffset - 4, height / 2), width: 48, height: 48);
return Path()
..moveTo(borderRadius.x, 0)
..lineTo(width - borderRadius.x - rightOffset, 0)
..arcToPoint(Offset(width - rightOffset, borderRadius.x), radius: borderRadius)
..lineTo(width - rightOffset, (height / 2) - rightOffset - 4)
// ..addRect(rect)
..arcTo(rect, -70 * math.pi / 180, 150 * math.pi / 180, false)
..lineTo(width - rightOffset, height - borderRadius.x)
..arcToPoint(Offset(width - borderRadius.x - rightOffset, height), radius: borderRadius)
..lineTo(borderRadius.x, height)
..arcToPoint(Offset(0, height - borderRadius.x), radius: borderRadius)
..lineTo(0, borderRadius.x)
..arcToPoint(Offset(borderRadius.x, 0), radius: borderRadius)
..fillType = PathFillType.evenOdd
..close();
}
}
everyone how can i get like this icon's border in flutter
i need to set color only for left , bottom and top sides
i heard someone talk about customPainter but i don't nkow how to use plz help me:
enter image description here
this my code:
Container(
decoration:BoxDecoration(
shape: BoxShape.circle,
border: new Border(
left: BorderSide(
color: Theme.of(context).primaryColor,
width: 2,),
right: BorderSide(
color: Theme.of(context).primaryColor,
width: 2,),
bottom: BorderSide( color: Theme.of(context).primaryColor,
width: 2,),
top: BorderSide(color: Theme.of(context).primaryColor,
width: 2,),),
) ,
child:ClipOval(
child: Image.asset('images/imageout1.png',width: 85,),
))
As you said, you can use a CustomPainter. The key is the use of drawArc
Return this somewhere in your widget build method
CustomPaint(
size: Size.square(100),
painter: CirclePainter(),
)
Create CirclePainter
class CirclePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
Paint innerLine = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
Paint outerLine = Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 3.0;
canvas.drawArc(Rect.fromCircle(center: Offset(size.width / 2, size.height / 2), radius: size.width / 2),
0, 2 * pi, false, innerLine);
canvas.drawArc(
Rect.fromCircle(center: Offset(size.width / 2, size.height / 2), radius: (size.width + 12) / 2),
1.9 * pi,
1.2 * pi,
false,
outerLine);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Result
I want to design a widget of shape of a chat bubble where one corner is pinned and its height should adjust to the lines of the text? For now I'm using ClipRRect widget with some borderRadius. But I want one corner pinned. Any suggestions ?
UPDATE
I know this can be done using a stack but I'm looking for a better solution since I have to use it many times in a single view and using many stacks might affect the performs. ( correct me here if I'm wrong )
For someone who want this get done with library. You can add bubble: ^1.1.9+1 (Take latest) package from pub.dev and wrap your message with Bubble.
Bubble(
style: right ? styleMe : styleSomebody,
//Your message content child here...
)
Here right is boolean which tells the bubble is at right or left, Write your logic for that and add the style properties styleMe and styleSomebody inside your widget as shown below. Change style according to your theme.
double pixelRatio = MediaQuery.of(context).devicePixelRatio;
double px = 1 / pixelRatio;
BubbleStyle styleSomebody = BubbleStyle(
nip: BubbleNip.leftTop,
color: Colors.white,
elevation: 1 * px,
margin: BubbleEdges.only(top: 8.0, right: 50.0),
alignment: Alignment.topLeft,
);
BubbleStyle styleMe = BubbleStyle(
nip: BubbleNip.rightTop,
color: Colors.grey,
elevation: 1 * px,
margin: BubbleEdges.only(top: 8.0, left: 50.0),
alignment: Alignment.topRight,
);
Hi im also searching for a chat bubble shaped widget finally i made one.
I have made this in custom Painter and i'm not good at it.
import 'package:flutter/material.dart';
class ChatBubble extends CustomPainter {
final Color color;
final Alignment alignment;
ChatBubble({
#required this.color,
this.alignment,
});
var _radius = 10.0;
var _x = 10.0;
#override
void paint(Canvas canvas, Size size) {
if (alignment == Alignment.topRight) {
canvas.drawRRect(
RRect.fromLTRBAndCorners(
0,
0,
size.width - 8,
size.height,
bottomLeft: Radius.circular(_radius),
topRight: Radius.circular(_radius),
topLeft: Radius.circular(_radius),
),
Paint()
..color = this.color
..style = PaintingStyle.fill);
var path = new Path();
path.moveTo(size.width - _x, size.height - 20);
path.lineTo(size.width - _x, size.height);
path.lineTo(size.width, size.height);
canvas.clipPath(path);
canvas.drawRRect(
RRect.fromLTRBAndCorners(
size.width - _x,
0.0,
size.width,
size.height,
topRight: Radius.circular(_radius),
),
Paint()
..color = this.color
..style = PaintingStyle.fill);
} else {
canvas.drawRRect(
RRect.fromLTRBAndCorners(
_x,
0,
size.width,
size.height,
bottomRight: Radius.circular(_radius),
topRight: Radius.circular(_radius),
topLeft: Radius.circular(_radius),
),
Paint()
..color = this.color
..style = PaintingStyle.fill);
var path = new Path();
path.moveTo(0, size.height);
path.lineTo(_x, size.height);
path.lineTo(_x, size.height-20);
canvas.clipPath(path);
canvas.drawRRect(
RRect.fromLTRBAndCorners(
0,
0.0,
_x,
size.height,
topRight: Radius.circular(_radius),
),
Paint()
..color = this.color
..style = PaintingStyle.fill);
}
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
copy and paste the above code and paste in your project
Align(
alignment: alignment, //Change this to Alignment.topRight or Alignment.topLeft
child: CustomPaint(
painter: ChatBubble(color: Colors.blue, alignment: alignment),
child: Container(
margin: EdgeInsets.all(10),
child: Stack(
children: <Widget>[
TextView("Hello World"),
],
),
),
),
)
paste this code where you have to show chatBubble Widget.
And i have also upload this code in bitbucket ChatBubble Widget Please be free if you have anything to contribute.
Sorry I am not able to show you the code for it but I can present an idea that might work if you implement it correctly. Suppose the Widget you made with ClipRect is called MyChatBubbleRect. Now, make another widget that draws a triangle using CustomPainter, let's call it MyChatBubbleTriangle, of course fill it with same color as the chat bubble but you can use a different color initially for debugging. Now that we have two widgets we can stack 'em together on top of each other and using Positioned widget over the MyChatBubbleTriangle. Something like this:
Stack(
children : [
MyChatBubbleRect(), // Maybe decrease the width a bit
Positioned(
top: 0,
right: 0,
child: MyChatBubbleTriangle()
)
]
)
This is just an idea I think you can pursue. Sorry couldn't provide the proper source code.
Chat Body
DecoratedBox(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8.0),
),
child: Text("your message goes here"),
);
Make a custom Triangle
class ChatBubbleTriangle 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;
}
}
Use both of them within a stack wrapping ChatBubbleTriangle with Positioned() widget
I would like to show a text between 2 containers in flutter. The problem is that the containers adapt to the size of the text. I do not want that behavior. want something like this. (Am very new to flutter).
I want to make a music player. The text can not be split.
Edit: Accordingly to what you've asked, you want to create a custom player that updates its color based on the song current position.
For that, you can create a CustomPaint widget with a CustomPainter player that updates whenever the song state changes.
class MyPlayerBar extends CustomPainter {
MyPlayerBar({this.fullSongTimeInSeconds, this.currentSecond});
final int fullSongTimeInSeconds;
final int currentSecond;
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
double cursor = (currentSecond * size.width) / fullSongTimeInSeconds;
Radius cornerRadius = Radius.circular(3.0);
// Already played half color (your darker orange)
paint.color = Color.fromRGBO(206, 69, 0, 1.0);
// Painting already played half
canvas.drawRRect(
RRect.fromRectAndCorners(Rect.fromLTWH(0.0, 0.0, cursor, size.height),
topLeft: cornerRadius, bottomLeft: cornerRadius),
paint);
// Yet to play half color (your lighter orange)
paint.color = Color.fromRGBO(227, 113, 18, 1.0);
// Painting the remaining space
canvas.drawRRect(
RRect.fromRectAndCorners(Rect.fromLTWH(cursor, 0.0, size.width - cursor, size.height),
bottomRight: cornerRadius, topRight: cornerRadius),
paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
I've created a full example that simulates a 3 minute song (180 seconds) that will result in the following:
Full example code:
class MyPlayer extends StatefulWidget {
_MyPlayerState createState() => _MyPlayerState();
}
class _MyPlayerState extends State<MyPlayer> {
int _songCurrentPosition = 0;
int _fullSongInSeconds = 180; // 3 minutes song
#override
void initState() {
super.initState();
_songPlaying();
}
void _songPlaying() async {
if (_songCurrentPosition >= _fullSongInSeconds) return;
await Future.delayed(Duration(seconds: 1));
setState(() => _songCurrentPosition += 1);
_songPlaying();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My player'),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: CustomPaint(
painter: MyPlayerBar(
currentSecond: _songCurrentPosition, // Your current song position in seconds
fullSongTimeInSeconds: _fullSongInSeconds,
),
child: Container(
alignment: Alignment.center,
height: 30.0,
width: double.infinity,
child: Text(
'Playing: 01 - Hey, this is my life',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w500),
),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(10.0),
),
),
),
),
),
);
}
}
class MyPlayerBar extends CustomPainter {
MyPlayerBar({this.fullSongTimeInSeconds, this.currentSecond});
final int fullSongTimeInSeconds;
final int currentSecond;
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
double cursor = (currentSecond * size.width) / fullSongTimeInSeconds;
Radius cornerRadius = Radius.circular(3.0);
// Already played half color (your darker orange)
paint.color = Color.fromRGBO(206, 69, 0, 1.0);
// Painting already played half
canvas.drawRRect(
RRect.fromRectAndCorners(Rect.fromLTWH(0.0, 0.0, cursor, size.height),
topLeft: cornerRadius, bottomLeft: cornerRadius),
paint);
// Yet to play half color (your lighter orange)
paint.color = Color.fromRGBO(227, 113, 18, 1.0);
// Painting the remaining space
canvas.drawRRect(
RRect.fromRectAndCorners(Rect.fromLTWH(cursor, 0.0, size.width - cursor, size.height),
bottomRight: cornerRadius, topRight: cornerRadius),
paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}