diff --git a/lib/src/blurhash_widget.dart b/lib/src/blurhash_widget.dart index e218ae0..dc2654a 100644 --- a/lib/src/blurhash_widget.dart +++ b/lib/src/blurhash_widget.dart @@ -16,6 +16,7 @@ class BlurHash extends StatefulWidget { this.decodingWidth = _DEFAULT_SIZE, this.decodingHeight = _DEFAULT_SIZE, this.image, + this.imgBgColor, this.onDecoded, this.onDisplayed, this.onReady, @@ -46,6 +47,9 @@ class BlurHash extends StatefulWidget { /// Displayed background color before decoding final Color color; + /// Image background color displayed after the network image is loaded + final Color? imgBgColor; + /// How to fit decoded & downloaded image final BoxFit imageFit; @@ -76,6 +80,7 @@ class BlurHashState extends State { late Future _image; late bool loaded; late bool loading; + late Image _networkImage; @override void initState() { @@ -84,9 +89,19 @@ class BlurHashState extends State { } void _init() { - _decodeImage(); loaded = false; loading = false; + + if (widget.image != null) { + _networkImage = prepareDisplayedImage(widget.image!); + } + _networkImage.image + .resolve(ImageConfiguration()) + .addListener(ImageStreamListener((ImageInfo info, bool syncCall) { + loaded = true; + })); + + _decodeImage(); } @override @@ -116,11 +131,11 @@ class BlurHashState extends State { alignment: Alignment.center, children: [ buildBlurHashBackground(), - if (widget.image != null) prepareDisplayedImage(widget.image!), + if (widget.image != null) _networkImage, ], ); - Widget prepareDisplayedImage(String image) => Image.network( + Image prepareDisplayedImage(String image) => Image.network( image, fit: widget.imageFit, headers: widget.httpHeaders, @@ -131,13 +146,14 @@ class BlurHashState extends State { loading = true; widget.onStarted?.call(); } - - if (loadingProgress == null) { + if (loaded) { // Image is now loaded, trigger the event loaded = true; widget.onReady?.call(); return _DisplayImage( - child: img, + child: (widget.imgBgColor != null) + ? Container(color: widget.imgBgColor, child: img) + : img, duration: widget.duration, curve: widget.curve, onCompleted: () => widget.onDisplayed?.call(), @@ -151,8 +167,9 @@ class BlurHashState extends State { /// Decode the blurhash then display the resulting Image Widget buildBlurHashBackground() => FutureBuilder( future: _image, - builder: (ctx, snap) => - snap.hasData ? Image(image: UiImage(snap.data!), fit: widget.imageFit) : Container(color: widget.color), + builder: (ctx, snap) => snap.hasData + ? Image(image: UiImage(snap.data!), fit: widget.imageFit) + : Container(color: widget.color), ); } @@ -175,7 +192,8 @@ class _DisplayImage extends StatefulWidget { _DisplayImageState createState() => _DisplayImageState(); } -class _DisplayImageState extends State<_DisplayImage> with SingleTickerProviderStateMixin { +class _DisplayImageState extends State<_DisplayImage> + with SingleTickerProviderStateMixin { late Animation opacity; late AnimationController controller; @@ -212,10 +230,12 @@ class UiImage extends ImageProvider { const UiImage(this.image, {this.scale = 1.0}); @override - Future obtainKey(ImageConfiguration configuration) => SynchronousFuture(this); + Future obtainKey(ImageConfiguration configuration) => + SynchronousFuture(this); @override - ImageStreamCompleter load(UiImage key, DecoderCallback decode) => OneFrameImageStreamCompleter(_loadAsync(key)); + ImageStreamCompleter load(UiImage key, DecoderCallback decode) => + OneFrameImageStreamCompleter(_loadAsync(key)); Future _loadAsync(UiImage key) async { assert(key == this); @@ -233,5 +253,6 @@ class UiImage extends ImageProvider { int get hashCode => hashValues(image.hashCode, scale); @override - String toString() => '$runtimeType(${describeIdentity(image)}, scale: $scale)'; + String toString() => + '$runtimeType(${describeIdentity(image)}, scale: $scale)'; }