Load image only if it's inside viewport
You need to use ListView.builder
, because it renders its children lazily. Also you'll need to use one single list.
The problem is that you don't know the image's size before it's loaded, so all images with 0 height will be built instantly. But if you set fixed height to them, it will work as intended.
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
final children = [
// Header
Placeholder(
fallbackHeight: 300,
),
Padding(
padding: EdgeInsets.all(32),
child: Text('CATEGORY TITLE'),
),
...images.map((image) => CategoryItem(image)),
SizedBox(height: 16),
Padding(
padding: EdgeInsets.all(32),
child: Text('CATEGORY TITLE'),
),
...images.map((image) => CategoryItem(image)),
];
return Scaffold(
body: ListView.builder(
itemCount: children.length,
itemBuilder: (context, i) => children[i],
)
);
}
}
Anton
Updated on December 16, 2022Comments
-
Anton over 1 year
I have a Scaffold with a ListView as a child to be able to scroll. There is a header container at top of it and a column with a list of images.
I want to load this images only when they appears in viewport but all of them are starting to load at the same time.
This is not happening when I'm adding this images to the children of ListView but I'd like to put them to a separate widget to keep my code clean. Is it possible?
Here is a simplified example:
const List<String> images = [ 'https://cataas.com/cat?hash=1', 'https://cataas.com/cat?hash=2', 'https://cataas.com/cat?hash=3', 'https://cataas.com/cat?hash=4', 'https://cataas.com/cat?hash=5', 'https://cataas.com/cat?hash=6', 'https://cataas.com/cat?hash=7', 'https://cataas.com/cat?hash=8', ]; // Scaffold child class Home extends StatelessWidget { @override Widget build(BuildContext context) { return ListView( children: [ // Header Placeholder( fallbackHeight: 300, ), Category(), SizedBox(height: 16), Category(), ], ); } } class Category extends StatelessWidget { @override Widget build(BuildContext context) { print('Build category'); return Column( children: [ Padding( padding: EdgeInsets.all(32), child: Text('CATEGORY TITLE'), ), ...images.map((image) => CategoryItem(image)), ], ); } } class CategoryItem extends StatelessWidget { final String imageUrl; CategoryItem(this.imageUrl); @override Widget build(BuildContext context) { print('Build image: $imageUrl'); return FadeInImage.memoryNetwork( fit: BoxFit.cover, placeholder: kTransparentImage, image: imageUrl, ); } }
-
Anton over 4 yearsOne more reason why I want to use category as a widget is because in my app it's using StreamBuilder to get a list of image urls so it's impossible to use this solution. I'll fix my example
-
Igor Kharakhordin over 4 years@Anton I don't understand how this would block you from using a single list. In that case, you could combine your multiple streams into single one and use it just once in StreamBuilder.