Saltar al contenido principal

Guía de migración para Color de amplia gama

Cambios para admitir color de amplia gama e instrucciones de migración.

Resumen

#

La API para la clase Color en dart:ui está cambiando para admitir espacios de color de amplia gama (wide gamut).

Contexto

#

El motor de Flutter ya admite color de amplia gama con Impeller, y el soporte se está añadiendo ahora al framework.

Los dispositivos iOS que Flutter admite renderizan en una gama más amplia de colores, específicamente en el espacio de color DisplayP3. Después de este cambio, el framework de Flutter puede renderizar todos esos colores en iOS Impeller, y la clase Color está mejor preparada para futuros espacios de color o cambios en la profundidad de bits de los componentes de color.

Descripción del cambio

#

Cambios en Color:

  1. Añade un campo enum que especifica su ColorSpace.
  2. Añade la API para usar componentes de color de punto flotante normalizados.
  3. Elimina la API que utiliza componentes de color de enteros sin signo de 8 bits que pueden provocar pérdida de datos.

Cambios en ColorSpace:

  1. Añade una propiedad displayP3.

Guía de migración

#

Constructores de enteros sin signo de 8 bits

#

Los constructores como Color.fromARGB permanecen sin cambios y continúan teniendo soporte. Para aprovechar los colores Display P3, debes usar el nuevo constructor Color.from que toma componentes de color de punto flotante normalizados.

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);

Implementadores de Color

#

Se están agregando nuevos métodos a Color, por lo que cualquier clase que implemente Color se romperá y tendrá que implementar los nuevos métodos, como Color.a y Color.b.

En última instancia, los implementadores deben migrar para aprovechar la nueva API. A corto plazo, estos métodos se pueden implementar fácilmente sin cambiar la estructura subyacente de tu clase.

Por ejemplo:

dart
class Foo implements Color {
  int _red;

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

Soporte para espacio de color

#

Los clientes que utilicen Color y realicen cualquier tipo de cálculo sobre los componentes de color ahora deben verificar primero el componente del espacio de color antes de realizar cálculos. Para ayudarte con eso, puedes usar el nuevo método Color.withValues para realizar conversiones de espacios de color.

Ejemplo de migración:

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;
}

Realizar cálculos con componentes de color sin alinear los espacios de color puede provocar resultados inesperados y sutiles. En el ejemplo anterior, el redRatio tendría una diferencia de 0.09 cuando se calcula con espacios de color diferentes frente a espacios de color alineados.

Acceder a los componentes de color

#

Si tu aplicación accede alguna vez a un componente de Color, considera aprovechar los componentes de punto flotante. A corto plazo, puedes escalar los componentes mismos.

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;
  }
}

Opacity

#

Antes de Flutter 3.27, Color tenía el concepto de "opacidad" que aparecía en los métodos opacity y withOpacity(). La opacidad se introdujo como una forma de comunicarse con Color sobre su canal alfa con valores de punto flotante ([0.0, 1.0]). Los métodos de opacidad eran métodos convenientes para establecer el valor alfa de 8 bits ([0, 255]), pero nunca ofrecieron la expresión completa de un número de punto flotante. Esto era suficiente cuando los componentes de color se almacenaban como enteros de 8 bits.

Desde Flutter 3.27, el canal alfa se almacena como un valor de punto flotante. El uso de .a y .withValues() proporcionará la expresión completa de un valor de punto flotante y no se cuantizará (restringirá a un rango limitado). Eso significa que "alfa" expresa la intención de "opacidad" de forma más correcta. La opacidad difiere de una manera sutil donde su uso puede resultar en una pérdida de datos inesperada, por lo que .withOpacity() y .opacity han sido deprecados y se ha mantenido su semántica para evitar romper el código de nadie.

Por ejemplo:

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

Prácticamente todo el uso se beneficiará directamente de colores más precisos. En el raro caso de que no sea así, se puede tener cuidado de cuantizar la opacidad a [0, 255] usando .alpha and .withAlpha() para que coincida con el comportamiento anterior a Flutter 3.27.

Migrar 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;

Migrar 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);

Igualdad

#

Una vez que Color almacena sus componentes de color como números de punto flotante, la igualdad funciona de manera ligeramente diferente. Al calcular colores, puede haber una pequeña diferencia en los valores que podrían considerarse iguales. Para acomodar esto, usa los matchers closeTo o isColorSameAs.

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)));

Timeline

#

Fase 1 - Introducción de nueva API, deprecación de la API antigua

#

Introducido en la versión: 3.26.0-0.1.pre
En la versión estable: 3.27.0

Fase 2 - Eliminación de la API antigua

#

Introducido en la versión: Aún no
En la versión estable: Aún no

Referencias

#

Problema relevante:

  • issue 127855: Implement wide gamut color support in the Framework

PRs relevantes: