spectrum 0.2.1+1
spectrum: ^0.2.1+1 copied to clipboard

A rainbow of Color and Gradient utilities. Interpolate gradients with a realized GradientTween. New type Steps & shaded varieties. Color generation & operators.

spectrum #

pub.dev Listing | API Doc | GitHub #

Gradient API References: GradientUtils | GradientTween | Steps | FooShadedSteps | AnimatedGradient
Color API References: Shading | ColorOperators | ColorOperatorsMethods | Spectrum | SpectrumUtils
More: ColorArithmetic Shades | StopsArithmetic Maths | SwatchMode | GradientStoryboard | NillGradients | MaterialColorToList
๐Ÿธ Zaba.app โ€• simple packages, simple names.

spectrum header image

A rainbow of Color and Gradient utilities such as GradientTween, gradient copyWith() any potential parameters for the many types, Steps-type gradients, complementary() for colors, AnimatedGradient, MaterialColor generation, and more!

๐Ÿ“š Table of Contents #

spectrum header

  1. ๐ŸŒŠ Gradients

    1. ๐Ÿ’ซ Interpolation
    2. ๐ŸŒˆ Steps
      1. ๐Ÿงน Softness
      2. ๐Ÿ•ถ๏ธ Shaded Steps
    3. โฐ Animation
      1. ๐Ÿ“– Gradient Storyboard
    4. ๐Ÿ“‹ Override Copy With
  2. ๐ŸŽจ Colors

    1. โž— Operators
    2. ๐Ÿ—œ๏ธ Spectrum
    3. ๐Ÿค Complementary Colors
  3. ๐Ÿ›ฃ๏ธ Roadmap

๐ŸŒŠ Gradients #

Feel free to consider any instantiated Gradient and use a fancy Gradient.copyWith() method to override some or all of its properties. This method accepts all manner of potential Gradient subtype properities and applies only the ones necessary for the return.

This GradientUtils extension method is employed as the default "copy with" function throughout other portions of spectrum. This is achieved by a private method spectrumCopyWith() which fulfills the GradientCopyWith type alias definition in default contexts where constructor argument overrideCopyWith is not initialized.

Another gradient utility is the simple getter Gradient.reversed which returns a gradient with same properties, but whose colors (and interpretted or explicit stops) are in reverse order.

๐Ÿ’ซ Interpolation #

Smooth Gradient tweening not just by Gradient.lerp()... oh no, that is not enough.

This package provides full bespoke IntermediateGradients for truly smooth and eye-popping gradient animations between dissimilar gradient types!

Incredibly beautiful GradientTweens with IntermediateGradients (accelerometer-translation by package:xl)

The interpolated Gradient is obtained via GradientTween.evaluate(Animation animation) or transform(double t).

Until better List interpolation is implemented, feel free to experiment with isAgressive.

transparent 600x1 pixel-pusher (ignore)๐Ÿ“š

๐ŸŒˆ Steps #

Spectrum also adds entirely new gradient types called Steps, as well as shaded varieties named FooShadedSteps. Imagine a gradient that somewhat defeats the idea of a gradient and, instead of smoothly transitioning between colors, creates a series of hard steps.

New Steps-type Gradients; three variety for Radial, Linear, and Sweep

This is achieved by intrinsically duplicating both the List<Color> of colors as well as the (interpreted or explicit) List<double> of stops. As a product of this process, any explicitly initalized list of stops ought to not end in 1.0 lest that stop get eaten. For example, a three-color Steps could have stops: [0.0, 0.3, 0.8].

transparent 600x1 pixel-pusher (ignore)๐Ÿ“š

๐Ÿงน Softness

A softness may be increased or zeroed out depending on preference or user device display density. A high-DPI screen would look just fine with an aliased, hard edge between colors. A low-DPI screen would benefit from a (still, incredibly small) softness to provide as an additive for each second entry when duplicating stops.

Imagine Steps.stops is [0.0, 0.3, 0.8]. Providing a softness of 0.001, the effective, resolved stops for this gradient is now: [0.0, 0.001, 0.3, 0.3001, 0.8, 0.8001].

