import 'package:flutter/material.dart'; import '../utils/logger.dart'; import 'dart:ui'; import '../theme/app_colors.dart'; import '../utils/reduce_motion.dart'; import 'themed_text.dart'; class GlassmorphismCard extends StatelessWidget { final Widget child; final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? margin; final double? width; final double? height; final double borderRadius; final double blur; final double opacity; final Color? backgroundColor; final Gradient? gradient; final Border? border; final List? boxShadow; final VoidCallback? onTap; const GlassmorphismCard({ super.key, required this.child, this.padding, this.margin, this.width, this.height, this.borderRadius = 16.0, this.blur = 10.0, this.opacity = 0.1, this.backgroundColor, this.gradient, this.border, this.boxShadow, this.onTap, }); @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; return Container( width: width, height: height, margin: margin, child: Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(borderRadius), child: ClipRRect( borderRadius: BorderRadius.circular(borderRadius), child: BackdropFilter( filter: ImageFilter.blur( sigmaX: ReduceMotion.scale(context, normal: blur, reduced: blur * 0.4), sigmaY: ReduceMotion.scale(context, normal: blur, reduced: blur * 0.4), ), child: Container( padding: padding, decoration: BoxDecoration( color: backgroundColor ?? AppColors.glassCard, gradient: gradient ?? LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: isDarkMode ? AppColors.glassGradientDark : AppColors.glassGradient, ), borderRadius: BorderRadius.circular(borderRadius), border: border ?? Border.all( color: isDarkMode ? AppColors.primaryColor.withValues(alpha: 0.3) : AppColors.glassBorder, width: 1, ), boxShadow: boxShadow ?? [ BoxShadow( color: AppColors .shadowBlack, // color.md: rgba(0,0,0,0.08) blurRadius: ReduceMotion.scale(context, normal: 20, reduced: 10), spreadRadius: -5, offset: const Offset(0, 10), ), ], ), child: GlassmorphicIndicator( child: child, ), ), ), ), ), ), ); } } // 애니메이션이 적용된 글래스모피즘 카드 class AnimatedGlassmorphismCard extends StatefulWidget { final Widget child; final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? margin; final double? width; final double? height; final double borderRadius; final double blur; final double opacity; final Duration animationDuration; final VoidCallback? onTap; const AnimatedGlassmorphismCard({ super.key, required this.child, this.padding, this.margin, this.width, this.height, this.borderRadius = 16.0, this.blur = 10.0, this.opacity = 0.1, this.animationDuration = const Duration(milliseconds: 200), this.onTap, }); @override State createState() => _AnimatedGlassmorphismCardState(); } class _AnimatedGlassmorphismCardState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _scaleAnimation; late Animation _blurAnimation; @override void initState() { super.initState(); _controller = AnimationController( duration: widget.animationDuration, vsync: this, ); _scaleAnimation = Tween( begin: 1.0, end: 0.98, ).animate(CurvedAnimation( parent: _controller, curve: Curves.easeInOut, )); _blurAnimation = Tween( begin: widget.blur, end: widget.blur * 1.5, ).animate(CurvedAnimation( parent: _controller, curve: Curves.easeInOut, )); } @override void dispose() { _controller.dispose(); super.dispose(); } void _handleTapDown(TapDownDetails details) { _controller.forward(); } void _handleTapUp(TapUpDetails details) { _controller.reverse(); } void _handleTapCancel() { _controller.reverse(); } @override Widget build(BuildContext context) { // onTap이 없으면 제스처 처리를 하지 않음 if (widget.onTap == null) { return GlassmorphismCard( padding: widget.padding, margin: widget.margin, width: widget.width, height: widget.height, borderRadius: widget.borderRadius, blur: widget.blur, opacity: widget.opacity, onTap: null, child: widget.child, ); } return GestureDetector( behavior: HitTestBehavior.opaque, onTapDown: _handleTapDown, onTapUp: (details) { _handleTapUp(details); // onTap 콜백 실행 if (widget.onTap != null) { Log.d('[AnimatedGlassmorphismCard] onTap 콜백 실행'); widget.onTap!(); } }, onTapCancel: _handleTapCancel, child: AnimatedBuilder( animation: _controller, builder: (context, child) { final scaleValue = ReduceMotion.scale(context, normal: _scaleAnimation.value, reduced: 1.0); return Transform.scale( scale: scaleValue, child: GlassmorphismCard( padding: widget.padding, margin: widget.margin, width: widget.width, height: widget.height, borderRadius: widget.borderRadius, blur: ReduceMotion.scale(context, normal: _blurAnimation.value, reduced: widget.blur), opacity: widget.opacity, onTap: null, // GlassmorphismCard의 onTap은 사용하지 않음 child: widget.child, ), ); }, ), ); } }