Palette Generator Master

Pub Version Flutter Dart License

A powerful Flutter package for extracting prominent colors from images with advanced features including multi-color space support, accessibility compliance, and color harmony generation.

This package is a complete rewrite and enhancement of the discontinued palette_generator package (0.3.3+7) with modern Flutter development practices, enhanced performance, and new capabilities.

✨ Features

🎨 Advanced Color Extraction

  • Multi-color space support: RGB, HSV, and LAB color spaces for more accurate analysis
  • Enhanced quantization algorithms for better color clustering
  • Improved dominant color detection with advanced scoring
  • Customizable target colors (vibrant, muted, light, dark variations)

♿ Accessibility First

  • WCAG 2.1 compliance checking for color contrast
  • Automatic contrast ratio calculation between color pairs
  • Best text color selection for any background color
  • Accessible color pair generation with customizable contrast thresholds

🌈 Color Harmony

  • Automatic harmony generation based on color theory principles
  • Complementary, analogous, triadic, and split-complementary color schemes
  • Customizable harmony algorithms for different design needs

⚡ Performance & Quality

  • Optimized algorithms for faster processing
  • Memory-efficient color analysis with 5-bit quantization
  • Null safety throughout the codebase
  • Modern Flutter practices with comprehensive error handling
  • Timeout management for network images

🚀 Getting Started

Installation

Add this package to your pubspec.yaml:

dependencies:
  palette_generator_master: ^1.1.0

Then run:

flutter pub get

Basic Usage

import 'package:flutter/material.dart';
import 'package:palette_generator_master/palette_generator_master.dart';

// Generate palette from an image
Future<void> generatePalette() async {
  // Load your image
  final ImageProvider imageProvider = AssetImage('assets/my_image.jpg');
  
  // Generate palette
  final PaletteGeneratorMaster paletteGenerator = 
      await PaletteGeneratorMaster.fromImageProvider(
    imageProvider,
    maximumColorCount: 16,
    generateHarmony: true,      // Generate color harmony
  );
  
  // Access extracted colors
  final Color? dominantColor = paletteGenerator.dominantColor?.color;
  final Color? vibrantColor = paletteGenerator.vibrantColor?.color;
  final Color? mutedColor = paletteGenerator.mutedColor?.color;
  
  // Get all extracted colors
  final List<PaletteColorMaster> allColors = paletteGenerator.paletteColors;
  
  // Get harmony colors
  final List<ColorHarmonyMaster> harmonyColors = paletteGenerator.harmonyColors;
  
  // Use colors in your UI
  return Container(
    color: dominantColor,
    child: Text(
      'Dominant Color',
      style: TextStyle(
        color: paletteGenerator.getBestTextColorFor(dominantColor!),
      ),
    ),
  );
}

📖 Advanced Usage

Custom Color Targets

final PaletteGeneratorMaster generator = 
    await PaletteGeneratorMaster.fromImageProvider(
  imageProvider,
  targets: [
    PaletteTargetMaster.vibrant,
    PaletteTargetMaster.darkVibrant,
    PaletteTargetMaster.lightMuted,
    // Create custom target
    PaletteTargetMaster(
      saturationWeight: 0.8,
      lightnessWeight: 0.6,
      populationWeight: 0.4,
      minimumSaturation: 0.3,
      maximumSaturation: 0.9,
      minimumLightness: 0.2,
      maximumLightness: 0.8,
      targetSaturation: 0.6,
      targetLightness: 0.5,
      isExclusive: true,
    ),
  ],
);

Accessibility Features

// Get accessible color pairs
final List<AccessibleColorPair> accessiblePairs = 
    generator.getAccessibleColorPairs(
  minimumContrast: 4.5, // WCAG AA standard
);

// Check each pair
for (var pair in accessiblePairs) {
  print('Foreground: ${pair.foreground}');
  print('Background: ${pair.background}');
  print('Contrast Ratio: ${pair.contrastRatio}');
  print('Meets WCAG AA: ${pair.meetsWcagAA}');
  print('Meets WCAG AAA: ${pair.meetsWcagAAA}');
}

// Get best text color for a background
final Color backgroundColor = Colors.blue;
final Color textColor = generator.getBestTextColorFor(
  backgroundColor,
  minimumContrast: 4.5,
);

// Check contrast ratio between two colors
final double contrastRatio = AccessibilityHelperMaster.calculateContrastRatio(
  Colors.white,
  Colors.blue,
);

Working with Different Image Sources

// From asset image
final generator1 = await PaletteGeneratorMaster.fromImageProvider(
  AssetImage('assets/image.jpg'),
);

// From network image with custom timeout
final generator2 = await PaletteGeneratorMaster.fromImageProvider(
  NetworkImage('https://example.com/image.jpg'),
  timeout: Duration(seconds: 30),
  maximumColorCount: 24,
);

