createShader method
Creates a Shader for this gradient to fill the given rect.
If the gradient's configuration is text-direction-dependent, for example
it uses AlignmentDirectional objects instead of Alignment
objects, then the textDirection argument must not be null.
The shader's transform will be resolved from the transform of this gradient.
Implementation
@override
Shader createShader(Rect rect, {TextDirection? textDirection}) {
if (borderEdge != null) {
rect = Rect.fromLTRB(rect.left + borderEdge!.left, rect.top + borderEdge!.top, rect.right - borderEdge!.right,
rect.bottom - borderEdge!.bottom);
}
double? angle;
if (_angle != null) {
angle = _angle;
} else {
Alignment point = end as Alignment;
angle = math.atan2(point.x * rect.height, -point.y * rect.width) - math.pi * 2;
}
// https://drafts.csswg.org/css-images-3/#linear-gradient-syntax
double sin = math.sin(angle!);
double cos = math.cos(angle);
double width = rect.width;
double height = rect.height;
// If the rect is degenerate, still create a valid gradient including
// implied stops to avoid the engine's "colorStops omitted" assertion.
if (width == 0 || height == 0) {
return ui.Gradient.linear(
Offset.zero,
Offset.zero,
colors,
_impliedStops(),
tileMode,
_resolveTransform(rect, textDirection),
);
}
double deviceLength = (sin * width).abs() + (cos * height).abs();
double length = deviceLength;
if (tileMode == TileMode.repeated && _repeatPeriod != null && _repeatPeriod! > 0) {
// Shrink the 0..1 span to the intended repeating period so TileMode.repeated
// repeats every _repeatPeriod device pixels along the gradient axis.
length = _repeatPeriod!;
}
double x = sin * length / width;
double y = cos * length / height;
final double halfWidth = rect.width / 2.0;
final double halfHeight = rect.height / 2.0;
Offset beginOffset = Offset(
rect.left + halfWidth + -x * halfWidth,
rect.top + halfHeight + y * halfHeight,
);
Offset endOffset = Offset(
rect.left + halfWidth + x * halfWidth,
rect.top + halfHeight - y * halfHeight,
);
if (DebugFlags.enableBackgroundLogs) {
final sampleStops = stops?.map((s) => s.toStringAsFixed(4)).toList();
final rp = (tileMode == TileMode.repeated && _repeatPeriod != null && _repeatPeriod! > 0)
? ' period=${_repeatPeriod!.toStringAsFixed(2)}'
: '';
renderingLogger.finer('[Background] shader(linear) rect=${rect.size} angle=${(angle * 180 / math.pi).toStringAsFixed(1)}deg'+rp+' '
'begin=${beginOffset.dx.toStringAsFixed(2)},${beginOffset.dy.toStringAsFixed(2)} '
'end=${endOffset.dx.toStringAsFixed(2)},${endOffset.dy.toStringAsFixed(2)} '
'stops=${sampleStops ?? const []}');
}
return ui.Gradient.linear(
beginOffset, endOffset, colors, _impliedStops(), tileMode, _resolveTransform(rect, textDirection));
}