import 'package:flutter/foundation.dart'; import 'package:image_fade/image_fade.dart'; import 'package:wonders/common_libs.dart'; import 'package:wonders/logic/common/retry_image.dart'; import 'package:wonders/ui/common/controls/app_loading_indicator.dart'; class AppImage extends StatefulWidget { const AppImage({ Key? key, required this.image, this.fit = BoxFit.scaleDown, this.alignment = Alignment.center, this.duration, this.syncDuration, this.distractor = false, this.progress = false, this.color, this.scale, }) : super(key: key); final ImageProvider? image; final BoxFit fit; final Alignment alignment; final Duration? duration; final Duration? syncDuration; final bool distractor; final bool progress; final Color? color; final double? scale; static String maybeAddImgProxy(String url) { String proxyUrl = 'proxy.wonderous.app:8081/'; if (!url.contains(proxyUrl) && (url.contains('images.metmuseum.org') || url.contains('img.youtube.com'))) { url = 'https://$proxyUrl$url'; } return url; } @override State createState() => _AppImageState(); } class _AppImageState extends State { ImageProvider? _displayImage; ImageProvider? _sourceImage; @override void didChangeDependencies() { _updateImage(); super.didChangeDependencies(); } @override void didUpdateWidget(AppImage oldWidget) { _updateImage(); super.didUpdateWidget(oldWidget); } void _updateImage() { if (widget.image == _sourceImage) return; _sourceImage = widget.image; /// Apply proxy to MET api images if(kIsWeb && _sourceImage is NetworkImage){ final url = (_sourceImage as NetworkImage).url; _sourceImage = NetworkImage(AppImage.maybeAddImgProxy(url)); } _displayImage = _capImageSize(_addRetry(_sourceImage)); } @override Widget build(BuildContext context) { return ImageFade( image: _displayImage, fit: widget.fit, alignment: widget.alignment, duration: widget.duration ?? $styles.times.fast, syncDuration: widget.syncDuration ?? 0.ms, loadingBuilder: (_, value, ___) { if (!widget.distractor && !widget.progress) return SizedBox(); return Center(child: AppLoadingIndicator(value: widget.progress ? value : null, color: widget.color)); }, errorBuilder: (_, __) => Container( padding: EdgeInsets.all($styles.insets.xs), alignment: Alignment.center, child: LayoutBuilder(builder: (_, constraints) { double size = min(constraints.biggest.width, constraints.biggest.height); if (size < 16) return SizedBox(); return Icon( Icons.image_not_supported_outlined, color: $styles.colors.white.withOpacity(0.1), size: min(size, $styles.insets.lg), ); }), ), ); } ImageProvider? _addRetry(ImageProvider? image) { return image == null ? image : RetryImage(image); } ImageProvider? _capImageSize(ImageProvider? image) { // Disable resizing for web as it is currently single-threaded and causes the UI to lock up when resizing large images if(kIsWeb) return image; // TODO: Remove this when the web engine is updated to support non-blocking image resizing if (image == null || widget.scale == null) return image; final MediaQueryData mq = MediaQuery.of(context); final Size screenSize = mq.size * mq.devicePixelRatio * widget.scale!; return ResizeImage(image, width: screenSize.width.round()); } }