Skip to content

Commit

Permalink
feat!: use different shade of the same color for primary, secondary, …
Browse files Browse the repository at this point in the history
…tertiary (#390)

- use different shade of the same color for primary, secondary and tertiary ;
- add cap and capDown Color extension.

Fixes #300
  • Loading branch information
Jupi007 committed Aug 15, 2023
1 parent be019d8 commit e1c8015
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 17 deletions.
44 changes: 44 additions & 0 deletions lib/src/colors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,50 @@ extension YaruColorExtension on Color {
.toColor();
}

Color cap({
double alpha = 1.0,
double saturation = 1.0,
double lightness = 1.0,
}) {
assert(alpha >= 0.0 && alpha <= 1.0);
assert(saturation >= 0.0 && saturation <= 1.0);
assert(lightness >= 0.0 && lightness <= 1.0);

final hslColor = _getPatchedHslColor();

return hslColor
.withAlpha(hslColor.alpha <= alpha ? hslColor.alpha : alpha)
.withSaturation(
hslColor.saturation <= saturation ? hslColor.saturation : saturation,
)
.withLightness(
hslColor.lightness <= lightness ? hslColor.lightness : lightness,
)
.toColor();
}

Color capDown({
double alpha = 0.0,
double saturation = 0.0,
double lightness = 0.0,
}) {
assert(alpha >= 0.0 && alpha <= 1.0);
assert(saturation >= 0.0 && saturation <= 1.0);
assert(lightness >= 0.0 && lightness <= 1.0);

final hslColor = _getPatchedHslColor();

return hslColor
.withAlpha(hslColor.alpha >= alpha ? hslColor.alpha : alpha)
.withSaturation(
hslColor.saturation >= saturation ? hslColor.saturation : saturation,
)
.withLightness(
hslColor.lightness >= lightness ? hslColor.lightness : lightness,
)
.toColor();
}

HSLColor _getPatchedHslColor() {
final hslColor = HSLColor.fromColor(this);

Expand Down
60 changes: 43 additions & 17 deletions lib/src/themes/common_themes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,15 @@ SnackBarThemeData _createSnackBarTheme(ColorScheme colorScheme) {
);
}

ChipThemeData _createChipTheme({
required Color selectedColor,
required ColorScheme colorScheme,
}) {
return ChipThemeData(
selectedColor: selectedColor.withOpacity(.4),
);
}

/// Helper function to create a new Yaru theme
ThemeData createYaruTheme({
required ColorScheme colorScheme,
Expand Down Expand Up @@ -730,6 +739,10 @@ ThemeData createYaruTheme({
iconColor: colorScheme.onSurface.withOpacity(0.8),
),
snackBarTheme: _createSnackBarTheme(colorScheme),
chipTheme: _createChipTheme(
selectedColor: elevatedButtonColor ?? colorScheme.primary,
colorScheme: colorScheme,
),
);
}

Expand All @@ -740,6 +753,13 @@ ThemeData createYaruLightTheme({
Color? elevatedButtonTextColor,
bool? useMaterial3 = true,
}) {
final secondary = primaryColor.scale(lightness: 0.2).cap(saturation: .9);
final secondaryContainer =
primaryColor.scale(lightness: 0.85).cap(saturation: .5);
final tertiary = primaryColor.scale(lightness: 0.5).cap(saturation: .8);
final tertiaryContainer =
primaryColor.scale(lightness: 0.75).cap(saturation: .75);

final colorScheme = ColorScheme.fromSeed(
seedColor: primaryColor,
error: YaruColors.light.error,
Expand All @@ -750,11 +770,10 @@ ThemeData createYaruLightTheme({
primaryContainer: YaruColors.porcelain,
onPrimaryContainer: YaruColors.jet,
inversePrimary: YaruColors.jet,
secondary: elevatedButtonColor ?? primaryColor,
onSecondary: contrastColor(elevatedButtonColor ?? primaryColor),
secondaryContainer:
elevatedButtonColor?.withOpacity(0.4) ?? primaryColor.withOpacity(0.4),
onSecondaryContainer: elevatedButtonTextColor ?? YaruColors.jet,
secondary: secondary,
onSecondary: contrastColor(secondary),
secondaryContainer: secondaryContainer,
onSecondaryContainer: contrastColor(secondaryContainer),
background: YaruColors.porcelain,
onBackground: YaruColors.jet,
surface: Colors.white,
Expand All @@ -763,10 +782,10 @@ ThemeData createYaruLightTheme({
onInverseSurface: YaruColors.porcelain,
surfaceTint: YaruColors.warmGrey,
surfaceVariant: YaruColors.warmGrey,
tertiary: const Color(0xFF18b6ec),
onTertiary: Colors.white,
tertiaryContainer: const Color(0xFF18b6ec),
onTertiaryContainer: Colors.white,
tertiary: tertiary,
onTertiary: contrastColor(tertiary),
tertiaryContainer: tertiaryContainer,
onTertiaryContainer: contrastColor(tertiaryContainer),
onSurfaceVariant: YaruColors.coolGrey,
outline: const Color.fromARGB(255, 221, 221, 221),
outlineVariant: Colors.black,
Expand All @@ -789,6 +808,15 @@ ThemeData createYaruDarkTheme({
bool? useMaterial3 = true,
bool highContrast = false,
}) {
final secondary = primaryColor.scale(lightness: -0.3, saturation: -0.15);
final secondaryContainer = primaryColor
.scale(lightness: -0.6, saturation: -0.75)
.capDown(lightness: .175);
final tertiary = primaryColor.scale(lightness: -0.5, saturation: -0.25);
final tertiaryContainer = primaryColor
.scale(lightness: -0.5, saturation: -0.65)
.capDown(lightness: .2);

final colorScheme = ColorScheme.fromSeed(
seedColor: primaryColor,
error: YaruColors.dark.error,
Expand All @@ -799,12 +827,10 @@ ThemeData createYaruDarkTheme({
onPrimary: contrastColor(primaryColor),
onPrimaryContainer: YaruColors.porcelain,
inversePrimary: YaruColors.porcelain,
secondary: elevatedButtonColor ?? primaryColor,
onSecondary: contrastColor(elevatedButtonColor ?? primaryColor),
secondaryContainer:
elevatedButtonColor?.withOpacity(0.4) ?? primaryColor.withOpacity(0.4),
onSecondaryContainer:
highContrast ? Colors.white : (elevatedButtonTextColor ?? Colors.white),
secondary: secondary,
onSecondary: contrastColor(primaryColor.scale(lightness: -0.25)),
secondaryContainer: secondaryContainer,
onSecondaryContainer: Colors.white,
background: YaruColors.darkJet,
onBackground: YaruColors.porcelain,
surface: YaruColors.jet,
Expand All @@ -813,9 +839,9 @@ ThemeData createYaruDarkTheme({
onInverseSurface: YaruColors.inkstone,
surfaceTint: YaruColors.coolGrey,
surfaceVariant: const Color.fromARGB(255, 34, 34, 34),
tertiary: const Color(0xFF18b6ec),
tertiary: tertiary,
onTertiary: YaruColors.porcelain,
tertiaryContainer: const Color(0xFF18b6ec),
tertiaryContainer: tertiaryContainer,
onTertiaryContainer: YaruColors.porcelain,
onSurfaceVariant: YaruColors.warmGrey,
outline: const Color.fromARGB(255, 68, 68, 68),
Expand Down
50 changes: 50 additions & 0 deletions test/yaru_color_extension_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,56 @@ void main() {
});
});

group('Color.cap() test -', () {
test('With out of range amount', () {
expect(() => midColor.cap(alpha: -1.1), throwsAssertionError);
expect(() => midColor.cap(alpha: 1.1), throwsAssertionError);
expect(() => midColor.cap(saturation: -1.1), throwsAssertionError);
expect(() => midColor.cap(saturation: 1.1), throwsAssertionError);
expect(() => midColor.cap(lightness: -1.1), throwsAssertionError);
expect(() => midColor.cap(lightness: 1.1), throwsAssertionError);
});
test('With various amount', () {
expect(
midColor.cap(alpha: 0.25),
const Color(0x4040bfbf),
);
expect(
midColor.cap(saturation: 0.25),
const Color(0x80609f9f),
);
expect(
midColor.cap(lightness: 0.25),
const Color(0x80206060),
);
});
});

group('Color.capDown() test -', () {
test('With out of range amount', () {
expect(() => midColor.capDown(alpha: -1.1), throwsAssertionError);
expect(() => midColor.capDown(alpha: 1.1), throwsAssertionError);
expect(() => midColor.capDown(saturation: -1.1), throwsAssertionError);
expect(() => midColor.capDown(saturation: 1.1), throwsAssertionError);
expect(() => midColor.capDown(lightness: -1.1), throwsAssertionError);
expect(() => midColor.capDown(lightness: 1.1), throwsAssertionError);
});
test('With various amount', () {
expect(
midColor.capDown(alpha: 0.75),
const Color(0xbf40bfbf),
);
expect(
midColor.capDown(saturation: 0.75),
const Color(0x8020dfdf),
);
expect(
midColor.capDown(lightness: 0.75),
const Color(0x80a0dfdf),
);
});
});

test('hex', () {
expect(const Color(0x12345678).toHex(), '#12345678');
});
Expand Down

0 comments on commit e1c8015

Please sign in to comment.