// From ui.Image
final ui.Image image = await loadImageFromSomewhere();
final generator3 = await PaletteGeneratorMaster.fromImage(
  image,
  region: Rect.fromLTWH(0, 0, 100, 100), // Process only a region
);

// From byte data
final ByteData imageData = await loadImageBytes();
final generator4 = await PaletteGeneratorMaster.fromByteData(
  EncodedImageMaster(
    imageData,
    width: 800,
    height: 600,
    name: 'my_image',
  ),
  maximumColorCount: 16,
);

// From network with error handling
try {
  final generator = await PaletteGeneratorMaster.fromImageProvider(
    NetworkImage('https://example.com/image.jpg'),
    timeout: Duration(seconds: 10),
  );
} catch (e) {
  print('Failed to load image: $e');
  // Handle error gracefully
}

Color Analysis and Information

// Get detailed color information
final PaletteColorMaster dominant = generator.dominantColor!;
print('Color: ${dominant.color}');
print('Population: ${dominant.population}');
print('Luminance: ${dominant.luminance}');
print('Is Dark: ${dominant.isDark}');
print('Is Light: ${dominant.isLight}');

// Get all colors with their populations
for (var color in generator.paletteColors) {
  print('Color: ${color.color}, Population: ${color.population}');
}

// Get specific target colors
final vibrant = generator.vibrantColor;
final lightVibrant = generator.lightVibrantColor;
final darkVibrant = generator.darkVibrantColor;
final muted = generator.mutedColor;
final lightMuted = generator.lightMutedColor;
final darkMuted = generator.darkMutedColor;

Color Harmony Examples

// Generate harmony colors
final generator = await PaletteGeneratorMaster.fromImageProvider(
  imageProvider,
  generateHarmony: true,
);

// Use different harmony types
for (var harmony in generator.harmonyColors) {
  switch (harmony.type) {
    case HarmonyType.complementary:
      print('Complementary colors: ${harmony.colors}');
      break;
    case HarmonyType.analogous:
      print('Analogous colors: ${harmony.colors}');
      break;
    case HarmonyType.triadic:
      print('Triadic colors: ${harmony.colors}');
      break;
    case HarmonyType.splitComplementary:
      print('Split complementary colors: ${harmony.colors}');
      break;
  }
}

// Create a color scheme widget
Widget buildColorScheme(PaletteGeneratorMaster generator) {
  final dominant = generator.dominantColor!.color;
  final harmonies = generator.harmonyColors;
  
  return Column(
    children: [
      Container(color: dominant, height: 50),
      if (harmonies.isNotEmpty)
        for (var harmony in harmonies.first.colors)
          Container(color: harmony, height: 50),
    ],
  );
}

Custom Filters

// Create custom filter to exclude specific colors
bool excludeRedColors(HSLColor color) {
  // Exclude colors with high red saturation
  return color.hue < 350 && color.hue > 10;
}

// Create filter for pastel colors only
bool pastelColorsOnly(HSLColor color) {
  return color.saturation < 0.3 && color.lightness > 0.7;
}

// Use custom filters
final generator = await PaletteGeneratorMaster.fromImageProvider(
  imageProvider,
  filters: [
    avoidRedBlackWhitePaletteFilterMaster,
    excludeRedColors,
    pastelColorsOnly,
  ],
);

🎯 API Reference

PaletteGeneratorMaster

The main class for generating color palettes from images.

Static Methods

// Generate from ImageProvider
static Future<PaletteGeneratorMaster> fromImageProvider(
  ImageProvider imageProvider, {
  Size? size,
  Rect? region,
  int maximumColorCount = 16,
  List<PaletteFilterMaster> filters = const [avoidRedBlackWhitePaletteFilterMaster],
  List<PaletteTargetMaster> targets = const [],
  Duration timeout = const Duration(seconds: 15),
  ColorSpace colorSpace = ColorSpace.rgb,
  bool generateHarmony = true,
})

// Generate from ui.Image
static Future<PaletteGeneratorMaster> fromImage(
  ui.Image image, {
  Rect? region,
  int maximumColorCount = 16,
  List<PaletteFilterMaster> filters = const [avoidRedBlackWhitePaletteFilterMaster],
  List<PaletteTargetMaster> targets = const [],
  ColorSpace colorSpace = ColorSpace.rgb,
  bool generateHarmony = true,
})

// Generate from encoded image data
static Future<PaletteGeneratorMaster> fromByteData(
  EncodedImageMaster encodedImage, {
  Rect? region,
  int maximumColorCount = 16,
  List<PaletteFilterMaster> filters = const [avoidRedBlackWhitePaletteFilterMaster],
  List<PaletteTargetMaster> targets = const [],
  ColorSpace colorSpace = ColorSpace.rgb,
  bool generateHarmony = true,
})

