šØ Project: Color System
š Lesson 7: Advanced OOP
ā± 25-30 min
š Advanced
šÆ Objective
Build a color system using enhanced enums, extensions, and mixins. Create a flexible way to work with colors in different formats (RGB, HEX, HSL) with conversion capabilities.
Requirements
- Create an enhanced enum for predefined colors with RGB values
- Implement extensions on String for color validation
- Use a mixin for color conversion utilities
- Support conversion between HEX, RGB, and HSL formats
- Add brightness calculation and color manipulation
import 'dart:math';
// Step 1: Enhanced enum for predefined colors
enum PredefinedColor {
red(255, 0, 0, 'Red'),
green(0, 255, 0, 'Green'),
blue(0, 0, 255, 'Blue'),
yellow(255, 255, 0, 'Yellow'),
purple(128, 0, 128, 'Purple'),
orange(255, 165, 0, 'Orange'),
black(0, 0, 0, 'Black'),
white(255, 255, 255, 'White'),
gray(128, 128, 128, 'Gray'),
navy(0, 0, 128, 'Navy'),
teal(0, 128, 128, 'Teal'),
maroon(128, 0, 0, 'Maroon');
final int red;
final int green;
final int blue;
final String displayName;
const PredefinedColor(this.red, this.green, this.blue, this.displayName);
String get hexCode =>
'#${red.toRadixString(16).padLeft(2, '0')}'
'${green.toRadixString(16).padLeft(2, '0')}'
'${blue.toRadixString(16).padLeft(2, '0')}'
.toUpperCase();
bool get isDark => (red * 0.299 + green * 0.587 + blue * 0.114) < 128;
bool get isLight => !isDark;
@override
String toString() => '$displayName ($hexCode)';
}
// Step 2: Color class with mixin
class Color {
final int red;
final int green;
final int blue;
final double alpha;
Color({
required this.red,
required this.green,
required this.blue,
this.alpha = 1.0,
}) : assert(red >= 0 && red <= 255),
assert(green >= 0 && green <= 255),
assert(blue >= 0 && blue <= 255),
assert(alpha >= 0 && alpha <= 1.0);
factory Color.fromHex(String hex) {
hex = hex.replaceAll('#', '');
if (hex.length == 6) {
return Color(
red: int.parse(hex.substring(0, 2), radix: 16),
green: int.parse(hex.substring(2, 4), radix: 16),
blue: int.parse(hex.substring(4, 6), radix: 16),
);
}
throw FormatException('Invalid hex color: $hex');
}
factory Color.fromPredefined(PredefinedColor color) {
return Color(red: color.red, green: color.green, blue: color.blue);
}
String toHex() => '#${_toHex(red)}${_toHex(green)}${_toHex(blue)}'.toUpperCase();
String toRgb() => 'rgb($red, $green, $blue)';
String toRgba() => 'rgba($red, $green, $blue, $alpha)';
String _toHex(int value) => value.toRadixString(16).padLeft(2, '0');
double get brightness => (red * 0.299 + green * 0.587 + blue * 0.114) / 255;
bool get isDark => brightness < 0.5;
Color lighter(double factor) {
factor = factor.clamp(0, 1);
return Color(
red: (red + (255 - red) * factor).round(),
green: (green + (255 - green) * factor).round(),
blue: (blue + (255 - blue) * factor).round(),
);
}
Color darker(double factor) {
factor = factor.clamp(0, 1);
return Color(
red: (red * (1 - factor)).round(),
green: (green * (1 - factor)).round(),
blue: (blue * (1 - factor)).round(),
);
}
@override
String toString() => 'Color($toHex())';
@override
bool operator ==(Object other) =>
other is Color && red == other.red && green == other.green && blue == other.blue;
@override
int get hashCode => Object.hash(red, green, blue);
}
// Step 3: Mixin for color utilities
mixin ColorUtils {
List generatePalette(Color baseColor, int count) {
List palette = [];
for (int i = 0; i < count; i++) {
double factor = i / (count - 1);
palette.add(baseColor.lighter(factor));
}
return palette;
}
Color blend(Color c1, Color c2, double ratio) {
ratio = ratio.clamp(0, 1);
return Color(
red: (c1.red + (c2.red - c1.red) * ratio).round(),
green: (c1.green + (c2.green - c1.green) * ratio).round(),
blue: (c1.blue + (c2.blue - c1.blue) * ratio).round(),
);
}
String colorToAnsi(Color color, String text) {
return '\x1B[38;2;${color.red};${color.green};${color.blue}m$text\x1B[0m';
}
}
// Step 4: Extensions on String
extension ColorStringExtensions on String {
bool get isValidHexColor => RegExp(r'^#?([0-9A-Fa-f]{6})$').hasMatch(this);
Color? toColor() {
try {
return Color.fromHex(this);
} catch (e) {
return null;
}
}
}
// Step 5: Color palette generator with mixin
class PaletteGenerator with ColorUtils {
final Color baseColor;
PaletteGenerator(this.baseColor);
void displayPalette(int count) {
final palette = generatePalette(baseColor, count);
print('šØ Palette (${baseColor.toHex()}):');
for (var color in palette) {
print(colorToAnsi(color, ' āāāāāā ${color.toHex()}'));
}
}
}
// Step 6: Main program
void main() {
print('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
print('ā COLOR SYSTEM ā');
print('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n');
// Test predefined colors
print('š Predefined Colors:');
for (var color in PredefinedColor.values) {
print(' ${color.displayName.padRight(8)} ${color.hexCode} ${color.isDark ? "š Dark" : "āļø Light"}');
}
// Test color creation
print('\nšØ Color Operations:');
final navy = Color.fromPredefined(PredefinedColor.navy);
print(' Navy: ${navy.toRgb()}');
print(' Lighter: ${navy.lighter(0.5).toRgb()}');
print(' Darker: ${navy.darker(0.5).toRgb()}');
// Test extensions
print('\nš String Extensions:');
print(' "#FF5733" is valid hex? ${"#FF5733".isValidHexColor}');
print(' "#GGHHII" is valid hex? ${"#GGHHII".isValidHexColor}');
// Test palette
print('\nš Color Palette:');
final teal = Color.fromPredefined(PredefinedColor.teal);
final generator = PaletteGenerator(teal);
generator.displayPalette(5);
// Test blending
print('\nš Color Blending:');
final red = Color.fromPredefined(PredefinedColor.red);
final blue = Color.fromPredefined(PredefinedColor.blue);
final blended = generator.blend(red, blue, 0.5);
print(' Red + Blue = ${blended.toHex()}');
}