introduce system color palette (#163335)

Introduce system colors:

* Add new `SystemColor` class.
* Add `PlatformDispatcher.systemColors` getter.

The web engine part for https://github.com/flutter/flutter/issues/118853
This commit is contained in:
Yegor 2025-02-20 14:32:06 -08:00 committed by GitHub
parent 7819cee8d6
commit 9b356c17c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 652 additions and 44 deletions

View File

@ -42633,6 +42633,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/font_fallbacks.dart + ../../.
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/fonts.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/frame_service.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/frame_timing_recorder.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/high_contrast.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/backdrop_filter.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/canvas.dart + ../../../flutter/LICENSE
@ -45600,6 +45601,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/font_fallbacks.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/fonts.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/frame_service.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/frame_timing_recorder.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/high_contrast.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/backdrop_filter.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/canvas.dart

View File

@ -1508,6 +1508,216 @@ class PlatformDispatcher {
external static double _getScaledFontSize(double unscaledFontSize, int configurationId);
}
/// A color specified in the operating system UI color palette.
///
/// The static getters in this class, such as [accentColor] and [buttonText],
/// provide standard system colors defined by the
/// [W3C CSS specification](https://drafts.csswg.org/css-color/#css-system-colors).
///
/// As of the current release, system colors are supported on web only. To check
/// if the current platform supports system colors, use the static
/// [platformProvidesSystemColors] field. If the field is `false`, other
/// functions in this class will throw [UnsupportedError].
///
/// This class is typically used in conjunction with
/// [AccessibilityFeatures.highContrast]. In particular, on Windows, when a user
/// enables high-contrast mode, they may also pick specific colors that should
/// be used by application user interfaces. While it is common for applications
/// to use custom color themes and design languages, in high-contrast mode it is
/// recommended that widgets use system-specified colors to make content more
/// legible for users.
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
/// * https://developer.mozilla.org/en-US/docs/Web/CSS/system-color
/// * https://developer.mozilla.org/en-US/docs/Web/CSS/@media/forced-colors
final class SystemColor {
/// Creates an instance of a system color.
///
/// [name] is the name of the color. System colors provided by static getters
/// in this class, such as [accentColor] and [buttonText], use standard names
/// defined by the [W3C CSS specification](https://drafts.csswg.org/css-color/#css-system-colors).
///
/// [value] is the color value, if this color name is supported, and null if
/// it's unsupported.
const SystemColor({required this.name, this.value});
/// Standard system color name, as defined by W3C CSS specification.
///
/// System color names in Flutter are case-sensitive. This is so that color
/// names can be easily used as [Map] keys. This is in contrast to CSS, where
/// system color names are not case-sensitive. That is, specifying
/// `background-color: aCcEnTcOlOr` is equivalent to specifying
/// `background-color: AccentColor`.
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
final String name;
/// The color value used for the color named [name], if supported.
///
/// If [isSupported] is false, the [value] is null. If [isSupported] is true,
/// the [value] is not null.
final Color? value;
/// Returns true if the current platform provides the system color with the
/// given [name].
///
/// See also:
///
/// * [platformProvidesSystemColors], which returns whether the current
/// platform provides system colors.
bool get isSupported => value != null;
/// Returns true if the current platform provides system colors.
///
/// As of the current release, system colors are supported on web only.
///
/// See also:
///
/// * [isSupported], which returns whether a specific color is supported.
static bool get platformProvidesSystemColors => false;
static UnsupportedError _systemColorUnsupportedError() {
return UnsupportedError('SystemColor not supported on the current platform.');
}
/// Returns system color named "AccentColor".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get accentColor => throw _systemColorUnsupportedError();
/// Returns system color named "AccentColorText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get accentColorText => throw _systemColorUnsupportedError();
/// Returns system color named "ActiveText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get activeText => throw _systemColorUnsupportedError();
/// Returns system color named "ButtonBorder".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get buttonBorder => throw _systemColorUnsupportedError();
/// Returns system color named "ButtonFace".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get buttonFace => throw _systemColorUnsupportedError();
/// Returns system color named "ButtonText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get buttonText => throw _systemColorUnsupportedError();
/// Returns system color named "Canvas".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get canvas => throw _systemColorUnsupportedError();
/// Returns system color named "CanvasText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get canvasText => throw _systemColorUnsupportedError();
/// Returns system color named "Field".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get field => throw _systemColorUnsupportedError();
/// Returns system color named "FieldText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get fieldText => throw _systemColorUnsupportedError();
/// Returns system color named "GrayText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get grayText => throw _systemColorUnsupportedError();
/// Returns system color named "Highlight".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get highlight => throw _systemColorUnsupportedError();
/// Returns system color named "HighlightText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get highlightText => throw _systemColorUnsupportedError();
/// Returns system color named "LinkText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get linkText => throw _systemColorUnsupportedError();
/// Returns system color named "Mark".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get mark => throw _systemColorUnsupportedError();
/// Returns system color named "MarkText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get markText => throw _systemColorUnsupportedError();
/// Returns system color named "SelectedItem".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get selectedItem => throw _systemColorUnsupportedError();
/// Returns system color named "SelectedItemText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get selectedItemText => throw _systemColorUnsupportedError();
/// Returns system color named "VisitedText".
///
/// See also:
///
/// * https://drafts.csswg.org/css-color/#css-system-colors
static SystemColor get visitedText => throw _systemColorUnsupportedError();
}
/// Configuration of the platform.
///
/// Immutable class (but can't use @immutable in dart:ui)

View File

@ -153,6 +153,75 @@ abstract class PlatformDispatcher {
double scaleFontSize(double unscaledFontSize);
}
final class SystemColor {
const SystemColor({required this.name, this.value});
final String name;
final Color? value;
bool get isSupported => value != null;
static bool get platformProvidesSystemColors => true;
static SystemColor _lookUp(String name) {
return engine.SystemColorPaletteDetector.instance.systemColors[name]!;
}
static SystemColor get accentColor => _accentColor;
static final SystemColor _accentColor = _lookUp('AccentColor');
static SystemColor get accentColorText => _accentColorText;
static final SystemColor _accentColorText = _lookUp('AccentColorText');
static SystemColor get activeText => _activeText;
static final SystemColor _activeText = _lookUp('ActiveText');
static SystemColor get buttonBorder => _buttonBorder;
static final SystemColor _buttonBorder = _lookUp('ButtonBorder');
static SystemColor get buttonFace => _buttonFace;
static final SystemColor _buttonFace = _lookUp('ButtonFace');
static SystemColor get buttonText => _buttonText;
static final SystemColor _buttonText = _lookUp('ButtonText');
static SystemColor get canvas => _canvas;
static final SystemColor _canvas = _lookUp('Canvas');
static SystemColor get canvasText => _canvasText;
static final SystemColor _canvasText = _lookUp('CanvasText');
static SystemColor get field => _field;
static final SystemColor _field = _lookUp('Field');
static SystemColor get fieldText => _fieldText;
static final SystemColor _fieldText = _lookUp('FieldText');
static SystemColor get grayText => _grayText;
static final SystemColor _grayText = _lookUp('GrayText');
static SystemColor get highlight => _highlight;
static final SystemColor _highlight = _lookUp('Highlight');
static SystemColor get highlightText => _highlightText;
static final SystemColor _highlightText = _lookUp('HighlightText');
static SystemColor get linkText => _linkText;
static final SystemColor _linkText = _lookUp('LinkText');
static SystemColor get mark => _mark;
static final SystemColor _mark = _lookUp('Mark');
static SystemColor get markText => _markText;
static final SystemColor _markText = _lookUp('MarkText');
static SystemColor get selectedItem => _selectedItem;
static final SystemColor _selectedItem = _lookUp('SelectedItem');
static SystemColor get selectedItemText => _selectedItemText;
static final SystemColor _selectedItemText = _lookUp('SelectedItemText');
static SystemColor get visitedText => _visitedText;
static final SystemColor _visitedText = _lookUp('VisitedText');
}
enum FramePhase {
vsyncStart,
buildStart,

View File

@ -67,6 +67,7 @@ export 'engine/font_fallbacks.dart';
export 'engine/fonts.dart';
export 'engine/frame_service.dart';
export 'engine/frame_timing_recorder.dart';
export 'engine/high_contrast.dart';
export 'engine/html/backdrop_filter.dart';
export 'engine/html/bitmap_canvas.dart';
export 'engine/html/canvas.dart';

View File

@ -735,6 +735,13 @@ extension DomElementExtension on DomElement {
external void setPointerCapture(num? pointerId);
}
extension type DomCSS(JSObject _) implements JSObject {
external bool supports(String proeprty, String value);
}
@JS('CSS')
external DomCSS get domCSS;
@JS()
@staticInterop
class DomCSSStyleDeclaration {}

View File

@ -0,0 +1,192 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:js_interop';
import 'package:ui/ui.dart' as ui;
import 'dom.dart';
/// Signature of functions added as a listener to high contrast changes
typedef HighContrastListener = void Function(bool enabled);
/// Determines if high contrast is enabled using media query 'forced-colors: active' for Windows
class HighContrastSupport {
static HighContrastSupport instance = HighContrastSupport();
static const String _highContrastMediaQueryString = '(forced-colors: active)';
final List<HighContrastListener> _listeners = <HighContrastListener>[];
/// Reference to css media query that indicates whether high contrast is on.
final DomMediaQueryList _highContrastMediaQuery = domWindow.matchMedia(
_highContrastMediaQueryString,
);
late final DomEventListener _onHighContrastChangeListener = createDomEventListener(
_onHighContrastChange,
);
bool get isHighContrastEnabled => _highContrastMediaQuery.matches;
/// Adds function to the list of listeners on high contrast changes
void addListener(HighContrastListener listener) {
if (_listeners.isEmpty) {
_highContrastMediaQuery.addListener(_onHighContrastChangeListener);
}
_listeners.add(listener);
}
/// Removes function from the list of listeners on high contrast changes
void removeListener(HighContrastListener listener) {
_listeners.remove(listener);
if (_listeners.isEmpty) {
_highContrastMediaQuery.removeListener(_onHighContrastChangeListener);
}
}
JSVoid _onHighContrastChange(DomEvent event) {
final DomMediaQueryListEvent mqEvent = event as DomMediaQueryListEvent;
final bool isHighContrastEnabled = mqEvent.matches!;
for (final HighContrastListener listener in _listeners) {
listener(isHighContrastEnabled);
}
}
}
const List<String> systemColorNames = <String>[
'AccentColor',
'AccentColorText',
'ActiveText',
'ButtonBorder',
'ButtonFace',
'ButtonText',
'Canvas',
'CanvasText',
'Field',
'FieldText',
'GrayText',
'Highlight',
'HighlightText',
'LinkText',
'Mark',
'MarkText',
'SelectedItem',
'SelectedItemText',
'VisitedText',
];
class SystemColorPaletteDetector {
SystemColorPaletteDetector() {
final hostDetector = createDomHTMLDivElement();
hostDetector.style
..position = 'absolute'
..transform = 'translate(-10000, -10000)';
domDocument.body!.appendChild(hostDetector);
final colorDetectors = <String, DomHTMLElement>{};
for (final systemColorName in systemColorNames) {
final detector = createDomHTMLDivElement();
detector.style.backgroundColor = systemColorName;
detector.innerText = '$systemColorName detector';
hostDetector.appendChild(detector);
colorDetectors[systemColorName] = detector;
}
final results = <String, ui.SystemColor>{};
colorDetectors.forEach((systemColorName, detector) {
final computedDetector = domWindow.getComputedStyle(detector);
final computedColor = computedDetector.backgroundColor;
final isSupported = domCSS.supports('color', systemColorName);
ui.Color? value;
if (isSupported) {
value = parseCssRgb(computedColor);
}
results[systemColorName] = ui.SystemColor(name: systemColorName, value: value);
});
systemColors = results;
// Once colors have been detected, this element is no longer needed.
hostDetector.remove();
}
static SystemColorPaletteDetector instance = SystemColorPaletteDetector();
late final Map<String, ui.SystemColor> systemColors;
}
/// Parses CSS RGB color written as `rgb(r, g, b)` or `rgba(r, g, b, a)`.
ui.Color? parseCssRgb(String rgbString) {
// Remove leading and trailing whitespace.
rgbString = rgbString.trim();
final isRgb = rgbString.startsWith('rgb(');
final isRgba = rgbString.startsWith('rgba(');
if ((!isRgb && !isRgba) || !rgbString.endsWith(')')) {
assert(() {
print('Bad CSS color "$rgbString": not an rgb or rgba color.');
return true;
}());
return null;
}
assert(isRgb || isRgba);
// Extract the comma-separated values.
final valuesString = rgbString.substring(isRgb ? 4 : 5, rgbString.length - 1);
final values = valuesString.split(',');
// Check if there are exactly three values for RGB, and four values for RGBA.
if ((isRgb && values.length != 3) || (isRgba && values.length != 4)) {
assert(() {
print(
'Bad CSS color "$rgbString": wrong number of color componets. For ${isRgb ? 'rgb' : 'rgba'} color, expected ${isRgb ? 3 : 4} components, but found ${values.length}.',
);
return true;
}());
return null;
}
// Parse the values as integers.
final r = int.tryParse(values[0].trim());
final g = int.tryParse(values[1].trim());
final b = int.tryParse(values[2].trim());
// Check if the values are valid integers between 0 and 255.
if (r == null ||
g == null ||
b == null ||
r < 0 ||
r > 255 ||
g < 0 ||
g > 255 ||
b < 0 ||
b > 255) {
assert(() {
print(
'Bad CSS color "$rgbString": one of RGB components failed to parse or outside the 0-255 range: r = $r, g = $g, b = $b.',
);
return true;
}());
return null;
}
if (isRgb) {
return ui.Color.fromRGBO(r, g, b, 1.0);
} else {
assert(isRgba);
final a = double.tryParse(values[3].trim());
if (a == null || a < 0.0 || a > 1.0) {
assert(() {
print('Bad CSS color "$rgbString": alpha component outside the 0.0-1.0 range: $a');
return true;
}());
return null;
} else {
return ui.Color.fromRGBO(r, g, b, a);
}
}
}

View File

@ -13,55 +13,11 @@ import 'package:ui/ui_web/src/ui_web.dart' as ui_web;
import '../engine.dart';
/// Signature of functions added as a listener to high contrast changes
typedef HighContrastListener = void Function(bool enabled);
typedef _KeyDataResponseCallback = void Function(bool handled);
const StandardMethodCodec standardCodec = StandardMethodCodec();
const JSONMethodCodec jsonCodec = JSONMethodCodec();
/// Determines if high contrast is enabled using media query 'forced-colors: active' for Windows
class HighContrastSupport {
static HighContrastSupport instance = HighContrastSupport();
static const String _highContrastMediaQueryString = '(forced-colors: active)';
final List<HighContrastListener> _listeners = <HighContrastListener>[];
/// Reference to css media query that indicates whether high contrast is on.
final DomMediaQueryList _highContrastMediaQuery = domWindow.matchMedia(
_highContrastMediaQueryString,
);
late final DomEventListener _onHighContrastChangeListener = createDomEventListener(
_onHighContrastChange,
);
bool get isHighContrastEnabled => _highContrastMediaQuery.matches;
/// Adds function to the list of listeners on high contrast changes
void addListener(HighContrastListener listener) {
if (_listeners.isEmpty) {
_highContrastMediaQuery.addListener(_onHighContrastChangeListener);
}
_listeners.add(listener);
}
/// Removes function from the list of listeners on high contrast changes
void removeListener(HighContrastListener listener) {
_listeners.remove(listener);
if (_listeners.isEmpty) {
_highContrastMediaQuery.removeListener(_onHighContrastChangeListener);
}
}
JSVoid _onHighContrastChange(DomEvent event) {
final DomMediaQueryListEvent mqEvent = event as DomMediaQueryListEvent;
final bool isHighContrastEnabled = mqEvent.matches!;
for (final HighContrastListener listener in _listeners) {
listener(isHighContrastEnabled);
}
}
}
/// Platform event dispatcher.
///
/// This is the central entry point for platform messages and configuration

View File

@ -0,0 +1,171 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;
void main() {
internalBootstrapBrowserTest(() => testMain);
}
void testMain() {
test('parseCssColor(rgb)', () {
final color1 = parseCssRgb('rgb(12, 34, 56)');
expect(color1, const ui.Color(0xff0c2238));
final color2 = parseCssRgb('rgb(255, 0, 0)');
expect(color2, const ui.Color(0xffff0000));
final color3 = parseCssRgb('rgb(0, 255, 0)');
expect(color3, const ui.Color(0xff00ff00));
final color4 = parseCssRgb('rgb(0, 0, 255)');
expect(color4, const ui.Color(0xff0000ff));
final color5 = parseCssRgb('rgb(255,255,255)');
expect(color5, const ui.Color(0xffffffff));
final color6 = parseCssRgb('rgb(0,0,0)');
expect(color6, const ui.Color(0xff000000));
final color7 = parseCssRgb(' rgb( 10, 20 ,30 ) ');
expect(color7, const ui.Color(0xff0a141e));
// Invalid input:
expect(parseCssRgb('rgb(256, 0, 0)'), isNull);
expect(parseCssRgb('rgb(255, 0)'), isNull);
expect(parseCssRgb('rgb255,0,0'), isNull);
});
test('parseCssColor(rgba)', () {
final color1 = parseCssRgb('rgba(12, 34, 56, 0.5)');
expect(color1?.toCssString(), const ui.Color.fromRGBO(12, 34, 56, 0.5).toCssString());
final color2 = parseCssRgb('rgba(255, 0, 0, 0.0)');
expect(color2, const ui.Color.fromRGBO(255, 0, 0, 0.0));
final color3 = parseCssRgb('rgba(0, 255, 0, 1.0)');
expect(color3, const ui.Color.fromRGBO(0, 255, 0, 1.0));
final color4 = parseCssRgb('rgba(0, 0, 255, 0.7)');
expect(color4, const ui.Color.fromRGBO(0, 0, 255, 0.7));
final color5 = parseCssRgb('rgba(255,255,255,0.2)');
expect(color5, const ui.Color.fromRGBO(255, 255, 255, 0.2));
final color6 = parseCssRgb('rgba(0,0,0,1.0)');
expect(color6, const ui.Color.fromRGBO(0, 0, 0, 1.0));
final color7 = parseCssRgb(' rgba( 10, 20 ,30, 0.8 ) ');
expect(color7, const ui.Color.fromRGBO(10, 20, 30, 0.8));
// Invalid input:
expect(parseCssRgb('rgba(256, 0, 0, 0.1)'), isNull);
expect(parseCssRgb('rgba(255, 0, 0.1)'), isNull);
expect(parseCssRgb('rgb255,0,0,0.1'), isNull);
expect(parseCssRgb('rgba(12, 34, 56, -0.1)'), isNull);
expect(parseCssRgb('rgba(12, 34, 56, 1.1)'), isNull);
});
test('ForcedColorPaletteDetector', () {
const systemColorNames = <String>[
'AccentColor',
'AccentColorText',
'ActiveText',
'ButtonBorder',
'ButtonFace',
'ButtonText',
'Canvas',
'CanvasText',
'Field',
'FieldText',
'GrayText',
'Highlight',
'HighlightText',
'LinkText',
'Mark',
'MarkText',
'SelectedItem',
'SelectedItemText',
'VisitedText',
];
final detector = SystemColorPaletteDetector();
expect(detector.systemColors.keys, containsAll(systemColorNames));
expect(
detector.systemColors.values.where((color) => color.isSupported),
// Different browser/OS combinations support different colors. It's
// impractical to encode the precise number for each combo. Instead, this
// test only makes sure that at least some "reasonable" number of colors
// were detected successfully. If the number is too low, it's a red flag.
// Perhaps the parsing logic is flawed, or the logic that enumerates the
// colors.
hasLength(greaterThan(15)),
);
});
test('SystemColor', () {
const supportedColor = ui.SystemColor(
name: 'SupportedColor',
value: ui.Color.fromRGBO(1, 2, 3, 0.5),
);
expect(supportedColor.name, 'SupportedColor');
expect(supportedColor.value, isNotNull);
expect(supportedColor.isSupported, isTrue);
const unsupportedColor = ui.SystemColor(name: 'UnsupportedColor');
expect(unsupportedColor.name, 'UnsupportedColor');
expect(unsupportedColor.value, isNull);
expect(unsupportedColor.isSupported, isFalse);
expect(ui.SystemColor.accentColor.name, 'AccentColor');
expect(ui.SystemColor.accentColorText.name, 'AccentColorText');
expect(ui.SystemColor.activeText.name, 'ActiveText');
expect(ui.SystemColor.buttonBorder.name, 'ButtonBorder');
expect(ui.SystemColor.buttonFace.name, 'ButtonFace');
expect(ui.SystemColor.buttonText.name, 'ButtonText');
expect(ui.SystemColor.canvas.name, 'Canvas');
expect(ui.SystemColor.canvasText.name, 'CanvasText');
expect(ui.SystemColor.field.name, 'Field');
expect(ui.SystemColor.fieldText.name, 'FieldText');
expect(ui.SystemColor.grayText.name, 'GrayText');
expect(ui.SystemColor.highlight.name, 'Highlight');
expect(ui.SystemColor.highlightText.name, 'HighlightText');
expect(ui.SystemColor.linkText.name, 'LinkText');
expect(ui.SystemColor.mark.name, 'Mark');
expect(ui.SystemColor.markText.name, 'MarkText');
expect(ui.SystemColor.selectedItem.name, 'SelectedItem');
expect(ui.SystemColor.selectedItemText.name, 'SelectedItemText');
expect(ui.SystemColor.visitedText.name, 'VisitedText');
final allColors = <ui.SystemColor>[
ui.SystemColor.accentColor,
ui.SystemColor.accentColorText,
ui.SystemColor.activeText,
ui.SystemColor.buttonBorder,
ui.SystemColor.buttonFace,
ui.SystemColor.buttonText,
ui.SystemColor.canvas,
ui.SystemColor.canvasText,
ui.SystemColor.field,
ui.SystemColor.fieldText,
ui.SystemColor.grayText,
ui.SystemColor.highlight,
ui.SystemColor.highlightText,
ui.SystemColor.linkText,
ui.SystemColor.mark,
ui.SystemColor.markText,
ui.SystemColor.selectedItem,
ui.SystemColor.selectedItemText,
ui.SystemColor.visitedText,
];
for (final color in allColors) {
expect(color.value != null, color.isSupported);
}
});
}