1

Lets say you have a ListView of variable height:

  • List items are a Container with a mix of text and images. As such, the list items are of variable height. Sometimes no images. The text renders immediately as expected, but the images may take time to retrieve and render on screen
  • The images are retrieved from the network using CachedNetworkImage
  • Images are of variable height
  • When the Screen is opened the ListView automatically scrolls to item#11 (using ensureVisible technique)
  • So there are items both above and below your current position

At this point, when one of the network images above your position load up, the entire ListView will be pushed and you will no longer be looking at Item #11, rather somewhere randomly higher up

I considered initiating a new scroll in a callback after each image loads, however, due to network speeds, usually the listview scroll will finish before all the images load. If there are a lot of images, the images could take time to load, so it would be unreasonable to initiate a new scroll each time a new image is loaded, the screen just keeps scrolling forward every few seconds. It becomes dizzying and annoying.

Alternatively, the scrollview could jumpTo a new position as soon as the image loads, but I'm imagining there would be a slight delay between the two events and the user perceive a small "glitch" as the image loads and the listview immediately jumps to offset the image load. Even using a Future.microtask there is a very small perceptible 'glitch' as the image loads and the jumpto fires

It would be most preferable to have the listview expand the content upward somehow, so that the users current scroll position is maintained, as far as they are concerned.

Is it possible to have the ListView keep its position as the images load?

Mark
  • 3,653
  • 10
  • 30
  • 62
  • do u have an image of thing you are trying to implement? im sure, that it is possible to make in some way, but i can not completely understand what are u trying to do. As i assumed u might Wrap the image in container with custom height, and expand listtile or whatever to size of container, but im not sure that it's your case. Code example would be very helpfull aswell – dancheg Mar 06 '22 at 19:26
  • @Danila Fominyh Any random image in this scenario. The key is there are `multiple` images and they are `random` images, meaning of any height. So the main problem is that the height is not known ahead of time until the image is being rendered. Consider also there is text above and below the image, so that when the image is downloaded and rendered on screen, the container will expand to show the text and the image – Mark Mar 06 '22 at 19:38
  • How about saving the image size in storage and using it later, it will only be annoying when the image is downloaded for the first time. – Mohammed Alfateh Mar 07 '22 at 21:29
  • Can you try precaching images and then show the UI? – ibhavikmakwana Mar 10 '22 at 13:58
  • @ibhavikmakwana I think it would significantly slow down the app as there is a large list of news items, and the app would only know which images for which news article to pre-cache only after the user selects a new item – Mark Mar 10 '22 at 15:20
  • How about a callback when an image load completes, if the image precedes the target, that calls ensureVisible again or jumps to scroll position + image height (retrievable at this point)? – Lee3 Mar 11 '22 at 07:28
  • @Lee3 Yup that's what I tried and it does correctly compensate position but there is a single frame delay between when the image is painted on the screen and the position change occurs, giving a nasty jump to the screen – Mark Mar 11 '22 at 15:11

4 Answers4

1

Assuming you have a predefined size for your images, you can wrap the image in a SizedBox(). This way your list will always have the same height and your items won't get pushed around.

EDIT: Since your images are of variable size, I would probably animate to the desired location on every image load.

CachedNetworkImage has a callback

imageBuilder: (context, imageProvider) {
  /// Animate to desired index
  return Image(image: imageProvider);
}
Christian
  • 834
  • 7
  • 18
0

Animated container, might help you. It can adjust the height automatically, depending on the height u provide in builder.

Also you can use this answer to determnin image height and width in rnutime.

dancheg
  • 549
  • 1
  • 4
  • 14
0

Images are of variable height

To overcome this, Either we take the image size or aspect ratio of the image while storing the image along with other data.

While retrieving data, along with other text data we will receive the aspect ratio or height for the image.

I would use the same height or ratio and show placeholder image till images are loaded.

CachedNetworkImage(
              imageUrl: countryList[index].flagUrl,
              height: 60, // Set your height according to aspect ratio or fixed height
              width: 60,
              fit: BoxFit.cover,
              placeholder: (_, __) => Container(
                alignment: Alignment.center,
                height: 60,  // Set your height 
                width: 60,
                color: Colors.red.withAlpha(80),
                child: Text(
                  countryList[index].name[0],
                  style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                ),
              ),
            )

Image must be of specific aspect ratio I believe. You can define height according to aspect ratio.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Deep Dave
  • 159
  • 1
  • 8
0

Try as follows:

enter image description here

 ListView.builder(
            shrinkWrap: true,
            itemCount: images.length,
            itemBuilder: (ctx, i) {
              return Column(children: [
                ButtonItems(i),
                const SizedBox(height: 10),
              ]);
            }));

Button Items class

    class ButtonItems extends StatefulWidget {
  final int i;
  ButtonItems(this.i);
  @override
  _ButtonItems createState() => _ButtonItems();
}

class _ButtonItems extends State<ButtonItems> {
  var images = [
    "https://opengraph.githubassets.com/2ddb0ff05ef9ccfce35abb56e30d9c5068e01d1d10995484cfd07becee9accf7/dartpad/dartpad.github.io",
    null,
    "https://opengraph.githubassets.com/2ddb0ff05ef9ccfce35abb56e30d9c5068e01d1d10995484cfd07becee9accf7/dartpad/dartpad.github.io"
  ];
 
  @override
  Widget build(BuildContext context) {
    print(images[widget.i]);
    return Container(
        height: 50,
        color: Colors.grey,
        child: Row(children: [
          AspectRatio(
            aspectRatio: 3 / 2,
            child: images[widget.i] == null
                ? Container()
                : Image.network(images[widget.i]!, fit: BoxFit.cover),
          ),
          Text("Title " + widget.i.toString()),
        ]));
  }
}
Nabin Dhakal
  • 1,949
  • 3
  • 20
  • 48