1
+ import 'dart:ui' ;
2
+
1
3
import 'package:flutter/foundation.dart' ;
4
+ import 'package:flutter/material.dart' ;
2
5
import 'package:flutter/services.dart' ;
3
- import 'package:flutter/widgets.dart' ;
4
6
5
7
import 'package:meta/meta.dart' ;
6
8
@@ -21,8 +23,11 @@ import 'package:forui/forui.dart';
21
23
/// Each color group includes a `-Foreground` suffixed color, i.e. [primaryForeground] , used to color text and other
22
24
/// visual elements on top of their respective background colors.
23
25
///
24
- /// Hovered and disabled colors are derived by adjusting the opacity. To derive these colors, use the [hover] and
25
- /// [disable] methods. The opacity can be adjusted with [enabledHoveredOpacity] and [disabledOpacity] .
26
+ /// Hovered colors are derived by adjusting the lightness of the original color. To derive these colors, use the [hover]
27
+ /// method. The lightness can be adjusted with [hoverLighten] .
28
+ ///
29
+ /// Disabled colors are derived by adjusting the opacity. To derive these colors, use the [disable] method. The opacity
30
+ /// can be adjusted with [disabledOpacity] .
26
31
///
27
32
/// See [FThemes] for predefined themes and color schemes.
28
33
final class FColors with Diagnosticable {
@@ -104,11 +109,21 @@ final class FColors with Diagnosticable {
104
109
/// The border color.
105
110
final Color border;
106
111
107
- /// The opacity of the foreground color when a widget is hovered and enabled. Defaults to 0.9.
112
+ /// The percentage to lighten dark colors by. A higher value will result in a more pronounced lightening effect.
113
+ ///
114
+ /// Defaults to 0.075.
108
115
///
109
116
/// ## Contract
110
- /// Throws [AssertionError] if the value is less than 0 or greater than 1.
111
- final double enabledHoveredOpacity;
117
+ /// `0.0 <= hoverLighten <= 1.0`
118
+ final double hoverLighten;
119
+
120
+ /// The percentage to darken light colors by. A higher value will result in a more pronounced darkening effect.
121
+ ///
122
+ /// Defaults to 0.05.
123
+ ///
124
+ /// ## Contract
125
+ /// `0.0 <= hoverDarken <= 1.0`
126
+ final double hoverDarken;
112
127
113
128
/// The opacity of the foreground color when a widget is disabled. Defaults to 0.5.
114
129
///
@@ -137,19 +152,31 @@ final class FColors with Diagnosticable {
137
152
required this .error,
138
153
required this .errorForeground,
139
154
required this .border,
140
- this .enabledHoveredOpacity = 0.9 ,
155
+ this .hoverLighten = 0.075 ,
156
+ this .hoverDarken = 0.05 ,
141
157
this .disabledOpacity = 0.5 ,
142
- }) : assert (
143
- 0 <= enabledHoveredOpacity && enabledHoveredOpacity <= 1 ,
144
- 'The enabledHoveredOpacity must be between 0 and 1.' ,
145
- ),
158
+ }) : assert (0.0 <= hoverLighten && hoverLighten <= 1.0 , 'The hoverLighten must be between 0 and 1.' ),
159
+ assert (0.0 <= hoverDarken && hoverDarken <= 1.0 , 'The hoverDarken must be between 0 and 1.' ),
146
160
assert (0 <= disabledOpacity && disabledOpacity <= 1 , 'The disabledOpacity must be between 0 and 1.' );
147
161
148
- /// Returns a hovered color for the [foreground] on the [background] .
162
+ /// Generates a hovered variant of the given [color] by darkening light colors and lighting dark colors based on their
163
+ /// HSL lightness.
149
164
///
150
- /// [FColors.background] is used if [background] is not given.
151
- Color hover (Color foreground, [Color ? background]) =>
152
- Color .alphaBlend (foreground.withValues (alpha: enabledHoveredOpacity), background ?? this .background);
165
+ /// Colors at the extremes (very light or very dark) will be adjusted more aggressively than colors in the middle.
166
+ ///
167
+ /// The lightening and darkening are controlled by [hoverLighten] and [hoverDarken] .
168
+ Color hover (Color color) {
169
+ final hsl = HSLColor .fromColor (color);
170
+ final l = hsl.lightness;
171
+
172
+ // More aggressive color change when close to extremes & less when in the middle.
173
+ final (space, factor, sign) = l > 0.5 ? (1.0 - l, hoverDarken, - 1 ) : (l, hoverLighten, 1 );
174
+ final aggressiveness = 1 + ((0.5 - space) / 0.5 );
175
+ final adjustment = factor * aggressiveness * sign;
176
+ final lightness = clampDouble (l + adjustment, 0 , 1 );
177
+
178
+ return hsl.withLightness (lightness).toColor ();
179
+ }
153
180
154
181
/// Returns a disabled color for the [foreground] on the [background] .
155
182
///
@@ -189,7 +216,8 @@ final class FColors with Diagnosticable {
189
216
Color ? error,
190
217
Color ? errorForeground,
191
218
Color ? border,
192
- double ? enabledHoveredOpacity,
219
+ double ? hoverLighten,
220
+ double ? hoverDarken,
193
221
double ? disabledOpacity,
194
222
}) => FColors (
195
223
brightness: brightness ?? this .brightness,
@@ -208,7 +236,8 @@ final class FColors with Diagnosticable {
208
236
error: error ?? this .error,
209
237
errorForeground: errorForeground ?? this .errorForeground,
210
238
border: border ?? this .border,
211
- enabledHoveredOpacity: enabledHoveredOpacity ?? this .enabledHoveredOpacity,
239
+ hoverLighten: hoverLighten ?? this .hoverLighten,
240
+ hoverDarken: hoverDarken ?? this .hoverDarken,
212
241
disabledOpacity: disabledOpacity ?? this .disabledOpacity,
213
242
);
214
243
@@ -232,7 +261,8 @@ final class FColors with Diagnosticable {
232
261
..add (ColorProperty ('error' , error))
233
262
..add (ColorProperty ('errorForeground' , errorForeground))
234
263
..add (ColorProperty ('border' , border))
235
- ..add (PercentProperty ('enabledHoveredOpacity' , enabledHoveredOpacity))
264
+ ..add (PercentProperty ('hoverLighten' , hoverLighten))
265
+ ..add (PercentProperty ('hoverDarken' , hoverDarken))
236
266
..add (PercentProperty ('disabledOpacity' , disabledOpacity));
237
267
}
238
268
@@ -256,7 +286,8 @@ final class FColors with Diagnosticable {
256
286
error == other.error &&
257
287
errorForeground == other.errorForeground &&
258
288
border == other.border &&
259
- enabledHoveredOpacity == other.enabledHoveredOpacity &&
289
+ hoverLighten == other.hoverLighten &&
290
+ hoverDarken == other.hoverDarken &&
260
291
disabledOpacity == other.disabledOpacity;
261
292
262
293
@override
@@ -277,6 +308,7 @@ final class FColors with Diagnosticable {
277
308
error.hashCode ^
278
309
errorForeground.hashCode ^
279
310
border.hashCode ^
280
- enabledHoveredOpacity.hashCode ^
311
+ hoverLighten.hashCode ^
312
+ hoverDarken.hashCode ^
281
313
disabledOpacity.hashCode;
282
314
}
0 commit comments