概述

#

dart:uiColor 類的 API 正在進行更改,以支援寬色域

背景

#

Flutter 引擎已支援寬色域,並集成了 Impeller,現在正在將此支援新增到框架

Flutter 支援的 iOS 裝置可以渲染更廣泛的顏色,特別是在DisplayP3 色域中。在此更改後,Flutter 框架可以在 iOS Impeller 上渲染所有這些顏色,並且 Color 類已更好地準備好支援未來的色域或顏色分量位深度的更改。

變更說明

#

Color 的更改

  1. 添加了一個列舉欄位,用於指定其ColorSpace
  2. 添加了使用歸一化浮點數顏色分量的 API。
  3. 移除了使用可能導致資料丟失的 8 位無符號整數顏色分量的 API。

ColorSpace 的更改

  1. 添加了 displayP3 屬性。

遷移指南

#

8 位無符號整數建構函式

#

Color.fromARGB 這樣的建構函式保持不變並繼續受支援。要利用 Display P3 顏色,您必須使用新的 Color.from 建構函式,該建構函式接受歸一化的浮點數顏色分量。

dart
// Before: Constructing an sRGB color from the lower 8 bits of four integers.
final magenta = Color.fromARGB(0xff, 0xff, 0x0, 0xff);

// After: Constructing a color with normalized floating-point components.
final magenta = Color.from(alpha: 1.0, red: 1.0, green: 0.0, blue: 1.0);

Color 的實現者

#

Color 中正在新增新方法,因此任何 implements Color 的類都會中斷,並且必須實現新方法,例如 Color.aColor.b

最終,實現者應遷移以利用新 API。短期內,這些方法可以輕鬆實現,而無需更改類的底層結構。

例如

dart
class Foo implements Color {
  int _red;

  @override
  double get r => _red / 255.0;
}

色域支援

#

使用 Color 並對顏色分量執行任何型別的計算的客戶端現在應首先檢查色域分量,然後再執行計算。為了做到這一點,您可以使用新的 Color.withValues 方法來執行色域轉換。

遷移示例

dart
// Before
double redRatio(Color x, Color y) => x.red / y.red;

// After
double redRatio(Color x, Color y) {
  final xPrime = x.withValues(colorSpace: ColorSpace.extendedSRGB);
  final yPrime = y.withValues(colorSpace: ColorSpace.extendedSRGB);
  return xPrime.r / yPrime.r;
}

在不匹配色域的情況下對顏色分量執行計算可能導致微妙的意外結果。在前面的示例中,當使用不同色域與匹配色域計算時,redRatio 會有 0.09 的差異。

訪問顏色分量

#

如果您的應用訪問 Color 分量,請考慮利用浮點數分量。短期內,您可以縮放分量本身。

dart
extension IntColorComponents on Color {
  int get intAlpha => _floatToInt8(this.a);
  int get intRed => _floatToInt8(this.r);
  int get intGreen => _floatToInt8(this.g);
  int get intBlue => _floatToInt8(this.b);

  int _floatToInt8(double x) {
    return (x * 255.0).round() & 0xff;
  }
}

不透明度

#

在 Flutter 3.27 之前,Color 具有“不透明度”的概念,體現在 opacitywithOpacity() 方法中。不透明度被引入作為一種方式,透過浮點數值 ([0.0, 1.0]) 與 Color 溝通其 alpha 通道。不透明度方法是設定 8 位 alpha 值 ([0, 255]) 的便捷方法,但從未提供浮點數的完整表達。當顏色分量儲存為 8 位整數時,這已經足夠了。

自 Flutter 3.27 起,alpha 以浮點數值儲存。使用 .a.withValues() 將提供浮點數的完整表達,並且不會被量化(限制在有限範圍內)。這意味著“alpha”比“不透明度”更準確地表達了意圖。不透明度在細微之處有所不同,其使用可能導致意外的資料丟失,因此 .withOpacity().opacity 已被棄用,並且其語義已得到維護,以避免破壞任何人。

例如

dart
// Prints 0.5019607843137255.
print(Colors.black.withOpacity(0.5).a);
// Prints 0.5.
print(Colors.black.withValues(alpha: 0.5).a);

實際上所有用法都將直接受益於更準確的顏色。在極少數情況下,如果不行,可以採取措施將不透明度量化到 [0, 255],使用 .alpha.withAlpha() 來匹配 Flutter 3.27 之前的行為。

遷移 opacity

#
dart
// Before: Access the alpha channel as a (converted) floating-point value.
final x = color.opacity;

// After: Access the alpha channel directly.
final x = color.a;

遷移 withOpacity

#
dart
// Before: Create a new color with the specified opacity.
final x = color.withOpacity(0.0);

// After: Create a new color with the specified alpha channel value,
// accounting for the current or specified color space.
final x = color.withValues(alpha: 0.0);

相等性

#

一旦 Color 將其顏色分量儲存為浮點數,相等性的工作方式會略有不同。在計算顏色時,值可能存在微小的差異,但這些差異可以被視為相等。為了適應這一點,請使用 closeToisColorSameAs 匹配器。

dart
// Before: Check exact equality of int-based color.
expect(calculateColor(), const Color(0xffff00ff));

// After: Check rough equality of floating-point-based color.
expect(calculateColor(), isSameColorAs(const Color(0xffff00ff)));

時間線

#

第一階段 - 新 API 介紹,舊 API 棄用

#

已釋出版本:3.26.0-0.1.pre
穩定版釋出:3.27.0

第二階段 - 移除舊 API

#

生效版本:尚未釋出
穩定版本:尚未釋出

參考資料

#

相關議題

相關 PR