Skip to content

domyd/better_gradients

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🌈 Beautiful and realistic gradients in Flutter using modern color spaces.

Getting started

  1. Add the necessary shaders to your app's pubspec.yaml:
flutter:
  shaders:
    - packages/better_gradients/shaders/linear_gradient.frag
    - packages/better_gradients/shaders/radial_gradient.frag
    - packages/better_gradients/shaders/conical_gradient.frag
    - packages/better_gradients/shaders/sweep_gradient.frag
  1. Call initializeShaders before runApp in main.dart:
import 'package:better_gradients/better_gradients.dart';

void main() async {
    // Add this line before `runApp`.
    await initializeShaders();

    runApp(MyApp());
}

Usage

The LinearGradient, RadialGradient, and SweepGradient are all drop-in replacements for the framework's built-in versions, but they take an extra (optional) colorSpace parameter:

Container(
  decoration: BoxDecoration(
    gradient: LinearGradient(
      colors: [
        Color(0xffe58042),
        Color(0xff00ade7),
      ],
      colorSpace: ColorSpace.oklab(),
    ),
  ),
),

Oklab

Tip

To avoid import conflicts with Flutter's own gradients, you may wish to import this package with a prefix or hide Flutter's gradients.

The above example uses the Oklab color space, which is the default and generally the recommended one to use. Oklab also comes in its polar form Oklch where interpolation occurs around the color wheel, with its HuePath argument describing the path that it takes:

Container(
  decoration: BoxDecoration(
    gradient: LinearGradient(
      colors: [
        Color(0xffe58042),
        Color(0xff00ade7),
      ],
      colorSpace: ColorSpace.oklch(HuePath.shorter),
    ),
  ),
),
HuePath.shorter
Oklch (shorter)
HuePath.longer
Oklch (longer)

Tip

You can use these gradients anywhere that the built-in gradients work 🥰

More Examples

A radial gradient:

import 'package:better_gradients/better_gradients.dart' as bg;
import 'package:flutter/material.dart';

Container(
  width: 1200,
  height: 100,
  decoration: const BoxDecoration(
    gradient: bg.RadialGradient(
      colors: [
        Color(0xfff5abb9),
        Color(0xff5bcffa),
      ],
      radius: 1,
      tileMode: TileMode.mirror,
      center: Alignment(-0.5, 0.0),
      colorSpace: bg.ColorSpace.oklch(bg.HuePath.shorter),
    ),
  ),
),

Radial Gradient in Oklch (shorter)

A sweep gradient:

import 'package:better_gradients/better_gradients.dart' as bg;
import 'package:flutter/material.dart';

Container(
  width: 256,
  height: 256,
  decoration: const BoxDecoration(
    gradient: bg.SweepGradient(
      colors: [
        Color(0xffe58042),
        Color(0xff00ade7),
        Color(0xffe58042),
      ],
      stops: [0.0, 0.5, 1.0],
      colorSpace: bg.ColorSpace.oklch(bg.HuePath.increasing),
    ),
  ),
),

Sweep Gradient in Oklch(increasing)

A conical gradient with decreasing opacity:

import 'package:better_gradients/better_gradients.dart' as bg;
import 'package:flutter/material.dart';

Container(
  width: 256,
  height: 256,
  decoration: const BoxDecoration(
    gradient: bg.RadialGradient(
      colors: [
        Color(0xffe58042),
        Color(0x0000ade7),
      ],
      radius: 0.400,
      tileMode: TileMode.clamp,
      center: Alignment(-0.053, 0.0),
      focal: Alignment(-0.784, 0.0),
      focalRadius: 0.010,
      colorSpace: bg.ColorSpace.oklch(bg.HuePath.shorter),
    ),
  ),
),

Conical Gradient in Oklch(shorter)


Important

The number of colors/stops you can pass to a gradient is currently capped at 16. This is due to technical limitations with custom shader support in flutter. It's an arbitrary number, but the limit has to be set somewhere and a higher limit impacts the performance for all gradients. If you have a reasonable need for more colors, please let me know ❤️

Supported color spaces

  • XYZ (aka CIE 1931)
  • CIELAB and CIELCh
  • Oklab and Oklch
  • sRGB

Comparison with the built-in gradients, or: why do I need this?

Flutter's built-in gradients mix colors in the sRGB color space, which is pretty much never what you actually want, and it can look especially bad when mixing two colors that are opposite of each other on the color wheel (like blue and yellow below). See Björn Ottosson's "How software gets color wrong" article for an in-depth explanation, but the gist of it can be visualized like this:

Perceptual
perceptual
Linear
linear
sRGB
srgb

This package enables drawing perceptually uniform and linear gradients, in addition to (or instead of) your default sRGB gradients.

Comparison with the gradients package

I originally found and tried the gradients package, but their Oklab implementation turns out to be broken and the package seems to no longer be maintained.

What theirs looks like:

What theirs looks like

What it's supposed to look like:

What it's supposed to look like

Also, this package uses shaders for drawing the gradients in high-fidelity, whereas gradients emulated that behavior by passing large lists of colors to flutter's gradients.

Further reading

These are good articles about color spaces, if you want to learn more (in ascending order of complexity):

Developing

For general-purpose debugging and experimenting with changes there's a playground example at example/lib/playground.dart.

Flutter doesn't hot-reload dependent shader files (the .glsl files) on their own, but it does reload them if you change a top-level fragment shader (.frag). So for quick iteration on, for example, color_space_math.glsl, you need to update one of the fragment shaders in order to hot reload everything.

About

Better color gradients for Flutter

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors