Reputation: 4731
I am trying to load images from network and show them in a GridView
. I am using a StatefulWidget
and loading the images inside the build
method. But according to my understanding its not good to make a network call inside the build
method. How can I download images from the network inside my BLoC
file and later pass the list of downloaded images to the widget? Below is my current implementation.
class MovieList extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return MovieListState();
}
}
class MovieListState extends State<MovieList> {
@override
void initState() {
super.initState();
bloc.fetchAllMovies();
}
@override
void dispose() {
bloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Popular Movies'),
),
body: StreamBuilder(
stream: bloc.allMovies,
builder: (context, AsyncSnapshot<ItemModel> snapshot) {
if (snapshot.hasData) {
return buildList(snapshot);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(child: CircularProgressIndicator());
},
),
);
}
Widget buildList(AsyncSnapshot<ItemModel> snapshot) {
return GridView.builder(
itemCount: snapshot.data.results.length,
gridDelegate:
new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (BuildContext context, int index) {
return GridTile(
child: InkResponse(
enableFeedback: true,
child: Image.network(
'https://image.tmdb.org/t/p/w185${snapshot.data
.results[index].poster_path}',
fit: BoxFit.cover,
),
onTap: () => openDetailPage(snapshot.data, index),
),
);
});
}
openDetailPage(ItemModel data, int index) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return MovieDetailBlocProvider(
child: MovieDetail(
title: data.results[index].title,
posterUrl: data.results[index].backdrop_path,
description: data.results[index].overview,
releaseDate: data.results[index].release_date,
voteAverage: data.results[index].vote_average.toString(),
movieId: data.results[index].id,
),
);
}),
);
}
}
Upvotes: 74
Views: 172959
Reputation: 535
Best way to Handle Network Images
The best way to load image from the network in flutter is to use flutter's built-in network function and customize it according to your requirements, I do not recommend/prefer to use any package like CachedNetworkImage because it sometimes works on mobile phone and give unexpected error on the web.
You can manage the Network image like this:
Image.network(
netWorkImageURL,
fit: BoxFit.fill,
// When image is loading from the server it takes some time
// So we will show progress indicator while loading
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
// When dealing with networks it completes with two states,
// complete with a value or completed with an error,
// So handling errors is very important otherwise it will crash the app screen.
// I showed dummy images from assets when there is an error, you can show some texts or anything you want.
errorBuilder: (context, exception, stackTrace) {
return Image.asset(
AppAssets.dummyPostImg,
fit: BoxFit.cover,
height: (widget.hideBelowImage == null ||
widget.hideBelowImage == false)
? 170.h
: 130.h,
width: double.infinity,
);
},
),
Upvotes: 6
Reputation: 3100
You can use loadingBuilder which is inbuilt feature from flutter for Image.Network
Image.network(
widget.networkUrl,
fit: BoxFit.fill,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
),
Upvotes: 194
Reputation: 157
It is a good practice to handle errors related to, for example, lack of Internet, when trying to load an image from the Internet. ErrorBuilder is really good if you use Image.network()
Image.network(
'https://example.does.not.exist/image.jpg',
errorBuilder: (BuildContext context, Object exception, StackTrace stackTrace) {
// Appropriate logging or analytics, e.g.
// myAnalytics.recordError(
// 'An error occurred loading "https://example.does.not.exist/image.jpg"',
// exception,
// stackTrace,
// );
return Text('😢');
},
),
Upvotes: 3
Reputation: 599
If you want a circular shape for your image. You can use Circle Avatar in such a way that it will act as loader and displayer both....
Parent circle avatar will be having loader and If we put transparent color to child circle avatar it will show loading until it is loaded...
Plus point with this way is you can simply give border also by setting background color of parent circle avatar and increasing it's radius slightly.
CircleAvatar(
backgroundColor: Colors.red,
radius: 65,
backgroundImage: AssetImage('assets/bottombar/loading.gif'),
child: CircleAvatar(
radius: 65,
backgroundColor: Colors.transparent,
backgroundImage: NetworkImage(_url),
),
),
Upvotes: 7
Reputation: 73
You can also use FadeInImage https://flutter.dev/docs/cookbook/images/fading-in-images
FadeInImage.assetNetwork(
placeholder: 'assets/loading.gif',
image: 'https://picsum.photos/250?image=9',
),
Upvotes: 4
Reputation: 2174
I would recommend you to use https://pub.dartlang.org/packages/cached_network_image
It's really works good for my cases.
Simple code example from their r
CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
placeholder: (context, url) => new CircularProgressIndicator(),
errorWidget: (context, url, error) => new Icon(Icons.error),
),
or
Image(image: CachedNetworkImageProvider(url))
You should add to the pubspec file
cached_network_image: <actual version here>
into the dependencies section
Upvotes: 93