Flutter canvas RadialGradient understanding - flutter

I'm trying to create an effect like a heatmap point, so a circle with 2 or 3 colors on a gradient and then likely blurred.
final paint = Paint()
..shader = RadialGradient(
center: Alignment.center,
colors: <Color>[
Colors.green.withOpacity(0.2),
Colors.red.withOpacity(0.5),
Colors.blue.withOpacity(0.6)
],
stops: const [
0.3,
0.6,
1.0
]
).createShader(Rect.fromCircle(
center: const Offset(1.0,1.0),
radius: 10.0,
));
canvas.drawCircle(Offset(item[c][0].toDouble(), item[c][1].toDouble()), 8.0, paint);
However, whatever radius, Alignment, colors/opacity, shader center, stops I try, basically I just get a blue circle every time ?
I've also taken a look at the dartui RadialGradient (was after something like example 2), but I can't get that to work either.
I guess I'm missing something obvious, but can't see any Flutter examples of RadialGradient on canvas to compare to, any ideas what I'm misunderstanding ?

Related

What is the right gradient to use to achieve the inner shadow effect of the native macOS graphical time picker with Flutter CustomPainter?

I'm recreating the graphical macOS time picker for macos_ui and I'm having some difficulty painting the inner shadow gradient correctly. This shadow, along with the gradient of the clock's border, help give the clock a sort of 3D look.
Here is a screenshot of the native time picker:
As you can see, there is a shadow effect for the inner top half of the clock. It starts out white right around hour 9, then gets gradually darker and thicker as it reaches hour 12, and reverses back down to white at hour 3.
I've so far achieved the following:
As you can see, this is not correct. Here is the code I'm using to achieve this incorrect effect:
void _paintInnerShadow(Canvas canvas, Size size) {
Paint innerShadowPainter = Paint()
..strokeWidth = 3.0
..isAntiAlias = true
..shader = const LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.bottomLeft,
stops: [0.0, 0.5],
colors: [
MacosColor(0xFFA3A4A5),
MacosColors.white,
],
).createShader(
Rect.fromCircle(
center: size.center(Offset.zero),
radius: clockHeight / 2,
),
)
..style = PaintingStyle.stroke;
canvas.drawCircle(
size.center(Offset.zero),
size.shortestSide / 2.0,
innerShadowPainter,
);
}
What is the correct gradient I need to use to match the native look? I've experimented with Linear, Radial, and Sweep gradients and while I think LinearGradient is the right one, I haven't been able to work out the correct parameters for it.

How to rotate Lottie animation in Flutter

