首页 > 解决方案 > ListView 中的有状态小部件在收听广播流时不会更新

问题描述

我有一个列表视图,其构建器方法只有一个文本和另一个有状态的小部件(下载小部件),它显示了内容下载的进度。下载小部件侦听来自平台通道的订阅流,然后更新正在下载/暂停/恢复按钮的状态以及百分比进度。

因此,当在列表视图中下载多个内容时,Download Widget 只会更新最近项目的 IconButton 的状态。它不会更新列表视图中的其他下载小部件。

例如,我开始下载内容 A 然后 B,列表视图将为 A 和 B 初始化下载小部件,但将接收与 B 对应的下载小部件的更新。

从日志中可以看出,当通过订阅流接收更新时,正确接收到更新,但必须更新状态的项目始终是最近的。检查以下打印语句

print("Download Widget: url for update : $_contentUrl");

上面的日志总是打印最近添加的下载项目的 url。

这是列表视图的片段:

ListView.builder(
          itemCount: downloadList.length,
          itemBuilder: (context, index) {
            DownloadModel downloadModel = downloadList[index];
            return GestureDetector(
                onTap: () {},
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Padding(
                        padding: const EdgeInsets.only(left: 8.0),
                        child: Text(
                          downloadList[index].name,
                          style: TextStyle(fontSize: 15),
                        ),
                      ),
                      Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: DownloadWidget(
                          key: ObjectKey(downloadModel),
                          media: ContentModel.fromDownloadContent(
                              downloadList[index]),
                          downloadInfo: {
                            "state": downloadModel.state,
                            "progress": downloadModel.percentage,
                            "localUrl": downloadModel.localUrl,
                            "keySetId": downloadModel.keySetId
                          },
                        ),
                      )
                    ],
            );
          })

以下是DownloadWidget的片段

class DownloadWidget extends StatefulWidget {
  final ContentModel media;
  final Map<dynamic, dynamic> downloadInfo;

  const DownloadWidget({Key key, this.media, this.downloadInfo})
      : super(key: key);

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

class _DownloadWidgetState extends State<DownloadWidget> {
  static const EventChannel _stream =
      EventChannel('com.example.flutterapp/download_event');
  StreamSubscription updateStream;
  int percentage = 0;
  int downloadState = 101;
  String _contentUrl;

  @override
  void initState() {
    super.initState();

    Map<dynamic, dynamic> downloadInfo = widget.downloadInfo;
    _contentUrl = widget.media.dashCmafUrl;
    print("Intialize Download Widget with url $_contentUrl");
    if (downloadInfo != null && downloadInfo.isNotEmpty) {
      downloadState = widget.downloadInfo["state"];
      double progress = widget.downloadInfo["progress"];
      percentage = (progress * 100).toInt();
    }

    print("Download Widget: hash code while intializing = ${this.hashCode}");
    if (updateStream == null) {
      updateStream = _stream.receiveBroadcastStream().listen((event) {
        print("Download Widget : $event");
        print("Download Widget: url for update : $_contentUrl");
        print("Download Widget: hash code = ${this.hashCode}");
        final Map<dynamic, dynamic> eventMap = event;
        String url = eventMap["url"];
        print("Download Widget: Event url for update : ${url}");
        if (widget.media.dashCmafUrl == url) {
          setState(() {
            double progress = eventMap["progress"];
            percentage = (progress * 100).toInt();
            print("Download Widget: Percentage complete = ${percentage}");
            downloadState = eventMap["state"];
          });
        }
      });
    }
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    print("Download Widget:Disposing Download Widget");
    updateStream?.cancel();
    if (updateStream != null) updateStream = null;
  }

  @override
  Widget build(BuildContext context) {
    return buildDownloadWidget();
  }

  Widget buildDownloadWidget() {
    return Column(
      children: [
        IconButton(
            iconSize: 30,
            icon: Icon(
              getIconByState(),
              color: Colors.grey[500],
            ),
            onPressed: () {
              handleDownloadState();
            }),
        if (percentage.toInt() != 0 && downloadState != 105)
          Text(
            "${percentage} %",
            style: TextStyle(
              color: Provider.of<DarkThemeProvider>(context).darkTheme
                  ? Colors.grey[400]
                  : Colors.black54,
            ),
          )
      ],
    );
  }

  IconData getIconByState() {
    switch (downloadState) {
      case 101:
        return Icons.download_rounded;
      case 102:
        return Icons.pause_circle_filled;
      case 103:
        return Icons.restore;
      case 105:
        return Icons.play_circle_filled;
    }
    return Icons.download_rounded;
  }

  void handleDownloadState() {
    switch (downloadState) {
      case 102:
        DownloadUtil.pauseDownload(widget.media.dashCmafUrl);
        break;

      case 103:
        DownloadUtil.resumeDownload(widget.media.dashCmafUrl);
        break;
      case 105:
        Map<dynamic, dynamic> downloadInfo = widget.downloadInfo;
        if (downloadInfo != null && downloadInfo.isNotEmpty) {
          Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) => OfflinePlayer(
                      offlineContent: DownloadModel(
                          localUrl: downloadInfo['localUrl'],
                          keySetId: downloadInfo['keySetId']))));
        }

        break;
      default:
        var content = {
          "name": widget.media.title,
          "url": widget.media.dashCmafUrl,
          "licenseUrl": widget.media.dashCmafLicenseUrl
        };
        DownloadUtil.downloadContent(content);
        break;
    }
  }
}

在此处输入图像描述

标签: androidflutterflutter-listviewstatefulwidget

解决方案


推荐阅读