Search code examples
flutterdartscrollcontroller

Expansion Panel List Flutter Scroll Controller


I want to add items to ExpansionPanelList when ExpansionPanelList is scrolled to the end but there is no ScrollController like ListView for ExpansionListView.is there a solution for this?


Solution

  • You wrap the ExpansionPanelList with scrollable widget and use the scrollController. I am extending the doc example

    // stores ExpansionPanel state information
    class Item {
      Item({
        required this.expandedValue,
        required this.headerValue,
        this.isExpanded = false,
      });
    
      String expandedValue;
      String headerValue;
      bool isExpanded;
    }
    
    List<Item> generateItems(int numberOfItems) {
      return List<Item>.generate(numberOfItems, (int index) {
        return Item(
          headerValue: 'Panel $index',
          expandedValue: 'This is item number $index',
        );
      });
    }
    
    class MyStatefulWidget extends StatefulWidget {
      const MyStatefulWidget({Key? key}) : super(key: key);
    
      @override
      State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
    }
    
    class _MyStatefulWidgetState extends State<MyStatefulWidget> {
      final List<Item> _data = generateItems(28);
    
      late ScrollController controller = ScrollController()
        ..addListener(() {
          if (controller.hasClients) {
            if (controller.offset > controller.position.maxScrollExtent * .95) {
              _data.add(Item(
                  expandedValue: "generated Item", headerValue: "headerValue"));
              setState(() {});
            }
          }
        });
    
      @override
      Widget build(BuildContext context) {
        return SingleChildScrollView(
          controller: controller,
          child: Container(
            child: _buildPanel(),
          ),
        );
      }
    
      Widget _buildPanel() {
        return ExpansionPanelList(
          expansionCallback: (int index, bool isExpanded) {
            setState(() {
              _data[index].isExpanded = !isExpanded;
            });
          },
          children: _data.map<ExpansionPanel>((Item item) {
            return ExpansionPanel(
              headerBuilder: (BuildContext context, bool isExpanded) {
                return ListTile(
                  title: Text(item.headerValue),
                );
              },
              body: ListTile(
                  title: Text(item.expandedValue),
                  subtitle:
                      const Text('To delete this panel, tap the trash can icon'),
                  trailing: const Icon(Icons.delete),
                  onTap: () {
                    setState(() {
                      _data.removeWhere((Item currentItem) => item == currentItem);
                    });
                  }),
              isExpanded: item.isExpanded,
            );
          }).toList(),
        );
      }