Now I have a canvas of CustomPaint which draws a sketch, the sketch illustrates a basic 2D movement of "fluids" in pipe. The pipe is generated only once, but the fluid movement is controlled by user input.
I have a result that needs optimization. I used one CustomPaint class to draw both in one canvas, and it redraws everything each time the user changes input.
What I need is optimization, i.e. to draw the pipe part which does not change and only repaint the fluids each time the user changes input. That would be better for resources as things get more complex.
I don't know how to use shouldRepaint or whether it's gonna be useful.
Would it be possible to draw on two layers, keep one static and the other "updateable".
class PipeProfile extends CustomPainter {
FluidsData pipeData;
WellProfileData fluidsData;
PipeProfile({
#required this.pipeData Data,
#required this.fluidsData});
#override
void paint(Canvas canvas, Size size) {
cutomFunctionToDrawPipe(pipeData);
cutomFunctionToDrawFluids(fluidsData);
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
Related
I have a non-interactive (i.e. no GestureDetector, etc.) widget subtree that I would like to be drawn identically > 1 time.
Currently I am achieving this by placing the widget subtree into the tree multiple times. There are some downsides to this:
Each StatefulWidget in the subtree is given its own unique State instance, whereas a single one would suffice and be preferable.
Performance: identical redundant layout and painting is performed for each subtree.
If possible, I would like to manually render (layout and paint) the subtree just once, and then draw the resulting rendering as needed.
I am able to draw to a separate canvas with PictureRecorder and paint the resulting picture as needed using Canvas.drawPicture().
What I am missing is how to perform layout and painting of widget trees in Flutter. I've not been successful in finding any information about this, perhaps because I'm unaware of the best search term to use.
What I'm looking for is high-level pointers about how to approach manual rendering of widget subtrees, including any links to relevant documentation, articles, example code that does something similar, or even just search terms that will lead me in the right direction. Legitimate, reasoned criticisms of this approach are also welcome from those with first-hand experience attempting something similar. :)
Thank you!
Although not directly answering the question of "how to manually layout and paint a widget subtree," an approach which achieves a similar net effect (disregarding performance differences) toward the stated goal of "a non-interactive widget subtree to be drawn identically > 1 time" is to paint the subtree multiple times:
/// Caveat: if the transform is accessed (either directly, or
/// indirectly via [RenderBox.globalToLocal], etc.) outside of
/// painting, the transform will be the one that would apply for
/// an offset of [Offset.zero].
class MultiOffsetPainter extends SingleChildRenderObjectWidget {
final List<Offset> offsets;
const MultiOffsetPainter({super.key, super.child, required this.offsets});
#override
RenderObject createRenderObject(BuildContext context) {
return _MultiOffsetPainterRenderObject(offsets);
}
#override
void updateRenderObject(BuildContext context, _MultiOffsetPainterRenderObject renderObject) { // ignore: library_private_types_in_public_api
renderObject.offsets = offsets;
}
}
class _MultiOffsetPainterRenderObject extends RenderProxyBox {
List<Offset> offsets;
Offset? _currentTranslation;
_MultiOffsetPainterRenderObject(this.offsets);
#override
void applyPaintTransform(RenderObject child, Matrix4 transform) {
if (_currentTranslation != null) {
transform.translate(_currentTranslation!.dx, _currentTranslation!.dy, 0);
}
super.applyPaintTransform(child, transform);
}
#override
void paint(PaintingContext context, Offset offset) {
for (var translation in offsets) {
_currentTranslation = translation;
super.paint(context, offset + translation);
_currentTranslation = null;
}
}
}
Note the important caveat.
Cataloging here on the chance this might be useful to a future reader.
The problem is that the animation only works if you start resizing the window. Tried on windows, web, android. Used by StatefulWidget and SlideTransition. The class is complex enough to post this code here.
Has anyone experienced this/similar issue?
The first problem is when we pass new coordinates(x, y) data to CustomPaint we may need override method shouldRepaint in CustomPainter with value true:
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
The second problem is when we pass mutable data in widget and such widget is many. The data should be immutable.
I am trying to create this shape but am struggling. Have tried using shape maker but am struggling also to make this shape. I also tried using path, but am unsure whether this will work.
Any suggestions?
what I am trying to achieve
With the help of CustomPainter we can draw any kind of custom button and container, Flutter gives you access to low-level graphics painting. Best of all, painting in Flutter is fast and efficient.
CustomPaint takes a painter argument of type CustomPainter, an abstract class from the Flutter APIs.
Container(
color: Colors.yellow,
child: CustomPaint(painter: FaceOutlinePainter()),
),
and in FaceOutlinePainter() class you can add the size , width , height and all other requirment.
class FaceOutlinePainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
// TODO: draw something with canvas
}
#override
bool shouldRepaint(FaceOutlinePainter oldDelegate) => false;
}
Here's a reference video, https://www.youtube.com/watch?v=AnKgtKxRLX4
I've used this few times and it worked pretty well
I've created a custom drawn widget using CustomPaint that has a path as an outline. But wrapping the widget in a GestureDetector makes the click area a rectangle around the whole canvas, Is there a way to clip the GestureDetector so that that click only works within the path?
You can implement the hitTest method from the CustomPainter, add your Path there and use the condition path.contains(position) to ensure the touch only covers the Path part.
class MyCustomPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return null;
}
#override
bool hitTest(Offset position) {
Path path = Path();
//add your lines/curves here
path.close();
return path.contains(position);
}
}
More info about bool hitTest(Offset position):
Called whenever a hit test is being performed on an object that is
using this custom paint delegate.
The given point is relative to the same coordinate space as the last
[paint] call.
The default behavior is to consider all points to be hits for
background painters, and no points to be hits for foreground painters.
Return true if the given position corresponds to a point on the drawn
image that should be considered a "hit", false if it corresponds to a
point that should be considered outside the painted image, and null to
use the default behavior.
I answered a similar question here: Flutter: What is the correct way to detect touch enter, move and exit on CustomPainter objects
Hi everyone ,I have a problem, I don’t understand the difference between AnimatedWidget and AnimatedBuilder. The comments in the source code are as follows:
AnimatedWidget:
/// For more complex case involving additional state, consider using
/// [AnimatedBuilder].
AnimatedBuilder:
/// For simple cases without additional state, consider using
/// [AnimatedWidget].
I want to know how to choose between them, because I don't quite understand the documentation, thanks!
There's no real difference between them besides the syntax needed to use it.
To be clear, this is the code of AnimatedBuilder :
class AnimatedBuilder extends AnimatedWidget {
const AnimatedBuilder({
Key key,
#required Listenable animation,
#required this.builder,
this.child,
}) : assert(builder != null),
super(key: key, listenable: animation);
final TransitionBuilder builder;
final Widget child;
#override
Widget build(BuildContext context) {
return builder(context, child);
}
}
...Yup, does nothing
From this code we can clearly see that AnimatedBuilder is just a different syntax of using AnimatedWidget. Since AnimatedBuilder is an AnimatedWidget that delegate all the layout logic to a callback
So in the end, it's really up to you. Both do the same thing. Use what makes it more readable for you
Both the animatedWidget and animatedBuilder do the same work related to the animations.
before moving on u must know that to create an animation we must require atleast two things 1. The animation itself and 2. The widget on which we are going to apply the animation.
The clear cut difference between then is :
AnimatedWidget only takes animation as a parameter whereas AnimatedBuilder takes two arguments "child" and "animation".
AnimatedWidget is implemented as a class extending AnimatedWidget.
E.g.
class abc extends AnimatedWidget
Whereas AnimatedBuilder is implemented as a widget inside a class.
E.g.
child : AnimatedBuilder( .....),
Now, although both do the same work but both have different ways of doing it. In AnimatedWidget it has its own child so we have to pass only the animation.whereas in AnimatedBuilder we need to pass both child and animation.
Take AnimatedWidget for example u can use the same AnimatedWidget class for any number of animations with different values.
Now you will be thinking that if AnimatedWidget can do the thing why we need AnimatedBuilder?
The answer is simple
Changing the animation in AnimatedWidget requires changing the widget that renders the logo or our child. So what AnimatedBuilder did is to provided a choice to is to pass both child of your choice and animation explicitly.