Reputation: 9139
I've successfully saved an image to my Firebase Storage reference. Now I need to download it. The examples I've seen are uploading and downloading in the same method, using the same StorageUploadTask with this line of code...
final Uri downloadUrl = (await uploadTask.future).downloadUrl;
My question is how can I get the downloadUrl from a separate method that doesn't require an uploadTask.future
since I'm only uploading an image when a FirebaseUser updates their profile image?
Upvotes: 0
Views: 2332
Reputation: 2974
VizGhar provided a nice solution. I've cleaned up the class, added some features and documentation.
It's available on this gist as well.
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
enum ImageDownloadState { Idle, GettingURL, Downloading, Done, Error }
class FirebaseStorageImage extends StatefulWidget {
/// The reference of the image that has to be loaded.
final StorageReference reference;
/// 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;
FirebaseStorageImage(
{Key key,
@required this.reference,
@required this.errorWidget,
this.fallbackWidget,
this.placeholderImage}) {
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(
reference, fallbackWidget, errorWidget, placeholderImage);
}
class _FirebaseStorageImageState extends State<FirebaseStorageImage>
with SingleTickerProviderStateMixin {
_FirebaseStorageImageState(StorageReference reference, this.fallbackWidget,
this.errorWidget, this.placeholderImage) {
var url = reference.getDownloadURL();
this._imageDownloadState = ImageDownloadState.GettingURL;
url.then(this._setImageData).catchError((err) {
this._setError();
});
}
/// 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;
/// Sets the [_networkImage] to the image downloaded from [url].
void _setImageData(dynamic url) {
this._networkImage = Image.network(url);
this
._networkImage
.image
.resolve(ImageConfiguration())
.addListener((_, __) {
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) {
switch (this._imageDownloadState) {
case ImageDownloadState.Idle:
case ImageDownloadState.GettingURL:
case ImageDownloadState.Downloading:
return Image(image: this.placeholderImage) ?? this.fallbackWidget;
case ImageDownloadState.Error:
return this.errorWidget;
case ImageDownloadState.Done:
return this._networkImage;
break;
default:
return this.errorWidget;
}
}
}
Upvotes: 2
Reputation: 3138
StorageReference
now has Future<dynamic> getDownloadURL()
method. Retype result to String and use it with your NetworkImage
widget:
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
class FirestoreImage extends StatefulWidget {
final StorageReference reference;
final Widget fallback;
final ImageProvider placeholder;
FirestoreImage(
{Key key,
@required this.reference,
@required this.fallback,
@required this.placeholder});
@override
FirestoreImageState createState() =>
FirestoreImageState(reference, fallback, placeholder);
}
class FirestoreImageState extends State<FirestoreImage> {
final Widget fallback;
final ImageProvider placeholder;
String _imageUrl;
bool _loaded = false;
_setImageData(dynamic url) {
setState(() {
_loaded = true;
_imageUrl = url;
});
}
_setError() {
setState(() {
_loaded = false;
});
}
FirestoreImageState(
StorageReference reference, this.fallback, this.placeholder) {
reference.getDownloadURL().then(_setImageData).catchError((err) {
_setError();
});
}
@override
Widget build(BuildContext context) => _loaded
? FadeInImage(
image: NetworkImage(_imageUrl),
placeholder: placeholder,
)
: fallback;
}
Old Answer:
I've just started developing in Flutter (Dart) so my answer will definitely not be perfect (maybe even bad) but here is how I did it:
import 'dart:typed_data';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
class FirestoreImage extends StatefulWidget {
final StorageReference _reference;
FirestoreImage(this._reference);
@override
FirestoreImageState createState() => FirestoreImageState(_reference);
}
class FirestoreImageState extends State<FirestoreImage> {
Uint8List _imageData;
_setImageData(Uint8List data) {
setState(() {
_imageData = data;
});
}
FirestoreImageState(StorageReference reference) {
reference
.getData(0x3FFFFFFF)
.then(_setImageData)
.catchError((err) {});
}
@override
Widget build(BuildContext context) =>
_imageData == null ? Container() : Image.memory(_imageData);
}
Now you can display FirestoreImage
by calling new FirestoreImage(imageStorageReference)
. Maybe there is better way by extending Image
Upvotes: 2
Reputation: 277457
Not possible (yet). You need to store that uri yourself inside a database.
But you may and should use getData
instead of using a download url within a firebase app.
Upvotes: 1