🌈 Beautiful and realistic gradients in Flutter using modern color spaces.
- 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- Call
initializeShadersbeforerunAppinmain.dart:
import 'package:better_gradients/better_gradients.dart';
void main() async {
// Add this line before `runApp`.
await initializeShaders();
runApp(MyApp());
}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(),
),
),
),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 |
|---|
![]() |
HuePath.longer |
|---|
![]() |
Tip
You can use these gradients anywhere that the built-in gradients work 🥰
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),
),
),
),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),
),
),
),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),
),
),
),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 ❤️
- XYZ (aka CIE 1931)
- CIELAB and CIELCh
- Oklab and Oklch
- sRGB
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 |
|---|
![]() |
| Linear |
|---|
![]() |
| sRGB |
|---|
![]() |
This package enables drawing perceptually uniform and linear gradients, in addition to (or instead of) your default sRGB gradients.
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 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.
These are good articles about color spaces, if you want to learn more (in ascending order of complexity):
- Okay, Color Spaces: https://ericportis.com/posts/2024/okay-color-spaces/
- Color Spaces: https://ciechanow.ski/color-spaces/
- How software gets color wrong: https://bottosson.github.io/posts/colorwrong/
- A perceptual color space for image processing: https://bottosson.github.io/posts/oklab/
- An interactive review of Oklab: https://raphlinus.github.io/color/2021/01/18/oklab-critique.html
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.










