Extracting Colors From An Image
A couple of months ago, we announced that some
packages would cease development and be deprecated as of April 30. One of them was palette_generator
.
I have good news and bad news regarding a replacement. The good news is that there has been a replacement in plain sight for several years now. The bad news is that it’s not a one to one replacement. But that’s not a totally bad thing.
Background
palette_generator
mirrors the Android Palette API that is
pretty ancient in Android terms. The initial Android support library dates back to at least 2017. Back then,
Material was only a couple years old (it just turned 10!), and public release numbers for Android were still
in the single digits. Eventually, that version of the API was locked as stable in 2018.
While that library has been etched in stone, the ways we use and define color for theming have continued to evolve. I wrote a post almost a year ago detailing how to think about color in Material 3. I won’t rehash it here other than to say that the relationships between color roles are well considered in Material 3. The output of the Palette API/palette_generator wouldn’t work well in a Material theme and would likely still be limiting in a more bespoke Flutter theme.
This limited set of tones (Vibrant, Vibrant Dark, Vibrant Light, Muted, Muted Dark, Muted Light) doesn’t have affordances for contrast levels or for generating complementary colors. It simply takes the dominant colors in the image and tries to slot them into one of those roles.
What to Use Instead?
So if the palette_generator
package and the Palette API is not a good solution, what should
you use instead, you might be wondering. material_color_utilities
[https://pub.dev/packages/material_color_utilities] is the answer. It’s bundled as a
dependency of Flutter, so it’s highly available across implementations. But what if you aren’t
using Material? You can still use it; you can determine how "Material-y" you want to be.
I’ve created a small sample app showing palette_generator
and
material_color_utilities
side by
side]. You only need to pay attention to the functions in this file: https://github.com/jwill/extract_palette_from_image/blob/main/lib/image.dart
If you just want the colors, use the function getColorsFromImage
. The scoring algorithm will
run some out as being unsuitable for a UI but it doesn’t mutate the colors. If no suitable colors are
found, material_color_utilities
will return blue or red (though that is an increasingly rare
error condition).
Future<List<Color>> getColorsFromImage(ImageProvider provider) async {
try {
// Extract dominant colors from image.
final quantizerResult = await _extractColorsFromImageProvider(provider);
final Map<int, int> colorToCount = quantizerResult.colorToCount.map(
(key, value) => MapEntry<int, int>(_getArgbFromAbgr(key), value),
);
// Score colors for color scheme suitability.
final List<int> filteredResults = Score.score(
colorToCount,
desired: 1,
filter: true,
);
final List<int> scoredResults = Score.score(
colorToCount,
desired: 4,
filter: false,
);
return <dynamic>{...filteredResults, ...scoredResults}
.toList()
.map((argb) => Color(argb))
.toList();
} catch (e) {
debugPrint('Error getting colors from image: $e');
return [];
}
}
You can pass any of the returned colors into ColorScheme.fromSeed()
to generate a full scheme (with a shifted version of that color in the primary slot). If you want that
literal color, you can call ColorScheme.fromSeed
with a dynamicSchemeVariant
parameter set to DynamicSchemeVariant.fidelity
or DynamicSchemeVariant.content
.
In the screenshots below, we can see another reason why the older API is not reliable; sometimes not all of the color roles are populated.


This is just one of the useful capabilities of material_color_utilities
and related classes
like ColorScheme
. Flutter developers can implement more sophisticated and flexible color
extraction in their apps, align with modern design principles, and choose how much they want to adhere to a
specific design system.