Skip to content

Instantly share code, notes, and snippets.

@romanbsd
Created October 14, 2025 22:13
Show Gist options
  • Select an option

  • Save romanbsd/40c5874d0e46b36821120a1a7df0659f to your computer and use it in GitHub Desktop.

Select an option

Save romanbsd/40c5874d0e46b36821120a1a7df0659f to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:sprung/sprung.dart';
class EnumPageView<T extends Enum> extends StatefulWidget {
const EnumPageView({
super.key,
required this.value,
required this.values,
required this.builder,
this.slideFraction = 1.0,
this.onEnd,
this.ignorePointerWhileAnimating = true,
});
final T value;
final List<T> values;
final Widget Function(BuildContext, T) builder;
final double slideFraction;
final VoidCallback? onEnd;
final bool ignorePointerWhileAnimating;
@override
State<EnumPageView<T>> createState() => _EnumPageViewState<T>();
}
class _EnumPageViewState<T extends Enum> extends State<EnumPageView<T>> {
final currentlyAnimating = <T>{};
@override
void didUpdateWidget(covariant EnumPageView<T> oldWidget) {
if (oldWidget.value != widget.value) {
setState(() {
currentlyAnimating.add(widget.value);
currentlyAnimating.add(oldWidget.value);
});
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
final valueIndex = widget.values.indexOf(widget.value);
return IgnorePointer(
ignoring:
currentlyAnimating.isNotEmpty && widget.ignorePointerWhileAnimating,
child: Stack(
clipBehavior: Clip.none,
children: [
for (final (i, x) in widget.values.indexed)
Builder(
builder: (context) {
final d = (i - valueIndex).toDouble();
var selected = x == widget.value;
var animating = currentlyAnimating.contains(x);
var k = widget.slideFraction;
return AnimatedSlide(
key: ValueKey(x),
offset: Offset((d).clamp(-k, 1).toDouble(), 0),
duration: 750.ms,
curve: Sprung.overDamped,
onEnd: () async {
if (mounted) setState(() => currentlyAnimating.remove(x));
if (currentlyAnimating.isEmpty) widget.onEnd?.call();
},
child: Visibility(
visible: animating || selected,
child: widget.builder(context, x),
),
);
},
),
],
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment