I search a lot how to apply frame on images but found no answer. I read stack function in which we can put an image inside other but its not good trick. I think there must be library for frame and by using that frame can automatically apply on border of image. Please guide me how can I use different design of frame outside image like some filters application.
I want to give options of frame to user like below and when user click on any frame that will apply on image
There are many approaches for that issue. I've used several in the past. It strongly differs in the way you want to make use of the image. Do you need to paint something on the image or just display it?
In my opinion multiple frames on an image can be achieved best with CustomPainter. Check out the documentation for more. The following Painter should do the trick.
class TestPainter extends CustomPainter {
final ui.Image image;
TestPainter({required this.image});
#override
void paint(Canvas canvas, Size size) {
//draw the image
canvas.drawImage(image, const Offset(0, 0), Paint());
double rectWidth = image.width.toDouble();
double rectHeight = image.height.toDouble();
final imageCenter = Offset(image.width / 2, image.height / 2);
//first frame
var border1 = Rect.fromCenter(
center: imageCenter,
width: rectWidth + 1,
height: rectHeight + 1);
var painting = Paint();
painting.color = Colors.red;
painting.strokeWidth = 2;
painting.style = PaintingStyle.stroke;
canvas.drawRect(border1, painting);
//second frame
var border2 = Rect.fromCenter(
center: imageCenter,
width: rectWidth + 3,
height: rectHeight + 3);
var painting2 = Paint();
painting2.color = Colors.green;
painting2.strokeWidth = 2;
painting2.style = PaintingStyle.stroke;
canvas.drawRect(
border2,
painting2,
);
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
Please be cautious. The Image in that example needs to be an Image of the dart:ui library (Documentation). There are various way to convert. Depends on your project.
Related
I want to draw a rectangle on the actual image and don't want to use the flutter renderer to add a rectangle on the top layer just as a view. I can get the pixel color, but how can I overwrite the bonch of pixels?
ByteData byteData = await rootBundle.load('assets/images/maps/map.jpg');
Uint8List bytes = byteData.buffer.asUint8List();
List<int> values = bytes;
img.Image photo = img.decodeImage(values)!;
final pixels = photo.getBytes(format: img.Format.rgba);
I´m not quite sure, if this is the answer you need, but what about using a Stack?
Stack(children: [
image,
Positioned.fill(
child: CustomPaint(
painter: Sky(r),
child: Container()));
}),
],
)
...
class Sky extends CustomPainter {
Rect rect;
Sky(this.rect);
#override
void paint(Canvas canvas, Size size) {
canvas.drawRect(rect, Paint()..color = Colors.black);
}
#override
bool shouldRepaint(Sky oldDelegate) => false;
}
This sketch shows what the final result should look like:
A CustomPainter fills its Canvas (yellow area) with a slightly translucent background color. (Amber with an opacity of 0.8 in the sketch).
The CustomPainter draws a rectangle onto the canvas. And here it's getting interesting: The rectangle should only change the alpha value of the background color drawn at the previous step. The idea is to highlight some points of interest, by fading some "holes" in and out (visualized by the smaller, darker rectangle inside the yellow rectangle in the sketch above).
In code it looks simple:
class Highlighter extends CustomPainter {
ValueListenable<double> valueListenable;
Color backgroundColor;
Highlighter({required this.valueListenable, this.backgroundColor = Colors.amber}) : super(repaint: valueListenable);
#override
void paint(Canvas canvas, Size size) {
Color colorHole = backgroundColor.withOpacity(0.40);
Paint holePainter = Paint();
holePainter.color = colorHole;
holePainter.blendMode = BlendMode.dstOut;
canvas.saveLayer(null, holePainter);
// Step 1: Draw the background:
canvas.drawColor(backgroundColor.withOpacity(0.80), BlendMode.srcOver);
// Step 2: Highlight a rectangle:
canvas.drawRect(const Rect.fromLTWH(100, 100, 100, 100), holePainter);
canvas.restore();
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
Problem is, the colors ain't right:
canvas.DrawColor() draws some shade of gray instead of Amber, although the holes appear to be ok.
Removing the saveLayer()/restore() calls draws the background with the right color, but then the holes ain't transparent.
Question now is: After filling the canvas with a color, how can you set parts of it to translucent?
If there's a more efficient/performant way to do it, please let me now as well - getting rid of the saveLayer() call would be great...
Any advise is welcome.
Thank you.
Try my version:
class Highlighter extends CustomPainter {
ValueListenable<double> valueListenable;
Color backgroundColor;
Rect target;
Highlighter({this.valueListenable, this.backgroundColor = Colors.amber, this.target = const Rect.fromLTWH(145, 320, 100, 100)})
: super(repaint: valueListenable);
#override
void paint(Canvas canvas, Size size) {
bool withRestore = false;
if (withRestore) {
//with canvas.restore
canvas.saveLayer(Rect.largest, Paint());
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = backgroundColor.withOpacity(0.4));
canvas.drawRect(target, Paint()..blendMode = BlendMode.clear);
canvas.restore();
} else {
//without canvas.restore
Paint backgroundPaint = Paint();
backgroundPaint.blendMode = BlendMode.src;
backgroundPaint.color = backgroundColor.withOpacity(0.4);
// // Step 1: Draw the background:
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, target.top), backgroundPaint);
canvas.drawRect(Rect.fromLTWH(0, 0, target.left, size.height), backgroundPaint);
canvas.drawRect(Rect.fromLTWH(target.left + target.width, 0, target.left, size.height), backgroundPaint);
canvas.drawRect(Rect.fromLTWH(0, target.top + target.height, size.width, target.top), backgroundPaint);
}
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
The difficulty was to fade in/out the holes from the background (yellow) barrier. The approaches mentioned by #cloudpham93 and #YeasinSheikh do work, as long as you just want to cut a hole into the barrier.
If the holes should fade in/out however, the following steps are needed:
Call setLayer() and pass a Paint() with blendMode set to srcOver.
Draw the background barrier with the desired opacity.
Call setLayer() again, but pass a Paint() with blendMode set to dstOut.
Draw draw the holes with the desired opacity.
Call restore() twice. The first call of restore() will overwrite the opacity of the pixels on the background layer. The second call will draw the background layer onto whatever is underneath it.
Thanks for all the advise!
I am drawing shapes using canvas and customPaint as shown in the image below.
And I'm going to drag each shape to rotate it.
I knew that I could rotate it through the canvas.rotate.
But if I use this, there's a problem with all the shapes rotating the same.
The following is part of my code.
class Shape {}
class Rectangle extends Shape {
Rect? rect;
double? left, top;
bool? isRectSelected;
final paint = Paint();
Rectangle(Color color, double width) {
this.paint
..color = color
..isAntiAlias = true
..strokeCap = StrokeCap.round
..strokeWidth = width
..style = PaintingStyle.stroke; // no fill
}
// ========================
class Sketcher extends CustomPainter {
final List<Shape> shapes;
Sketcher(
this.shapes,
) : super();
#override
void paint(Canvas canvas, Size size) {
for (var shape in shapes) {
if (shape is Rectangle) {
final degrees = 30; // angle
final radians = degrees * math.pi / 180;
// ! If use it, all shape rotate same angle
canvas.translate(shape.rect!.left, shape.rect!.top); // left top
canvas.rotate(radians);
canvas.translate(-shape.rect!.left, -shape.rect!.top);
// ractangle shape
canvas.drawRect(shape.rect!, shape.paint);
}
// same code circle...
}
}
How do I rotate each shape instead of the entire canvas?
Could you tell me how to do this?
You can use canvas.save() to save the state the canvas then adjust the canvas according to your need and then use canvas.restore() to restore the canvas to the state before save(). The action inside the save-restore action will still be draw to the canvas.
canvas.save();
// do something
// canvas.rotate(radians);
// canvas.drawRect(); // this rect will be rotated
canvas.restore();
canvas.drawRect(); // this rect will not be rotated
I want to create a custom baseline for TextFormField in Flutter. The example for what I wanna do is given below.
You can create your custom input border which would extend UnderlineInputBorder. What you need to override is the paint method.
I implemented here in this manner so that you can easily add colors and it would then draw lines for you giving same width to each color, but feel free to update it for your needs.
That would be something like this:
class CustomInputBorder extends UnderlineInputBorder {
#override
void paint(Canvas canvas, Rect rect,
{double gapStart,
double gapExtent = 0.0,
double gapPercentage = 0.0,
TextDirection textDirection}) {
drawLines(
canvas, rect, [Colors.red, Colors.green, Colors.blue, Colors.orange]);
}
void drawLines(Canvas canvas, Rect rect, List<Color> colors) {
var borderWidth = rect.bottomRight.dx - rect.bottomLeft.dx;
var sectionWidth = borderWidth / colors.length;
var startingPoint = rect.bottomLeft;
var endPoint = getEndPoint(startingPoint, sectionWidth);
colors.forEach((color) {
var paint = Paint();
paint.color = color;
paint.strokeWidth = 1.0;
canvas.drawLine(startingPoint, endPoint, paint);
startingPoint = getNewStartingPoint(startingPoint, sectionWidth);
endPoint = getEndPoint(startingPoint, sectionWidth);
});
}
Offset getNewStartingPoint(Offset oldStartingPoint, double width) {
return Offset(oldStartingPoint.dx + width, oldStartingPoint.dy);
}
Offset getEndPoint(Offset startingPoint, double width) {
return Offset(startingPoint.dx + width, startingPoint.dy);
}
}
And then you can use it:
TextField(
decoration: InputDecoration(
labelText: 'Username',
border: CustomInputBorder(),
enabledBorder: CustomInputBorder(),
focusedBorder: CustomInputBorder(),
),
),
This is how it looks like right now:
https://i.stack.imgur.com/fDTBu.png
https://i.stack.imgur.com/z4SjM.png
https://i.stack.imgur.com/l5aW8.png
First place worth looking at is the UnderlineInputBorder class. You should be able to set the decoration: of a TextFormField as an InputDecoration, which can take an UnderlineInputBorder as its border:. You should then be able to style the border's `BorderSide' property appropriately to match your design.
Flutter provides several ways for masks based on paths i.e. clip paths. I am trying to figure out a way where one could take an image with transparency layer like example below and use that image to mask another image / view or as a general mask.
My first instinct was to look at CustomPaint class, but I can't figure it out past this initial idea.
Flutter has BoxDecoration class that takes in BlendMode enum. By utilising these you can achieve various mask effects using images, for my particular case above dstIn was a solution.
I have posted an answer to my own same problem here, using a custom painter class with a squircle mask and image.
#override
void paint(Canvas canvas, Size size) {
if (image != null && mask != null) {
var rect = Rect.fromLTRB(0, 0, 200, 200);
Size outputSize = rect.size;
Paint paint = new Paint();
//Mask
Size maskInputSize = Size(mask.width.toDouble(), mask.height.toDouble());
final FittedSizes maskFittedSizes =
applyBoxFit(BoxFit.cover, maskInputSize, outputSize);
final Size maskSourceSize = maskFittedSizes.source;
final Rect maskSourceRect = Alignment.center
.inscribe(maskSourceSize, Offset.zero & maskInputSize);
canvas.saveLayer(rect, paint);
canvas.drawImageRect(mask, maskSourceRect, rect, paint);
//Image
Size inputSize = Size(image.width.toDouble(), image.height.toDouble());
final FittedSizes fittedSizes =
applyBoxFit(BoxFit.cover, inputSize, outputSize);
final Size sourceSize = fittedSizes.source;
final Rect sourceRect =
Alignment.center.inscribe(sourceSize, Offset.zero & inputSize);
canvas.drawImageRect(
image, sourceRect, rect, paint..blendMode = BlendMode.srcIn);
canvas.restore();
}
}
Result: