Flutter canvas, how to paint over the child widget? - flutter

I am trying to draw red dots over the child textbox widget like this:
For that, I wrapped the child widget with CustomPaint() widget:
return CustomPaint(
painter: DotsPainter(), //draws red dots based on child's size
child: child, //textbox
);
But the result is this:
How to make CustomPainter "overlay" its child widget?
Thanks.

The CustomPaint has 2 possible painters:
When asked to paint, CustomPaint first asks its painter to paint on the current canvas, then it paints its child, and then, after painting its child, it asks its foregroundPainter to paint.
(emphasis mine)
So if you move your painter to the foregroundPainter instead, it should work fine:
return CustomPaint(
foregroundPainter: DotsPainter(), //draws red dots based on child's size
child: child, //textbox
);

Related

How can I draw a widget on top of a widget in flutter?

I want to draw a yellow arrow over the 9 blue squares as shown in the picture below.
But I don't know which widget to use to draw arrows freely on each rectangle. I want to draw an arrow like a screen lock pattern. I would like to know the different ways in which this arrow can be implemented.
You can put the whole Containers inside a Stack widget and then paint the line by using the CustomPaint widget shown in this question Draw lines with flutter.
Stack(
children: [
Arrow(child: Container()),
ContainerWidgets(),
],
)
And for the painter:
class Arrow extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
// Add your paint logic here
}
}

Flutter: how to make card expand to whole screen?

I have some cards in gridview. I want ontap of any card, they should expand to whole screen.
this are the cards,
HAVE TO DELETE IMAGES BECAUSE OF CONFIDENTIALITY
I want them to expand to whole screen like this,
what I have tried,
-> I have tried using OpenContainer widget,
-> custom hero animation
but they both use pageroute but I want them to do it without the page route because there is some content like appbar which would be same for both screen so I don't want them rebuild it everytime user tap on any of the card.
if anyone can lead me to right direction that would be awesome
Take a look at this package: https://pub.dev/packages/animations
if you don't like using pageroute you can just switch your widget using StatefulWidget and Visibility.
And wrap your Card with InkWell then use the onTap / onPressed callback to set change the state so the Visibility will start work now
Create a function
Widget transition(Widget _child) {
return PageTransitionSwitcher(
duration: const Duration(milliseconds: 800),
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return SharedAxisTransition(
child: child,
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.horizontal,
);
},
child: _child,
);
}
Add it on your scaffold:
transition(child:condition ? widget_1 : widget_2);
I'm not 100% sure what you meant by expand to the whole screen, but you probably need to use the Hero() widget, wrap each widget you want to expand with a Hero() widget and give it a tag: 'spo2' for example, make a new screen for the second image, wrap each card with a Hero() and the same tag in the previous screen for the expanded card.
For further reading check out https://api.flutter.dev/flutter/widgets/Hero-class.html
Try using a dialogue and animating the opening.

Flutter. Set parent's height based on child canvas drawing's

Is there any way to set parent container's height based on height of what is drawn in the child canvas?
I am using a custom painter like this:
double width = MediaQuery.of(context).size.width;
return Container(
color: Colors.yellow,
height: 240,
width: width,
child: CustomPaint(
painter: ShapePainter(),
),
);
Then ShapePainter() draws different shapes (1 shape for each canvas in the list).
But as you can see, some shapes like 2nd rectangle, take twice the space they actually need.
I can calculate height of a shape inside ShapePainter() easily,
but I have no idea how to apply it to its parent's height. (Except calling an insta-setState() from child, but there should be a better way, because this may flicker for one frame)
Thanks for any help!
Flutter has several phases that it goes through when creating a frame, including building, layout, and painting. The size of widgets is determined during the layout phase. So you can't set the height based it what was painted. (Except, as you've said, by using something like setState to generate a new frame.) You'll have to determine the the size you want before any painting has happened. For example, you can give your CustomPainter a getter to provide the shape:
class ShapePainter extends CustomPainter {
ShapePainter({#required this.shape});
final Shape shape;
void paint(Canvas canvas, Size size) {
// the painting
}
Size get size {
// determine size based on shape
}
}
...
Widget build(BuildContext context) {
final shapePainter = ShapePainter();
return Container(
color: Colors.yellow,
height: shapePainter.size.height,
align: Alignment.center,
child: CustomPaint(
painter: shapePainter,
),
);
}

Flutter clip without space around the clipped widget

I want to clip a widget and use this clipped image in a layout and the bounderies should be the visible part of the imgage when clipped.
Using this custom clipper
#override
Rect getClip(Size size) {
Rect rect = Rect.fromLTRB(25,0,size.width - 25, size.height);
return rect;
}
#override
bool shouldReclip(CustomRect oldClipper) {
return true;
}
}
results in 25 px blank space left of the clipped image and 25 px blank space right of the clipped image.
And at least we ant to copy specific areas of an image and scale/position it exactly in the app... -> more complex desired result:
You need to add a transform to translate the widget over to the newly empty space. Just be aware that the widget itself will still occupy the same width in this example - so if there is some sibling in a row, for example, it will still get "pushed over" by the same amount of space. If you need to change that you'll need to add a SizedBox to the final part of this example so that you can trim down the size of the widget to the portion you've clipped.
Also note that this is not a very good practice - ideally you should be fetching the image you actually want to display. Flutter will still need to load your entire image into memory and then do some non-trivial work to add the clip you want. That takes up plenty of extra CPU and memory. But sometimes you don't have a choice I guess.
This example shows just displaying an image, followed by applying a custom clip, followed by applying a translation, which is what OP is looking for.
import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart';
void main() {
final Widget image = Image.network(
'https://via.placeholder.com/300x60?text=This is just a placeholder');
const double widthAmount = 100;
runApp(MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: <Widget>[
Spacer(),
image,
Spacer(),
ClipRect(
clipper: CustomRect(widthAmount),
child: image,
),
Spacer(),
Transform(
transform: Matrix4.translation(Vector3(-widthAmount, 0.0, 0.0)),
child: ClipRect(
clipper: CustomRect(widthAmount),
child: image,
),
),
Spacer(),
],
),
),
),
));
}
class CustomRect extends CustomClipper<Rect> {
CustomRect(this.widthAmount);
final double widthAmount;
#override
Rect getClip(Size size) {
Rect rect =
Rect.fromLTRB(widthAmount, 0, size.width - widthAmount, size.height);
return rect;
}
#override
bool shouldReclip(CustomRect oldClipper) {
return oldClipper.widthAmount != widthAmount;
}
}
Okay, the solution above is not really working as if we translate the clipped image we get some free space (the amount that we translate).
Isn't there any option in Flutter to just define an area of an widget and get this area as a new Widget/Image with exactly the width and height of the defined area to be able to position it (without any paddings/white space around),to scale it, ...?

Flutter: How to make a ListView transparent to pointer events (but not its non-transparent contents)?

I have a Scrollable (ListView or any other) and it contains a transparent widget, say Container(height:200). I can see through both the widget and the scrollable (in other words I can see the widgets behind the scrollable).
How can I be able to click through the transparent widget and the scrollable, so that I reach the widgets behind the scrollable?
ListView(
children: [
Container(height: 200), // Transparent.
Container(color: Colors.red, height: 200),
],
);
Note, I cannot wrap the scrollable with IgnorePointer, because I still want to click the non-transparent widgets in the scrollable.
The only reasonable solution I can think of is to have a GestureDetector on the transparent container, which will give you the global position of the taps:
GestureDetector(
onTapUp: (TapUpDetails tapUpDetails) {
print("onTapUp global: " + tapUpDetails.globalPosition.toString());
},
And then add a Key to the widget behind the list, and use it to get the global position of the top left corner of the widget's rectangle, as well as the size of the widget, and use those to get the rectangle of the widget:
RenderBox renderBox = _key.currentContext.findRenderObject();
Offset topLeftCorner = renderBox.localToGlobal(Offset.zero);
Size size = renderBox.size;
Rect rectangle = topLeftCorner & size;
If the background widget does not move, you can do it within initState on the very next frame using WidgetsBinding.instance.addPostFrameCallback (the render object will be null if do it synchronously within initState) and save the Rect - otherwise you will have to recalculate it on every tap.
And then finally on each tap on the transparent container you can calculate whether the tap's position is within the boundaries of the background widget, and invoke the corresponding code:
// if tap is within boundaries of the background widget
if (rectangle.contains(tapUpDetails.globalPosition)) {
// your code here
}