Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Gperez88/f8f360abb50c58377aa4cb5927a72ddf to your computer and use it in GitHub Desktop.
Save Gperez88/f8f360abb50c58377aa4cb5927a72ddf to your computer and use it in GitHub Desktop.
A Flutter widget that handles and shows an image downloaded from a Firebase Storage document.
import 'package:cached_network_image/cached_network_image.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import '../../../injection_container.dart' as di;
enum ImageDownloadState {
Idle,
GettingReference,
GettingURL,
Downloading,
Done,
Error
}
class FirebaseStorageImage extends StatefulWidget {
/// The reference url of the image that has to be loaded.
final String referenceUrl;
/// The widget that will be displayed when loading if no [placeholderImage] is set.
final Widget fallbackWidget;
/// The widget that will be displayed if an error occurs.
final Widget errorWidget;
/// The image that will be displayed when loading if no [fallbackWidget] is set.
final ImageProvider placeholderImage;
final BoxFit fit;
final double width;
final double height;
FirebaseStorageImage({
Key key,
@required this.referenceUrl,
@required this.errorWidget,
this.fallbackWidget,
this.placeholderImage,
this.fit = BoxFit.cover,
this.width,
this.height,
}) {
assert(
(this.fallbackWidget == null && this.placeholderImage != null) ||
(this.fallbackWidget != null && this.placeholderImage == null),
"Either [fallbackWidget] or [placeholderImage] must not be null.");
}
@override
_FirebaseStorageImageState createState() => _FirebaseStorageImageState(
referenceUrl,
fallbackWidget,
errorWidget,
placeholderImage,
fit,
width,
height,
);
}
class _FirebaseStorageImageState extends State<FirebaseStorageImage>
with SingleTickerProviderStateMixin {
_FirebaseStorageImageState(
String referenceUrl,
this.fallbackWidget,
this.errorWidget,
this.placeholderImage,
this.fit,
this.width,
this.height,
) {
final reference = _storage.getReferenceFromUrl(referenceUrl);
this._imageDownloadState = ImageDownloadState.GettingReference;
reference.then(this._getReferenceByUrl).catchError((err) {
this._setError();
});
}
final BoxFit fit;
final double width;
final double height;
// Storage firebase.
final _storage = di.sl<FirebaseStorage>();
/// The widget that will be displayed when loading if no [placeholderImage] is set.
final Widget fallbackWidget;
/// The widget that will be displayed if an error occurs.
final Widget errorWidget;
/// The image that will be displayed when loading if no [fallbackWidget] is set.
final ImageProvider placeholderImage;
/// The image that will be/has been downloaded from the [reference].
Image _networkImage;
/// The state of the [_networkImage].
ImageDownloadState _imageDownloadState = ImageDownloadState.Idle;
/// Gets reference from reference url.
void _getReferenceByUrl(StorageReference reference) {
final url = reference.getDownloadURL();
this._imageDownloadState = ImageDownloadState.GettingURL;
url.then(this._setImageData).catchError((err) {
this._setError();
});
}
/// Sets the [_networkImage] to the image downloaded from [url].
void _setImageData(dynamic url) {
this._networkImage = Image.network(url, fit: fit);
this._networkImage.image.resolve(ImageConfiguration()).addListener(
ImageStreamListener((ImageInfo image, bool synchronousCall) {
if (mounted)
setState(() => this._imageDownloadState = ImageDownloadState.Done);
}));
if (this._imageDownloadState != ImageDownloadState.Done)
this._imageDownloadState = ImageDownloadState.Downloading;
}
/// Sets the [_imageDownloadState] to [ImageDownloadState.Error] and redraws the UI.
void _setError() {
if (mounted)
setState(() => this._imageDownloadState = ImageDownloadState.Error);
}
@override
Widget build(BuildContext context) {
Widget widget = SizedBox.shrink();
switch (this._imageDownloadState) {
case ImageDownloadState.Idle:
case ImageDownloadState.GettingReference:
case ImageDownloadState.GettingURL:
case ImageDownloadState.Downloading:
widget = this.placeholderImage != null
? Image(image: this.placeholderImage)
: Center(child: this.fallbackWidget);
break;
case ImageDownloadState.Error:
widget = this.errorWidget;
break;
case ImageDownloadState.Done:
widget = this._networkImage;
break;
default:
widget = this.errorWidget;
}
return SizedBox(
height: height,
width: width,
child: widget,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment