Error retrieving URL from Firebase, Flutter

Asked

Viewed 365 times

1

Good night!!! I am developing an app in which it is possible to post photos. The user opens the camera (or picks it up from the gallery with Imagepicker) and clicks a button on the Appbar. When pressed, the app goes back to the home screen and uploads the image to Firestorage. It is possible to put various information in this photo, such as the name of the responsible person etc, which will be stored in the Firestore in a collection "photos". However, when I upload the photo, all the information appears in the Firestore, except the image URL. When I try to upload a new image, the app process everything right, but the URL that appears in the collection, is from the previous image.

Follows the code:

Future _uploadImagem(Modelo modelo) async {

    String nomeImagem = DateTime.now().millisecondsSinceEpoch.toString();
    FirebaseStorage storage = FirebaseStorage.instance;
    StorageReference pastaRaiz = storage.ref();
    StorageReference arquivo = pastaRaiz
      .child("fotos")
      .child(nomeImagem + ".jpg");

    // Recuperar url da imagem
    StorageUploadTask task = arquivo.putFile(_imagem);
    task.onComplete.then((StorageTaskSnapshot snap){
      _recuperarUrl(snap);
    });

    // Coloca os dados da imagem no Cloud FireStore
    Firestore db = Firestore.instance;
    db.collection("fotos")
    .document(nomeImagem + ".jpg")
    .setData(modelo.toMap());
  }

  // Recuperar url
  Future _recuperarUrl(StorageTaskSnapshot snapshot) async {
    String url = await snapshot.ref.getDownloadURL();
    setState(() {
      urlImagemRecuperada = url;
    });
  }

  // Radio
  String _escolhaZona;
  String _escolhaArea;
  String _escolhaEdificacao;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomPadding: false,
      appBar: AppBar(
        title: Text("Upload da Foto"),
        backgroundColor: Colors.deepOrange,
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.beenhere),
            onPressed: (){
              if(_imagem == null){
                Navigator.pop(context);
              }else{

                // Instancia as informações da imagem

                Modelo modelo = new Modelo();
                String nomeLog = _nomeLog.text;
                String numero = _numero.text;
                String complemento = _complemento.text;
                String municipio = _municipio.text;
                String estado = _estado.text;
                String metrosQuadrados = _metrosQuadrados.text;
                String nomeResponsavel = _nomeResponsavel.text;


                modelo.nomeLog = nomeLog;
                modelo.numero = numero;
                modelo.complemento = complemento;
                modelo.municipio = municipio;
                modelo.estado = estado;
                modelo.metrosQuadrados = metrosQuadrados;
                modelo.responsalvel = nomeResponsavel;
                modelo.edificacao = _escolhaEdificacao;
                modelo.area = _escolhaArea;
                modelo.zona = _escolhaZona;
                modelo.url = urlImagemRecuperada;

                _uploadImagem(modelo);
                Navigator.pop(context);
              }
            },
          )
        ],
      ),

inserir a descrição da imagem aqui

How can I get the url to be retrieved and placed in the collection along with other information ?

My Model class was as follows:

 class Modelo {
  String _url;
  String _nomeLog;
  String _numero;
  String _complemento;
  String _municipio;
  String _estado;
  String _zona;
  String _area;
  String _edificacao;
  String _metrosQuadrados;
  String _responsavel;

  Modelo();
  Map<String, dynamic> toMap() {
    Map<String, dynamic> map = {
      "urlImagem":this.url,
      "nomeLog": this.nomeLog,
      "numero":this.numero,
      "complemento":this.complemento,
      "municipio":this.municipio,
      "estado":this.estado,
      "zona":this.zona,
      "area":this.area,
      "edificacao":this.edificacao,
      "metrosQuadrados":this.metrosQuadrados,
      "responsavel":this.responsavel,
    };
    return map;
  }

  String get url => _url;
  set url(String value){
    _url = value;
  }

  String get nomeLog => _nomeLog;
  set nomeLog(String value){
    _nomeLog=value;
  }

  String get numero => _numero;
  set numero(String value){
    _numero = value;
  }

  String get complemento => _complemento;
  set complemento(String value){
    _complemento = value;
  }

  String get municipio => _municipio;
  set municipio(String value){
    _municipio = value;
  }

  String get estado => _estado;
  set estado(String value){
    _estado = value;
  }

  String get zona => _zona;
  set zona(String value){
    _zona = value;
  }

  String get area => _area;
  set area(String value){
    _area = value;
  }

  String get edificacao => _edificacao;
  set edificacao(String value){
    _edificacao = value;
  }

  String get metrosQuadrados => _metrosQuadrados;
  set metrosQuadrados(String value){
    _metrosQuadrados = value;
  }

  String get responsavel => _responsavel;
  set responsalvel(String value){
    _responsavel = value;
  }
}
  • How is the method toMap() model-class?

  • Good day!! I just edited the post putting how is my model class

  • All right, I’m gonna take a test run here

1 answer

1


Your problem is the method _uploadImagem you are using the .then() of an async function, thus its setData will be executed before the return of your task.onComplete.

In case you need to use the await who will await the return of his task and then do what you need.

Future _uploadImagem(Modelo modelo) async {

    String nomeImagem = DateTime.now().millisecondsSinceEpoch.toString();
    FirebaseStorage storage = FirebaseStorage.instance;
    StorageReference pastaRaiz = storage.ref();
    StorageReference arquivo = pastaRaiz
      .child("fotos")
      .child(nomeImagem + ".jpg");

    // Recuperar url da imagem
    StorageUploadTask task = arquivo.putFile(_imagem);
    final snapshot = await task.onComplete;     
    model.url = await snapshot.ref.getDownloadURL();

    // Coloca os dados da imagem no Cloud FireStore
    Firestore db = Firestore.instance;
    await db.collection("fotos")
    .document(nomeImagem + ".jpg")
    .setData(modelo.toMap());
  }

Note. 1: I do not know for sure what will be returned in final snapshot = await task.onComplete so I used it as FINAL, you adjust it accordingly with what you need.

Note. 2: You are filling the variable urlImagemRecuperada after have filled your entire model... so you need to also fill in the your model with the recovered url before saving the data...

You also need to modify your button click:

IconButton(
  icon: Icon(Icons.beenhere),
  onPressed: () async {
    if(_imagem == null){
    Navigator.pop(context);
    }else{

    // Instancia as informações da imagem

    Modelo modelo = new Modelo();
    String nomeLog = _nomeLog.text;
    String numero = _numero.text;
    String complemento = _complemento.text;
    String municipio = _municipio.text;
    String estado = _estado.text;
    String metrosQuadrados = _metrosQuadrados.text;
    String nomeResponsavel = _nomeResponsavel.text;


    modelo.nomeLog = nomeLog;
    modelo.numero = numero;
    modelo.complemento = complemento;
    modelo.municipio = municipio;
    modelo.estado = estado;
    modelo.metrosQuadrados = metrosQuadrados;
    modelo.responsalvel = nomeResponsavel;
    modelo.edificacao = _escolhaEdificacao;
    modelo.area = _escolhaArea;
    modelo.zona = _escolhaZona;

    await _uploadImagem(modelo);

    Navigator.pop(context);
    }
  },
)

Another thing, because you are creating GET/SET for all your model properties?

In Flutter/Dart you can do as follows:

class Modelo {

  Modelo({this.url, this.nomeLog, this.numero, this.complemento, this.municipio, this.estado, this.zona, this.area, this.edificacao, this.metrosQuadrados, this.responsavel});

  String url;
  String nomeLog;
  String numero;
  String complemento;
  String municipio;
  String estado;
  String zona;
  String area;
  String edificacao;
  String metrosQuadrados;
  String responsavel;

  void meuMetodo(){  
    print(url);
  }

}
  • But in this case, that snapshot that receives the task.onComplete will be passed in the recoverUrl ?

  • With _recuperarUrl out of then, the document is not created in the firestore :(

  • Unhandled Exception: setState() called after Dispose(): _Uploadfotostate#19dbc(Lifecycle state: defunct, not Mounted)

  • I made some modifications to my answer, see if it resolves. Case of any problem related to await _uploadImagem(modelo);, at the click of the button, try to remove the await.

  • You were right!!!! Thank you very much, you are the guy!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.