2023-09-05 17:48:20 -06:00
|
|
|
import 'package:flutter/foundation.dart';
|
2022-08-29 20:38:28 -06:00
|
|
|
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({
|
2024-02-20 13:56:39 -08:00
|
|
|
super.key,
|
2022-08-29 20:38:28 -06:00
|
|
|
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,
|
2024-02-20 13:56:39 -08:00
|
|
|
});
|
2022-08-29 20:38:28 -06:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
@override
|
|
|
|
State<AppImage> createState() => _AppImageState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _AppImageState extends State<AppImage> {
|
|
|
|
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;
|
|
|
|
_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) {
|
2023-09-06 20:11:04 -06:00
|
|
|
// Disable resizing for web as it is currently single-threaded and causes the UI to lock up when resizing large images
|
2024-02-20 13:56:39 -08:00
|
|
|
if (kIsWeb) {
|
|
|
|
return image; // TODO: Remove this when the web engine is updated to support non-blocking image resizing
|
|
|
|
}
|
2022-08-29 20:38:28 -06:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|