You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中Firebase Storage上传状态获取的最新方法求助

Firebase Storage 上传状态获取问题解决(Flutter)

我尝试获取Firebase Storage文件的上传状态,但原代码中使用task.isComplete、task.isInProgress等属性时出现编译错误,提示属性不存在。原因是Firebase Storage的Flutter SDK更新后,上传任务的API发生了变更,以下是适配最新版本的解决方案。

原代码

Stateful类中的上传逻辑

uploadToFirebase() {
    String fileName = _path!.split('/').last;
    String filePath = _path!;
    upload(fileName, filePath);
  }

  upload(fileName, filePath) {
    FileType _pickType = FileType.custom;
    _extension = fileName.toString().split('.').last;
    Reference storageRef = FirebaseStorage.instance.ref().child(fileName);
    final UploadTask uploadTask = storageRef.putFile(
      File(filePath),
      SettableMetadata(
        contentType: '$_pickType/$_extension',
      ),
    );
    setState(() {
      _tasks.add(uploadTask);
    });
  }

@override
  Widget build(BuildContext context) {
    final List<Widget> children = <Widget>[];
    _tasks.forEach((UploadTask task) {
      final Widget tile = UploadTaskListTile(
        task: task,
        onDismissed: () => setState(() => _tasks.remove(task)),
        onDownload: () => downloadFile(task.snapshot.ref),
      );
      children.add(tile);
    });
    return Container();
  }

UploadTaskListTile组件

class UploadTaskListTile extends StatelessWidget {
  const UploadTaskListTile(
      {Key? key, this.task, this.onDismissed, this.onDownload})
      : super(key: key);

  final UploadTask? task;
  final VoidCallback? onDismissed;
  final VoidCallback? onDownload;

  String get status {
    String result;
    if (task!.isComplete) {
      if (task.isSuccessful) {
        result = 'Complete';
      } else if (task.isCanceled) {
        result = 'Canceled';
      } else {
        result = 'Failed ERROR: ${task.lastSnapshot.error}';
      }
    } else if (task.isInProgress) {
      result = 'Uploading';
    } else if (task.isPaused) {
      result = 'Paused';
    }
    return result;
  }

  String _bytesTransferred(StorageTaskSnapshot snapshot) {
    return '${snapshot.bytesTransferred}/${snapshot.totalByteCount}';
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<StorageTaskEvent>(
      stream: task.events,
      builder: (BuildContext context,
          AsyncSnapshot<StorageTaskEvent> asyncSnapshot) {
        Widget subtitle;
        if (asyncSnapshot.hasData) {
          final StorageTaskEvent event = asyncSnapshot.data;
          final StorageTaskSnapshot snapshot = event.snapshot;
          subtitle = Text('$status: ${_bytesTransferred(snapshot)} bytes sent');
        } else {
          subtitle = const Text('Starting...');
        }
        return Dismissible(
          key: Key(task.hashCode.toString()),
          onDismissed: (_) => onDismissed(),
          child: ListTile(
            title: Text('Upload Task #${task.hashCode}'),
            subtitle: subtitle,
            trailing: Row(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                Offstage(
                  offstage: !task.isInProgress,
                  child: IconButton(
                    icon: const Icon(Icons.pause),
                    onPressed: () => task.pause(),
                  ),
                ),
                Offstage(
                  offstage: !task.isPaused,
                  child: IconButton(
                    icon: const Icon(Icons.file_upload),
                    onPressed: () => task.resume(),
                  ),
                ),
                Offstage(
                  offstage: task.isComplete,
                  child: IconButton(
                    icon: const Icon(Icons.cancel),
                    onPressed: () => task.cancel(),
                  ),
                ),
                Offstage(
                  offstage: !(task.isComplete && task.isSuccessful),
                  child: IconButton(
                    icon: const Icon(Icons.file_download),
                    onPressed: onDownload,
                  ),
                ),
              ],
            ),
          ),
        );
      },
    );
  }

解决方案(适配Firebase Storage最新SDK)

Firebase Storage Flutter SDK更新后,UploadTask的API做了简化,原有的isComplete、isInProgress等属性被移除,改为通过UploadTaskSnapshot.state(枚举类型TaskState)来判断上传状态,同时监听上传进度的Stream也从events改为snapshotChanges()。

修改后的UploadTaskListTile组件

class UploadTaskListTile extends StatelessWidget {
  const UploadTaskListTile({
    Key? key,
    this.task,
    this.onDismissed,
    this.onDownload,
  }) : super(key: key);

  final UploadTask? task;
  final VoidCallback? onDismissed;
  final VoidCallback? onDownload;

  String get status {
    if (task == null) return 'Unknown';
    final snapshot = task!.snapshot;
    switch (snapshot.state) {
      case TaskState.success:
        return 'Complete';
      case TaskState.canceled:
        return 'Canceled';
      case TaskState.error:
        return 'Failed ERROR: ${snapshot.error}';
      case TaskState.running:
        return 'Uploading';
      case TaskState.paused:
        return 'Paused';
    }
  }

  String _bytesTransferred(UploadTaskSnapshot snapshot) {
    return '${snapshot.bytesTransferred}/${snapshot.totalBytes}';
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<UploadTaskSnapshot>(
      stream: task?.snapshotChanges(),
      builder: (context, asyncSnapshot) {
        Widget subtitle;
        if (asyncSnapshot.hasData) {
          final snapshot = asyncSnapshot.data!;
          subtitle = Text('$status: ${_bytesTransferred(snapshot)} bytes sent');
        } else {
          subtitle = const Text('Starting...');
        }

        final isRunning = task?.snapshot.state == TaskState.running;
        final isPaused = task?.snapshot.state == TaskState.paused;
        final isComplete = task?.snapshot.state == TaskState.success;
        final isCanceledOrError = task?.snapshot.state == TaskState.canceled || task?.snapshot.state == TaskState.error;

        return Dismissible(
          key: Key(task.hashCode.toString()),
          onDismissed: (_) => onDismissed?.call(),
          child: ListTile(
            title: Text('Upload Task #${task.hashCode}'),
            subtitle: subtitle,
            trailing: Row(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                Offstage(
                  offstage: !isRunning,
                  child: IconButton(
                    icon: const Icon(Icons.pause),
                    onPressed: task?.pause,
                  ),
                ),
                Offstage(
                  offstage: !isPaused,
                  child: IconButton(
                    icon: const Icon(Icons.file_upload),
                    onPressed: task?.resume,
                  ),
                ),
                Offstage(
                  offstage: isComplete || isCanceledOrError,
                  child: IconButton(
                    icon: const Icon(Icons.cancel),
                    onPressed: task?.cancel,
                  ),
                ),
                Offstage(
                  offstage: !isComplete,
                  child: IconButton(
                    icon: const Icon(Icons.file_download),
                    onPressed: onDownload,
                  ),
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

关键修改点说明

  • 替换监听流:将StreamBuilder<StorageTaskEvent>改为StreamBuilder<UploadTaskSnapshot>,并使用task?.snapshotChanges()作为stream源。
  • 状态判断:通过task!.snapshot.state(TaskState枚举)判断上传状态,替代原有的isComplete、isInProgress等属性。
  • 进度计算:StorageTaskSnapshot改为UploadTaskSnapshot,对应的字节数属性从totalByteCount改为totalBytes。
  • 空安全优化:增加空值判断,避免空指针异常,同时使用?.call()简化回调调用。

内容的提问来源于stack exchange,提问作者Abhay Varma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 13:54:38