Open esDotDev opened 4 years ago
I think the core problem here is that, without knowing the item extent is fixed, we have no way of knowing that it won't dynamically change on each scroll, and so have to do all the work to build all the intermediate items so we can figure out scroll position and keep the scrollbar accurate.
I suspect if you really profiled this down to the expensive methods, it's not just in calculating the max extents, but also in calculating the extent of each sliver that has to be rendered for the screen as you jump 1000s of items in the list. With a fixed extent list we can take a bunch of shortcuts. With a variable extent we can't.
I suspect that suggestion 1 would not help because scrolling means something has changed, and we have no way of knowing that the application didn't decide to make certain items bigger or smaller as you scroll (imagine an expansion card that collapses when the user starts scrolling for example).
Suggestion 2 would not help because we'd still need to calculate all the actual extents when you make a very large jump.
Suggestion 3 might actually make things more expensive when you're using layout builders, since those will require a speculative layout pass in addition to the actual layout pass.
We might be able to fudge something here and check how fast you're scrolling, and take some lower fidelity shortcut when scrolling very rapidly. But we'd have to really profile this to know where exactly the expensive parts are first.
At your application level, you might be able to do some similar shortcut, e.g. rebuild the listview with a fixed extent once the user starts scrolling fast, and then rebuild it again with a variable extent once they slow down/stop. But this would probably result ins ome weird jumpiness of the scroll bar.
Thanks for the thoughts, given the performance I'm seeing, > 1000ms frame times, it feels like a speculative pass that takes a ms or two, could be a viable tradeoff?
Those were just my initial ideas, I'm sure theres many others. They key takeaway I think is that the algorithm chosen for drag-based scrolling on very large lists is not performant when used on desktop/web with scrollbar-based control.
What I was trying to get at with my solutions, were to somehow offload the calculations to the renderer, so they could either be cached, or calculated by the list item, rather than having to go through the expensive layout pass. Obviously it would be ideal if the layout pass was cheap enough to just work, but if not, this could be an alternative approach.
"we have no way of knowing that it won't dynamically change on each scroll, " To this end, it would be nice if there was someway for the application to inform the list that no, we will not change things during scroll, and in the rare case we we do, we will let you know so you can re-calculate. Then, in a worst-case scenario, we get what we have now... but in most cases, it would run really well.
[Edit] Tried setting itemExtents while dragging large amounts, and removing when dragging small. Doesn't really work, it runs smooth initially, but then just grinds to a halt as soon as extents is removed.
[Edit] Tried setting itemExtents while dragging large amounts, and removing when dragging small. Doesn't really work, it runs smooth initially, but then just grinds to a halt as soon as extents is removed.
May I ask how you implement such a logic? @esDotDev
Here's a second, simpler example that reproduces this issue. (copied over from #54195)
import 'package:flutter/material.dart';
void main() {
runApp(MyHomePage());
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView.builder(
itemCount: 10000,
itemBuilder: (context, index) => Text("$index"),
),
),
);
}
}
At the moment I have the same issue on severals projects. I noticed that problem is more apparent when you add physics: BouncingScrollPhysics() to ListView.builder.
I'm offering a workaround related to this issue in here. I hope it will be useful for you.
That looks promising, thanks!
Any news on this? Any workaround??
I tried that workaround and it made things much worse.
ItemExtent works, but a very large list with dynamic heights in Flutter is not really doable with the current implementation of ListView/ScrollController + JumpTo. There's just way too much constant measurement occurring.
We have an update on the stable channel (1.17), I don't see any improvements on this topic, still sticking with the workaround.
This is the major bug in flutter, a very clear performance mirror as compared to react-native. Flutter vertical scrolling specially with images have jerking issues.
here is an example app on playstore (Flutter demo: CINEMA) you can download and test, but that i am sure its flutter bug because all similar apps or with long list views this happens.
More examples in flutter official app: Flutter Gallery
Flutter Gallery -> (Crane A personalized travel app) -> vertical scroll listview in bottom (Jerk is clearly visible)
Flutter Gallery -> (Rally A personalized finance app) -> Login -> vertical scroll it (choppy scrolling Jerks)
Above examples are not debug all are on production playstore, on stackoverflow this is under discussion but everyone is doing just tricks which is not appropriate way to develop an production grade application.
We have an update on the stable channel (1.17), I don't see any improvements on this topic, still sticking with the workaround.
Well I have suggested a workaround but it seems there is much greater issue related to the scrolling.
One thing I noticed that is in every issue there is a member saying "Please include a small reproducable peace of code.".
The issue is literally can be seen on Flutter Gallery app, please don't copy paste same line as the mentioned bug literally occurs in the very simple scenarios like in the Flutter Gallery: Crane.
We have an update on the stable channel (1.17), I don't see any improvements on this topic, still sticking with the workaround.
Well I have suggested a workaround but it seems there is much greater issue related to the scrolling.
One thing I noticed that is in every issue there is a member saying "Please include a small reproducable peace of code.".
The issue is literally can be seen on Flutter Gallery app, please don't copy paste same line as the mentioned bug literally occurs in the very simple scenarios like in the Flutter Gallery: Crane.
100% i agree, everyone can see that bug in their own flutter gallery app, i don't know why they are asking for piece of code.. Even after this response i start developing my projects on react-native because its big risk to put million dollars on flutter right know. Very basic widgets are buggy.
Jank while scrolling large lists is a decade old problem in client side development. This being assigned a P4
is disheartening.
This problem is relatively easy to solve for fixed extent lists. It's much harder to solve efficiently for variable extent lists.
I think what we'll need to do - which is what other platforms do - is to cheat on the scrollbar positioning. A few people have started to look at this over the last couple years and not finished it.
This is a very solvable problem, but it's not as easy as it first seems. It's related to https://github.com/flutter/flutter/issues/31637 and https://github.com/flutter/flutter/issues/12319 - which was closed with a package available. Can you see if the package solves your needs?
Specifically, https://github.com/google/flutter.widgets/tree/master/packages/scrollable_positioned_list
/cc @tarobins
This problem is relatively easy to solve for fixed extent lists. It's much harder to solve efficiently for variable extent lists.
I think what we'll need to do - which is what other platforms do - is to cheat on the scrollbar positioning. A few people have started to look at this over the last couple years and not finished it.
This is a very solvable problem, but it's not as easy as it first seems. It's related to #31637 and #12319 - which was closed with a package available. Can you see if the package solves your needs?
All these tweaks i already go through, as i already mentioned tricks is not the solution. Flutter team should accept and fix this bug on high priority. Apps links i shared are production apps even 1 app is flutter official app and bug still existed in it.
Just try to make Google Photos like gallery app with just 1000 photos, and flutter sucks.. same experiment i did with react-native with 10K photos it works like charm no jerking or any wrong user experience.
The Flutter team is a highly distributed group, all of whom are pretty busy with important issues. The best way to show the importance of this issue is to give it a thumbs up on the top level comment, which raises visibility to the triage process.
Anyone is welcome to write a doc or open a patch on how to fix this :) It's mainly a matter of finding someone interested enough and with the time to do it.
This problem is relatively easy to solve for fixed extent lists. It's much harder to solve efficiently for variable extent lists.
I think what we'll need to do - which is what other platforms do - is to cheat on the scrollbar positioning. A few people have started to look at this over the last couple years and not finished it.
This is a very solvable problem, but it's not as easy as it first seems. It's related to #31637 and #12319 - which was closed with a package available. Can you see if the package solves your needs?
I think the main issue is the performance of the scroll behavior. It is understandable the performance won't be good as the fixed extend lists but it should not cause any jank when dealing with dynamic sized children on Listview
, especially with the simple widgets.
The Flutter team is a highly distributed group, all of whom are pretty busy with important issues. The best way to show the importance of this issue is to give it a thumbs up on the top level comment, which raises visibility to the triage process.
How about #22314? This was closed as there are lots of comments causing a "noise". I think there is an endless loop here: Client opens a new issue, there are many comments regarding of the issue, Flutter Team decides to close the issue and propose to open a new issue. Anyone is welcome to write a doc or open a patch on how to fix this :) It's mainly a matter of finding someone interested enough and with the time to do it.
Yeah, clients would definitely figure out how to solve this problem and consider being a collaborator instead of developing their own Flutter application.
@firatcetiner - that issue is similar to this one but had a different root cause. It's possible that issue was meant to include this one, but in any case it's better to have issues that are more focused on specific problems than umbrella issues (e.g. it's better to have an issue about the touch input aspect and another issue about the layout speed aspect).
My understanding is that this issue covers the layout speed aspect, specifically when a scrollbar is used. No one is suggesting closing this issue, because it's not fixed :)
And if you're not interested in working on this issue that's fine - someone else will at some point. It's just a matter of when, and how many other issues exist that are higher priority (as determined by customer demand) than this one.
Specifically, https://github.com/google/flutter.widgets/tree/master/packages/scrollable_positioned_list
Thanks for suggesting the workaround. However, it does not address the use case of using a scrollbar: https://github.com/fluttercommunity/flutter-draggable-scrollbar/issues/2.
The best way to show the importance of this issue is to give it a thumbs up on the top level comment, which raises visibility to the triage process.
it's better to have issues that are more focused on specific problems than umbrella issues
I completely agree with the first statement. But if a certain problem is triggered by a combination of underlying problems, I don't thing the best way to solve it is to break it down into Github issues and to solve them based on the amount of traction each of them receive. The root problem might never get fixed.
At the end of the day Flutter is a product, and it makes life difficult for consumers when the team does not take ownership of larger problems and treats it like a code-base instead.
I don't really understand the pushback here. The current behavior is extremely inefficient when moving large distances in the list. So, something needs to be changed in the ScrollController no?
"With a fixed extent list we can take a bunch of shortcuts."
We as the app developer (working in a specific domain) can take many more shortcuts than you (working in the general), if you provide us the means. If the 3 original proposals don't work, can you offer one that does? How can we provide the hinting for you??
This is the core of the issue. Solving the general use case for measuring variable extent renderers is a cpl orders of magnitude harder than doing it inside the specific app domain. Help us, to help you, to help us :)
I'm not quite clear on who's pushing back on anything. I think everyone involved in this thread agrees there's a bug that needs to be fixed.
Fair. But I'm not sure if we're working towards the ability for app developers to have more control, or chasing a more optimized internal approach.
FWIW, the solution proposed in https://github.com/flutter/flutter/issues/12319#issuecomment-569296423 seems to be setting the cacheExtent
property to inflate all the list items, even those that are outside the current viewport.
This will be very expensive for large lists with non-trivial widgets.
Maybe related #28204
I've recently profiled one list view in my app that wasn't performing that well. Many frames were taking too long because of a Semantics step.
Putting an ExcludeSemantics
above my list turned out to be day and night in terms of scrolling performance.
Obviously, not ideal to turn off a11 features. Just wanted to share in case it is related or it helps someone.
@davidmartos96 Thank you for sharing.
Yesterday I tried wrapping child widgets in the ListView
with TweenAnimationBuilder
using with a Transform.scale()
just for fun, not a big deal. I don't know why but the scroll performance is much better now. I remember someone else has mentioned this behavior few months ago but I couldn't find the exact comment.
I am going to investigate why this has an impact on scroll performance in my spare time.
@davidmartos96 Thank you for sharing.
Yesterday I tried wrapping child widgets in the
ListView
withTweenAnimationBuilder
using with aTransform.scale()
just for fun, not a big deal. I don't know why but the scroll performance is much better now. I remember someone else has mentioned this behavior few months ago but I couldn't find the exact comment.I am going to investigate why this has an impact on scroll performance in my spare time.
best way to test its performance, simply try to create image gallery like, google photos. i tested in both flutter and react-native.. and results are horrible.. in react its smoothness and scroll experience is exactly as native, no one can judge its react, but flutter after 30-40 photos you will feel its not smooth at all, it jerks also sometimes screen flicker. i don't know exact english terms to explain. but if you will try with different speed of scrolling, randomly move up down etc you will feel huge difference.
There is only way flutter can satisfy they must release official demo app of image gallery.. 😄
Maybe related #28204 I've recently profiled one list view in my app that wasn't performing that well. Many frames were taking too long because of a Semantics step. Putting an
ExcludeSemantics
above my list turned out to be day and night in terms of scrolling performance. Obviously, not ideal to turn off a11 features. Just wanted to share in case it is related or it helps someone.
Seems flutter should change the statement "Flutter is Google’s UI toolkit for building beautiful , natively compiled applications for mobile, web, and desktop from a single codebase."
performance and user experience is everything, otherwise nobody care either its running on ARM or Converting Javascript to Native..
@vishnukvmd @esDotDev : I believe the ScrollablePositionedList
suggested by @dnfield will have a dramatic speedup for your scroll bar use case. See the demo code at https://gist.github.com/liyuqian/6fe7a6be4b5035bbdbdba57d3356b43d
As Flutter is making web and desktop its first-class platforms on par with mobile, I feel that having ScrollablePositionedList
as a 3rd party package is probably insufficient. We should perhaps add it or something similar directly to the Flutter framework. It's so common to use scroll bars, trackpads, and mouse wheels on web/desktop to jump to a scroll position super fast. It's very different on mobile where the fastest touch scroll speed is just one screen per input event sampling period (which is usually 1/60 second). CC @Hixie @goderbauer to see if introducing this in the framework makes sense.
BTW, I would not call a solution such as ScrollablePositionedList
as "cheat on the scrollbar positioning" because there's still a nice 1:1 mapping between scroll bar and the scroll list position, and you can always expect scroll x, scroll y, scroll -(x + y) to get you back to the original position.
Similar approaches could also be applied to the use case in https://github.com/flutter/flutter/issues/52207#issuecomment-610486875 without a scroll bar (I believe the fast jump there is triggered either by trackpad or mouse wheels). When Flutter framework detects a very large jump (e.g., scrolling more than 1 screen of pixels within 1 frame), it no longer jumpTo
by pixel offsets, but instead jumpTo
by item index differences. The index difference could be calculated by something like pixelOffset / averageItemExtentOnScreen
.
Our first step towards fixing this might be to add the sample code provided by @esDotDev and @modulovalue as Flutter benchmarks, so we can track our progress and avoid future regressions once we fixed it. So thank you for providing the sample code, and please feel encouraged to follow https://github.com/flutter/flutter/wiki/How-to-write-a-render-speed-test-for-Flutter to directly write a PR to add your sample code as a Flutter performance test!
@liyuqian the "cheat on scroll bar position" comment is if we try to make the current default implementation of ListView etc. work better for scroll bars.
For example, we could estimate the extent of the ListView based on what's currently laid out within its cache extents. But this won't be accurate for lists that currently have very large or very small items in the extent compared to the rest of the list - so the scroll bar would have to "jump around" as you scrolled, appearing to near the end and then actually adding more, or appearing to be very far from the end and then suddenly more rapidly getting closer. You can observe this effect in scroll views on native platforms today if they're infinite (or at least dynamically loading). For example, as you scroll down the Facebook timeline page the scrollbar jumps around.
@vishnukvmd @esDotDev : I believe the
ScrollablePositionedList
suggested by @dnfield will have a dramatic speedup for your scroll bar use case. See the demo code at https://gist.github.com/liyuqian/6fe7a6be4b5035bbdbdba57d3356b43d
I don't think so, since this only scrolls by index, but we are desiring a scrollbar that scrolls using pixel values as is expected in most lists on both desktop and mobile.
It's frustrating as the sizing info for non fixed lists is usually trivial to calculate in the app domain, but the framework locks us out from trying to help it, so instead it just spins its wheels doing millions of pointless layout calculations.
@dnfield : it seems that we're already "cheating" in that sense due to _extrapolateMaxScrollOffset
. I think we could use the same extrapolation logic to quickly jump to a new offset that's either far above or far below the current offset. A jump-around scroll bar seems to be much more acceptable than a non-responsive list.
@esDotDev : I agree that allowing developers to send us a list of item sizes will surely make our implementation much easier and faster. Allowing ListView
to take a List<double> itemExtents
instead of a single double itemExtent
seems to be simplest API change I can think of. However, it also increases our API surface, maintenance cost, and developers' burden to maintain or calculate that list. It would be even better if we can achieve fast jump without that list or any modification to our existing APIs, but that may force us to have a jump-around scroll bar as other platforms have. @dnfield @Hixie @goderbauer : what's your thought of allowing developers to provide ListView
a list of extents?
You can already provide a the extent if it's fixed. If it's not fixed, I'm not clear what mechanism would be helpful here. It'd be really helpful to see a test or concrete use case.
It just has to support lazily loading/building the elements - but I think as soon as you allow that to happen and allow for variable sized slivers, you'll run into this problem where you just have to estimate it and try to get as close as possible with what's available in the cache extent.
The use case here is variable height extents, but the developer can cheat and provide those to you, so you that you do not need to measure via layout.
Use Case 1 I have renderers with 2 states, open and closed, I know my closed are 40 my open are 100. I can simply track the state of each renderer in an array, and provide you the list of extents for each. No measuring needed, no layout needed, just pure math. I can take this a step further, and have animated extents that update each frame as a specific renderer is opening or closing. You can now easily and cheaply jump to any pixel position, and know exactly which renderers should be there.
Use Case 2 I have renders with several rows of data (name, address, country, email etc), not each renderer has all data, some are 1 row, some are 4. Each row is 20px. I can easily determine how many rows each render takes (by just looking at my data) and pass that size into the list, so the list again, does not need to do a single measurement, while my list retains the ability to have renders of 4 different sizes.
Use Case 3
I have a very large list, each row is 100px tall, I also have a few "Header" rows, that are 30px each. I could simply provide you the height, based on whether widget.isHeader=true
, saving the list all of that measuring. Otherwise this very basic use case is simply not achievable.
@liyuqian I agree this API sounds simple and effective for the use case. I don't think the general case is actually solvable, and I also don't think developers expect to have 5000 variable height items, at 60fps without doing a little bit to help the framework, we realize this is not realistic. It's totally fine to expect them to do a little extra work to get performance here imo, we will be happy to if it gets the end result we need.
And in case you think this is a made up use case, we recently built a Google Contacts App for desktop, you can have 25,000 emails in a google contacts account, and we would really have liked to have variable height renderers but could not because the list + scrollbar could not be trusted to perform at higher renderer counts.
@liyuqian To address the developer effort, I believe as you are proposing, List<double> ItemExtents
would be optional ya? Otherwise it will fallback to the layout method? That seems very reasonable.
I just created a simple JsFiddle example (in new window) that I believe illustrates the case. I tested in in all major browsers (Chrome, Firefox, Edge) and it seems that it has good scrolling performance. Items have variable heights but I did not provide any itemExtents
.
It seems that browsers defer layout calculation for long lists and give maximum priority to have scroll thumb moving with 60+
FPS. Can we have an easy way to develop a similar case in Flutter without providing itemExtents
and still having good scrolling performance?
@dnfield : I just coded a sample app LongListVariableExtent
https://github.com/flutter/flutter/pull/61594/files that's similar to @esDotDev 's open-or-closed-item case. It also includes a potential RenderSliverFixedVariableExtentList
implementation to demonstrate the speed difference. It's quite simple as it reuses most of RenderSliverFixedExtentBoxAdaptor
.
For a more polished implementation, I think we can either (1) use binary search to further speed it up when the list itemExtents
is mostly unchanged, or (2) change the List<double> itemExtents
to a generator double itemExtent(int index)
for more dynamic cases and avoid the big initialization cost.
@roman-petrov : theoretically I don't think there's anything that prevents Flutter to match browser's behavior and performance. We can even avoid the black flash and just show the list scrolling as fast as possible but not being able to catch up with the scrollbar until a couple hundred milliseconds later. But that seems to be a more complicated change than either to extrapolate the extent for fast jump, or to let developers send in List<double> itemExtents
.
@dnfield , I understand that implementing browser-like scrolling behavior and performance might be more complicated. But IMHO this also can give more value in terms of Flutter usability & performance because Flutter users will not have to specify any itemExtents
to implement efficient scrolling.
Moreover, in many cases (including the case in my JsFiddle) itemExtents
are not known at compile time and require layout computation. So, if I understand correctly, it would not be possible to improve scrolling performance using itemExtents
when ListView items have "dynamic" heights.
Its true that it's not always practical for even the developer to compute the size, for example paragraphs of multi-line text. So passing sizes is not a silver-bullet, just a very nice tool in the toolbox.
Even with the above example though, a renderer can actually paint the text, and measure it once and from then on return it's cached height.
This is actually a critical problem in my view for flutter. Unfortunately I can't get my head around how flutter exactly handles the viewport + scrolling.
In my view the scroll controller is missing the following methods:
By providing such cheap methods, you can implement a datastructure where you can efficiently jump to any index of the list by actually modifing the under laying data which correspond to the indices. This is demonstrated here: https://dartpad.dev/efcc5d97ff9db42c3f17d2310695282f . Try to inspect the logs when pressing the buttons at the bottom.
However the performance penalty occurs when you press "jump to 500" and scroll up, say 100 items. After pressing "jump to 500" again, it renders all the "scrolled" items again which is actually causing the biggest performance penalty (#24791).
Another point I want to mention is when dealing with items with dynamic height an "cheap" approximation for extents should be used, consider whatsapp as example. Something like viewportSize/itemsInViewPort * totalItems
Hi @liyuqian, I really like your idea. Do you think that it would be possible to extract the LongListVariableExtent
into a library for now?
One solution would be to do something similar to SliverPrototypeExtentList but with multiple prototypes, where each item in the list has a way to report which prototype it'll be like. That would work if the children aren't all arbitrarily sized but instead all fit into a small number of sizes. (It could even be adjusted to support the occasional special case where we'd have to measure it.)
Fundamentally the problem here isn't the scroll bar, it's just that to scroll to a position, we need to know what's at that position, and to do that, we have to know how big everything is before that point. If we scroll to position 10,000, we have to lay out 10,000 pixels' worth of widgets to figure out which one is at that position.
@Hixie To piggy back on what @esDotDev proposed earlier, would it help if each list item could optionally set its width
and height
attributes so that we don't actually have to lay out all the pixels to compute which item is going to be at what position?
If you know the precise height of each item then it's trivial. Just copy all the code that currently implements FixedExtentList and make it be able to get the height of each item when it does its math, without having to actually build or lay out the children.
If you know the precise height of each item then it's trivial. Just copy all the code that currently implements FixedExtentList and make it be able to get the height of each item when it does its math, without having to actually build or lay out the children.
Is the suggestion that developers should do this everytime they want this ability? This may be trivial to the Flutter team, but there's no way your typical developer will have any clue what FixedExtentList even is. In practice it's more like: "Just understand the entire inner workings of the flutter scroll implementation, and then copy all the code that currently implements FixedExtentList ".
My argument is that quite often the developers do know the height of their 'variable' content, or they can devise some fairly simple system to figure it out. The framework should just be friendly to this concept that it needs help here, and to let us provide the hints. In a simple way that doesn't force us to dive into thousand line class files. I described some examples here, of course they are literally endless: https://github.com/flutter/flutter/issues/52207#issuecomment-659127615
Personally whenever I've ever needed this, I have known the sizes or could easily devise the sizes of each of my rows. The exception I can think of, is a facebook style feed of mixed media where you really don't know what will be there. However those types of lists are almost always lazily loaded and the scrollbar is resized on the fly.
Here we're really talking about displaying massive lists of data, on Desktop, that have some degree of variability to them, often minor. Think a long list with single, double or triple row entries (height => _padding * 2 + rowCount * _rowHeight
), or a collection of headers and rows (height => _isHeader? 30 : 60
). There is absolutely no reason the list should be doing thousands of measurements in this case, when I can simply give you the value.
The problem isn't the scrollbar, but it's the scrollbar that exposes the inherent flaw in the design, which seems clearly optimized for the limitations of finger-based scrolling.
Probably just the naive developer in me, but I don't understand why we cant use some sort Size getSize()
callback, the ScrollController can ask us our size, we can give it width, height or nothing, and then the controller can use that data opportunistically. This seems pretty elegant and powerful to me.
I totally agree with @esDotDev and I also think many scrolling performance problems could be solved if itemExtend could be set per item instead for all the items. However I am not sure if it is sufficient to do that. It means if you have 10000 items it still needs to call the height function 10000 times if a big jump is made. Instead the scrollcontroller should be extended to have a ability to jump x numbers of items, and after the jump the scrolling is resetted. In conclusion the next api's should be added:
Size getItemSize(int idx)
as named constructor parameter to the list classesScrollController.jumpToIndex(int, {int anchor})
, where anchor is from 0 - 1.0 where 0.5 is the center of the scrollview.When jumpToIndex is used, the list is rerendered from that index (up and down) and the scrollposition is resetted. This requires different layouting mechanism of the viewport however...
Related: https://github.com/flutter/flutter/issues/153085
Update September 2023:
Update from July 2023: https://github.com/flutter/flutter/issues/52207#issuecomment-1648672384
Update from September 2021: After much discussion, this https://github.com/flutter/flutter/issues/52207#issuecomment-782708343 suggestion emerged as a pretty good proposal. Basically, SliverList could short-circuit actually instantiating and laying out children if, once built, they are discovered to implement the PreferredHeight protocol. The next step is to implement this. -@Hixie
[√] Flutter (Channel master, v1.15.4-pre.241, on Microsoft Windows [Version 10.0.18362.657], locale en-US)
Here is an example (~80 lines) of a 10,000 item ListView, which attempts to uses a simple scrollbar to drive scroll position: https://gist.github.com/esDotDev/792425c2cdfef947ce514b8ab70511e6
The performance is very poor on all platforms, Desktop, Web, Android, even when using profile mode on Android. iOS has not been tested.
I'm seeing frame render times up to 1300ms on Windows 10, with a Ryzen 3700x. Android, in profile mode, with a Snapdragon 845, has similar results. Visually, you can easily produce a 2-3 second lag by just scrolling up and down a couple of times. http://screens.gskinner.com/shawn/2020-03-08_12-57-31.mp4
Setting the .itemExtent property solves the problem, but severely limits the flexibility of the list, no expanding cards, no ability to size rows to content, etc.
Problem in a nutshell We need the ability to have many list items, of variable height, while retaining the ability to set scroll position efficiently. This is especially important on Web and Desktop which typically use scrollbars to traverse lists.
Proposed Solutions
It would be nice if the list could handle this itself, seems like it calculates maxExtents each frame, when we really only need it calculating when the size of something has changed. Potentially this could be exposed so we could call it manually (scrollController.calculateExtents()) when we know something is changing.
As a simpler workaround, you could expose .maxExtents so I as the programmer of the list can specify max extents with some basic math. ie, if I know my open cards are 40px heigh, and my closed are 20px, I can track open/close cards, and provide the list with the max extents very quickly, without it needing to measure.
Could potentially leverage PreferredSizeWidget to reduce the calculation costs for the list. Then each list item can return it's own size, and the developer can come up with novel ways to reduce that calculation cost. For example, I could have my closed items all return a hardcoded value, while my "expanded items" use a LayoutBuilder to calculate their height. Or I could simply have multiple hardcoded values, and return the right one depending on the state of the renderer.