Skip to content

Commit 08a668e

Browse files
PanteCopilot
andauthored
Fix checkbox flickering (#698)
* Fix checkbox flickering Additionally updates checkbox and radio to use new motion system. * Prepare Forui for review * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Pante <Pante@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 6b39fda commit 08a668e

File tree

4 files changed

+87
-33
lines changed

4 files changed

+87
-33
lines changed

forui/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ We've improved the styles' generated documentation. They should be much easier t
1818
* **Breaking** Change `FAutocompleteContentStyle.loadingIndicatorStyle` to `FAutocompleteContentStyle.progressStyle`.
1919

2020

21+
### `FCheckbox`
22+
* Add `FCheckboxMotion`.
23+
24+
* **Breaking** Move animation related fields from `FCheckboxStyle` to `FCheckboxMotion`.
25+
* Fix `FCheckbox` flickering when rapidly hovering.
26+
27+
2128
### `FDateField`
2229
* Add `FDateField.onReset`.
2330

@@ -60,6 +67,12 @@ We've reworked `FProgress` to be more customizable and easier to use.
6067
* **Breaking** Remove `FProgressStyles`.
6168

6269

70+
### `FRadio`
71+
* Add `FRadioMotion`.
72+
73+
* **Breaking** Move animation related fields from `FRadioStyle` to `FRadioMotion`.
74+
75+
6376
### `FSelect` & `FMultiSelect`
6477
* Add `FSelect.onReset`.
6578
* Add `FMultiSelect.onReset`.

forui/bin/commands/style/style.dart

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

forui/lib/src/widgets/checkbox.dart

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ class FCheckbox extends StatelessWidget {
122122
states = {...states, ...formStates};
123123

124124
final iconTheme = style.iconStyle.maybeResolve(states);
125+
final decoration = style.decoration.resolve(states);
125126
return FLabel(
126127
axis: Axis.horizontal,
127128
states: formStates,
@@ -135,15 +136,19 @@ class FCheckbox extends StatelessWidget {
135136
focused: states.contains(WidgetState.focused),
136137
style: style.focusedOutlineStyle,
137138
child: AnimatedSwitcher(
138-
duration: style.animationDuration,
139-
switchInCurve: style.curve,
139+
duration: style.motion.fadeInDuration,
140+
reverseDuration: style.motion.fadeOutDuration,
141+
switchInCurve: style.motion.fadeInCurve,
142+
switchOutCurve: style.motion.fadeOutCurve,
140143
// This transition builder is necessary because of https://github.com/flutter/flutter/issues/121336#issuecomment-1482620874
141144
transitionBuilder: (child, opacity) => FadeTransition(opacity: opacity, child: child),
142145
child: SizedBox.square(
143-
key: SetKey(states),
146+
// We use the derived iconTheme and decoration as keys to prevent the animation from triggering between
147+
// states with the same appearance.
148+
key: ListKey([iconTheme, decoration]),
144149
dimension: style.size,
145150
child: DecoratedBox(
146-
decoration: style.decoration.resolve(states),
151+
decoration: decoration,
147152
child: iconTheme == null
148153
? const SizedBox()
149154
: IconTheme(data: iconTheme, child: const Icon(FIcons.check)),
@@ -234,16 +239,6 @@ class _Checkbox<T> extends StatelessWidget with FSelectGroupItem<T> {
234239

235240
/// A checkboxes style.
236241
class FCheckboxStyle extends FLabelStyle with _$FCheckboxStyleFunctions {
237-
/// The duration of the animation when the checkbox switches between checked and unchecked. Defaults to 100ms.
238-
@override
239-
final Duration animationDuration;
240-
241-
/// The curve of the animation when the checkbox switches between checked and unchecked.
242-
///
243-
/// Defaults to [Curves.linear].
244-
@override
245-
final Curve curve;
246-
247242
/// The tappable style.
248243
@override
249244
final FTappableStyle tappableStyle;
@@ -274,6 +269,10 @@ class FCheckboxStyle extends FLabelStyle with _$FCheckboxStyleFunctions {
274269
@override
275270
final FWidgetStateMap<BoxDecoration> decoration;
276271

272+
/// The motion-related properties.
273+
@override
274+
final FCheckboxMotion motion;
275+
277276
/// Creates a [FCheckboxStyle].
278277
const FCheckboxStyle({
279278
required this.tappableStyle,
@@ -283,9 +282,8 @@ class FCheckboxStyle extends FLabelStyle with _$FCheckboxStyleFunctions {
283282
required super.labelTextStyle,
284283
required super.descriptionTextStyle,
285284
required super.errorTextStyle,
286-
this.animationDuration = const Duration(milliseconds: 100),
287-
this.curve = Curves.linear,
288285
this.size = 16,
286+
this.motion = const FCheckboxMotion(),
289287
super.labelPadding,
290288
super.descriptionPadding,
291289
super.errorPadding,
@@ -344,3 +342,30 @@ class FCheckboxStyle extends FLabelStyle with _$FCheckboxStyleFunctions {
344342
);
345343
}
346344
}
345+
346+
/// The motion-related properties for a [FCheckbox].
347+
class FCheckboxMotion with Diagnosticable, _$FCheckboxMotionFunctions {
348+
/// The duration of the fade in animation. Defaults to 100ms.
349+
@override
350+
final Duration fadeInDuration;
351+
352+
/// The duration of the fade out animation. Defaults to 100ms.
353+
@override
354+
final Duration fadeOutDuration;
355+
356+
/// The curve of the fade in animation. Defaults to [Curves.linear].
357+
@override
358+
final Curve fadeInCurve;
359+
360+
/// The curve of the fade out animation. Defaults to [Curves.linear].
361+
@override
362+
final Curve fadeOutCurve;
363+
364+
/// Creates a [FCheckboxMotion].
365+
const FCheckboxMotion({
366+
this.fadeInDuration = const Duration(milliseconds: 100),
367+
this.fadeOutDuration = const Duration(milliseconds: 100),
368+
this.fadeInCurve = Curves.linear,
369+
this.fadeOutCurve = Curves.linear,
370+
});
371+
}

forui/lib/src/widgets/radio.dart

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,9 @@ class FRadio extends StatelessWidget {
146146
DecoratedBox(
147147
decoration: BoxDecoration(color: style.indicatorColor.resolve(states), shape: BoxShape.circle),
148148
child: AnimatedSize(
149-
duration: style.animationDuration,
150-
curve: style.curve,
149+
duration: style.motion.duration,
150+
reverseDuration: style.motion.reverseDuration,
151+
curve: style.motion.curve,
151152
child: value ? const SizedBox.square(dimension: 9) : const SizedBox.shrink(),
152153
),
153154
),
@@ -238,16 +239,6 @@ class _Radio<T> extends StatelessWidget with FSelectGroupItem<T> {
238239

239240
/// A [FRadio]'s style.
240241
class FRadioStyle extends FLabelStyle with _$FRadioStyleFunctions {
241-
/// The duration of the animation when the radio switches between selected and unselected. Defaults to 100ms.
242-
@override
243-
final Duration animationDuration;
244-
245-
/// The curve of the animation when the radio switches between selected and unselected.
246-
///
247-
/// Defaults to [Curves.easeOutCirc].
248-
@override
249-
final Curve curve;
250-
251242
/// The tappable style.
252243
@override
253244
final FTappableStyle tappableStyle;
@@ -292,8 +283,12 @@ class FRadioStyle extends FLabelStyle with _$FRadioStyleFunctions {
292283
@override
293284
final FWidgetStateMap<Color> indicatorColor;
294285

286+
/// The motion-related properties.
287+
@override
288+
final FRadioMotion motion;
289+
295290
/// Creates a [FRadioStyle].
296-
FRadioStyle({
291+
const FRadioStyle({
297292
required this.tappableStyle,
298293
required this.focusedOutlineStyle,
299294
required this.borderColor,
@@ -302,12 +297,11 @@ class FRadioStyle extends FLabelStyle with _$FRadioStyleFunctions {
302297
required super.labelTextStyle,
303298
required super.descriptionTextStyle,
304299
required super.errorTextStyle,
300+
this.motion = const FRadioMotion(),
305301
super.labelPadding,
306302
super.descriptionPadding,
307303
super.errorPadding,
308304
super.childPadding,
309-
this.animationDuration = const Duration(milliseconds: 100),
310-
this.curve = Curves.easeOutCirc,
311305
});
312306

313307
/// Creates a [FRadioStyle] that inherits its properties.
@@ -337,3 +331,25 @@ class FRadioStyle extends FLabelStyle with _$FRadioStyleFunctions {
337331
);
338332
}
339333
}
334+
335+
/// The motion-related properties for a [FRadio].
336+
class FRadioMotion with Diagnosticable, _$FRadioMotionFunctions {
337+
/// The duration of the animation when selected. Defaults to 100ms.
338+
@override
339+
final Duration duration;
340+
341+
/// The duration of the reverse animation when unselected. Defaults to 100ms.
342+
@override
343+
final Duration reverseDuration;
344+
345+
/// The curve of the animation. Defaults to [Curves.easeOutCirc].
346+
@override
347+
final Curve curve;
348+
349+
/// Creates a [FRadioMotion].
350+
const FRadioMotion({
351+
this.duration = const Duration(milliseconds: 100),
352+
this.reverseDuration = const Duration(milliseconds: 100),
353+
this.curve = Curves.easeOutCirc,
354+
});
355+
}

0 commit comments

Comments
 (0)