Gradient.radial() from the documentation isn't available? - dart

I want to create a gradient shader.
The Gradient.radial() constructor should return one.
However, VS Code claims that this method is not defined for Gradient. huh.
Now, from what I've seen online, you can create a RadialGradient object and then use .createShader(rect) to convert it into a shader (haven't got it work yet, but maybe I'm implementing it wrong), but that can't be the intended way, right?
Update:
As #pskink pointed out, I'm using a newer version of Flutter and have to import the Gradient shader class as part of the Dart ui library.
The resulting code is valid and looks like this:
paint.shader = ui.Gradient.radial(
Offset(-.5, -.5),
1, // The radius
List.from({
Colors.lightBlueAccent,
Colors.blue,
Colors.deepPurpleAccent},
growable: false
)
);
Unfortunately it doesn't render anything.
This is how the code with the .createShader() method on the other Gradient class looks.
RadialGradient _gradient = RadialGradient(
colors: List.from({
Colors.lightBlueAccent,
Colors.blue,
Colors.deepPurpleAccent},
growable: false),
);
// center is the center of the canvas
paint.shader = _gradient.createShader(
Rect.fromCircle(
center: center,
radius: size.shortestSide / 2
)
);
This is kinda ugly IMO (and less efficient?), but it does render the expected result.
2nd Update:
When having more than 2 color elements in the Gradient shader class you can't omit the stops. This info is just being presented as an errorlog, which is why I didn't see it. With that being fixed: Both versions return different results...
This code returns this gradient:
#override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final paint = Paint();
RadialGradient _gradient = RadialGradient(
radius: 1,
center: Alignment(-.5, -.5),
tileMode: TileMode.clamp,
colors: List.from({Colors.lightBlueAccent, Colors.blue, Colors.deepPurpleAccent}, growable: false),
stops: List.from({0.0, 0.5, 1.0}, growable: false),
);
paint.shader = _gradient.createShader(
Rect.fromCircle(
center: center,
radius: size.shortestSide / 2
)
);
canvas.drawCircle(center, size.shortestSide / 2, paint);
}
While this code renders this gradient:
#override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final paint = Paint();
paint.shader = ui.Gradient.radial(
Offset(-.5, -.5),
1, // this is the radius
List.from({Colors.lightBlueAccent, Colors.blue, Colors.deepPurpleAccent},
growable: false),
List.from({0.0, 0.5, 1.0}, growable: false),
TileMode.clamp,
);
canvas.drawCircle(center, size.shortestSide / 2, paint);
}

Related

Custom Hit Detection on Konva Group

I'm attempting to expand the click area of a group of shapes, but there appears to be no hitFunc property on groups.
var patternControl = new Konva.Group();
patternControl.hitFunc(function(context) {
context.beginPath();
context.arc(0, 0, outerRadius + patternWidth, 0, Math.PI * 2, true);
context.fillStrokeShape(this);
});
Is there any way to do apply custom hit functions to a group?
Only shapes can be used for hit detection. As a workaround, you can disable hits for all shapes with shape.listeting(false) and then add a "fake" shape to the group that will be used as a hit area:
var patternControl = new Konva.Group();
var hitShape = new Konva.Shape({
// make it transparent, so it is not visible
fill: 'rgba(0,0,0,0)',
hitFunc: (context, shape) => {
context.beginPath();
context.arc(0, 0, outerRadius + patternWidth, 0, Math.PI * 2, true);
context.fillStrokeShape(shape);
}
});
patternControl.add(hitShape);

Flipping an animation controller view property value in Flutter

I'm trying to flip the progress value of an AnimatedIcon. For example:
icon: AnimatedIcon(
icon: AnimatedIcons.close_menu,
progress: _controller.view,
),
Right now the icon animation is backwards for what I need. So when I'm expecting _controller.view to be 0.0 it's actually showing 1.0.
I tried:
progress: _controller.view == 1.0 ? 0.0 : 1.0
but _controller.view is an Animation<double> and not just a <double>.
How can I set progress to a hard coded value?
You can use a Tween to create an animation that transforms the controller's range of values [0.0, 1.0] to an inverted range [1.0, 0.0] using Tween.animate. For example,
// create your tween.
final tween = Tween<double>(begin: 1.0, end: 0.0);
// apply it to a controller to create an animation.
final animation<double> = tween.animate(controller);
AnimatedIcon(
icon: AnimatedIcons.close_menu,
progress: animation,
);
I find using ReverseAnimation is more intuitive for me
icon: AnimatedIcon(
icon: AnimatedIcons.close_menu,
progress: ReverseAnimation(_controller),
),

Flutter drawArc() method draws full circle not just arc

