import 'dart:async'; import 'dart:ui'; import 'package:flutter/material.dart'; class FikoytOvalResim extends StatefulWidget { final String resimUrl; final FikoYTOvalResimTheme? tema; final VoidCallback? onPressed; const FikoytOvalResim({super.key, required this.resimUrl, this.tema, this.onPressed}); @override State createState() => _FikoytOvalResimState(); } class _FikoytOvalResimState extends State { late String _currentImageUrl; bool _isImageLoaded = true; @override void initState() { super.initState(); _currentImageUrl = widget.resimUrl; _isImageLoaded = true; } @override void didUpdateWidget(covariant FikoytOvalResim oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.resimUrl != widget.resimUrl) { _loadNewImage(widget.resimUrl); } } void _loadNewImage(String newUrl) { setState(() { _isImageLoaded = false; }); // Bir süre sonra yeni URL'yi kullan Future.delayed(const Duration(milliseconds: 300), () { if (mounted) { setState(() { _currentImageUrl = newUrl; }); } }); } @override Widget build(BuildContext context) { final double radius = widget.tema?.radius ?? Theme.of(context).extension()?.radius ?? 54; final double spacing = widget.tema?.spacing ?? Theme.of(context).extension()?.spacing ?? 10.0; return Center( child: Stack( alignment: Alignment.center, children: [ // Border Circle Container( width: (radius + spacing) * 2, height: (radius + spacing) * 2, decoration: BoxDecoration( shape: BoxShape.circle, color: widget.tema?.borderColor ?? Theme.of(context).extension()?.borderColor ?? Colors.green.shade700, ), ), // Image Circle with Fade Transition ClipOval( child: AnimatedContainer( duration: const Duration(milliseconds: 500), width: radius * 2, height: radius * 2, decoration: BoxDecoration( shape: BoxShape.circle, color: widget.tema?.backgroundColor ?? Theme.of(context).extension()?.backgroundColor ?? Colors.grey.shade200, ), child: GestureDetector( onTap: widget.onPressed, child: Stack( alignment: Alignment.center, children: [ AnimatedOpacity( duration: const Duration(milliseconds: 500), opacity: _isImageLoaded ? 1.0 : 0.0, child: _currentImageUrl.startsWith("https") ? Image.network( _currentImageUrl, fit: BoxFit.cover, width: radius * 2, height: radius * 2, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) { Future.delayed(Duration(seconds: 1), () { if (mounted) { setState(() { _isImageLoaded = true; }); } }); return child; } return const SizedBox(); }, ): Image.asset( _currentImageUrl, fit: BoxFit.cover, width: radius * 2, height: radius * 2, ), ), if (!_isImageLoaded) const Center( child: CircularProgressIndicator(), ), ], ), ), ), ), ], ), ); } } class FikoYTOvalResimTheme extends ThemeExtension { FikoYTOvalResimTheme({ this.backgroundColor, this.borderColor, this.shadowColor, this.radius, this.spacing, this.elevation, }); Color? backgroundColor; Color? borderColor; Color? shadowColor; double? radius; double? spacing; double? elevation; @override FikoYTOvalResimTheme copyWith({ Color? backgroundColor, Color? borderColor, Color? shadowColor, double? radius, double? spacing, double? elevation, }) { return FikoYTOvalResimTheme( backgroundColor: backgroundColor ?? this.backgroundColor, radius: radius ?? this.radius, spacing: spacing ?? this.spacing, elevation: elevation ?? this.elevation, borderColor: borderColor ?? this.borderColor, shadowColor: shadowColor ?? this.shadowColor, ); } @override FikoYTOvalResimTheme lerp(ThemeExtension other, double t) { if (other is! FikoYTOvalResimTheme) { return this; } return FikoYTOvalResimTheme( backgroundColor: Color.lerp(backgroundColor, other.backgroundColor, t), radius: lerpDouble(radius, other.radius, t), spacing: lerpDouble(spacing, other.spacing, t), elevation: lerpDouble(elevation, other.elevation, t), borderColor: Color.lerp(borderColor, other.borderColor, t), shadowColor: Color.lerp(shadowColor, other.shadowColor, t), ); } }