6

I have a list of ExpansionTile with a list of ListTile in a Drawer. What I want to achieve is, when I press an ExpansionTile, the another ExpansionTile must be collapsed. I had been stuck with this problem for two days and could not find an answer. Can anybody know how to collapse the ExpansionTile programmatically?

Note:

I don't want to mess up the animation of the widget.

Here is my code,

ListView.builder(
                itemCount: userList.length,
                shrinkWrap: true,
                itemBuilder: (BuildContext context, findex) {
                  return ExpansionTile(
                    key: Key(findex.toString()),
                    title: Text(userList[findex].parentdata[0].title,
                      style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold,color: Colors.black),
                    ),
                    onExpansionChanged: (value) {
                    },
                    children: [
                      ListView.builder(
                        itemCount: userList[findex].document.length,
                        shrinkWrap: true,
                        itemBuilder: (BuildContext context, sindex) {
                          return ListTile(
                            title: Text(
                                userList[findex].document[sindex].title,
                              style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold,color: Colors.black),
                            ),
                            onTap: () {
                              print(
                                  userList[findex].document[sindex].title);
                            },
                          );
                        },
                      ),
                    ],
                  );
                },
              ),
Senthur Kumaran
  • 1,135
  • 1
  • 7
  • 18

5 Answers5

5

Try below code

declare one int variable

int selectedTile = -1;

Your widget

ListView.builder(
  key: Key(selectedTile.toString()),
  itemCount: 5,
  itemBuilder: (context, index) {
    return ExpansionTile(
      key: Key(index.toString()),
      initiallyExpanded: index == selectedTile,
      title: Text('ExpansionTile $index'),
      subtitle: Text('Trailing expansion arrow icon'),
      children: [
        ListTile(
          title: Text('This is tile number $index'),
        ),
      ],
      onExpansionChanged: ((newState) {
        if (newState)
          setState(() {
            selectedTile = index;
          });
        else
          setState(() {
            selectedTile = -1;
          });
      }),
    );
  },
);
Ravindra S. Patil
  • 11,757
  • 3
  • 13
  • 40
  • 1
    It is working. but in my case the above ExpansionTile key is not working. instead of I used like this - key: UniqueKey(), – Ilyas Arafath Feb 21 '23 at 04:59
  • In my case the above key is working if you used `key: UniqueKey(),` this also working I have checked both are worked Thanks for sharing – Ravindra S. Patil Feb 21 '23 at 06:09
2

Use ExpansionPanel widget.

You need to create a variable and maintain the expansion state of expansion panel index.

expansionCallback: (int index, bool isExpanded) {
        setState(() {
          // when any of expansionPanel is Tapped
          // set all expansion to false
          for(int i = 0; i<_data.length; i++){
            _data[i].isExpanded = false;
          }
          // then set the tapped index to its state
          _data[index].isExpanded = !isExpanded;
        });
      },

Here is an live demo for expansion panel

Bharath
  • 1,036
  • 10
  • 13
  • In the `ExpansionPanel` widget, We can only tap on the icon. If we tap on the text there will be no response. – Senthur Kumaran May 20 '22 at 11:14
  • https://gist.github.com/bharathraj-e/7a71a6d73ed000b137f767760bad8e16 Set `canTapOnHeader: true` in ExpansionPanel. Ref: https://api.flutter.dev/flutter/material/ExpansionPanel/canTapOnHeader.html – Bharath May 22 '22 at 05:53
0

Try this:

Create a variable: int selected = -1;

And listview:

ListView.builder(
          itemCount: 10,
          shrinkWrap: true,
          itemBuilder: (BuildContext context, findex) {
            return ExpansionTile(
              initiallyExpanded: findex == selected,
              key: Key(selected.toString()),
              title: Text(userList[findex].parentdata[0].title,
                style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold,color: Colors.black),
              ),
              onExpansionChanged: (newState) {
                setState(() {
                  selected = findex;
                });
              },
              children: [
                ListView.builder(
                  itemCount: 10,
                  shrinkWrap: true,
                  itemBuilder: (BuildContext context, sindex) {
                    return ListTile(
                      title: Text(
                        userList[findex].document[sindex].title,
                        style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold,color: Colors.black),
                      ),
                      onTap: () {
                        print(userList[findex].document[sindex].title);
                      },
                    );
                  },
                ),
              ],
            );
          },
        ),
Xuuan Thuc
  • 2,340
  • 1
  • 5
  • 22
  • This one is not working. The `initiallyExpanded` property is only called when the `ExpandedTile` Widget is created. So, passing a Boolean to the `initiallyExpanded` property on every tapping is pointless. – Senthur Kumaran May 20 '22 at 10:49
  • this works on me, but the ExpansionTile animation not so smooth when I open the one ExpansionTile to another. Any idea for this? Thanks – Soveyyy Dec 07 '22 at 06:22
0

Make sure ExpansionTile be in stateful widget

   ListView.builder(
                        itemCount: 5,
                          shrinkWrap: true,
                          itemBuilder: (BuildContext context, index) {
                            return CustomExpansionTile(index: index);
                          },
                        ),

// Expansion Tile Widget

       class CustomExpansionTile extends StatefulWidget {
              final int index;
            
              const CustomExpansionTile({Key? key, required this.index}) : super(key: key);
            
              @override
              State<CustomExpansionTile> createState() => _CustomExpansionTileState();
            }
            
            class _CustomExpansionTileState extends State<CustomExpansionTile> {
              int selectedIndexExpansionTile = -1;
            
              @override
              Widget build(BuildContext context) {
                return ExpansionTile(
                    initiallyExpanded: widget.index == selectedIndexExpansionTile,
                    key: Key(selectedIndexExpansionTile.toString()),
                    title: Text(
                      widget.index.toString(),
                    ),
                    onExpansionChanged: (newState) {
                 
                      if (newState) {
                        selectedIndexExpansionTile = widget.index;
                      } else {
                        selectedIndexExpansionTile = -1;
                      }
                      setState(() {});
                    },
                    children: [Text(widget.index.toString())]);
              }
            }
Paras Arora
  • 605
  • 6
  • 12
0
key: index == lastOne ? const Key("selected") : Key(index.toString()),
                                                initiallyExpanded: index == selectedIndex,
                                                expandedCrossAxisAlignment: CrossAxisAlignment.start,
                                                onExpansionChanged: ((newState) {
                                                  lastOne = selectedIndex;
                                                  if (newState) {
                                                    setState(() {
                                                      selectedIndex = index;
                                                    });
                                                  } else {
                                                    setState(() {
                                                      selectedIndex = -1;
                                                    });
                                                  }
                                                }),

in this way you can also have the animation as well

Sepehr Marashi
  • 111
  • 1
  • 1
  • 4
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 17 '23 at 18:05