Versione OK Pour l'onglet événements.

This commit is contained in:
DahoudG
2025-09-15 20:15:34 +00:00
parent 8a619ee1bf
commit 12d514d866
73 changed files with 11508 additions and 674 deletions

View File

@@ -0,0 +1,368 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// Widget avec micro-interactions pour les boutons
class InteractiveButton extends StatefulWidget {
final Widget child;
final VoidCallback? onPressed;
final Color? backgroundColor;
final Color? foregroundColor;
final EdgeInsetsGeometry? padding;
final BorderRadius? borderRadius;
final bool enableHapticFeedback;
final bool enableSoundFeedback;
final Duration animationDuration;
const InteractiveButton({
super.key,
required this.child,
this.onPressed,
this.backgroundColor,
this.foregroundColor,
this.padding,
this.borderRadius,
this.enableHapticFeedback = true,
this.enableSoundFeedback = false,
this.animationDuration = const Duration(milliseconds: 150),
});
@override
State<InteractiveButton> createState() => _InteractiveButtonState();
}
class _InteractiveButtonState extends State<InteractiveButton>
with TickerProviderStateMixin {
late AnimationController _scaleController;
late AnimationController _rippleController;
late Animation<double> _scaleAnimation;
late Animation<double> _rippleAnimation;
bool _isPressed = false;
@override
void initState() {
super.initState();
_scaleController = AnimationController(
duration: widget.animationDuration,
vsync: this,
);
_rippleController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 0.95,
).animate(CurvedAnimation(
parent: _scaleController,
curve: Curves.easeInOut,
));
_rippleAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(CurvedAnimation(
parent: _rippleController,
curve: Curves.easeOut,
));
}
@override
void dispose() {
_scaleController.dispose();
_rippleController.dispose();
super.dispose();
}
void _handleTapDown(TapDownDetails details) {
if (widget.onPressed != null) {
setState(() => _isPressed = true);
_scaleController.forward();
_rippleController.forward();
if (widget.enableHapticFeedback) {
HapticFeedback.lightImpact();
}
}
}
void _handleTapUp(TapUpDetails details) {
_handleTapEnd();
}
void _handleTapCancel() {
_handleTapEnd();
}
void _handleTapEnd() {
if (_isPressed) {
setState(() => _isPressed = false);
_scaleController.reverse();
Future.delayed(const Duration(milliseconds: 100), () {
_rippleController.reverse();
});
}
}
void _handleTap() {
if (widget.onPressed != null) {
if (widget.enableSoundFeedback) {
SystemSound.play(SystemSoundType.click);
}
widget.onPressed!();
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: _handleTapDown,
onTapUp: _handleTapUp,
onTapCancel: _handleTapCancel,
onTap: _handleTap,
child: AnimatedBuilder(
animation: Listenable.merge([_scaleAnimation, _rippleAnimation]),
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: Container(
padding: widget.padding ?? const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
decoration: BoxDecoration(
color: widget.backgroundColor ?? Theme.of(context).primaryColor,
borderRadius: widget.borderRadius ?? BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: (widget.backgroundColor ?? Theme.of(context).primaryColor)
.withOpacity(0.3),
blurRadius: _isPressed ? 8 : 12,
offset: Offset(0, _isPressed ? 2 : 4),
spreadRadius: _isPressed ? 0 : 2,
),
],
),
child: Stack(
alignment: Alignment.center,
children: [
// Effet de ripple
if (_rippleAnimation.value > 0)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
borderRadius: widget.borderRadius ?? BorderRadius.circular(8),
color: Colors.white.withOpacity(
0.2 * _rippleAnimation.value,
),
),
),
),
// Contenu du bouton
DefaultTextStyle(
style: TextStyle(
color: widget.foregroundColor ?? Colors.white,
fontWeight: FontWeight.w600,
),
child: widget.child,
),
],
),
),
);
},
),
);
}
}
/// Widget avec effet de parallax pour les cartes
class ParallaxCard extends StatefulWidget {
final Widget child;
final double parallaxOffset;
final Duration animationDuration;
const ParallaxCard({
super.key,
required this.child,
this.parallaxOffset = 20.0,
this.animationDuration = const Duration(milliseconds: 200),
});
@override
State<ParallaxCard> createState() => _ParallaxCardState();
}
class _ParallaxCardState extends State<ParallaxCard>
with TickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _offsetAnimation;
late Animation<double> _elevationAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.animationDuration,
vsync: this,
);
_offsetAnimation = Tween<Offset>(
begin: Offset.zero,
end: Offset(0, -widget.parallaxOffset),
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
));
_elevationAnimation = Tween<double>(
begin: 4.0,
end: 12.0,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeOut,
));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) => _controller.forward(),
onExit: (_) => _controller.reverse(),
child: GestureDetector(
onTapDown: (_) => _controller.forward(),
onTapUp: (_) => _controller.reverse(),
onTapCancel: () => _controller.reverse(),
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.translate(
offset: _offsetAnimation.value,
child: Card(
elevation: _elevationAnimation.value,
child: widget.child,
),
);
},
),
),
);
}
}
/// Widget avec effet de morphing pour les icônes
class MorphingIcon extends StatefulWidget {
final IconData icon;
final IconData? alternateIcon;
final double size;
final Color? color;
final Duration animationDuration;
final VoidCallback? onPressed;
const MorphingIcon({
super.key,
required this.icon,
this.alternateIcon,
this.size = 24.0,
this.color,
this.animationDuration = const Duration(milliseconds: 300),
this.onPressed,
});
@override
State<MorphingIcon> createState() => _MorphingIconState();
}
class _MorphingIconState extends State<MorphingIcon>
with TickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scaleAnimation;
late Animation<double> _rotationAnimation;
bool _isAlternate = false;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.animationDuration,
vsync: this,
);
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 0.0,
).animate(CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.5, curve: Curves.easeIn),
));
_rotationAnimation = Tween<double>(
begin: 0.0,
end: 0.5,
).animate(CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
));
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
setState(() {
_isAlternate = !_isAlternate;
});
_controller.reverse();
}
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _handleTap() {
if (widget.alternateIcon != null) {
_controller.forward();
}
widget.onPressed?.call();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value == 0.0 ? 1.0 : _scaleAnimation.value,
child: Transform.rotate(
angle: _rotationAnimation.value * 3.14159,
child: Icon(
_isAlternate && widget.alternateIcon != null
? widget.alternateIcon!
: widget.icon,
size: widget.size,
color: widget.color,
),
),
);
},
),
);
}
}