Line data Source code
1 : import 'dart:async';
2 :
3 : import 'package:flutter/material.dart';
4 :
5 : import '../buttons/custom_action_button.dart';
6 :
7 : /// A type definition for a callback function that updates the quantity value.
8 : ///
9 : /// The [ValueUpdate] function takes an integer parameter [updateValue] representing
10 : /// the new quantity and returns a dynamic value. This allows flexibility in handling
11 : /// the updated value, whether it's immediately applied or processed asynchronously.
12 : typedef ValueUpdate = dynamic Function(int updateValue);
13 :
14 : /// A widget that provides increment and decrement functionality with customizable
15 : /// buttons and display. It allows users to increase or decrease a numerical
16 : /// value within optional bounds, making it suitable for quantity selectors in
17 : /// shopping carts, forms, and other interactive UI components.
18 : ///
19 : /// The [IncrementDecrementWidget] offers multiple factory constructors to cater
20 : /// to different design requirements, such as flat, raised, minimal, and squared
21 : /// styles. It also supports long-press actions for continuous incrementing or
22 : /// decrementing.
23 : class IncrementDecrementWidget extends StatefulWidget {
24 : // Quantity properties
25 :
26 : /// The initial quantity to display.
27 : ///
28 : /// Must be a non-negative integer. Represents the current value of the widget.
29 : final int quantity;
30 :
31 : /// The maximum allowed quantity.
32 : ///
33 : /// If provided, the quantity cannot exceed this value. If `null`, there is no upper bound.
34 : final int? maxQuantity;
35 :
36 : /// The minimum allowed quantity.
37 : ///
38 : /// If provided, the quantity cannot go below this value. If `null`, there is no lower bound.
39 : final int? minValue;
40 :
41 : // Callback functions
42 :
43 : /// A callback function that is invoked whenever the quantity changes.
44 : ///
45 : /// It receives the updated quantity as an integer. This callback can return
46 : /// a new integer value to override the incremented or decremented value.
47 : /// It supports both synchronous and asynchronous operations.
48 : final ValueUpdate? onChanged;
49 :
50 : // Customization properties
51 :
52 : /// The background color of the widget.
53 : ///
54 : /// Applies to the area surrounding the buttons and the quantity display.
55 : final Color? backgroundColor;
56 :
57 : /// The color of the widget's icons.
58 : ///
59 : /// Applies to both increment and decrement icons. If not specified, defaults to the theme's icon color.
60 : final Color? iconColor;
61 :
62 : /// The elevation (shadow depth) of the widget.
63 : ///
64 : /// A higher value increases the shadow's prominence. Defaults vary based on the factory constructor used.
65 : final double? elevation;
66 :
67 : /// The external margin around the widget.
68 : ///
69 : /// Adds space outside the widget's boundaries.
70 : final EdgeInsetsGeometry? margin;
71 :
72 : /// The padding inside the quantity display.
73 : ///
74 : /// Controls the space between the quantity text and its container.
75 : final EdgeInsetsGeometry? valuePadding;
76 :
77 : /// The text style for displaying the quantity.
78 : ///
79 : /// If not provided, defaults to the theme's `titleLarge` text style.
80 : final TextStyle? quantityTextStyle;
81 :
82 : /// The border radius of the widget's buttons.
83 : ///
84 : /// Controls the roundness of the buttons' corners. Defaults vary based on the factory constructor used.
85 : final double? borderRadius;
86 :
87 : /// The width of the entire widget.
88 : ///
89 : /// If not specified, the widget adapts to its parent constraints.
90 : final double? width;
91 :
92 : /// The height of the entire widget.
93 : ///
94 : /// If not specified, the widget adapts to its content.
95 : final double? height;
96 :
97 : /// The padding inside the increment and decrement buttons.
98 : ///
99 : /// Controls the space between the button's icon and its boundaries.
100 : final EdgeInsetsGeometry? buttonPadding;
101 :
102 : /// The external margin around each increment and decrement button.
103 : ///
104 : /// Adds space outside the buttons' boundaries.
105 : final EdgeInsetsGeometry? buttonMargin;
106 :
107 : /// The width of the increment and decrement buttons.
108 : ///
109 : /// If not specified, buttons adapt to their content.
110 : final double? buttonWidth;
111 :
112 : /// The height of the increment and decrement buttons.
113 : ///
114 : /// If not specified, buttons adapt to their content.
115 : final double? buttonHeight;
116 :
117 : // CustomActionButton parameters
118 :
119 : /// The splash color of the buttons when tapped.
120 : ///
121 : /// Defines the color of the ripple effect upon interaction.
122 : final Color? splashColor;
123 :
124 : /// The border color of the buttons.
125 : ///
126 : /// If specified, buttons will have a border with this color.
127 : final Color? borderColor;
128 :
129 : /// The splash factory to define interaction effects.
130 : ///
131 : /// Allows customization of the splash effect (e.g., ripple type).
132 : final InteractiveInkFeatureFactory? splashFactory;
133 :
134 : // Factory-specific properties
135 :
136 : /// The icon widget for the increment button.
137 : ///
138 : /// If not provided, defaults to a plus icon.
139 : final Widget? incrementIcon;
140 :
141 : /// The icon widget for the decrement button.
142 : ///
143 : /// If not provided, defaults to a minus icon.
144 : final Widget? decrementIcon;
145 :
146 : // Button shape
147 :
148 : /// The shape of the increment and decrement buttons.
149 : ///
150 : /// Allows for customizing the buttons' outline and borders.
151 : final OutlinedBorder? buttonShape;
152 :
153 : // Long-press settings
154 :
155 : /// The interval between repeated increment/decrement actions during a long press.
156 : ///
157 : /// Determines how quickly the quantity changes when the button is held down.
158 : final Duration longPressInterval;
159 :
160 : // Alignment
161 :
162 : /// The alignment of the buttons and quantity display within the widget.
163 : ///
164 : /// Controls how the child widgets are placed along the main axis (horizontal).
165 : final MainAxisAlignment? alignment;
166 :
167 : /// Creates an [IncrementDecrementWidget] with the specified properties.
168 : ///
169 : /// The [quantity] parameter is required and sets the initial value.
170 1 : const IncrementDecrementWidget({
171 : super.key,
172 : required this.quantity,
173 : this.maxQuantity,
174 : this.minValue,
175 : this.onChanged,
176 : this.backgroundColor,
177 : this.iconColor,
178 : this.elevation,
179 : this.margin,
180 : this.valuePadding,
181 : this.quantityTextStyle,
182 : this.borderRadius,
183 : this.width,
184 : this.height,
185 : this.buttonPadding,
186 : this.buttonMargin,
187 : this.buttonWidth,
188 : this.buttonHeight,
189 : this.splashColor,
190 : this.borderColor,
191 : this.splashFactory,
192 : this.incrementIcon,
193 : this.decrementIcon,
194 : this.buttonShape,
195 : this.longPressInterval = const Duration(milliseconds: 100),
196 : this.alignment,
197 : });
198 :
199 : /// Factory constructor for a flat design.
200 : ///
201 : /// The flat design has no elevation and transparent background by default.
202 : ///
203 : /// Example usage:
204 : /// ```dart
205 : /// IncrementDecrementWidget.flat(
206 : /// quantity: 1,
207 : /// maxQuantity: 10,
208 : /// onChanged: (newValue) {
209 : /// // Handle value change
210 : /// },
211 : /// );
212 : /// ```
213 0 : factory IncrementDecrementWidget.flat({
214 : required int quantity,
215 : int? maxQuantity,
216 : int? minValue,
217 : ValueUpdate? onChanged,
218 : Color? backgroundColor,
219 : Color? iconColor,
220 : EdgeInsetsGeometry? margin,
221 : EdgeInsetsGeometry? valuePadding,
222 : EdgeInsetsGeometry? buttonMargin,
223 : EdgeInsetsGeometry? buttonPadding,
224 : TextStyle? quantityTextStyle,
225 : double? borderRadius,
226 : double? width,
227 : double? height,
228 : double? buttonWidth,
229 : double? buttonHeight,
230 : Color? splashColor,
231 : Color? borderColor,
232 : InteractiveInkFeatureFactory? splashFactory,
233 : Widget? incrementIcon,
234 : Widget? decrementIcon,
235 : Duration longPressInterval = const Duration(milliseconds: 100),
236 : MainAxisAlignment? alignment,
237 : }) {
238 0 : return IncrementDecrementWidget(
239 : quantity: quantity,
240 : maxQuantity: maxQuantity,
241 : minValue: minValue,
242 : onChanged: onChanged,
243 : backgroundColor: backgroundColor ?? Colors.transparent,
244 : iconColor: iconColor,
245 : elevation: 0.0,
246 : margin: margin,
247 : valuePadding: valuePadding,
248 : buttonMargin: buttonMargin,
249 : buttonPadding: buttonPadding,
250 : quantityTextStyle: quantityTextStyle,
251 : borderRadius: borderRadius ?? 10.0,
252 : width: width,
253 : height: height,
254 : buttonWidth: buttonWidth,
255 : buttonHeight: buttonHeight,
256 : splashColor: splashColor,
257 : borderColor: borderColor,
258 : splashFactory: splashFactory,
259 : incrementIcon: incrementIcon,
260 : decrementIcon: decrementIcon,
261 : longPressInterval: longPressInterval,
262 : alignment: alignment,
263 : );
264 : }
265 :
266 : /// Factory constructor for a raised design.
267 : ///
268 : /// The raised design has elevation and an optional background color.
269 : ///
270 : /// Example usage:
271 : /// ```dart
272 : /// IncrementDecrementWidget.raised(
273 : /// quantity: 2,
274 : /// maxQuantity: 5,
275 : /// onChanged: (newValue) {
276 : /// // Handle value change
277 : /// },
278 : /// );
279 : /// ```
280 0 : factory IncrementDecrementWidget.raised({
281 : required int quantity,
282 : int? maxQuantity,
283 : int? minValue,
284 : ValueUpdate? onChanged,
285 : Color? backgroundColor,
286 : Color? iconColor,
287 : double? elevation,
288 : EdgeInsetsGeometry? margin,
289 : EdgeInsetsGeometry? valuePadding,
290 : EdgeInsetsGeometry? buttonMargin,
291 : EdgeInsetsGeometry? buttonPadding,
292 : TextStyle? quantityTextStyle,
293 : double? borderRadius,
294 : double? width,
295 : double? height,
296 : double? buttonWidth,
297 : double? buttonHeight,
298 : Color? borderColor,
299 : Widget? incrementIcon,
300 : Widget? decrementIcon,
301 : Duration longPressInterval = const Duration(milliseconds: 100),
302 : MainAxisAlignment? alignment,
303 : }) {
304 0 : return IncrementDecrementWidget(
305 : quantity: quantity,
306 : maxQuantity: maxQuantity,
307 : minValue: minValue,
308 : onChanged: onChanged,
309 : backgroundColor: backgroundColor,
310 : iconColor: iconColor,
311 : elevation: elevation ?? 6.0,
312 : margin: margin,
313 : valuePadding: valuePadding,
314 : buttonMargin: buttonMargin,
315 : buttonPadding: buttonPadding,
316 : quantityTextStyle: quantityTextStyle,
317 : borderRadius: borderRadius ?? 10.0,
318 : width: width,
319 : height: height,
320 : buttonWidth: buttonWidth,
321 : buttonHeight: buttonHeight,
322 : borderColor: borderColor,
323 : splashFactory: NoSplash.splashFactory,
324 : splashColor: Colors.transparent,
325 : incrementIcon: incrementIcon,
326 : decrementIcon: decrementIcon,
327 : longPressInterval: longPressInterval,
328 : alignment: alignment,
329 : );
330 : }
331 :
332 : /// Factory constructor for a minimalistic design.
333 : ///
334 : /// The minimal design has no elevation and transparent background by default,
335 : /// focusing on simplicity and compactness.
336 : ///
337 : /// Example usage:
338 : /// ```dart
339 : /// IncrementDecrementWidget.minimal(
340 : /// quantity: 3,
341 : /// minValue: 1,
342 : /// onChanged: (newValue) {
343 : /// // Handle value change
344 : /// },
345 : /// );
346 : /// ```
347 0 : factory IncrementDecrementWidget.minimal({
348 : required int quantity,
349 : int? maxQuantity,
350 : int? minValue,
351 : ValueUpdate? onChanged,
352 : Color? iconColor,
353 : EdgeInsetsGeometry? margin,
354 : EdgeInsetsGeometry? valuePadding,
355 : EdgeInsetsGeometry? buttonMargin,
356 : EdgeInsetsGeometry? buttonPadding,
357 : TextStyle? quantityTextStyle,
358 : double? width,
359 : double? height,
360 : double? buttonWidth,
361 : double? buttonHeight,
362 : Widget? incrementIcon,
363 : Widget? decrementIcon,
364 : Duration longPressInterval = const Duration(milliseconds: 100),
365 : MainAxisAlignment? alignment,
366 : }) {
367 0 : return IncrementDecrementWidget(
368 : quantity: quantity,
369 : maxQuantity: maxQuantity,
370 : minValue: minValue,
371 : onChanged: onChanged,
372 : backgroundColor: Colors.transparent,
373 : iconColor: iconColor,
374 : elevation: 0.0,
375 : margin: margin,
376 : valuePadding: valuePadding,
377 : buttonMargin: buttonMargin,
378 : buttonPadding: buttonPadding,
379 : quantityTextStyle: quantityTextStyle,
380 : width: width,
381 : height: height,
382 : buttonWidth: buttonWidth,
383 : buttonHeight: buttonHeight,
384 : splashFactory: NoSplash.splashFactory,
385 : splashColor: Colors.transparent,
386 : incrementIcon: incrementIcon,
387 : decrementIcon: decrementIcon,
388 : longPressInterval: longPressInterval,
389 : alignment: alignment,
390 : );
391 : }
392 :
393 : /// Factory constructor for squared buttons with equal width and height
394 : /// and customizable border radius.
395 : ///
396 : /// The squared design ensures that the increment and decrement buttons are
397 : /// square-shaped, with equal width and height, suitable for compact layouts.
398 : ///
399 : /// Example usage:
400 : /// ```dart
401 : /// IncrementDecrementWidget.squared(
402 : /// quantity: 4,
403 : /// maxQuantity: 8,
404 : /// onChanged: (newValue) {
405 : /// // Handle value change
406 : /// },
407 : /// );
408 : /// ```
409 0 : factory IncrementDecrementWidget.squared({
410 : required int quantity,
411 : int? maxQuantity,
412 : int? minValue,
413 : ValueUpdate? onChanged,
414 : Color? backgroundColor,
415 : Color? iconColor,
416 : double? elevation,
417 : EdgeInsetsGeometry? margin,
418 : EdgeInsetsGeometry? valuePadding,
419 : EdgeInsetsGeometry? buttonMargin,
420 : EdgeInsetsGeometry? buttonPadding,
421 : TextStyle? quantityTextStyle,
422 : double? width,
423 : double? height,
424 : double? buttonSize,
425 : double? borderRadius, // Allow specifying border radius
426 : Color? borderColor,
427 : Widget? incrementIcon,
428 : Widget? decrementIcon,
429 : Duration longPressInterval = const Duration(milliseconds: 100),
430 : MainAxisAlignment? alignment,
431 : }) {
432 : final double size = buttonSize ?? 50.0;
433 : final double effectiveBorderRadius = borderRadius ?? 10.0;
434 0 : return IncrementDecrementWidget(
435 : quantity: quantity,
436 : maxQuantity: maxQuantity,
437 : minValue: minValue,
438 : onChanged: onChanged,
439 : backgroundColor: backgroundColor,
440 : iconColor: iconColor,
441 : elevation: elevation ?? 0.0,
442 : margin: margin,
443 : valuePadding: valuePadding,
444 : buttonMargin: buttonMargin,
445 : buttonPadding: buttonPadding,
446 : quantityTextStyle: quantityTextStyle,
447 : width: width,
448 : height: height,
449 : buttonWidth: size,
450 : buttonHeight: size,
451 : borderColor: borderColor,
452 : borderRadius: effectiveBorderRadius,
453 : // Set the border radius
454 0 : splashColor: Colors.grey.withOpacity(0.2),
455 : incrementIcon: incrementIcon,
456 : decrementIcon: decrementIcon,
457 0 : buttonShape: RoundedRectangleBorder(
458 0 : borderRadius: BorderRadius.circular(effectiveBorderRadius),
459 : ),
460 : longPressInterval: longPressInterval,
461 : alignment: alignment ?? MainAxisAlignment.center,
462 : );
463 : }
464 :
465 1 : @override
466 : State<IncrementDecrementWidget> createState() =>
467 1 : _IncrementDecrementWidgetState();
468 : }
469 :
470 : class _IncrementDecrementWidgetState extends State<IncrementDecrementWidget> {
471 : /// The current quantity value displayed by the widget.
472 : ///
473 : /// Initialized with the [widget.quantity] value.
474 : late int _currentQuantity;
475 :
476 : /// Initializes the state and sets the initial quantity.
477 1 : @override
478 : void initState() {
479 1 : super.initState();
480 3 : _currentQuantity = widget.quantity;
481 : }
482 :
483 : /// Increments the quantity by one, respecting the [maxQuantity] if set.
484 : ///
485 : /// Invokes the [onChanged] callback with the updated value, allowing for
486 : /// custom handling or validation. Updates the UI with the new quantity.
487 1 : Future<void> _increment() async {
488 6 : if (widget.maxQuantity == null || _currentQuantity < widget.maxQuantity!) {
489 2 : int updatedQuantity = _currentQuantity + 1;
490 : int newQuantity = updatedQuantity;
491 :
492 2 : if (widget.onChanged != null) {
493 3 : var result = widget.onChanged!(updatedQuantity);
494 :
495 1 : if (result is Future<int?>) {
496 : newQuantity = await result ?? updatedQuantity;
497 1 : } else if (result is int?) {
498 : newQuantity = result ?? updatedQuantity;
499 : }
500 : // If result is void (null), use updatedQuantity
501 : }
502 :
503 2 : setState(() {
504 1 : _currentQuantity = newQuantity;
505 : });
506 : }
507 : }
508 :
509 : /// Decrements the quantity by one, respecting the [minValue] if set.
510 : ///
511 : /// Invokes the [onChanged] callback with the updated value, allowing for
512 : /// custom handling or validation. Updates the UI with the new quantity.
513 1 : Future<void> _decrement() async {
514 6 : if (widget.minValue == null || _currentQuantity > widget.minValue!) {
515 2 : int updatedQuantity = _currentQuantity - 1;
516 : int newQuantity = updatedQuantity;
517 :
518 2 : if (widget.onChanged != null) {
519 3 : var result = widget.onChanged!(updatedQuantity);
520 :
521 1 : if (result is Future<int?>) {
522 : newQuantity = await result ?? updatedQuantity;
523 1 : } else if (result is int?) {
524 : newQuantity = result ?? updatedQuantity;
525 : }
526 : // If result is void (null), use updatedQuantity
527 : }
528 :
529 2 : setState(() {
530 1 : _currentQuantity = newQuantity;
531 : });
532 : }
533 : }
534 :
535 : /// Builds the increment and decrement buttons along with the quantity display.
536 : ///
537 : /// Utilizes [CustomActionButton.longPress] to handle both tap and long-press
538 : /// interactions for continuous incrementing or decrementing.
539 1 : @override
540 : Widget build(BuildContext context) {
541 : final Color effectiveBackgroundColor =
542 4 : widget.backgroundColor ?? Theme.of(context).cardColor;
543 : final EdgeInsetsGeometry valuePadding =
544 2 : widget.valuePadding ?? const EdgeInsets.symmetric(horizontal: 10);
545 : final EdgeInsetsGeometry buttonMargin =
546 2 : widget.buttonMargin ?? EdgeInsets.zero;
547 2 : final double? effectiveWidth = widget.width;
548 : final MainAxisAlignment effectiveAlignment =
549 2 : widget.alignment ?? MainAxisAlignment.spaceBetween;
550 :
551 1 : return SizedBox(
552 : width: effectiveWidth,
553 1 : child: Row(
554 : mainAxisAlignment: effectiveAlignment,
555 1 : children: <Widget>[
556 1 : _buildActionButton(
557 : context,
558 2 : widget.decrementIcon ?? const Icon(Icons.remove),
559 : onPressed:
560 6 : (widget.minValue == null || _currentQuantity > widget.minValue!)
561 1 : ? _decrement
562 : : null,
563 : isEnabled:
564 6 : widget.minValue == null || _currentQuantity > widget.minValue!,
565 1 : onLongPress: _decrement,
566 : effectiveBackgroundColor: effectiveBackgroundColor,
567 : effectiveMargin: buttonMargin,
568 2 : effectiveWidth: widget.buttonWidth,
569 2 : effectiveHeight: widget.buttonHeight,
570 : ),
571 1 : _buildQuantityDisplay(context, valuePadding),
572 1 : _buildActionButton(
573 : context,
574 2 : widget.incrementIcon ?? const Icon(Icons.add),
575 2 : onPressed: (widget.maxQuantity == null ||
576 4 : _currentQuantity < widget.maxQuantity!)
577 1 : ? _increment
578 : : null,
579 2 : isEnabled: widget.maxQuantity == null ||
580 4 : _currentQuantity < widget.maxQuantity!,
581 1 : onLongPress: _increment,
582 : effectiveBackgroundColor: effectiveBackgroundColor,
583 : effectiveMargin: buttonMargin,
584 2 : effectiveWidth: widget.buttonWidth,
585 2 : effectiveHeight: widget.buttonHeight,
586 : ),
587 : ],
588 : ),
589 : );
590 : }
591 :
592 : /// Builds an individual action button (increment or decrement).
593 : ///
594 : /// Configures the button's appearance and behavior based on its enabled state.
595 : ///
596 : /// - [icon]: The icon to display on the button.
597 : /// - [onPressed]: The callback to invoke when the button is tapped.
598 : /// - [isEnabled]: Determines if the button is active.
599 : /// - [onLongPress]: The callback to invoke on a long press.
600 : /// - [effectiveBackgroundColor]: The background color of the button.
601 : /// - [effectiveMargin]: The external margin around the button.
602 : /// - [effectiveWidth]: The width of the button.
603 : /// - [effectiveHeight]: The height of the button.
604 1 : Widget _buildActionButton(
605 : BuildContext context,
606 : Widget icon, {
607 : required VoidCallback? onPressed,
608 : required bool isEnabled,
609 : required VoidCallback onLongPress,
610 : required Color effectiveBackgroundColor,
611 : required EdgeInsetsGeometry effectiveMargin,
612 : double? effectiveWidth,
613 : double? effectiveHeight,
614 : }) {
615 : final Color effectiveIconColor =
616 1 : _iconColor(context, isEnabled) ?? Theme.of(context).iconTheme.color!;
617 :
618 : // **KEY MODIFICATION:**
619 : // When the button is disabled, set onLongPress to null to prevent the timer from running.
620 : final VoidCallback? effectiveOnLongPress = isEnabled ? onLongPress : null;
621 :
622 1 : Widget button = CustomActionButton.longPress(
623 : margin: effectiveMargin,
624 : width: effectiveWidth,
625 : height: effectiveHeight,
626 : backgroundColor: effectiveBackgroundColor,
627 2 : shape: widget.buttonShape ??
628 1 : RoundedRectangleBorder(
629 3 : borderRadius: BorderRadius.circular(widget.borderRadius ?? 10.0),
630 : ),
631 2 : elevation: widget.elevation ?? 0,
632 2 : padding: widget.buttonPadding,
633 : onPressed: onPressed,
634 : onLongPress: effectiveOnLongPress,
635 : // Pass null if disabled
636 1 : child: IconTheme(
637 1 : data: IconThemeData(color: effectiveIconColor),
638 : child: icon,
639 : ),
640 : );
641 :
642 : if (effectiveWidth == null || effectiveHeight == null) {
643 1 : return Expanded(child: button);
644 : } else {
645 0 : return SizedBox(
646 : width: effectiveWidth,
647 : height: effectiveHeight,
648 : child: button,
649 : );
650 : }
651 : }
652 :
653 : /// Builds the quantity display widget.
654 : ///
655 : /// Displays the current quantity in the center between the increment and decrement buttons.
656 : ///
657 : /// - [context]: The build context.
658 : /// - [padding]: The padding around the quantity text.
659 1 : Widget _buildQuantityDisplay(
660 : BuildContext context,
661 : EdgeInsetsGeometry padding,
662 : ) {
663 1 : return Expanded(
664 : flex: 0,
665 1 : child: Container(
666 : alignment: Alignment.center,
667 : padding: padding,
668 1 : child: Text(
669 2 : _currentQuantity.toString(),
670 2 : style: widget.quantityTextStyle ??
671 3 : Theme.of(context).textTheme.titleLarge,
672 : ),
673 : ),
674 : );
675 : }
676 :
677 : /// Determines the icon color based on the button's enabled state.
678 : ///
679 : /// - [context]: The build context.
680 : /// - [isEnabled]: Whether the button is active.
681 : ///
682 : /// Returns a color with reduced opacity if the button is disabled.
683 1 : Color? _iconColor(BuildContext context, bool isEnabled) {
684 : final Color defaultColor =
685 5 : widget.iconColor ?? Theme.of(context).iconTheme.color!;
686 1 : return isEnabled ? defaultColor : defaultColor.withOpacity(0.2);
687 : }
688 : }
|