android - 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;
}
}
}
解决方案
推荐阅读
- web-scraping - Web Scraper 不返回任何内容
- firefox-addon-webextensions - 带有 PDF 文件的选项卡上的 Firefox 附加主机权限错误
- java - RabbitMq 监听器在两个不同的队列上监听两种不同的消息类型
- java - JAXB 解组 2.7gb xml 文件
- android - 在android中录制视频时旋转图像帧
- php - isset 函数的 PHP 问题
- .net - 即使表单最小化如何刷新任务栏预览
- django - Django 从 is_valid 打印无效值
- hadoop - 如何从 Java 创建 HAR(Hadoop 存档)?
- haskell - Haskell 中的记忆