Colorgram
Extract the most prominent colors from any image in Flutter. A lightweight, fast Dart port of Colorgram.js.
Features
- Simple: one function call, from any
ImageProviderto a list of colors - Fast: a 512x512 JPEG in ~6ms, a 5120x1440 JPEG in ~110ms including decoding (Apple M4 Pro)
- Lightweight: no dependencies besides Flutter
- Every platform: Android, iOS, Web (including WebAssembly), Windows, macOS and Linux
- Any format Flutter can decode: JPG, PNG, GIF, BMP, WebP, and animated APNG/GIF/WebP (first frame)
Colors are grouped by their relative luminance, hue and lightness, then ranked by how much of the image they cover.
Installation
flutter pub add colorgram
Usage
Pass any ImageProvider (FileImage, NetworkImage, AssetImage, MemoryImage, ...) and the number of colors you want:
import 'package:colorgram/colorgram.dart';
final List<CgColor> colors = await extractColor(AssetImage('assets/photo.jpg'), 10);
for (final color in colors) {
print('${color.r}, ${color.g}, ${color.b} covers ${color.percentage * 100}%');
}
Colors are sorted from most to least prominent. Each CgColor has:
| Field | Type | Description |
|---|---|---|
r, g, b |
int |
Color channels, 0 to 255 |
percentage |
num |
Share of the image this color covers, 0 to 1 |
You may get fewer colors than you asked for if the image doesn't have that many.
Using the colors in widgets
Container(color: colors.first.toColor());
toColor() is the same as Color.fromARGB(255, color.r, color.g, color.b).
Multiple images
final List<List<CgColor>> palettes = await Future.wait(
providers.map((provider) => extractColor(provider, 10)),
);
Already have the image or its pixels?
Skip the ImageProvider if you have a decoded ui.Image, or its raw RGBA bytes (4 bytes per pixel, e.g. from ui.Image.toByteData()):
final fromImage = await extractColorFromImage(image, 10); // doesn't dispose the image
final fromPixels = extractColorFromRgba(rgbaBytes, 10);
Performance tips
Resize large images
Decoding is the slowest step for large images, and a few hundred pixels is plenty to find the main colors. Wrapping the provider in a ResizeImage makes Flutter decode it at a smaller size:
final colors = await extractColor(
ResizeImage(provider, width: 256, height: 256, policy: ResizeImagePolicy.fit),
10,
);
ResizeImagePolicy.fit keeps the aspect ratio. For a 5120x1440 JPEG this takes ~66ms instead of ~110ms, and the percentages stay within about 1 percentage point of the full size result. Shrinking much further can start to reorder colors that cover a similar share of the image.
Keep huge images off the UI thread
extractColorFromRgba is synchronous, so you can run it in a background isolate (not available on Flutter Web):
final colors = await Isolate.run(() => extractColorFromRgba(rgbaBytes, 10));
Notes
- Alpha is ignored. Pixels are read as premultiplied RGBA, so transparent areas count as black, and all results are fully opaque.
- Version 2.0 dropped pure Dart support in exchange for much faster decoding through Flutter. For pure Dart projects, use version 1.0.1.
Example
For a full app built with Colorgram, see colorgram-app.
Credits
- darosh for the original Colorgram.js
- And96 for the suggestions that made 2.0 much faster
Contributions are welcome, especially if you find a faster approach!
Libraries
- colorgram
- Extracts the most prominent colors from an image.