[web] Improve null safety for color->css (flutter/engine#41699)
Turning `colorToCssString` into an extension method on `ui.Color` improves null safety and developer ergonomics.
This commit is contained in:
@@ -541,7 +541,7 @@ class CanvasPool extends _SaveStackTracking {
|
||||
void drawColor(ui.Color color, ui.BlendMode blendMode) {
|
||||
final DomCanvasRenderingContext2D ctx = context;
|
||||
contextHandle.blendMode = blendMode;
|
||||
contextHandle.fillStyle = colorToCssString(color);
|
||||
contextHandle.fillStyle = color.toCssString();
|
||||
contextHandle.strokeStyle = '';
|
||||
ctx.beginPath();
|
||||
// Fill a virtually infinite rect with the color.
|
||||
@@ -997,7 +997,7 @@ class ContextStateHandle {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final String? colorString = colorValueToCssString(paint.color);
|
||||
final String colorString = colorValueToCssString(paint.color);
|
||||
fillStyle = colorString;
|
||||
strokeStyle = colorString;
|
||||
}
|
||||
@@ -1019,7 +1019,7 @@ class ContextStateHandle {
|
||||
context.save();
|
||||
context.shadowBlur = convertSigmaToRadius(maskFilter.webOnlySigma);
|
||||
// Shadow color must be fully opaque.
|
||||
context.shadowColor = colorToCssString(ui.Color(paint.color).withAlpha(255));
|
||||
context.shadowColor = ui.Color(paint.color).withAlpha(255).toCssString();
|
||||
|
||||
// On the web a shadow must always be painted together with the shape
|
||||
// that casts it. In order to paint just the shadow, we offset the shape
|
||||
|
||||
@@ -572,7 +572,7 @@ class BitmapCanvas extends EngineCanvas {
|
||||
void _applyFilter(DomElement element, SurfacePaintData paint) {
|
||||
if (paint.maskFilter != null) {
|
||||
final bool isStroke = paint.style == ui.PaintingStyle.stroke;
|
||||
final String cssColor = colorValueToCssString(paint.color)!;
|
||||
final String cssColor = colorValueToCssString(paint.color);
|
||||
final double sigma = paint.maskFilter!.webOnlySigma;
|
||||
if (browserEngine == BrowserEngine.webkit && !isStroke) {
|
||||
// A bug in webkit leaves artifacts when this element is animated
|
||||
@@ -808,7 +808,7 @@ class BitmapCanvas extends EngineCanvas {
|
||||
case ui.BlendMode.srcOver:
|
||||
style
|
||||
..position = 'absolute'
|
||||
..backgroundColor = colorToCssString(filterColor)!;
|
||||
..backgroundColor = filterColor!.toCssString();
|
||||
case ui.BlendMode.dst:
|
||||
case ui.BlendMode.dstIn:
|
||||
style
|
||||
@@ -820,7 +820,7 @@ class BitmapCanvas extends EngineCanvas {
|
||||
..backgroundImage = "url('${image.imgElement.src}')"
|
||||
..backgroundBlendMode =
|
||||
blendModeToCssMixBlendMode(colorFilterBlendMode) ?? ''
|
||||
..backgroundColor = colorToCssString(filterColor)!;
|
||||
..backgroundColor = filterColor!.toCssString();
|
||||
break;
|
||||
}
|
||||
return imgElement;
|
||||
@@ -839,7 +839,7 @@ class BitmapCanvas extends EngineCanvas {
|
||||
final DomHTMLElement imgElement = _reuseOrCreateImage(image);
|
||||
imgElement.style.filter = 'url(#${svgFilter.id})';
|
||||
if (colorFilterBlendMode == ui.BlendMode.saturation) {
|
||||
imgElement.style.backgroundColor = colorToCssString(filterColor)!;
|
||||
imgElement.style.backgroundColor = filterColor!.toCssString();
|
||||
}
|
||||
return imgElement;
|
||||
}
|
||||
@@ -900,7 +900,7 @@ class BitmapCanvas extends EngineCanvas {
|
||||
if (shadows != null) {
|
||||
ctx.save();
|
||||
for (final ui.Shadow shadow in shadows) {
|
||||
ctx.shadowColor = colorToCssString(shadow.color);
|
||||
ctx.shadowColor = shadow.color.toCssString();
|
||||
ctx.shadowBlur = shadow.blurRadius;
|
||||
ctx.shadowOffsetX = shadow.offset.dx;
|
||||
ctx.shadowOffsetY = shadow.offset.dy;
|
||||
@@ -1009,7 +1009,7 @@ class BitmapCanvas extends EngineCanvas {
|
||||
final ui.Color color = ui.Color(paint.color);
|
||||
_canvasPool.contextHandle
|
||||
..fillStyle = null
|
||||
..strokeStyle = colorToCssString(color);
|
||||
..strokeStyle = color.toCssString();
|
||||
glRenderer!.drawHairline(ctx, positions);
|
||||
restore();
|
||||
return;
|
||||
|
||||
@@ -231,7 +231,7 @@ class PersistedPhysicalShape extends PersistedContainerSurface
|
||||
}
|
||||
|
||||
void _applyColor() {
|
||||
rootElement!.style.backgroundColor = colorToCssString(color)!;
|
||||
rootElement!.style.backgroundColor = color.toCssString();
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -352,7 +352,7 @@ SvgFilter _srcInColorFilterToSvg(ui.Color? color) {
|
||||
result: 'destalpha',
|
||||
);
|
||||
builder.setFeFlood(
|
||||
floodColor: colorToCssString(color) ?? '',
|
||||
floodColor: color?.toCssString() ?? '',
|
||||
floodOpacity: '1',
|
||||
result: 'flood',
|
||||
);
|
||||
@@ -374,7 +374,7 @@ SvgFilter _srcInColorFilterToSvg(ui.Color? color) {
|
||||
SvgFilter _dstATopColorFilterToSvg(ui.Color? color) {
|
||||
final SvgFilterBuilder builder = SvgFilterBuilder();
|
||||
builder.setFeFlood(
|
||||
floodColor: colorToCssString(color) ?? '',
|
||||
floodColor: color?.toCssString() ?? '',
|
||||
floodOpacity: '1',
|
||||
result: 'flood',
|
||||
);
|
||||
@@ -390,7 +390,7 @@ SvgFilter _dstATopColorFilterToSvg(ui.Color? color) {
|
||||
SvgFilter _srcOutColorFilterToSvg(ui.Color? color) {
|
||||
final SvgFilterBuilder builder = SvgFilterBuilder();
|
||||
builder.setFeFlood(
|
||||
floodColor: colorToCssString(color) ?? '',
|
||||
floodColor: color?.toCssString() ?? '',
|
||||
floodOpacity: '1',
|
||||
result: 'flood',
|
||||
);
|
||||
@@ -406,7 +406,7 @@ SvgFilter _srcOutColorFilterToSvg(ui.Color? color) {
|
||||
SvgFilter _xorColorFilterToSvg(ui.Color? color) {
|
||||
final SvgFilterBuilder builder = SvgFilterBuilder();
|
||||
builder.setFeFlood(
|
||||
floodColor: colorToCssString(color) ?? '',
|
||||
floodColor: color?.toCssString() ?? '',
|
||||
floodOpacity: '1',
|
||||
result: 'flood',
|
||||
);
|
||||
@@ -425,7 +425,7 @@ SvgFilter _compositeColorFilterToSvg(
|
||||
ui.Color? color, double k1, double k2, double k3, double k4) {
|
||||
final SvgFilterBuilder builder = SvgFilterBuilder();
|
||||
builder.setFeFlood(
|
||||
floodColor: colorToCssString(color) ?? '',
|
||||
floodColor: color?.toCssString() ?? '',
|
||||
floodOpacity: '1',
|
||||
result: 'flood',
|
||||
);
|
||||
@@ -478,7 +478,7 @@ SvgFilter _blendColorFilterToSvg(ui.Color? color, SvgBlendMode svgBlendMode,
|
||||
{bool swapLayers = false}) {
|
||||
final SvgFilterBuilder builder = SvgFilterBuilder();
|
||||
builder.setFeFlood(
|
||||
floodColor: colorToCssString(color) ?? '',
|
||||
floodColor: color?.toCssString() ?? '',
|
||||
floodOpacity: '1',
|
||||
result: 'flood',
|
||||
);
|
||||
|
||||
@@ -60,7 +60,7 @@ class DomCanvas extends EngineCanvas with SaveElementStackTracking {
|
||||
..right = '0'
|
||||
..bottom = '0'
|
||||
..left = '0'
|
||||
..backgroundColor = colorToCssString(color)!;
|
||||
..backgroundColor = color.toCssString();
|
||||
currentElement.append(box);
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ DomHTMLElement buildDrawRectElement(
|
||||
..transformOrigin = '0 0 0'
|
||||
..transform = effectiveTransform;
|
||||
|
||||
String cssColor = colorValueToCssString(paint.color)!;
|
||||
String cssColor = colorValueToCssString(paint.color);
|
||||
|
||||
if (paint.maskFilter != null) {
|
||||
final double sigma = paint.maskFilter!.webOnlySigma;
|
||||
@@ -265,7 +265,7 @@ DomHTMLElement buildDrawRectElement(
|
||||
// A bug in webkit leaves artifacts when this element is animated
|
||||
// with filter: blur, we use boxShadow instead.
|
||||
style.boxShadow = '0px 0px ${sigma * 2.0}px $cssColor';
|
||||
cssColor = colorToCssString(blurColor(ui.Color(paint.color), sigma))!;
|
||||
cssColor = blurColor(ui.Color(paint.color), sigma).toCssString();
|
||||
} else {
|
||||
style.filter = 'blur(${sigma}px)';
|
||||
}
|
||||
@@ -345,14 +345,14 @@ SVGSVGElement pathToSvgElement(SurfacePath path, SurfacePaintData paint) {
|
||||
(paint.style != ui.PaintingStyle.fill &&
|
||||
paint.strokeWidth != 0 &&
|
||||
paint.strokeWidth != null)) {
|
||||
svgPath.setAttribute('stroke', colorValueToCssString(paint.color)!);
|
||||
svgPath.setAttribute('stroke', colorValueToCssString(paint.color));
|
||||
svgPath.setAttribute('stroke-width', '${paint.strokeWidth ?? 1.0}');
|
||||
if (paint.strokeCap != null) {
|
||||
svgPath.setAttribute('stroke-linecap', '${stringForStrokeCap(paint.strokeCap)}');
|
||||
}
|
||||
svgPath.setAttribute('fill', 'none');
|
||||
} else {
|
||||
svgPath.setAttribute('fill', colorValueToCssString(paint.color)!);
|
||||
svgPath.setAttribute('fill', colorValueToCssString(paint.color));
|
||||
}
|
||||
if (path.fillType == ui.PathFillType.evenOdd) {
|
||||
svgPath.setAttribute('fill-rule', 'evenodd');
|
||||
|
||||
@@ -268,7 +268,7 @@ class SurfacePaintData {
|
||||
if (strokeJoin != null) {
|
||||
buffer.write('strokeJoin = $strokeJoin; ');
|
||||
}
|
||||
buffer.write('color = ${colorToCssString(ui.Color(color))}; ');
|
||||
buffer.write('color = ${ui.Color(color).toCssString()}; ');
|
||||
if (shader != null) {
|
||||
buffer.write('shader = $shader; ');
|
||||
}
|
||||
|
||||
@@ -407,13 +407,13 @@ void _addColorStopsToCanvasGradient(DomCanvasGradient gradient,
|
||||
}
|
||||
if (colorStops == null) {
|
||||
assert(colors.length == 2);
|
||||
gradient.addColorStop(offset, colorToCssString(colors[0])!);
|
||||
gradient.addColorStop(1 - offset, colorToCssString(colors[1])!);
|
||||
gradient.addColorStop(offset, colors[0].toCssString());
|
||||
gradient.addColorStop(1 - offset, colors[1].toCssString());
|
||||
} else {
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
final double colorStop = colorStops[i].clamp(0.0, 1.0);
|
||||
gradient.addColorStop(
|
||||
colorStop * scale + offset, colorToCssString(colors[i])!);
|
||||
colorStop * scale + offset, colors[i].toCssString());
|
||||
}
|
||||
}
|
||||
if (isDecal) {
|
||||
@@ -841,7 +841,7 @@ class ModeHtmlColorFilter extends EngineHtmlColorFilter {
|
||||
if (blendMode == ui.BlendMode.saturation ||
|
||||
blendMode == ui.BlendMode.multiply ||
|
||||
blendMode == ui.BlendMode.modulate) {
|
||||
filterElement!.style.backgroundColor = colorToCssString(color)!;
|
||||
filterElement!.style.backgroundColor = color.toCssString();
|
||||
}
|
||||
return svgFilter.element;
|
||||
}
|
||||
|
||||
@@ -791,13 +791,13 @@ void applyTextStyleToElement({
|
||||
final double adaptedWidth = strokeWidth != null && strokeWidth > 0
|
||||
? strokeWidth
|
||||
: 1.0 / ui.window.devicePixelRatio;
|
||||
cssStyle.textStroke = '${adaptedWidth}px ${colorToCssString(color)}';
|
||||
cssStyle.textStroke = '${adaptedWidth}px ${color?.toCssString()}';
|
||||
} else if (color != null) {
|
||||
cssStyle.color = colorToCssString(color)!;
|
||||
cssStyle.color = color.toCssString();
|
||||
}
|
||||
final ui.Color? background = style.background?.color;
|
||||
if (background != null) {
|
||||
cssStyle.backgroundColor = colorToCssString(background)!;
|
||||
cssStyle.backgroundColor = background.toCssString();
|
||||
}
|
||||
final double? fontSize = style.fontSize;
|
||||
if (fontSize != null) {
|
||||
@@ -843,7 +843,7 @@ void applyTextStyleToElement({
|
||||
}
|
||||
final ui.Color? decorationColor = style.decorationColor;
|
||||
if (decorationColor != null) {
|
||||
cssStyle.textDecorationColor = colorToCssString(decorationColor)!;
|
||||
cssStyle.textDecorationColor = decorationColor.toCssString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -878,7 +878,7 @@ String _shadowListToCss(List<ui.Shadow> shadows) {
|
||||
}
|
||||
final ui.Shadow shadow = shadows[i];
|
||||
sb.write('${shadow.offset.dx}px ${shadow.offset.dy}px '
|
||||
'${shadow.blurRadius}px ${colorToCssString(shadow.color)}');
|
||||
'${shadow.blurRadius}px ${shadow.color.toCssString()}');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -339,20 +339,15 @@ bool rectContainsOther(ui.Rect rect, ui.Rect other) {
|
||||
rect.bottom >= other.bottom;
|
||||
}
|
||||
|
||||
/// Converts color to a css compatible attribute value.
|
||||
String? colorToCssString(ui.Color? color) {
|
||||
if (color == null) {
|
||||
return null;
|
||||
extension CssColor on ui.Color {
|
||||
/// Converts color to a css compatible attribute value.
|
||||
String toCssString() {
|
||||
return colorValueToCssString(value);
|
||||
}
|
||||
final int value = color.value;
|
||||
return colorValueToCssString(value);
|
||||
}
|
||||
|
||||
// Converts a color value (as an int) into a CSS-compatible value.
|
||||
String? colorValueToCssString(int? value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String colorValueToCssString(int value) {
|
||||
if (value == 0xFF000000) {
|
||||
return '#000000';
|
||||
}
|
||||
@@ -690,7 +685,7 @@ void setThemeColor(ui.Color? color) {
|
||||
..name = 'theme-color';
|
||||
domDocument.head!.append(theme);
|
||||
}
|
||||
theme.content = colorToCssString(color)!;
|
||||
theme.content = color.toCssString();
|
||||
} else {
|
||||
theme?.remove();
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ Future<void> testMain() async {
|
||||
const ui.Color expectedPrimaryColor = ui.Color(0xFF00FF00);
|
||||
|
||||
expect(domDocument.title, 'Title Test');
|
||||
expect(getCssThemeColor(), colorToCssString(expectedPrimaryColor));
|
||||
expect(getCssThemeColor(), expectedPrimaryColor.toCssString());
|
||||
|
||||
ui.window.sendPlatformMessage(
|
||||
'flutter/platform',
|
||||
@@ -64,7 +64,7 @@ Future<void> testMain() async {
|
||||
const ui.Color expectedNewPrimaryColor = ui.Color(0xFFFABADA);
|
||||
|
||||
expect(domDocument.title, 'Different title');
|
||||
expect(getCssThemeColor(), colorToCssString(expectedNewPrimaryColor));
|
||||
expect(getCssThemeColor(), expectedNewPrimaryColor.toCssString());
|
||||
});
|
||||
|
||||
test('supports null title and primaryColor', () {
|
||||
@@ -89,7 +89,7 @@ Future<void> testMain() async {
|
||||
);
|
||||
|
||||
expect(domDocument.title, '');
|
||||
expect(getCssThemeColor(), colorToCssString(expectedNullColor));
|
||||
expect(getCssThemeColor(), expectedNullColor.toCssString());
|
||||
|
||||
domDocument.title = 'Something Else';
|
||||
expect(domDocument.title, 'Something Else');
|
||||
@@ -104,7 +104,7 @@ Future<void> testMain() async {
|
||||
);
|
||||
|
||||
expect(domDocument.title, '');
|
||||
expect(getCssThemeColor(), colorToCssString(expectedNullColor));
|
||||
expect(getCssThemeColor(), expectedNullColor.toCssString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ void testMain() {
|
||||
|
||||
const ui.Color statusBarColor = ui.Color(0xFFF44336);
|
||||
sendSetSystemUIOverlayStyle(statusBarColor: statusBarColor);
|
||||
expect(getCssThemeColor(), colorToCssString(statusBarColor));
|
||||
expect(getCssThemeColor(), statusBarColor.toCssString());
|
||||
|
||||
sendSetSystemUIOverlayStyle();
|
||||
expect(getCssThemeColor(), null);
|
||||
|
||||
@@ -194,14 +194,14 @@ DomElement pathToSvgElement(Path path, Paint paint, bool enableFill) {
|
||||
root.append(pathElement);
|
||||
if (paint.style == PaintingStyle.stroke ||
|
||||
paint.strokeWidth != 0.0) {
|
||||
pathElement.setAttribute('stroke', colorToCssString(paint.color)!);
|
||||
pathElement.setAttribute('stroke', paint.color.toCssString());
|
||||
pathElement.setAttribute('stroke-width', paint.strokeWidth);
|
||||
if (!enableFill) {
|
||||
pathElement.setAttribute('fill', 'none');
|
||||
}
|
||||
}
|
||||
if (paint.style == PaintingStyle.fill) {
|
||||
pathElement.setAttribute('fill', colorToCssString(paint.color)!);
|
||||
pathElement.setAttribute('fill', paint.color.toCssString());
|
||||
}
|
||||
pathElement.setAttribute('d', pathToSvg((path as SurfacePath).pathRef)); // This is what we're testing!
|
||||
return root;
|
||||
|
||||
Reference in New Issue
Block a user