Mikael Wills
Mikael Wills

Reputation: 195

Flutter Firebase storage image retrieval stream

Is it possible to have the .getDownloadURL() string in a stream so that it can be accessed by multiple places in a widget tree and be constantly up to date whenever its changed?

at the moment i'm attempting to setup a streambuilder:

  return Scaffold(
      body: ListView.builder(
        itemCount: products.length,
        itemBuilder: (context, index) {
          return StreamBuilder<String>(
              stream: ImageDatabaseService().getImageLocation(products[index]),
              builder: (context, snapshot) {
                return ProductTile(product: products[index]);
              });
        },

The stream is coming from the ImageDataseService class:

class ImageDatabaseService {
  String _imageLocation;
  StorageReference ref;

  void init() {}

  Stream<String> getImageLocation(Product product) {
    ref = FirebaseStorage.instance
        .ref()
        .child('images/' + product.kaizenID + '.png');
    return getURL();
  }

  Future<String> getURL() async {
    await ref.getDownloadURL().then((location) {
      return location;
    });
  }
}

The issue is that ImageDatabaseService returns a Future String when it needs to return a Stream String but then at the widget end i just need a String

Ive been scouring these forums and the net but haven't come across anything useful.

Upvotes: 0

Views: 305

Answers (2)

Mikael Wills
Mikael Wills

Reputation: 195

What was interesting though was that the provider doesn't count a showModalSheet in a void method in the widget build method as a child. I had to put another stream provider in it for the EditProduct tree to pick it up

Widget build(BuildContext context) {
    var _imageLocation = Provider.of<String>(context);

    void _showEditProductPanel() {
      showModalBottomSheet(
          isScrollControlled: true,
          context: context,
          builder: (context) {
            return Container(
                padding: EdgeInsets.symmetric(vertical: 50.0, horizontal: 60.0),
                child: StreamProvider<String>.value(
                  initialData: '',
                  value: ImageDatabaseService().imageLocation(widget.product),
                  child: EditProductForm(product: widget.product),
                ));
          });
    }

    return Padding(
      padding: EdgeInsets.only(top: 8.0),
      child: Card(
        margin: EdgeInsets.fromLTRB(20, 6, 20, 0),
        child: ListTile(
          leading: Image.network(_imageLocation),
          title: Text(widget.product.name ?? ''),
          subtitle: Text('KaizenID: ${widget.product.kaizenID}'),
          trailing: FlatButton(
            child: Icon(Icons.edit),
            onPressed: () => _showEditProductPanel(),
          ),
        ),
      ),
    );
  }

Upvotes: 0

Tonny Bawembye
Tonny Bawembye

Reputation: 576

Change your ImageDatabaseService class to;

class ImageDatabaseService {
  StorageReference ref;
  void init() {}
  Stream<String> get imageLocation(Product product) {
    ref = FirebaseStorage.instance
        .ref()
        .child('images/' + product.kaizenID + '.png');
    return ref.getDownloadURL()
        .asStream().map((downloadUrl) 
    => downloadUrl;
  }
}

It will return a String in snapshot.data inside your StreamBuilder.

Dont forget to change your stream to;

stream: ImageDatabaseService().imageLocation(products[index]),

Dont forget to add a condition to check for the connection state in your builder.

if(snapshot.ConnectionState == ConnectionState.active){
  return ProductTile(product: products[index]);
} else{
  return Center(child: CircularProgressIndicator(),);
}

Upvotes: 1

Related Questions