fluffychat/lib/pangea/common/widgets/shimmer_background.dart
avashilling e83f76b95b
5385 explore tv static shimmer for loading (#5554)
* glitch static loading prototypes

* feat: shimmer pulse for clickable widget

---------

Co-authored-by: ggurdin <ggurdin@gmail.com>
2026-02-09 10:03:05 -05:00

126 lines
2.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
class ShimmerBackground extends StatefulWidget {
final Widget child;
final Color shimmerColor;
final bool enabled;
final BorderRadius? borderRadius;
final Duration delayBetweenPulses;
const ShimmerBackground({
super.key,
required this.child,
this.shimmerColor = AppConfig.goldLight,
this.enabled = true,
this.borderRadius,
this.delayBetweenPulses = Duration.zero,
});
@override
State<ShimmerBackground> createState() => _ShimmerBackgroundState();
}
class _ShimmerBackgroundState extends State<ShimmerBackground>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
Duration pulseDuration = const Duration(milliseconds: 1000);
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: pulseDuration,
vsync: this,
);
_animation = Tween<double>(
begin: 0.0,
end: 0.3,
).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
),
);
if (widget.enabled) {
_startPulsing();
}
}
void _startPulsing() {
if (widget.delayBetweenPulses == Duration.zero) {
_controller.repeat(reverse: true);
} else {
_pulseOnce();
}
}
void _pulseOnce() async {
await _controller.forward();
await _controller.reverse();
if (mounted && widget.enabled) {
await Future.delayed(widget.delayBetweenPulses);
if (mounted && widget.enabled) {
_pulseOnce();
}
}
}
@override
void didUpdateWidget(ShimmerBackground oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.enabled != oldWidget.enabled) {
if (widget.enabled) {
_startPulsing();
} else {
_controller.stop();
_controller.reset();
}
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!widget.enabled) {
return widget.child;
}
final borderRadius =
widget.borderRadius ?? BorderRadius.circular(AppConfig.borderRadius);
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Stack(
children: [
widget.child,
Positioned.fill(
child: IgnorePointer(
child: ClipRRect(
borderRadius: borderRadius,
child: Container(
decoration: BoxDecoration(
color: widget.shimmerColor
.withValues(alpha: _animation.value),
borderRadius: borderRadius,
),
),
),
),
),
],
);
},
);
}
}