Добавьте StreamBuilders программно для загрузки и прослушивания прогресса изображений в Firebase Storage - PullRequest
0 голосов
/ 28 октября 2019

Я пытаюсь создать экран Flutter, который принимает List<Map> элементов с информацией из другого экрана, который делает / импортирует фотографии. Целью этого экрана является загрузка ВСЕХ изображений, которые он получает, в Firebase Storage. Мне нужно слушать прогресс каждого изображения, и когда ВСЕ изображения будут загружены, запустите некоторую логику, чтобы решить, куда перейти к следующему (автоматически). На этом экране не должно быть никаких действий, инициированных пользователем, он будет запускаться автоматически и автоматически перемещаться.

Я могу сделать это очень успешно для одного изображения, используя один StreamBuilder, но мне нужно одно для каждогоизображение я собираюсь загрузить. Я использую цикл for для генерации каждого StreamBuilder, но он не работает, как я ожидаю. Я думаю, потому что StreamBuilders перестраивает себя, они снова запрашивают индекс цикла и переназначают его, так что все они заканчиваются тем же индексом (последним), и все же, загружается только одно изображение (обычно первое),

Как мне это сделать?

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/widgets.dart';
import 'package:my_surgery/components/shared_scaffold.dart';
import 'package:my_surgery/constants/styles.dart';
import 'package:my_surgery/screens/single_album.dart';
import 'package:path_provider/path_provider.dart';

// FIREBASE INSTANCES
final FirebaseAuth _auth = FirebaseAuth.instance;
final FirebaseStorage _storage =
    FirebaseStorage(storageBucket: 'gs://mysurgery-col19.appspot.com');
final Firestore _db = Firestore.instance;

class PhotoUpload extends StatefulWidget {
  PhotoUpload({
    @required this.photoPaths,
    // GET ALBUM IF ANY
    this.album = 'none',
  });

  //PATHS TO TAKEN/IMPORTED PHOTOS, IN MAP FORM WITH SOME OTHER INFO (path in fireStorage, and its unique ID)
  List photoPaths;
  //IF IMAGES WERE UPLOADED TO AN ALBUM OR IF EMPTY, ONE NEEDS TO BE CREATED
  String album;
  // TO PREVENT LEAVING WITHOUT SAVING PHOTOS - NOT IMPLEMENTED
  bool canLeave = false;

  @override
  _PhotoUploadState createState() => _PhotoUploadState();
}

class _PhotoUploadState extends State<PhotoUpload> {
  List mapPaths;
  List<bool> completed;
  bool started = false;
  double progress = 0;
  StorageUploadTask _uploadTask;
  int uploadCount = 0;
  List<bool> currentUpload = [];
  bool done = false;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    mapPaths = widget.photoPaths;
    uploadCount = mapPaths.length;
    //GENERATE INITIAL UPLOAD TASK TO ALLOW WIDGET TREE TO BUILD WITHOUT ERRORS
    _uploadTask = _storage
        .ref()
        .child(mapPaths[0]['dbPath'])
        .putFile(File(mapPaths[0]['localPath']));
  }

  @override
  Widget build(BuildContext context) {
    if (currentUpload.length < uploadCount) {
      return MyScaffold(
        iconList: <Widget>[],
        body: Center(
          child: Container(
            height: 800.0,
            child: Column(
              children: <Widget>[
                Container(
                  width: 100.0,
                  height: 100.0,
                  child: CircularProgressIndicator(
                    value: null,
                    valueColor: AlwaysStoppedAnimation(kPrimaryColor),
                    strokeWidth: 2.0,
                  ),
                ),
                SizedBox(height: 20.0),
                Text('UPLOADING YOUR PHOTOS'),
                Column(
                  children: streamList(),
                ),
              ],
            ),
          ),
        ),
      );
    } else {
      return MyScaffold(
        body: Center(
          child: Container(
            child: Text('SUCCESS!'),
          ),
        ),
      );
    }
  }

//-----------------------------------------------------------
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
// CLASS: PhotoUpload METHODS
//•••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
//-----------------------------------------------------------

// LOOP TO CREATE THE STREAMBUILDERS FOR ALL IMAGES IN mapPaths
  streamList() {
    List<Widget> streams = [];
    List<StorageUploadTask> _uploadTasks = [];
    int index = 0;
    for (var items in mapPaths) {
      //CREATE LIST OF UPLOAD TASKS
      _uploadTasks.add(_storage
          .ref()
          .child(mapPaths[index]['dbPath'])
          .putFile(File(mapPaths[index]['localPath'])));

// CREATE LIST OF STREAMS
      streams.add(
        StreamBuilder(
            stream: _uploadTasks[index].events,
            builder: (context, snapshot) {
              // GET SNAPSHOT DATA
              var event = snapshot?.data?.snapshot;
              //KEEP TRACK OF PROGRESS
              double progressPercent = event != null
                  ? event.bytesTransferred / event.totalByteCount
                  : 0;

              if (progressPercent == 1) {
                done = true;
                nextUpload();
              }
              //END
              return Container(
                  child: Text(
                      '${(progressPercent * 100).toStringAsFixed(2)} % --- $index'));
            }),
      );
      index++;
    } //end for
    return streams;
  }

// This was an alternate method I was using before, where inside the stream I would listen 
// for progress on an upload and when done, setState of uploadTask to the nex image in the list. 
// Had to ditch it because it always gave me error since one can't trigger a new build from within
// The element that is being built.
  nextUpload() {
    if (currentUpload.length < uploadCount) {
      // THERE ARE STILL IMAGES TO UPLOAD
      // GET THE NEXT IMAGE
      String path = '${mapPaths[currentUpload.length]['dbPath']}';
      File file = File(mapPaths[currentUpload.length]['localPath']);
      setState(() {
        _uploadTask = _storage.ref().child(path).putFile(file);
        currentUpload.add(true);
      });
    } else {
      // ALL IMAGES ARE UP
      Timer(Duration(seconds: 1), () {
        Navigator.pushReplacement(context,
            MaterialPageRoute(builder: (context) {
          return SingleAlbum();
        }));
      });
    }
  }
} // CLASS ENDS```
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...