spectrum 0.2.0 copy "spectrum: ^0.2.0" to clipboard
spectrum: ^0.2.0 copied to clipboard

outdated

A rainbow of Color and Gradient utilities; including GradientTweens, Gradient.copyWith(), Steps-type gradients, Color.generateComplements(), AnimatedGradients, MaterialColor generation, and more!

spectrum #

pub.dev Listing | API Doc | GitHub #

Color API References: Shading | ColorOperators | ColorOperatorsMethods | Spectrum | SpectrumUtils
Gradient API References: GradientUtils | GradientTween | Steps | FooShadedSteps | AnimatedGradient
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, generateComplements() 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 #

๐Ÿ’ซ 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.

๐ŸŒˆ Steps #

spectrum header 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].

๐Ÿงน 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 back A 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.

๐Ÿ•ถ๏ธ 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.

โฐ 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.

๐Ÿ“– 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.

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

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

GradientAnimation (storyboard key) Description Object (storyboard value) Literal
colorArithmetic ColorArithmetic Color Function(Color color, double factor)
stopsArithmetic StopsArithmetic double Function(double stop, double factor)
tweenSpec TweenSpec Map<GradientProperty, Tween<dynamic>>
none null / anything null / 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 [TweenSpec], which itself is 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),
      GradientProperty.startAngle:
          Tween<double>(begin: -1.0 * 3.1415927, end: 0.0 * 3.1415927),
      GradientProperty.endAngle:
          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.

๐Ÿ“‹ 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);

๐ŸŽจ 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 { . . . }

๐Ÿ—œ๏ธ 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(
    color,
    mode: SwatchMode.shade,
    factor: 200,
);
// ๐Ÿ’ก The above has a shorthand: `color.asMaterialColor`, using shade @ 200
final color0a = Spectrum.materialAccent(
    color,
    mode: SwatchMode.shade,
    factor: 200,
);
// ๐Ÿ’ก The above has a shorthand: `color.asMaterialAccent`, using shade @ 200

final color1 = Spectrum.materialColor(
    color,
    mode: SwatchMode.desaturate,
    // factor: 0.2, // override opacity/alpha
);
final color1a = Spectrum.materialAccent(
    color,
    mode: SwatchMode.desaturate,
    // factor: 0.2, // override opacity/alpha
);

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

final color3 = Spectrum.materialColor(
    color,
    mode: SwatchMode.complements,
    // This SwatchMode does not consider any factor.
    // (TODO: Allow tweaking the distance of the complements on the color wheel)
);
final color3a = Spectrum.materialAccent(
    color,
    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.

๐Ÿค Complementary Colors #

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

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

Color, Color.complementPair, Color.complementTriad An example would be red.generateComplements(3), which actually has a more efficient getter dubbed Color.complementTriad, that would return [red, green, blue]. It follows that red.generateComplements(2), more efficiently gotten as red.complementPair would return red and its inversion: [red, cyan].

A call like red.generateComplements(36) returns a list beginning with red and progressing through another 35 colors that, on the whole, resemble a gently transitioning rainbow of colors.

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

Color.generateComplements(4), Color.generateComplements(5), Color.generateComplements(6) Consider Color.generateComplements(4), Color.generateComplements(5) or Color.generateComplements(6) for a nice assortment of pairings to pick from.

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


๐Ÿ›ฃ๏ธ 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.generateComplements() 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 #

11
likes
0
pub points
41%
popularity

Publisher

verified publisherzaba.app

A rainbow of Color and Gradient utilities; including GradientTweens, Gradient.copyWith(), Steps-type gradients, Color.generateComplements(), AnimatedGradients, MaterialColor generation, and more!

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

flutter

More

Packages that depend on spectrum