Animation showing transition from Steps.softness == 0 to some greater value then transitioning backA larger softness has the effect of making Steps more like their original Gradient counterpart.

The neighboring example transitions from softness: 0.0 -> softness: 0.14.
transparent 600x1 pixel-pusher (ignore)๐Ÿ“š

๐Ÿ•ถ๏ธ Shaded Steps

Now imagine more intrinsic color and stops math for a variety of self-elaborating gradients... something like FooShadedSteps may be born.

LinearShadedSteps transitioning from a negative value to a positive value with `LinearShadedSteps.shadeFunction` defaulting with `Shades.withWhite`Each of Linear, Radial, and Sweep extends from the original Steps counterpart and overrides its steppedColors & steppedStops properties to actually "quadruplicate" these lists, optionally with a variety of customizable parameters (including the function to perform the color shading).

The neighboring example is a LinearShadedSteps transitioning from a negative value to a positive value with LinearShadedSteps.shadeFunction defaulting with Shades.withWhite.
transparent 600x1 pixel-pusher (ignore)๐Ÿ“š

โฐ Animation #

Provide an animation such as an AnimationController to an AnimatedGradient along with a gradient for which to alter the properties.

Obtain the actual Gradient-type output by calling AnimatedGradient.observe or by using the Gradient.animate(...) convenience method, which inherently returns observe.

Of course you may provide a number of Tweens for properties like Gradient.center or Gradient.begin, and these are provided with a TweenSpec; but consider other specialized functionalities: GradientAnimation.colorArithmetic & GradientAnimation.stopsArithmetic.

โ“ A TweenSpec is a new form of typedef wherein the definition is for a type of Map and not a type of Function.

transparent 600x1 pixel-pusher (ignore)๐Ÿ“š

๐Ÿ“– Gradient Storyboard

All the animatable descriptions fit snugly into a "GradientStoryboard". The storyboard maps one or more GradientAnimation enum constants to a description object that correlates.

(Expect potential changes in functionality here in the future.)

The above softness and ShadedSteps example gifs are made by an AnimatedGradient.

GradientAnimation (storyboard key)Description Object (storyboard value)Literal
colorArithmeticColorArithmeticColor Function(Color color, double factor)
stopsArithmeticStopsArithmeticdouble Function(double stop, double factor)
tweenSpec"TweenSpec" MapMap<GradientProperty, Tween<dynamic>>
nonenull / anythingnull / anything

In terms of GradientPropertys, the mapping is what would be expected. A property like GradientProperty.center is mapped to a Tween<AlignmentGeometry?>, and one like GradientProperty.radius expects a value of a Tween<double>.

