Ionic-framework: Virtual scroll flickering when we update it with new data

Created on 20 Feb 2019  路  30Comments  路  Source: ionic-team/ionic-framework

Bug Report

Ionic version:

[x] 4.0.0

Current behavior:
We've a scenario in our app. We load the cached data from the local store and when we receive the latest data from server, we will update the list.

So when we're doing this, the page is flickering when update happens. I understand that when we set the data it may flicker, but trackBy option should solve this, which is not happening.

Expected behavior:
It should not flicker when we update the list with the latest data.

Steps to reproduce:
Add a viritual-scroll to a page and initialize with some items and update the list with new set of data.

Related code:

https://github.com/roopeshreddy/virtual-scroll-issue

<ion-virtual-scroll [items]="items" [trackBy]="trackByFn">
    <ion-item *virtualItem="let item">
      <ion-icon [name]="item.icon" slot="start"></ion-icon>
      {{ item.title }}
      <div class="item-note" slot="end">
        {{ item.note }}
      </div>
    </ion-item>
  </ion-virtual-scroll>

Other information:

We've tried the same in Chrome browser, iOS simulator and real iPhone 7 device.

Ionic info:

Ionic:

   ionic (Ionic CLI)             : 4.10.3 (/usr/local/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.0.1
   @angular-devkit/build-angular : 0.12.4
   @angular-devkit/schematics    : 7.2.4
   @angular/cli                  : 7.2.4
   @ionic/angular-toolkit        : 1.4.0

System:

   NodeJS : v10.15.0 (/usr/local/bin/node)
   npm    : 6.5.0
   OS     : macOS Mojave
core bug

Most helpful comment

Same problem here. As a solution I have set the itemHeight function, so the virtual scroll is not flickering any more.

In my particular case it was easy since all my items have the same height:
HTML:

<ion-virtual-scroll [items]="items" [trackBy]="trackByFn
approxItemHeight="52px" [itemHeight]="itemHeightFn">
   ...
</ion-virtual-scroll>

TS:

...

itemHeightFn(item, index) {
    return 52;
}

Now only the headers are flickering...

All 30 comments

Same problem here

Same here, despite I could verify that the trackBy function is getting called.

@manucorporat - Any help on this?

Same here, despite I could verify that the trackBy function is getting called.

Can confirm that the trackBy function is called.

I see this issue too, if it鈥檚 the same thing for what I am seeing it looks like the cards are all being generating and their height expanded/stretched out within milliseconds.

It happens so fast it just looks like a flicker of all the cards being rendered real fast, but overall this gives the app a poor look and experience.

I have two virtual scroll lists that show/hide based on an ngIf, and each time I toggle I see this flicker. Tried to use trackBy, tried to use hidden, but with no luck. Using hidden with virtual scroll is another issue...

Same problem here. As a solution I have set the itemHeight function, so the virtual scroll is not flickering any more.

In my particular case it was easy since all my items have the same height:
HTML:

<ion-virtual-scroll [items]="items" [trackBy]="trackByFn
approxItemHeight="52px" [itemHeight]="itemHeightFn">
   ...
</ion-virtual-scroll>

TS:

...

itemHeightFn(item, index) {
    return 52;
}

Now only the headers are flickering...

Is there any workaround this flickering issue? Unfortunately I am stuck with items of varying heights in the virtual scroll.

---edit----

I have found a solution for my situation!

  1. Do not recreate the array that goes into virtual scroll's [items] (it's already warned in the doc).
  2. When loading more, run virtual scroll's "checkEnd" method.

Hinted from reading the documentation closely!

Happy coding!

Are you using headers/footers? Are they flickering if you do not recreate the array?

Just tried. For me, headers are still flickering even if you do not recreate the source array.

Looking at the source, i'd assume that:

  1. The trackBy is indeed used to track changes (which is why it is being called):
    https://github.com/ionic-team/ionic/blob/8beeff2c52b331eb8901602d88aadea37101e89e/angular/src/directives/virtual-scroll/virtual-scroll.ts#L145
  1. The tracked changes are correctly calculated and then completely ignored, and the whole list gets updated, no matter what changed:
    https://github.com/ionic-team/ionic/blob/8beeff2c52b331eb8901602d88aadea37101e89e/angular/src/directives/virtual-scroll/virtual-scroll.ts#L156-L161

@gartorware using the itemHeight-function does prevent the flicker, but i'd assume (haven't tested it enough) that it is still unnecessarily updating the whole list.

It probably doesn't flicker with itemHeight set, because with itemHeight the virtualScoll doesn't need to ask the browser for positions and sizes of the elemets, so the browser isn't forced to actually paint the items before the virtualScroll can continue.

Setting itemHeight is definitely always a good option, if you can calculate the exact size yourself (without asking the browser for measurements). However, the virtualScroll should only update the parts of the list that changed, regardless of whether itemHeight is set or not.

Hello! I've just opened a PR adding 2 more functions for header and footer, that work like the itemHeight @gartorware used to resolve the flicker on data change. If this is merged, headerHeight and footerHeight can be optionally used to provide the exact height size of them, resolving the flicker they have.

