Better dart code.

This commit is contained in:
stonegate 2020-07-13 15:41:59 +08:00
parent d73fdeca1d
commit 6d755c5d92
16 changed files with 237 additions and 168 deletions

View File

@ -832,8 +832,8 @@ class _RecentUpdateState extends State<_RecentUpdate>
_group = ['All'];
});
} else {
groupList.groups
.forEach((group) {
for (var group
in groupList.groups) {
if (group.name ==
value) {
setState(() {
@ -842,7 +842,8 @@ class _RecentUpdateState extends State<_RecentUpdate>
.podcastList;
});
}
});
}
;
}
},
),

View File

@ -41,16 +41,16 @@ class Import extends StatelessWidget {
List<EpisodeBrief> episodes = await dbHelper.getNewEpisodes('all');
// For safety
if (episodes.length < 100 && episodes.length > 0)
episodes.forEach((episode) async {
for (var episode in episodes) {
await downloader.startTask(episode, showNotification: true);
});
}
} else if (result == ConnectivityResult.wifi) {
List<EpisodeBrief> episodes = await dbHelper.getNewEpisodes('all');
//For safety
if (episodes.length < 100 && episodes.length > 0)
episodes.forEach((episode) async {
for (var episode in episodes) {
await downloader.startTask(episode, showNotification: true);
});
}
}
}

View File

@ -27,9 +27,9 @@ class _PlaylistPageState extends State<PlaylistPage> {
if (episodes.length == 0) {
return sum;
} else {
episodes.forEach((episode) {
for (var episode in episodes) {
sum += episode.duration ~/ 60;
});
}
return sum;
}
}