I have a Flutter app that is showing some Lottie animations. I sometimes need to flip the animation by 180 degrees on Y axis so that it it is a mirror image of itself.
In C# this is easily achievable by setting animated visual player's plane projection rotationY property to 180 (see below).
<muxc:AnimatedVisualPlayer x:Name="LottiePlayer">
<muxc:AnimatedVisualPlayer.Projection>
<PlaneProjection RotationY="180" x:Name="LottiePayerRotation"/>
</muxc:AnimatedVisualPlayer.Projection>
In flutter I tried using RotationBox, but that only rotates around X axis. I need to rotate around Y axis (see image below).
I also tried wrapping Lottie animation inside Transform widget (see below), but that doesn't work. After I added that, the Lottie animation completely disappears. I don't quite understand how Matrix4 works, there is very little documentation on it. I found this Matrix4 explanation but I still don't understand it. :-(
Transform(
transform: Matrix4(
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1,
)..rotateX(0)..rotateY(180)..rotateZ(0),
child: Lottie(
frameRate: FrameRate.max,
composition: _composition,
controller: _controller,
),
),
Note that I do not need the flip to be animated, I just want to flip the Lottie animation instantly so that it looks like a mirror image of itself. So an instant change, not a transition animated.
Any help appreciated.
I have used something like this before to rotate items... not sure if its what you are after
child: Transform.rotate(
angle: 180 / Math.pi, // Rotations are supplied in radians
child: Container())
Turns out I almost had it! What was missing was the alignment. The default value was causing the rotation to get outside the visible area. Changing alignment to "center" fixed that:
child: Transform(
alignment: FractionalOffset.center,
transform: Matrix4(
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1,
)..rotateY(180),
child: Lottie(
frameRate: FrameRate.max,
composition: _composition,
controller: _controller,
),
Simply wrap your widget with RotatedBox & give quarterTurns value(0, 1, 2, 3)
Assuming my widget is image of right arrow : →
0 no rotation - →
1 90 rotation - ↓
2 180 rotation - ←
3 270 rotation - ↑
So if I want my image to face left side ←:
RotatedBox(
quarterTurns: 2,
child: Image.asset('assets/right_arrow.png'),
),
EDIT
To rotate on axis, you need to use Transform widget.
Give the transform property as Matrix4
NOTE: You have to give rotation in radians.
import 'dart:math' as math;
Transform(
// to mirror, rotate Y axis
// math.pi is 180 degree in radian
transform: Matrix4.rotationY(math.pi),
child: Text('Hello world!'),
),

How to add a buffer with fixed size in pixels using leaflet and turf

I'm trying to create a buffer with fixed size using leaflet and turf, the buffer should be created when the mapClick event is emitted, so basically a buffer is created when I click the map
when creating a buffer or a circle you need to pass a radius property which is pretty much the "size" of the buffer, which can be in Kilometers, Meters, Miles and so on
The problem is: I need the buffer to always be the same size in pixels regardless of the mapZoom or Scale, for instance, using circle:
const center = [LatlongFromMouseEvent];
const radius = 5;
const options = {steps: 10, units: 'kilometers', properties: {foo: 'bar'}};
const circle = turf.circle(center, radius, options);
OR using buffer
const point = turf.point([LatlongFromMouseEvent]);
const buffered = turf.buffer(point, 5, {units: 'kilometers'});
OR using native Leaflet "Circle" constructor
const lCircle = new Circle([event.latlng.lat, event.latlng.lng], {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5,
radius: 500
})
lCircle.addTo(mapInstance)
All of those "buffers" will change it's size depending on the level of zoom of the map
AND "circleMarker" from leaflet automatically changes size when you change the zoom
Found a solution by using the metersPerPixel formula
const zoom = map.getZoom()
const lat = map.getCenter().lat
const metersPerPixel = 156543.03392 * Math.cos(lat * Math.PI / 180) / Math.pow(2, zoom)
const radius = metersPerPixel * sizeInPixels
More info about the formula in:
https://gis.stackexchange.com/questions/7430/what-ratio-scales-do-google-maps-zoom-levels-correspond-to

Creating a responsive scatter plot with flutter

I am trying to create a venn diagram of two elements from a scatter plot widget on package fl_chart.
scatterPlot(screenSize) {
return ListView(children: [
SizedBox(
width: 500,
height: 500,
child: ScatterChart(
ScatterChartData(
scatterSpots:
_createSpots(determineCircleLocation(screenSize), screenSize),
minX: 0,
minY: 0,
maxX: screenSize.width,
maxY: screenSize.height,
borderData: FlBorderData(show: false),
...
I have this problem that whenever I resize my window the scatter circles are moving - so if at a full screen it appeared that they had no intersection, when I resize the screen it appears like it has an intersection.
I was trying to wrap my plot in a SizedBox thinking that would stop the resizing side effects but it didn't.
Here's a demo of the problem
Is there a way to prevent this from happening?
While writing this I thought that by changing maxX and maxY to be some constants could help, but it resulted even worst outcome.
because we set a direct size (no matter in which size is it running) this problem happens.
You can check the window size, then update the dot's radius based on the window size.
(I calculated a number, then multiplied with radiuses).
Watch the video below. and check the code here:
LayoutBuilder(
builder: (context, constraints) {
final size = constraints.biggest.shortestSide;
final radiusMultiplier = size / 800;
return Padding(
padding: const EdgeInsets.only(right: 18.0),
child: ScatterChart(
ScatterChartData(
scatterSpots: [
ScatterSpot(5, 7, radius: 60 * radiusMultiplier,),
ScatterSpot(8, 4, radius: 20 * radiusMultiplier,),
ScatterSpot(3, 8, radius: 70 * radiusMultiplier),
],
minX: 1,
maxX: 10,
minY: 1,
maxY: 10,
)
),
);
},
)

SKPhysicsBody circleOfRadius area wrong

Since Xcode 8.2.1 and iOS 10.2, I'm seeing (randomly) wrong area for SKPhysicsBody when constructed as circle. This is my code:
let boundingBox = (node.path)?.boundingBox
let radius = (boundingBox?.size.width)! / 2.0
node.physicsBody = SKPhysicsBody(circleOfRadius: radius)
print("bubbleRadius: \(radius), boundingBox: \(boundingBox), physicsArea: \(node.physicsBody?.area), physicsAreaShouldBe: \((CGFloat.pi * radius/100 * radius/100))")
Now random factor. I couldn't find any clue when this printed 'physicsArea' is correct and when it's wrong. Sometimes print shows this (where you will see that area of PhysicsBody is calculated differently?):
bubbleRadius: 50.0, boundingBox: Optional((-50.0, -50.0, 100.0,
100.0)), physicsArea: Optional(0.349065870046616), physicsAreaShouldBe: 0.785398163397448
And sometimes print shows this (where you can see that area is calculated normally - per my understanding of area of circle):
bubbleRadius: 50.0, boundingBox: Optional((-50.0, -50.0, 100.0,
100.0)), physicsArea: Optional(0.785398185253143), physicsAreaShouldBe: 0.785398163397448
radius is always the same, only area gets calculated differently sometimes. This makes my circles overlap each other in the scene because their body is smaler than it looks.
Any ideas what might this be? Thanks.