Using the latest flutter I would like to draw an arch from 0 to 10 using the Canvas.drawArc method. Here's what my code looks like:
#override
void paint(Canvas canvas, Size size) {
final double radius = 1000.0;
final Paint paint = new Paint()
..isAntiAlias = true
..strokeWidth = 1.0
..color = Colors.blue[500]
..style = PaintingStyle.stroke;
canvas.drawArc(new Rect.fromLTWH(0.0, 0.0, size.width/2, size.height/2),
10.0, 20.0, false, paint);
}
It's being drawn inside of a CustomPainter, and is called in the layout like this:
children: <Widget>[
new CustomPaint(
painter: new CircleGraphWidget(),
child: new Center(
child: new Text(
'Here\'s text',
style: const TextStyle(
fontSize: 40.0,
fontWeight: FontWeight.w900,
color: const Color(0xFFFFFFFFF),
),
),
),
)
],
I expect the drawArc call to draw an arc within the rect from 10 to 20 on the circle, however this is what I get:
I'm wondering what it takes to draw just a fraction of the oval instead of the whole thing. For instance, if I only wanted 1/4 of the circle drawn, how would I go about it?
How to draw arcs
To paint in Flutter you use the CustomPaint widget. The CustomPaint widget takes a CustomPainter object as a parameter. In that class you have to override the paint method, which gives you a canvas that you can paint on. Here is the code to draw the arc in the image above.
// Don't forget: import 'dart:math' as math;
#override
void paint(Canvas canvas, Size size) {
final rect = Rect.fromLTRB(50, 100, 250, 200);
final startAngle = -math.pi / 2;
final sweepAngle = math.pi;
final useCenter = false;
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 4;
canvas.drawArc(rect, startAngle, sweepAngle, useCenter, paint);
}
Notes:
The rect is what the full oval would be inscribed within.
The startAngle is the location on the oval that the line starts drawing from. An angle of 0 is at the right side. Angles are in radians, not degrees. The top is at 3π/2 (or -π/2), the left at π, and the bottom at π/2.
The sweepAngle is how much of the oval is included in the arc. Again, angles are in radians. A value of 2π would draw the entire oval.
If you set useCenter to true, then there will be a straight line from both sides of the arc to the center.
Context
Here is the main.dart code so that you can see it in context.
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: HomeWidget(),
),
);
}
}
class HomeWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: CustomPaint( // <-- CustomPaint widget
size: Size(300, 300),
painter: MyPainter(),
),
);
}
}
class MyPainter extends CustomPainter { // <-- CustomPainter class
#override
void paint(Canvas canvas, Size size) {
// <-- Insert your painting code here.
}
#override
bool shouldRepaint(CustomPainter old) {
return false;
}
}
See also
See this article for my fuller answer.
https://docs.flutter.io/flutter/dart-ui/Canvas/drawArc.html
It starts from startAngle radians around the oval up to startAngle + sweepAngle radians around the oval, with zero radians
It expects Radians instead of Degrees:
canvas.drawArc(new Rect.fromLTWH(0.0, 0.0, size.width/2, size.height/2),
0.175, 0.349, false, paint);

How to create a custom blured shape with rounded corners in Flutter

I want to draw a custom shape similar to the marked area of below image. Is there a way to clip this custom shape with blur effect and then specify the radius for the corners?
Here is the
full example
class Customclipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var path = new Path();
path.lineTo(0.0, size.height - 20);
path.quadraticBezierTo(0.0, size.height, 20.0, size.height);
path.lineTo(size.width - 20.0, size.height);
path.quadraticBezierTo(size.width, size.height, size.width, size.height - 20);
path.lineTo(size.width, 50.0);
path.quadraticBezierTo(size.width, 30.0, size.width - 20.0, 30.0);
path.lineTo(20.0, 5.0);
path.quadraticBezierTo(0.0, 0.0, 0.0, 20.0);
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
Created all the rounded corners using quadraticBezierTo
Created a Container inside a ClipPath
Used the Colors.white70 for the container color
I use this library, which helps to build shapes from svg files:
https://fluttershapemaker.com/
All you need to do is, export your shape as svg and paste it there

Two ribbon with same z, they covered each other

I'm drawing map roads with THREE.Ribbon, every road has border with different color, so I use two ribbons, one is wider with the color of border, one is narrower, then I can achieve my purpose.
I also draw cap on two ends of ribbon with circles, certainly draw two circles, the smaller one covers bigger one, just like linecap in canvas2D.
I can't use THREE.Line as my alternative, because the maximum value of width of Line is 1 in my webgl.
My codes are just like this:
var circleShape = new THREE.Shape();
var circleRadius = lineWidth/2;
circleShape.moveTo( 0, circleRadius );
circleShape.quadraticCurveTo( circleRadius, circleRadius, circleRadius, 0 );
circleShape.quadraticCurveTo( circleRadius, -circleRadius, 0, -circleRadius );
circleShape.quadraticCurveTo( -circleRadius, -circleRadius, -circleRadius, 0 );
circleShape.quadraticCurveTo( -circleRadius, circleRadius, 0, circleRadius );
var circle = new THREE.ShapeGeometry( circleShape);
var material = new THREE.MeshBasicMaterial( { color: color, depthWrite: false} );
var mesh = new THREE.Mesh( circle, material );
this.parent.add( mesh );
var material = new THREE.MeshBasicMaterial( { color: widerColor, depthWrite: false} );
var widerRibbon = new THREE.Ribbon( widerGeometry, material );
this.parent.add( widerRibbon );
var material = new THREE.MeshBasicMaterial( { color: narrowerColor, depthWrite: false} );
var narrowerRibbon = new THREE.Ribbon( narrowerGeometry, material );
this.parent.add( narrowerRibbon );
My logic is the latter will cover the former. so narrower ribbon will cover wider ribbon, ribbon will cover half of circle.
My difficulty:
The consequence is they cover each other(without depthWrite:false).
I have found that THREE.MeshBasicMaterial can set depthWrite to false, I add it, then I find that narrower ribbon covered wider ribbon successfully, but what is weird is the cap on the ends of ribbon seems abnormal, when I look at the ribbon right on top, it works well, but when I look it with a angle, I find that the ribbon can't cover circle.
I don't know how to deal this issue, I just want the later drawing covers the former drawing when they all have same z coordinate.
Thanks for your guidance!
If you want to use "painter's algorith" (whatevr is painted last is always what you see), just turn Z off in the material(s). This is, set the "depthTest" attribute to false when declaring the material

Resources