3

I have a list of items (5-6 items) that are displayed using a ListView.builder. Each item contains a DropdownButton widget for the user to pick a number from 1 - 1000, thus containing 1000 DropdownMenuItems.

I implemented it as shown below, but the problem is that scrolling down the ListView is too slow and stutters. Even if the listView has 5 or 6 items, but note that each of them has an embedded DropdownButton containing 1000 DropdownMenuItems.

Is there a fix? Or another way to achieve my requirement?

N.B: Even if I reduce the number of DropdownMenuItems to 100, it still stutters when scrolling down the ListView.

class List extends StatelessWidget {
   final List<Item> // Contains 5 items.
   final List<int> quantityList = List<int>.generate(1000, (int i) => i);
 //--
   child: ListView.builder(
                  itemBuilder: (buildContext, i) {
                    return MyItem(
                      quantityList,
                    );
                  },
                  itemCount: items.length(),
                )

class MyItem extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        child: DropdownButton<int>(
          items: quantityList
              .map(
                (int e) =>
                DropdownMenuItem<int>(
                  value: e,
                  child: Text(e.toString()),
                ),
          )
              .toList(),
        ),
      ),
    );
  }

Edit

I changed MyItem class to be as below, but still, the same problem exists.

Tried using ListView and ListView.custom instead of ListView.builder, to build the whole list in advance instead of lazily according to this, but still same issue.

I also tried running the app using --profile configuration to simulate a release version. The performance is better but still suffers from terrible stuttering and lag. Tested on emulator and physical device.

 class MyItem extends StatelessWidget {
      List<DropDownMenuItem> quantityList; // contains 1k 
      @override
      Widget build(BuildContext context) {
        return Container(
            width:300,
            height:300,
            child: DropdownButton<int>(
              items: quantityList, 
            ),
          ),
        );
      }
Mena
  • 3,019
  • 1
  • 25
  • 54
  • What happens if you replace DropdownButton() with a SizedBox() of the same width/height? What happens if you replace DropdownButton() with a Column(children: quantityList)? Do either of these exhibit the same performance issues? – Pat9RB Sep 21 '21 at 15:47
  • @Pat9RB No, if I use a very long column of 1000 Text Widgets there is no problem. – Mena Sep 22 '21 at 14:07

2 Answers2

2

ListView will create and destroy widgets as they come in and out of view. Your MyItem widget is a very heavy widget (lots of stuff to create and destroy).

You probably don't want each MyItem to be a Scaffold(). Normally you only have 1 Scaffold() visible as it's a fancy root view for an app. It has an app bar, snack bar, drawer, etc. Try having just your Container(...) that's currently under body: as the return from your MyItem.build().

In the items: of your DropdownButton, you build and destroy the list of items when the DropdownButton scrolls in and out of view. If this list is the same for every widget in your list like in your test code above, create one List<Widget>[] that contains your DropdownMenuItem widgets and pass it in to your MyItem() widgets. Something like:

//In your widget with the ListView
List<Widget> myItems;

//In the initState() of your widget with the ListView
...
myItems = quantitySelection.map(
   (int e) => DropdownMenuItem<int>(
      value: e,
      child: Text(e.toString()),
    ),
).toList(),
...


//In your ListView.builder()
return MyItem(
  ...  
  items: myItems,
  ...
);

//In your MyItem.build() ->  DropdownButton()
...
DropDownButton(
   items: items
),
...

FWIW - we have a ListView with complex children that we test with 10,000 items. There's a significant difference in performance between the debug and release builds in Flutter. It stutters a little in the debug builds but is very smooth in the release builds.

Pat9RB
  • 580
  • 3
  • 7
0

I was able to solve the issue by only using the cacheExtent property of ListView.builder, setting it to list.length*200. This is kind of a workaround knowing that in my case the list length will always be small.

Pre-building the DropDownMenuItems had no sensed performance enhancement by a user, but it is a good recommended practice regardless, as instead of building the same DropDownMenuItems over and over again for every list item.

Although according to the docs: ListView and ListView.separated does not lazy load items rather build them all up at the beginning, I kept experiencing the same stuttering and lag during scrolling as with ListView.builder.

Mena
  • 3,019
  • 1
  • 25
  • 54