i have asynchronous function which is scraping data from website.
(I am using http
and html
plugins).
This function fills up my imageUrls List
with url's. I am calling out that function in initState and in FutureBuilder. I pass this list as a parameter into MyCard
.
1.) That is the first time when user see loadingCard()
, (when function is gathering url's data from website). Looks like this :
in MyHomePage class i have :
FutureBuilder(
future: getWebsiteBasics(),
builder: ((context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return ListView(children: [
loadingCard(),
loadingCard(),
loadingCard(),
loadingCard(),
loadingCard(),
]);
} else if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return const Text('Error');
} else {
return ListView(
children: List.generate(10,
(index) => MyCard(
imgUrl: imageUrls[index],
)));
}
} else {
return Text('State: ${snapshot.connectionState}');
}
}))
I am loading those images in MyCard class()
.
2.) That is the second loading screen, (after seeing loadingCard()
user see CircularProgressIndicator()
), and finally image is shown to user. Looks like this :
in MyCard class i have :
Image.network(
widget.imgUrl[0],
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child;
}
return const CircularProgressIndicator();
}),
Where is problem ?
I want to get rid of second loading CircularProgressIndicator()
and load image first off after loadingCard()
will be gone. I want to include loading image (2.) sub) into function and load everyting at one loading screen ( loadingCard()
).
My scraping data function :
Future getWebsiteBasics() async {
final response = await http.get(Uri.parse(
"url"));
dom.Document html = dom.Document.html(response.body);
final imgs = html
.querySelectorAll("picking up correct selectors")
.toList()
.map((e) =>
e.querySelectorAll('img').map((a) => a.attributes['src']!).toList())
.toList();
imageUrls = imgs;
}
CodePudding user response:
Don't try to fix this.
The real solution to this problem is state management. There are several good solutions for this available as concepts and flutter packages. Personally I use the BLoC pattern regularly.
You need to separate you view (widget) from your model (the data you wand to show) and the controller (which loads the data from the web pages).
You'll probably end up with a StreamBuilder() widget that updates the view whenever the controller has received new images.
CodePudding user response:
If I understood what the problem is, it is that when you receive a new image or change in the build it is rendered again, so you have to wait again for all images to load.
The best way for me to handle this is using a flutter package cached_network_image this save on phone's cache the imagen. If you recieve a new image form the server this only loads the new image. If if a change on the build the loaded images will be there without need to wait again.
CachedNetworkImage(
fadeOutDuration: const Duration(milliseconds: 300),
fadeInDuration: const Duration(milliseconds: 300),
imageUrl: widget.imgUrl[0],
fit: BoxFit.cover,
width: double.infinity,
progressIndicatorBuilder: (context, url, progress) {
return Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color(Colors.blueGrey),
value: progress.totalSize != null
? progress.progress
: null,
),
);
},),
I hope it works for you, regards