final animatedGradient = AnimatedGradient(
  controller: _controller,
  gradient: RadialSteps(colors: Colors.red.complementTriad),
  storyboard: {
    // GradientAnimation.none: null, // disables any animations

    /// "colorArithmetic" expects a [ColorArithmetic],
    // such as a method from [Shades].
    GradientAnimation.colorArithmetic: Shades.withOpacity,

    /// "stopsArithmetic" expects a [StopsArithmetic],
    // such as a method from [Maths].
    GradientAnimation.stopsArithmetic: Maths.subtraction,
    // GradientAnimation.stopsArithmetic:Maths.addition,
    // GradientAnimation.stopsArithmetic:Maths.division,

    /// "tweenSpec" expects a `Map<GradientProperty, Tween<dynamic>>`.
    GradientAnimation.tweenSpec: {
      GradientProperty.center: Tween<AlignmentGeometry>(
          begin: const Alignment(1, 1), end: const Alignment(-1, -1)),
      GradientProperty.focal: Tween<AlignmentGeometry>(
          begin: const Alignment(3, -1), end: const Alignment(-3, -1)),
      GradientProperty.radius: Tween<double>(begin: 0.5, end: 0),
      GradientProperty.focalRadius: Tween<double>(begin: 2, end: 0),
          Tween<double>(begin: -1.0 * 3.1415927, end: 0.0 * 3.1415927),
          Tween<double>(begin: 2.0 * 3.1415927, end: 4.0 * 3.1415927),
      GradientProperty.shadeFactor: StepTween(begin: -200, end: 0),
      GradientProperty.softness: Tween<double>(begin: 0, end: 0.14),
      . . .

Any properties that do not apply to the provided Gradient type are ignored.

transparent 600x1 pixel-pusher (ignore)๐Ÿ“š

๐Ÿ“‹ Override Copy With #

As an advanced feature, if you are operating with bespoke Gradient types that would not be hard-code recognized by this package, feel free to override the GradientCopyWith function in either a GradientTween or an AnimatedGradient.

This overriding function, which is expected to accept a large number of potential parameters, can be programmed to return your bespoke type.

Provide this GradientCopyWith function as GradientTween.overrideCopyWith, for example, such as:

Gradient customCopyWith(Gradient original, { List<Color>? colors, List<double>? stops, . . . /* remaining potential parameters */ })
    => CustomGradient(
         colors: colors ?? original.colors,
         stops: stops ?? original.stops,
         . . . );
final tween = GradientTween(
  begin: customGradient,
  end: differentCustomGradient,
  overrideCopyWith: customCopyWith);

transparent 600x1 pixel-pusher (ignore)๐Ÿ“š

๐ŸŽจ Colors #

โž— Operators #

ColorOperators are on deck as well as some nice methods to go along.

Aside from operators for objectives such as color inversion, averaging two colors, adding or subtracting them from one another, randomly making a choice of one amongst several options, or comparing the luminance of two colors...

ColorOperatorsMethods exist for exposure of these extensions as well. These methods allow a convenience pass for a value denoted strength that may represent an "opacity" at values ranging 0.0 .. 1.0 or represent an "alpha" component when greater than or equal to 2 (clamped to 255).

/// - [-], to invert a `Color`
/// - [>] & [<], to compare the luminance of `Color`s
/// - [+] & [-], to add/subtract the RGB components to/from one another,
/// maintaining the [alpha] from the original color `this`
/// - [~/], to average all components of two colors together, including [alpha]
/// - [|], to randomly choose a `Color`, either `this` or `Color other`;
/// unless the operand on the right is a `List<Color>`, then the random choice
/// may come from the list or `this`.
extension ColorOperators on Color { . . . }

/// - [inverted], for returning `-this`
/// - [compareLuminance], for returning the brighter or darker `Color`
///   utilizing `>`
/// - [or], for randomization by `Color | List<Color>`
/// The following methods resemble the operator counterparts but they have a
/// slot for provision of a `strength` or alpha/opacity override
/// (see [Spectrum.alphaChannel] for details):
/// - [add], to `+` one `Color` to another
/// - [subtract], to `-` one `Color` from another
/// - [average], to `~/` all channels of two `Color`s
extension ColorOperatorsMethods on Color { . . . }

// TODO: Examples of operators usage.
transparent 600x1 pixel-pusher (ignore)๐Ÿ“š

๐Ÿ—œ๏ธ Spectrum #

An abstract class called Spectrum provides a few helper functions for Colors, such as Spectrum.alphaChannel() that performs the same function as described by the parameter strength above.

Another static function Spectrum.materialColor() allows the generation of an entire MaterialColor swatch from a single Color value with customizable options. Many color generation modes are available: SwatchMode.shade or SwatchMode.desaturate, as well as more specialized modes SwatchMode.complements and SwatchMode.fade.

To generate a MaterialAccentColor instead, check out Spectrum.materialAccent().

Spectrum.materialColor generated palettes with SwatchMode descriptions

final color0 = Spectrum.materialColor(
    mode: SwatchMode.shade,
    factor: 200,
// ๐Ÿ’ก The above has a shorthand: `color.asMaterialColor`, using shade @ 200
final color0a = Spectrum.materialAccent(
    mode: SwatchMode.shade,
    factor: 200,
// ๐Ÿ’ก The above has a shorthand: `color.asMaterialAccent`, using shade @ 200

final color1 = Spectrum.materialColor(
    mode: SwatchMode.desaturate,
    // factor: 0.2, // override opacity/alpha

final color2 = Spectrum.materialColor(
    mode: SwatchMode.fade,
    // factor: 100 // TODO: provide same shading spread as SwatchMode.shade,
    // (currently only performs .withWhite(factor) on the provided color)

final color3a = Spectrum.materialAccent(
    mode: SwatchMode.complements,

With extensions on MaterialColor and MaterialAccentColor, employ getter asList or method toList() to convert these palettes into boiled-down List<Color>, such as:

// The getter does not insert the "primary" color at the beginning of the list.
// (This value is typically mapped as `shade500` anyway.)
final List<Color> shadesColor0 = color0.asList;

// The method has a flag to insert the "primary" at the beginning of the list.
// Default is `true` to differentiate versatility with `asList`.
final List<Color> shadesColor3a = color3a.toList(includePrimary: false);

This makes a MaterialColor or accent color a great candidate for feeding Gradient.colors.

final matColorGradient = LinearGradient(colors: shadesColor0);

Another extension adds a number of convenient getters and methods to any instantiated Color. These utilities, called SpectrumUtils offer two "categories" of functionality: the first currently has one method, a.blend(b,t) which is shorthand for Color.lerp(a,b,t); the second category deals with the generation of complementary colors.

transparent 600x1 pixel-pusher (ignore)๐Ÿ“š

๐Ÿค Complementary Colors #

Color.complementary(int count) is a method for turning a single Color into a List of length count containing the original and a quantity of complements that you specify.

๐Ÿ’ก Use Color.complementary() to create Gradient.colors in a cinch!

Color, Color.complementPair, Color.complementTriadConsider the color red. An example would be red.complementary(2), which actually has a more efficient getter dubbed red.complementPair, that would return red and its inverse: [red, cyan]. It follows that red.complementary(3), more efficiently gotten as red.complementTriad would return red and its complements: [red, green, blue].
Color.complementary(4), Color.complementary(5), Color.complementary(6)Consider Color.complementary(4), Color.complementary(5) or Color.complementary(6) for a nice assortment of pairings to pick from.

A call like red.complementary(36) returns a list beginning with red and progressing through another 35 colors that, on the whole, resemble a gently transitioning rainbow of colors, as each chip is 10ยฐ apart on the 360ยฐ color wheel.

Color.complementary(36) presents a rainbow where each color is 10 degrees apart on the color wheel

The red color is Color(0xFFFF0000) and the pink color is Color(0xFFFF61ED).

transparent 600x1 pixel-pusher (ignore)๐Ÿ“š

๐Ÿ›ฃ๏ธ Roadmap #

More to come. Package in progress.
๐Ÿž Bug reports very welcome!

  • Flesh out README and documentation with example images and code snippets; full comments pass for maintained accuracy and increased clarity.
  • Improve different methods for GradientTween, especially when considering new Steps type gradients (see flag isAgressive) & tweening IntermediateGradients themselves. Offer options for tween "mode". Support tweening AnimatedGradient.
  • Provide options for Color.complementary() such that the "distance" in color-wheel degrees could be customized, etc.
  • New Color utilities.
  • Self-animating variety of AnimatedGradient, which currently requires an Animation<double> parameter such as an AnimationController.
  • Provide true gradient storyboarding with multiple gradients and keyframes.
  • Brand new Gradient types? Consider expanding upon "shaded" concept.

๐Ÿธ Zaba.app โ€• simple packages, simple names. #

More by Zaba

Widgets to wrap other widgets #

Container widget that wraps many functionalities #

Side-kick companions, work great alone or employed above #

  • ๐Ÿ†• neu #

  • ๐Ÿ™‹โ€โ™‚๏ธ img #

  • ๐Ÿ™‹โ€โ™‚๏ธ icon #

  • ๐Ÿ“ ball #

  • ๐Ÿ‘ฅ shadows #

  • ๐ŸŽจ spectrum header #

pub points



A rainbow of Color and Gradient utilities. Interpolate gradients with a realized GradientTween. New type Steps & shaded varieties. Color generation & operators.

Repository (GitHub)
View/report issues


API reference


BSD-3-Clause (LICENSE)




Packages that depend on spectrum