View File

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tsacdop/local_storage/key_value_storage.dart';
import 'package:tsacdop/service/ompl_build.dart';
import 'package:xml/xml.dart' as xml;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/services.dart';
@ -66,68 +67,98 @@ class _PopupMenuState extends State<PopupMenu> {
}
}
void _saveOmpl(String path) async {
var subscribeWorker = Provider.of<SubscribeWorker>(context, listen: false);
final s = context.s;
File file = File(path);
try {
Map data = PodcastsBackup.parseOMPL(file);
data.forEach((title, total) async {
for (int i = 0; i < total.length; i++) {
if (total[i].xmlUrl != null) {
SubscribeItem item = SubscribeItem(total[i].xmlUrl, total[i].text);
await subscribeWorker.setSubscribeItem(item);
await Future.delayed(Duration(milliseconds: 500));
print(total[i].text);
}
}
});
} catch (e) {
print(e);
Fluttertoast.showToast(
msg: s.toastFileError,
gravity: ToastGravity.TOP,
);
// try {
// String opml = file.readAsStringSync();
// var content = xml.XmlDocument.parse(opml);
// String title = content
// .findAllElements('head')
// .first
// .findElements('title')
// .first
// .text;
// print(title);
// if (title != 'Tsacdop Subscriptions') {
// var total = content
// .findAllElements('outline')
// .map((ele) => OmplOutline.parse(ele))
// .toList();
// if (total.length == 0) {
// Fluttertoast.showToast(
// msg: s.toastFileNotValid,
// gravity: ToastGravity.BOTTOM,
// );
// } else {
// for (int i = 0; i < total.length; i++) {
// if (total[i].xmlUrl != null) {
// // importOmpl.rssTitle = total[i].text;
// //await saveOmpl(total[i].xmlUrl);
// SubscribeItem item =
// SubscribeItem(total[i].xmlUrl, total[i].text);
// await subscribeWorker.setSubscribeItem(item);
// await Future.delayed(Duration(milliseconds: 500));
// print(total[i].text);
// }
// }
// }
// print('Import fisnished');
// }
// } catch (e) {
// print(e);
// Fluttertoast.showToast(
// msg: s.toastFileError,
// gravity: ToastGravity.TOP,
// );
//await Future.delayed(Duration(seconds: 5));
// importOmpl.importState = ImportState.stop;
}
}
void _getFilePath() async {
final s = context.s;
try {
String filePath = await FilePicker.getFilePath(type: FileType.any);
if (filePath == '') {
return;
}
print('File Path' + filePath);
//importOmpl.importState = ImportState.start;
Fluttertoast.showToast(
msg: s.toastReadFile,
gravity: ToastGravity.TOP,
);
_saveOmpl(filePath);
} on PlatformException catch (e) {
print(e.toString());
}
}
@override
Widget build(BuildContext context) {
var refreshWorker = Provider.of<RefreshWorker>(context, listen: false);
var subscribeWorker = Provider.of<SubscribeWorker>(context, listen: false);
final s = context.s;
void _saveOmpl(String path) async {
File file = File(path);
try {
String opml = file.readAsStringSync();
var content = xml.XmlDocument.parse(opml);
var total = content
.findAllElements('outline')
.map((ele) => OmplOutline.parse(ele))
.toList();
if (total.length == 0) {
Fluttertoast.showToast(
msg: s.toastFileNotValid,
gravity: ToastGravity.BOTTOM,
);
} else {
for (int i = 0; i < total.length; i++) {
if (total[i].xmlUrl != null) {
// importOmpl.rssTitle = total[i].text;
//await saveOmpl(total[i].xmlUrl);
SubscribeItem item =
SubscribeItem(total[i].xmlUrl, total[i].text);
await subscribeWorker.setSubscribeItem(item);
await Future.delayed(Duration(milliseconds: 500));
print(total[i].text);
}
}
print('Import fisnished');
}
} catch (e) {
print(e);
Fluttertoast.showToast(
msg: s.toastFileError,
gravity: ToastGravity.TOP,
);
//await Future.delayed(Duration(seconds: 5));
// importOmpl.importState = ImportState.stop;
}
}
void _getFilePath() async {
try {
String filePath = await FilePicker.getFilePath(type: FileType.any);
if (filePath == '') {
return;
}
print('File Path' + filePath);
//importOmpl.importState = ImportState.start;
Fluttertoast.showToast(
msg: s.toastReadFile,
gravity: ToastGravity.TOP,
);
_saveOmpl(filePath);
} on PlatformException catch (e) {
print(e.toString());
}
}
return PopupMenuButton<int>(
shape: RoundedRectangleBorder(

View File

@ -63,7 +63,8 @@ class DBHelper {
Future<List<PodcastLocal>> getPodcastLocal(List<String> podcasts) async {
var dbClient = await database;
List<PodcastLocal> podcastLocal = [];
await Future.forEach(podcasts, (s) async {
for (var s in podcasts) {
List<Map> list;
list = await dbClient.rawQuery(
"""SELECT id, title, imageUrl, rssUrl, primaryColor, author, imagePath , provider,
@ -82,7 +83,7 @@ class DBHelper {
list.first['link'],
upateCount: list.first['update_count'],
episodeCount: list.first['episode_count']));
});
}
return podcastLocal;
}
@ -260,11 +261,11 @@ class DBHelper {
ORDER BY add_date DESC LIMIT ?
""", [top]);
List<PlayHistory> playHistory = [];
list.forEach((record) {
for (var record in list) {
playHistory.add(PlayHistory(record['title'], record['enclosure_url'],
record['seconds'], record['seek_value'],
playdate: DateTime.fromMillisecondsSinceEpoch(record['add_date'])));
});
}
return playHistory;
}
@ -276,9 +277,9 @@ class DBHelper {
if (list.length == 0)
return 0;
else {
list.forEach((element) {
for (var element in list) {
i += element['listen_time'];
});
}
return i;
}
}
@ -311,10 +312,10 @@ class DBHelper {
"SELECT seconds FROM PlayHistory WHERE add_date > ? AND add_date < ?",
[start, end]);
double sum = 0;
if (list.length == 0) {
if (list.isEmpty) {
sum = 0;
} else {
list.forEach((record) => sum += record['seconds']);
for (var record in list) sum += record['seconds'];
}
return (sum ~/ 60).toDouble();
}
@ -325,7 +326,7 @@ class DBHelper {
"""SELECT title, enclosure_url, seconds, seek_value, add_date FROM PlayHistory
WHERE enclosure_url = ? ORDER BY add_date DESC LIMIT 1""",
[episodeBrief.enclosureUrl]);
return list.length > 0
return list.isEmpty
? PlayHistory(list.first['title'], list.first['enclosure_url'],
list.first['seconds'], list.first['seek_value'],
playdate:
@ -340,7 +341,7 @@ class DBHelper {
"""SELECT title, enclosure_url, seconds, seek_value, add_date FROM PlayHistory
WHERE enclosure_url = ? AND seek_value = 1 ORDER BY add_date DESC LIMIT 1""",
[episodeBrief.enclosureUrl]);
return list.length > 0;
return list.isEmpty;
}
DateTime _parsePubDate(String pubDate) {

View File

@ -70,17 +70,15 @@ class _PodcastDetailState extends State<PodcastDetail> {
await dbHelper.getNewEpisodes(podcastLocal.id);
// For safety
if (episodes.length < 100)
episodes.forEach((episode) {
for (var episode in episodes)
downloader.startTask(episode, showNotification: false);
});
} else if (result == ConnectivityResult.wifi) {
List<EpisodeBrief> episodes =
await dbHelper.getNewEpisodes(podcastLocal.id);
//For safety
if (episodes.length < 100)
episodes.forEach((episode) {
for (var episode in episodes)
downloader.startTask(episode, showNotification: false);
});
}
}
} else {
@ -125,7 +123,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
DBHelper dbHelper = DBHelper();
List<EpisodeBrief> episodes =
await dbHelper.getRssItem(podcastId, -1, true);
await Future.forEach(episodes, (episode) async {
for (var episode in episodes) {
bool marked = await dbHelper.checkMarked(episode);
if (!marked) {
final PlayHistory history =
@ -133,7 +131,7 @@ class _PodcastDetailState extends State<PodcastDetail> {
await dbHelper.saveHistory(history);
if (mounted) setState(() {});
}
});
}
}
Widget podcastInfo(BuildContext context) {

View File

@ -1,38 +1,87 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:xml/xml.dart' as xml;
import '../state/podcast_group.dart';
class OmplOutline {
final String text;
final String xmlUrl;
OmplOutline({this.text, this.xmlUrl});
factory OmplOutline.parse(xml.XmlElement element) {
if (element == null) return null;
return OmplOutline(
text: element.getAttribute("text")?.trim(),
xmlUrl: element.getAttribute("xmlUrl")?.trim(),
);
}
}
class PodcastsBackup {
///Group list for backup.
final List<PodcastGroup> groups;
PodcastsBackup(this.groups) : assert(groups.length > 0);
omplBuilder() async {
omplBuilder() {
var builder = xml.XmlBuilder();
builder.processing('xml', 'version="1.0" encoding="UTF-8"');
builder.element('ompl', nest: () {
builder.attribute('version', '1.0');
builder.element('head', nest: () {
builder.element('title', nest: 'Tsacdop Feeds');
builder.element('title', nest: 'Tsacdop Subscriptions');
});
builder.element('body', nest: () {
builder.element('outline', nest: () {
groups.forEach((group) {
for (var group in groups) {
builder.element('outline', nest: () {
builder.attribute('text', '${group.name}');
builder.attribute('title', '${group.name}');
group.podcasts.forEach((e) => builder.element(
'outline',
nest: () {
builder.attribute('type', 'rss');
builder.attribute('text', '${e.title}');
builder.attribute('title', '${e.title}');
builder.attribute('xmlUrl', '${e.rssUrl}');
},
isSelfClosing: true,
));
for (var e in group.podcasts)
builder.element(
'outline',
nest: () {
builder.attribute('type', 'rss');
builder.attribute('text', '${e.title}');
builder.attribute('title', '${e.title}');
builder.attribute('xmlUrl', '${e.rssUrl}');
},
isSelfClosing: true,
);
});
});
}
});
});
return builder.build();
}
static parseOMPL(File file) {
var data = Map();
String opml = file.readAsStringSync();
var content = xml.XmlDocument.parse(opml);
String title =
content.findAllElements('head').first.findElements('title').first.text;
var groups = content.findAllElements('body').first.findElements('outline');
if (title != 'Tsacdop Subscriptions' &&
groups.first.getAttribute('title') != 'Home') {
var total = content
.findAllElements('outline')
.map((ele) => OmplOutline.parse(ele))
.toList()
..removeWhere((element) => element == null);
data['Home'] = total;
return data;
}
for (var element in groups) {
String title = element.getAttribute('title');
var total = element
.findElements('outline')
.map((ele) => OmplOutline.parse(ele))
.toList()
..removeWhere((element) => element == null);
data[title] = total;
}
return data;
}
}

View File

@ -59,11 +59,12 @@ class _DownloadsManageState extends State<DownloadsManage> {
_delSelectedEpisodes() async {
setState(() => _clearing = true);
await Future.forEach(_selectedList, (EpisodeBrief episode) async {
// await Future.forEach(_selectedList, (EpisodeBrief episode) async
for (EpisodeBrief episode in _selectedList) {
var downloader = Provider.of<DownloadState>(context, listen: false);
await downloader.delTask(episode);
if (mounted) setState(() {});
});
}
await Future.delayed(Duration(seconds: 1));
if (mounted)
setState(() {
@ -94,9 +95,7 @@ class _DownloadsManageState extends State<DownloadsManage> {
if (_selectedList.length == 0) {
return sum;
} else {
_selectedList.forEach((episode) {
sum += episode.enclosureLength;
});
for (var episode in _selectedList) sum += episode.enclosureLength;
return sum;
}
}

View File

@ -26,9 +26,7 @@ class _PlayedHistoryState extends State<PlayedHistory>
DBHelper dbHelper = DBHelper();
List<PlayHistory> playHistory;
playHistory = await dbHelper.getPlayHistory(top);
await Future.forEach(playHistory, (playHistory) async {
await playHistory.getEpisode();
});
for (var record in playHistory) await record.getEpisode();
return playHistory;
}
@ -58,10 +56,11 @@ class _PlayedHistoryState extends State<PlayedHistory>
Future<List<FlSpot>> getData() async {
var dbHelper = DBHelper();
List<FlSpot> stats = [];
await Future.forEach(list, (day) async {
for (var day in list) {
double mins = await dbHelper.listenMins(7 - day);
stats.add(FlSpot(day.toDouble(), mins));
});
}
return stats;
}

View File

@ -82,10 +82,11 @@ class Playlist {
_playlist = [];
} else {
_playlist = [];
await Future.forEach(urls, (url) async {
for (String url in urls) {
EpisodeBrief episode = await dbHelper.getRssItemWithUrl(url);
if (episode != null) _playlist.add(episode);
});
}
}
}
@ -302,9 +303,8 @@ class AudioPlayerNotifier extends ChangeNotifier {
//Check autoplay setting
await _getAutoPlay();
if (_autoPlay) {
await Future.forEach(_queue.playlist, (episode) async {
for (var episode in _queue.playlist)
await AudioService.addQueueItem(episode.toMediaItem());
});
} else {
await AudioService.addQueueItem(_queue.playlist.first.toMediaItem());
}
@ -481,9 +481,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
else
newEpisodes = await dbHelper.getGroupNewRssItem(group);
if (newEpisodes.length > 0 && newEpisodes.length < 100)
await Future.forEach(newEpisodes, (episode) async {
await addToPlaylist(episode);
});
for (var episode in newEpisodes) await addToPlaylist(episode);
if (group.first == 'All')
await dbHelper.removeAllNewMark();
else

View File

@ -63,7 +63,8 @@ class AutoDownloader {
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
_episodeTasks.forEach((episodeTask) {
for (var episodeTask in _episodeTasks) {
if (episodeTask.taskId == id) {
episodeTask.status = status;
episodeTask.progress = progress;
@ -76,7 +77,7 @@ class AutoDownloader {
if (_episodeTasks.length == 0) _unbindBackgroundIsolate();
}
}
});
}
});
}
@ -103,7 +104,7 @@ class AutoDownloader {
Future startTask(List<EpisodeBrief> episodes,
{bool showNotification = false}) async {
episodes.forEach((episode) async {
for (var episode in episodes) {
final dir = await getExternalStorageDirectory();
String localPath = path.join(dir.path, episode.feedTitle);
final saveDir = Directory(localPath);
@ -130,7 +131,7 @@ class AutoDownloader {
_episodeTasks.add(EpisodeTask(episode, taskId));
var dbHelper = DBHelper();
await dbHelper.saveDownloaded(episode.enclosureUrl, taskId);
});
}
await _completer.future;
return;
}
@ -159,7 +160,7 @@ class DownloadState extends ChangeNotifier {
DBHelper dbHelper = DBHelper();
var tasks = await FlutterDownloader.loadTasks();
if (tasks.length != 0)
await Future.forEach(tasks, (DownloadTask task) async {
for (var task in tasks) {
EpisodeBrief episode = await dbHelper.getRssItemWithUrl(task.url);
if (episode == null)
await FlutterDownloader.remove(
@ -167,7 +168,7 @@ class DownloadState extends ChangeNotifier {
else
_episodeTasks.add(EpisodeTask(episode, task.taskId,
progress: task.progress, status: task.status));
});
}
notifyListeners();
}
@ -185,7 +186,8 @@ class DownloadState extends ChangeNotifier {
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
_episodeTasks.forEach((episodeTask) {
for (var episodeTask in _episodeTasks) {
if (episodeTask.taskId == id) {
episodeTask.status = status;
episodeTask.progress = progress;
@ -196,7 +198,7 @@ class DownloadState extends ChangeNotifier {
} else
notifyListeners();
}
});
}
});
}
@ -312,11 +314,12 @@ class DownloadState extends ChangeNotifier {
await FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
await dbHelper.delDownloaded(episode.enclosureUrl);
_episodeTasks.forEach((episodeTask) {
for (var episodeTask in _episodeTasks) {
if (episodeTask.taskId == task.taskId)
episodeTask.status = DownloadTaskStatus.undefined;
notifyListeners();
});
}
_removeTask(episode);
}
@ -336,17 +339,15 @@ class DownloadState extends ChangeNotifier {
.subtract(Duration(days: autoDelete))
.millisecondsSinceEpoch;
List<EpisodeBrief> episodes = await dbHelper.getOutdatedEpisode(deadline);
if (episodes.length > 0) {
await Future.forEach(
episodes, (episode) async => await delTask(episode));
if (episodes.isNotEmpty) {
for (var episode in episodes) await delTask(episode);
}
final tasks = await FlutterDownloader.loadTasksWithRawQuery(
query:
'SELECT * FROM task WHERE time_created < $deadline AND status = 3');
await Future.forEach(
tasks,
(task) async => FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true));
for (var task in tasks)
FlutterDownloader.remove(
taskId: task.taskId, shouldDeleteContent: true);
}
}
}

View File

@ -103,9 +103,7 @@ class GroupList extends ChangeNotifier {
clearOrderChanged() async {
if (_orderChanged.length > 0) {
await Future.forEach(_orderChanged, (PodcastGroup group) async {
await group.getPodcasts();
});
for (var group in _orderChanged) await group.getPodcasts();
_orderChanged.clear();
// notifyListeners();
}
@ -130,9 +128,7 @@ class GroupList extends ChangeNotifier {
notifyListeners();
storage.getGroups().then((loadgroups) async {
_groups.addAll(loadgroups.map((e) => PodcastGroup.fromEntity(e)));
await Future.forEach(_groups, (group) async {
await group.getPodcasts();
});
for (var group in _groups) await group.getPodcasts();
_isLoading = false;
notifyListeners();
});
@ -140,9 +136,7 @@ class GroupList extends ChangeNotifier {
//update podcasts of each group
Future updateGroups() async {
await Future.forEach(_groups, (group) async {
await group.getPodcasts();
});
for (var group in _groups) await group.getPodcasts();
notifyListeners();
}
@ -156,11 +150,10 @@ class GroupList extends ChangeNotifier {
Future delGroup(PodcastGroup podcastGroup) async {
_isLoading = true;
podcastGroup.podcastList.forEach((podcast) {
for (var podcast in podcastGroup.podcastList)
if (!_groups.first.podcastList.contains(podcast)) {
_groups[0].podcastList.insert(0, podcast);
}
});
await _saveGroup();
_groups.remove(podcastGroup);
await _groups[0].getPodcasts();
@ -191,13 +184,12 @@ class GroupList extends ChangeNotifier {
Future updatePodcast(String id) async {
int counts = await dbHelper.getPodcastCounts(id);
_groups.forEach((group) {
for (var group in _groups)
if (group.podcastList.contains(id)) {
group.podcasts.firstWhere((podcast) => podcast.id == id)
..episodeCount = counts;
notifyListeners();
}
});
}
Future subscribeNewPodcast(String id) async {
@ -211,11 +203,10 @@ class GroupList extends ChangeNotifier {
List<PodcastGroup> getPodcastGroup(String id) {
List<PodcastGroup> result = [];
_groups.forEach((group) {
for (var group in _groups)
if (group.podcastList.contains(id)) {
result.add(group);
}
});
return result;
}
@ -223,20 +214,21 @@ class GroupList extends ChangeNotifier {
changeGroup(String id, List<PodcastGroup> list) async {
_isLoading = true;
notifyListeners();
getPodcastGroup(id).forEach((group) {
for (var group in getPodcastGroup(id)) {
if (list.contains(group)) {
list.remove(group);
} else {
group.podcastList.remove(id);
}
});
list.forEach((s) {
s.podcastList.insert(0, id);
});
}
for (var s in list) s.podcastList.insert(0, id);
await _saveGroup();
await Future.forEach(_groups, (group) async {
await group.getPodcasts();
});
for (var group in _groups) await group.getPodcasts();
_isLoading = false;
notifyListeners();
}
@ -245,14 +237,10 @@ class GroupList extends ChangeNotifier {
removePodcast(String id) async {
_isLoading = true;
notifyListeners();
_groups.forEach((group) async {
group.podcastList.remove(id);
});
for (var group in _groups) group.podcastList.remove(id);
await _saveGroup();
await dbHelper.delPodcastLocal(id);
await Future.forEach(_groups, (group) async {
await group.getPodcasts();
});
for (var group in _groups) await group.getPodcasts();
_isLoading = false;
notifyListeners();
}

View File

@ -71,10 +71,10 @@ Future<void> refreshIsolateEntryPoint(SendPort sendPort) async {
await refreshstorage.saveInt(DateTime.now().millisecondsSinceEpoch);
var dbHelper = DBHelper();
List<PodcastLocal> podcastList = await dbHelper.getPodcastLocalAll();
await Future.forEach<PodcastLocal>(podcastList, (podcastLocal) async {
for (var podcastLocal in podcastList) {
sendPort.send([podcastLocal.title, 1]);
int updateCount = await dbHelper.updatePodcastRss(podcastLocal);
print('Refresh ' + podcastLocal.title + updateCount.toString());
});
}
sendPort.send("done");
}

View File

@ -21,10 +21,11 @@ void callbackDispatcher() {
//if the app wes opend,then the old marked new episode would be marked not new.
KeyValueStorage lastWorkStorage = KeyValueStorage(lastWorkKey);
int lastWork = await lastWorkStorage.getInt();
await Future.forEach<PodcastLocal>(podcastList, (podcastLocal) async {
for (PodcastLocal podcastLocal in podcastList) {
await dbHelper.updatePodcastRss(podcastLocal, removeMark: lastWork);
print('Refresh ' + podcastLocal.title);
});
}
;
await FlutterDownloader.initialize();
AutoDownloader downloader = AutoDownloader();

View File

@ -22,10 +22,12 @@ class SubscribeItem {
SubscribeState subscribeState;
String id;
String imgUrl;
String group;
SubscribeItem(this.url, this.title,
{this.subscribeState = SubscribeState.none,
this.id = '',
this.imgUrl = ''});
this.imgUrl = '',
this.group = 'Home'});
}
class SubscribeWorker extends ChangeNotifier {

View File

@ -35,7 +35,8 @@ class FiresideData {
.toString());
var ul = doc.body.getElementsByClassName('episode-hosts').first.children;
List<PodcastHost> hosts = [];
ul.forEach((element) {
for (var element in ul) {
PodcastHost host;
String name = element.text.trim();
String image =
@ -48,7 +49,7 @@ class FiresideData {
'http://xuanmei.us/assets/default/avatar_small-170afdc2be97fc6148b283083942d82c101d4c1061f6b28f87c8958b52664af9.jpg');
hosts.add(host);
});
}
List<String> data = [
id,
backgroundImage,