Properties

// Target colors
PaletteColorMaster? get vibrantColor;
PaletteColorMaster? get lightVibrantColor;
PaletteColorMaster? get darkVibrantColor;
PaletteColorMaster? get mutedColor;
PaletteColorMaster? get lightMutedColor;
PaletteColorMaster? get darkMutedColor;
PaletteColorMaster? get dominantColor;

// All extracted colors
List<PaletteColorMaster> get paletteColors;
Iterable<Color> get colors;

// Harmony colors (if generated)
List<ColorHarmonyMaster> get harmonyColors;

// Source image info
ImageInfoMaster? get sourceImageInfo;

Methods

// Get accessible color pairs
List<AccessibleColorPair> getAccessibleColorPairs({
  double minimumContrast = 4.5,
});

// Get best text color for background
Color getBestTextColorFor(
  Color backgroundColor, {
  double minimumContrast = 4.5,
});

PaletteColorMaster

Represents a color in the palette with additional information.

class PaletteColorMaster {
  final Color color;
  final int population;
  
  // Properties
  double get luminance;
  bool get isDark;
  bool get isLight;
}

PaletteTargetMaster

Defines target characteristics for color extraction.

class PaletteTargetMaster {
  final double saturationWeight;
  final double lightnessWeight;
  final double populationWeight;
  final double minimumSaturation;
  final double maximumSaturation;
  final double targetSaturation;
  final double minimumLightness;
  final double maximumLightness;
  final double targetLightness;
  final bool isExclusive;
  
  // Predefined targets
  static const PaletteTargetMaster vibrant;
  static const PaletteTargetMaster lightVibrant;
  static const PaletteTargetMaster darkVibrant;
  static const PaletteTargetMaster muted;
  static const PaletteTargetMaster lightMuted;
  static const PaletteTargetMaster darkMuted;
  static const List<PaletteTargetMaster> baseTargets;
}

EncodedImageMaster

Container for encoded image data.

class EncodedImageMaster {
  const EncodedImageMaster(
    ByteData byteData, {
    required int width,
    required int height,
    ImageByteFormat format = ImageByteFormat.rawRgba,
    String? name,
  });
  
  final ByteData byteData;
  final int width;
  final int height;
  final ImageByteFormat format;
  final String? name;
  
  int get pixelCount;
  double get aspectRatio;
}

ColorHarmonyMaster

Represents a color harmony scheme.

class ColorHarmonyMaster {
  const ColorHarmonyMaster(HarmonyType type, List<Color> colors);
  
  final HarmonyType type;
  final List<Color> colors;
  
  static List<ColorHarmonyMaster> generateHarmonyColors(Color base);
}

HarmonyType

enum HarmonyType { 
  complementary,    // 180° apart
  triadic,         // 120° apart
  analogous,       // 30° apart
  splitComplementary  // Base + complement neighbors
}

AccessibilityHelperMaster

Static utility class for accessibility features.

class AccessibilityHelperMaster {
  static Color getBestTextColor(Color background);
  static bool meetsWcagAA(double contrastRatio, {bool isLargeText = false});
  static bool meetsWcagAAA(double contrastRatio, {bool isLargeText = false});
  static double calculateContrastRatio(Color foreground, Color background);
  static List<AccessibleColorPair> generateAccessiblePairs(
    List<PaletteColorMaster> colors, {
    double minimumContrast = 4.5
  });
}

AccessibleColorPair

Represents a pair of colors with contrast information.

class AccessibleColorPair {
  const AccessibleColorPair(Color foreground, Color background, double contrastRatio);
  
  final Color foreground;
  final Color background;
  final double contrastRatio;
  
  bool get meetsWcagAA;
  bool get meetsWcagAAA;
  bool get meetsWcagAALarge;
  bool get meetsWcagAAALarge;
}

🎨 Color Spaces

RGB (Red, Green, Blue)

  • Best for: General purpose, web colors
  • Characteristics: Device-dependent, intuitive for developers
  • Use when: Working with standard web/mobile colors

HSV (Hue, Saturation, Value)

  • Best for: Color manipulation, artistic applications
  • Characteristics: More intuitive for humans, easier color adjustments
  • Use when: Need to adjust brightness/saturation programmatically

LAB (Lightness, A*, B*)

  • Best for: Perceptually uniform color analysis
  • Characteristics: Device-independent, perceptually uniform
  • Use when: Need most accurate color analysis (recommended)

♿ Accessibility Guidelines

This package follows WCAG 2.1 guidelines:

Contrast Ratios

  • AA Level: 4.5:1 for normal text, 3:1 for large text
  • AAA Level: 7:1 for normal text, 4.5:1 for large text

Usage Examples

// Check if colors meet WCAG AA
final bool isAccessible = AccessibilityHelperMaster.meetsWcagAA(
  contrastRatio,
  isLargeText: false,
);