Thanks! This issue has been resolved and will be available in the Ionic 4.7.0 release. If anyone encounters new issues with virtual-scroll, please open a new issue.

@liamdebeasi a related issue was resolved, but not this one.

Sure, the headerHeight and footerHeight methods do help if you can calculate those heights, but:

  • trackBy is still broken (because the ion-virtual-scroll always updates every element in the list, even if it is unchanged)
  • and incorrectly documented (the documentation calls it virtualTrackBy, which doesn't exist).

The broken trackBy is a big part of the reason this issue was opened, and the cause of unnecessary re-rendering, and consequently flickering of list items (if you cannot calculate the heights beforehand)

Thanks! I reopened the issue and we will investigate.

Thank you for reopening the issue!
If you need some place to start investigating, you might want to take a look an earlier comment from me:
https://github.com/ionic-team/ionic/issues/17540#issuecomment-511136665

i wouldn't bet money on it, but otherwise i'm somewhat sure, that that is the cause (at least one of the causes) why the trackBy has no effect

I just found a work around for the flickering header.
I was checking the css and during the loading part a class .virtual-loading is assign to ion-item-divider element. Forcing the class with a state of opacity: 1 did the trick.

ionic 4.11.4 and the flicker still happens even if approxItemHeight is added. Man, this needs to be a priority.

Inspired by @9i0xin9 It seems I figured it out by putting opacity:1 on my virtualItem div like this :

<div *virtualItem="let item;" style="opacity:1;"> <custom-comp [data]="item" ></custom-comp> </div>

the flicker does not happen anymore (Ionic 4.11.5) but this not a very elegant solution ...

As mentioned by @9i0xin9, to fix the Header flicker, I used opacity: 1 like so:
<ion-item-divider sticky="true" *virtualHeader="let header" style="opacity:1;">

Now the Header would not flicker, BUT the would start flickering again, despite adding the following function to the <ion-virtual-scroll>:

//HTML
<ion-virtual-scroll [items]="allData" approxItemHeight="320px" [headerFn]="populateListHeader" [itemHeight]="itemHeightFn">

// TS
itemHeightFn(item, index) {
        return 71;
}

As of now, I don't see a way to fix both Virtual Item flicker and Header flicker at the same time. If anyone knows of any solution for this, please share.

Thank you.

Seeing the same thing with ionic 5 in chrome, with a very basic virtual scroll, even with just 3 items in the array.

I do use the approxItemHeight.

Ionic info

Ionic:

   Ionic CLI                     : 5.4.13 (C:\Users\Kees\AppData\Roaming\npm\node_modules\ionic)
   Ionic Framework               : @ionic/angular 5.0.0
   @angular-devkit/build-angular : 0.803.23
   @angular-devkit/schematics    : 8.1.3
   @angular/cli                  : 8.1.3
   @ionic/angular-toolkit        : 2.1.2

Utility:

   cordova-res : not installed
   native-run  : not installed

System:

   NodeJS : v12.14.1 (C:\Program Files\nodejs\node.exe)
   npm    : 6.14.4
   OS     : Windows 10

Setting the right heights and approx heights did do the trick for me.

HTML
[headerHeight]="headerHeightFn" [itemHeight]="itemHeightFn" approxItemHeight="50px" approxHeaderHeight="30px"

TS File

headerHeightFn(item, index) {
    return 30;
}

itemHeightFn(item, index) {
   return 50;
}

Same problem here using the ionic version 5, apparently it's related to trackBy, without it it works..

Same problem with 5.26.0

I have same idea as https://github.com/ionic-team/ionic-framework/issues/17540#issuecomment-511136665

trackByFn don't effect to rendering. We expand this line will effect to nodeRender method:

https://github.com/ionic-team/ionic-framework/blob/master/angular/src/directives/virtual-scroll/virtual-scroll.ts#L172-L175

Expected behavior:
trackByFn must effect to rendering. Diff patten handle rendering pattern like Angular ngFor:

https://github.com/angular/angular/blob/master/packages/common/src/directives/ng_for_of.ts#L229-L246

Related code:
https://stackblitz.com/edit/ionic-v4-angular-tabs-4ehr1o

for any new item i get my virtual screll ion list items rebuild,

itemHeight fix the problem

[INFO] I created support ion-virtual-scroll service sample in angular instead of trackBy:
https://github.com/rdlabo-team/support-virtual-scroll-service

OR you can use virtual-scroll of Angular CDK:
https://github.com/ionic-team/ionic-docs/pull/1406/files

For items added to the top none of the work arounds is fixing it for me, I tried itemHeight, trackBy but these don't work when item is added to the top.

I also tried not changing the reference to items and just adding the item with unshift then using checkRange but the problem is calling checkRange(0) creates the flicker while using something like checkRange(0, 1) will update the first item only but not the rest of the items. (Note checkEnd doesn't work since I add the item to the top).

Are there any other solutions?

Version: 5.4.3

Regarding the issue I brought up with adding items to the top the virtual scroll is working just fine it was an issue on my side of removing items from the DOM (user error). The only issue I see now is the broken trackBy which as been mentioned previously. Hopefully there is a fix for that at some point.

Was this page helpful?
0 / 5 - 0 ratings