How to make short ListView of DropDownButtons build faster?

Issue

I have a short ListView of a maximum of 10 items. Each list item will contain a DropDownButton which will hold around 1K DropDownMenuItems for selections.

In native Android, I was able to implement one that performed very smoothly, but with Flutter it takes a while to build the ListView which causes the UI to freeze.

In my case, I will need to rebuild the ListView upon every change in one of its items, so It will be a major issue.

Is there a way to make the ListView build faster, or at least be able to display a ProgressBar till it builds?

N.B: Using --profile configuration to simulate a release version improves the performance a lot, but still there is a sensed freeze.

Here’s my sample code which you can directly copy/paste if you want to test it yourself.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool showList = false;
  final List<DropdownMenuItem<int>> selections = List.generate(
    1000,
    (index) => DropdownMenuItem<int>(
      value: index,
      child: Text("$index"),
    ),
  );

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Container(
          width: double.infinity,
          child: Column(
            children: [
              ElevatedButton(
                child: Text("toggle list visibility"),
                onPressed: () {
                  setState(() {
                    showList = !showList;
                  });
                },
              ),
              Expanded(
                child: showList
                    ? ListView.builder(
                        cacheExtent: 2000,
                        itemCount: 10,
                        itemBuilder: (context, index) {
                          return Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: Center(
                              child: Container(
                                height: 200,
                                color: Colors.green,
                                child: Column(
                                  children: [
                                    Text("List Item: $index"),
                                    DropdownButton<int>(
                                      onChanged: (i) {},
                                      value: 1,
                                      items: selections,
                                    ),
                                  ],
                                ),
                              ),
                            ),
                          );
                        })
                    : Text("List Not Built"),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Solution

Load dropdown when clicking the button.

Add this widget on your main List View

   InkWell(
          onTap: () {
            showDialog(
                context: context,
                builder: (_) {
                  return VendorListAlert(selectVendor: selectVendorTap);
                });
          },
          child: // create a widget, looks like your drop down
        ),

Handle tap event

void selectVendorTap(pass your model){
// logic
}

Sample for custom Alert

No need to create a mutable widget, the immutable widget is better.

class VendorListAlert extends StatefulWidget {
  final Function selectVendor;

  const VendorListAlert({Key key, this.selectVendor}) : super(key: key);

  @override
  _VendorListAlertState createState() => _VendorListAlertState();
}

class _VendorListAlertState extends State<VendorListAlert> {
  List<UserModel> _searchVendor = [];

  @override
  void initState() {
    super.initState();
    _searchVendor = List.from(ypModel);
  }

  @override
  Widget build(BuildContext context) {

    return AlertDialog(
      content: Container(
        width: width,
        child: ListView.builder(
          shrinkWrap: true,
          itemCount: _searchVendor.length,
          itemBuilder: (BuildContext context, int index) {
            return Card(
              child: InkWell(
                onTap: () {
                  widget.selectVendor(_searchVendor[index]);
                  Navigator.pop(context);
                },
                child: 
              ),
            );
          },
        ),
      ),
    );
  }
}

Answered By – BIS Tech

Answer Checked By – Clifford M. (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.