Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/src/components/accordion.dart
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ class ShadAccordionItem<T> extends StatefulWidget {
this.duration,
this.focusNode,
this.effects,
this.clipBehavior,
});

/// {@template ShadAccordionItem.value}
Expand Down Expand Up @@ -315,6 +316,13 @@ class ShadAccordionItem<T> extends StatefulWidget {
/// {@endtemplate}
final List<Effect<dynamic>>? effects;

/// {@template ShadAccordionItem.clipBehavior}
/// The clip behavior of the size transition animation.
/// Defaults to [Clip.none] to prevent clipping of focus rings and other
/// content that extends beyond the widget's boundary.
/// {@endtemplate}
final Clip? clipBehavior;

@override
State<ShadAccordionItem<T>> createState() => _ShadAccordionItemState<T>();
}
Expand Down Expand Up @@ -435,6 +443,9 @@ class _ShadAccordionItemState<T> extends State<ShadAccordionItem<T>>
theme.accordionTheme.padding ??
const EdgeInsets.symmetric(vertical: 16);

final effectiveClipBehavior =
widget.clipBehavior ?? theme.accordionTheme.clipBehavior ?? Clip.none;

final effectiveEffects =
widget.effects ??
theme.accordionTheme.effects ??
Expand All @@ -453,6 +464,7 @@ class _ShadAccordionItemState<T> extends State<ShadAccordionItem<T>>
SizeEffect(
curve: effectiveCurve,
duration: effectiveDuration,
clipBehavior: effectiveClipBehavior,
),
];

Expand Down
4 changes: 4 additions & 0 deletions lib/src/theme/components/accordion.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ShadAccordionTheme with _$ShadAccordionTheme {
this.duration,
this.maintainState,
this.effects,
this.clipBehavior,
}) : _canMerge = canMerge;

@ignore
Expand Down Expand Up @@ -53,6 +54,9 @@ class ShadAccordionTheme with _$ShadAccordionTheme {
/// {@macro ShadAccordionItem.effects}
final List<Effect<dynamic>>? effects;

/// {@macro ShadAccordionItem.clipBehavior}
final Clip? clipBehavior;

static ShadAccordionTheme? lerp(
ShadAccordionTheme? a,
ShadAccordionTheme? b,
Expand Down
8 changes: 7 additions & 1 deletion lib/src/theme/components/accordion.g.theme.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 47 additions & 1 deletion lib/src/utils/effects.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,30 @@ class SizeEffect extends Effect<double> {
super.curve,
double? begin,
double? end,
this.clipBehavior = Clip.hardEdge,
}) : super(
begin: begin ?? (end == null ? defaultValue : neutralValue),
end: end ?? neutralValue,
);

/// {@template SizeEffect.clipBehavior}
/// The clip behavior of the size transition.
/// Defaults to [Clip.hardEdge].
/// Set to [Clip.none] to prevent clipping of focus rings and other
/// content that extends beyond the widget's boundary.
/// {@endtemplate}
final Clip clipBehavior;

@override
Widget build(
BuildContext context,
Widget child,
AnimationController controller,
EffectEntry entry,
) {
return SizeTransition(
return _ShadSizeTransition(
sizeFactor: buildAnimation(controller, entry),
clipBehavior: clipBehavior,
child: child,
);
}
Expand All @@ -33,6 +43,42 @@ class SizeEffect extends Effect<double> {
static const defaultValue = 0.0;
}

/// A custom size transition widget that supports [clipBehavior].
///
/// This is similar to Flutter's [SizeTransition] but with configurable
/// clip behavior to allow focus rings and other content to extend beyond
/// the widget's boundary during animation.
class _ShadSizeTransition extends AnimatedWidget {
const _ShadSizeTransition({
required Animation<double> sizeFactor,
this.clipBehavior = Clip.hardEdge,
this.child,
}) : super(listenable: sizeFactor);

final Clip clipBehavior;
final Widget? child;

Animation<double> get sizeFactor => listenable as Animation<double>;

@override
Widget build(BuildContext context) {
final result = Align(
alignment: AlignmentDirectional.topStart,
heightFactor: sizeFactor.value,
child: child,
);

if (clipBehavior == Clip.none) {
return result;
}

return ClipRect(
clipBehavior: clipBehavior,
child: result,
);
}
}

@immutable
class PaddingEffect extends Effect<double> {
const PaddingEffect({
Expand Down