// Generate only AA compliant pairs
final accessiblePairs = generator.getAccessibleColorPairs(
  minimumContrast: 4.5,
);

// Get best text color with custom logic
final textColor = generator.getBestTextColorFor(backgroundColor);

🔄 Migration from palette_generator

If you're migrating from the discontinued palette_generator package:

Class Name Changes

Old Class New Class
PaletteGenerator PaletteGeneratorMaster
PaletteColor PaletteColorMaster
PaletteTarget PaletteTargetMaster
EncodedImage EncodedImageMaster

API Changes

// Old way
final PaletteGenerator generator = await PaletteGenerator.fromImageProvider(
  AssetImage('image.jpg'),
);

// New way
final PaletteGeneratorMaster generator = await PaletteGeneratorMaster.fromImageProvider(
  AssetImage('image.jpg'),
  generateHarmony: true,      // New: harmony generation
);

Property Changes

// Old
Color? dominantColor = generator.dominantColor;

// New
PaletteColorMaster? dominant = generator.dominantColor;
Color? dominantColor = generator.dominantColor?.color;

New Features Not in Original

  • Multi-color space support (RGB, HSV, LAB)
  • Accessibility features and WCAG compliance
  • Color harmony generation with 4 different schemes
  • Enhanced performance and accuracy with 5-bit quantization
  • Modern null-safe API
  • Region-based processing
  • Custom filters support
  • Timeout handling for network images

📱 Example App

The package includes a comprehensive example app that demonstrates all features:

  • Interactive color space selection
  • Real-time accessibility information
  • Color harmony visualization
  • Detailed color information with copy-to-clipboard
  • Modern Material 3 design
  • Image picker integration
  • Custom target configuration

To run the example:

cd example
flutter run

⚡ Performance Tips

  1. Use appropriate color space: RGB for speed, LAB for accuracy
  2. Limit maximum colors: 16 colors usually sufficient for most use cases
  3. Use region parameter: Process only relevant parts of large images
  4. Use filters: Filter out unwanted colors early in the process
  5. Cache results: Store generated palettes for reuse
  6. Adjust sampling rate: The package uses step 2 sampling by default for balance
// Optimized for performance
final generator = await PaletteGeneratorMaster.fromImageProvider(
  imageProvider,
  maximumColorCount: 12,     // Reasonable limit
  generateHarmony: false,     // Skip if not needed
  filters: [avoidRedBlackWhitePaletteFilterMaster], // Filter unwanted colors
);

// Optimized for accuracy
final generator = await PaletteGeneratorMaster.fromImageProvider(
  imageProvider,
  maximumColorCount: 24,
  generateHarmony: true,
  colorSpace: ColorSpace.lab, // Most accurate
);

// Cache the result
final cachedGenerator = generator;

🐛 Troubleshooting

Common Issues

Q: Colors look different than expected A: Try using LAB color space for more perceptually accurate results.

final generator = await PaletteGeneratorMaster.fromImageProvider(
  imageProvider,
  colorSpace: ColorSpace.lab,
);

Q: Performance is slow A: Reduce maximumColorCount or use RGB color space instead of LAB.

final generator = await PaletteGeneratorMaster.fromImageProvider(
  imageProvider,
  maximumColorCount: 8,
  colorSpace: ColorSpace.rgb,
);

Q: Not getting vibrant colors A: Adjust the target parameters or use a custom PaletteTargetMaster.

final customVibrant = PaletteTargetMaster(
  minimumSaturation: 0.5,
  targetSaturation: 0.8,
  saturationWeight: 0.7,
  // ... other parameters
);

Q: Accessibility pairs are empty A: Lower the minimumContrast threshold or ensure your image has sufficient color variety.

final pairs = generator.getAccessibleColorPairs(
  minimumContrast: 3.0, // Lower threshold
);

Q: Network images timeout A: Increase the timeout duration for large images or slow connections.

final generator = await PaletteGeneratorMaster.fromImageProvider(
  NetworkImage('https://example.com/large-image.jpg'),
  timeout: Duration(seconds: 30),
);

Q: Memory issues with large images A: Use region parameter to process only a portion of the image.

final generator = await PaletteGeneratorMaster.fromImageProvider(
  imageProvider,
  region: Rect.fromLTWH(0, 0, 500, 500),
  maximumColorCount: 12,
);

📄 License

This package is licensed under the BSD 3-Clause License. See the LICENSE file for details.

🙏 Acknowledgements

  • Original palette_generator package authors
  • Android Palette API for inspiration
  • WCAG guidelines documentation
  • Flutter community for feedback and contributions

📞 Support

🤝 Contributing

Contributions are welcome! Please read our Contributing Guide for details.

DME is written in clear, professional English with proper formatting and structure suitable for pub.dev publication.