16

I’m just trying to use ExpansionTile in Flutter, from example I modified to become like this:

enter image description here

I want to hide the arrow and use Switch to expand the tile, is it possible? Or do I need custom widget which render children programmatically? Basically, I just need to show/hide the children

Here’s my code:

import 'package:flutter/material.dart';

void main() {
  runApp(ExpansionTileSample());
}
class ExpansionTileSample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('ExpansionTile'),
        ),
        body: ListView.builder(
          itemBuilder: (BuildContext context, int index) =>
              EntryItem(data[index]),
          itemCount: data.length,
        ),
      ),
    );
  }
}

// One entry in the multilevel list displayed by this app.
class Entry {
  Entry(this.title,[this.question='',this.children = const <Entry>[]]);

  final String title;
  final String question;
  final List<Entry> children;
}

// The entire multilevel list displayed by this app.
final List<Entry> data = <Entry>[
  Entry(
    'Chapter A',
    '',
    <Entry>[
      Entry(
        'Section A0',
        '',
        <Entry>[
          Entry('Item A0.1'),
          Entry('Item A0.2'),
          Entry('Item A0.3'),
        ],
      ),
      Entry('Section A1','text'),
      Entry('Section A2'),
    ],
  ),
  Entry(
    'Chapter B',
    '',
    <Entry>[
      Entry('Section B0'),
      Entry('Section B1'),
    ],
  ),
  Entry(
    'Chapter C',
    '',
    <Entry>[
      Entry('Section C0'),
      Entry('Section C1')
    ],
  ),
];

// Displays one Entry. If the entry has children then it's displayed
// with an ExpansionTile.
class EntryItem extends StatelessWidget {
  const EntryItem(this.entry);

  final Entry entry;

  Widget _buildTiles(Entry root) {
    if (root.children.isEmpty) return  Container(
        child:Padding(
          padding: const EdgeInsets.symmetric(
            vertical: 8.0,
            horizontal: 32.0,
          ),
          child:Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children:[
                  Text(root.title),
                  Divider(height: 10.0,),
                  root.question=='text'?Container(
                      width: 100.0,
                      child:TextField(
                        decoration: const InputDecoration(helperText: "question")
                      ),
                  ):Divider()
              ]
          )
        )
    );
    return ExpansionTile(
      //key: PageStorageKey<Entry>(root),
      title: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children:[
          Text(root.title),
          Switch(
            value:false,
            onChanged: (_){},
          )
        ]
      ),
      children: root.children.map(_buildTiles).toList(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return _buildTiles(entry);
  }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
kira
  • 181
  • 1
  • 2
  • 11

6 Answers6

8

@diegoveloper 's answer is almost ok,one small issue not covereed is: it doesn't propogate click on Switch further to ExpansionTile, so if you click outside switch it's expanding, while clicking on Switch does nothing. Wrap it with IgnorePointer, and on expansion events set the value for switch. It's a bit backwards logic, but works good.

...
        return ExpansionTile(
          onExpansionChanged: _onExpansionChanged,
          // IgnorePointeer propogates touch down to tile
          trailing: IgnorePointer(
            child: Switch(
                value: isExpanded,
                onChanged: (_) {},
             ),
          ),
          title: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
            Text(root.title),
          ]),
          children: root.children.map((entry) => EntryItem(entry)).toList(),
        );
...
5

I think this will help you initiallyExpanded : true

                  itemBuilder: (context, index) {                        
                    return Column(
                      children: <Widget>[
                        Divider(
                          height: 17.0,
                          color: Colors.white,
                        ), 
                              ExpansionTile(  

                                    key: Key(index.toString()), //attention                                                                  
                                    initiallyExpanded : true,
                                    leading: Icon(Icons.person, size: 50.0, color: Colors.black,),
                                    title: Text('Faruk AYDIN ${index}',style: TextStyle(color: Color(0xFF09216B), fontSize: 17.0, fontWeight: FontWeight.bold)), 
                                    subtitle: Text('Software Engineer', style: TextStyle(color: Colors.black, fontSize: 13.0, fontWeight: FontWeight.bold),),
                                    children: <Widget>[                                       
                                      Padding(padding: EdgeInsets.all(25.0), 
                                                  child : Text('DETAİL ${index} \n' + 'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using "Content here, content here", making it look like readable English.',)
                                                  ) 
                                    ],
                                    onExpansionChanged: ((newState){
                                        if(newState)
                                            setState(() {
                                              Duration(seconds:  20000);
                                              selected = index; 
                                            });
                                            else setState(() {
                                              selected = -1; 
                                            });        
                                    })
                                  ),
                             
                          ]
                        );
             
Anand
  • 4,355
  • 2
  • 35
  • 45
1

Yes, it's possible, I modified your code a little :

        class EntryItem extends StatefulWidget {
          const EntryItem(this.entry);
          final Entry entry;

          @override
          EntryItemState createState() {
            return new EntryItemState();
          }
        }

        class EntryItemState extends State<EntryItem> {
          var isExpanded = false;

          _onExpansionChanged(bool val) {
            setState(() {
              isExpanded = val;
            });
          }

          Widget _buildTiles(Entry root) {
            if (root.children.isEmpty)
              return Container(
                  child: Padding(
                      padding: const EdgeInsets.symmetric(
                        vertical: 8.0,
                        horizontal: 32.0,
                      ),
                      child: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Text(root.title),
                            Divider(
                              height: 10.0,
                            ),
                            root.question == 'text'
                                ? Container(
                                    width: 100.0,
                                    child: TextField(
                                        decoration: const InputDecoration(
                                            helperText: "question")),
                                  )
                                : Divider()
                          ])));
            return ExpansionTile(
              onExpansionChanged: _onExpansionChanged,
              trailing: Switch(
                value: isExpanded,
                onChanged: (_) {},
              ),
              //key: PageStorageKey<Entry>(root),
              title: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
                Text(root.title),
              ]),
              children: root.children.map((entry) => EntryItem(entry)).toList(),
            );
          }

          @override
          Widget build(BuildContext context) {
            return _buildTiles(widget.entry);
          }
        }

Basically I changed from Stateless to Stateful because you need to handle the state of your Switch widget.

There is a trailing property from ExpansionTile where I put the Switch to remove the arrow widget by default.

Listen the onExpansionChanged: _onExpansionChanged,, to change the status of the Switch.

And finally build the children as new widgets:

children: root.children.map((entry) => EntryItem(entry)).toList(),
diegoveloper
  • 93,875
  • 20
  • 236
  • 194
  • using this approach in order to expand panel I can't use the switch, but need to click the text on panel – kira Aug 10 '18 at 15:00
  • what do you mean? I don't understand. Does it works for you? – diegoveloper Aug 10 '18 at 15:02
  • I guess he wants to display the options also when the switch is enabled, not only by clicking the ExpansionTile and that I'm afraid is not possible (yet): https://github.com/flutter/flutter/issues/7024 – Dani Dec 22 '20 at 23:56
1

Short answer: Set initiallyExpanded true or false, accordingly with the help of onExpansionChanged. But remember initiallyExpanded applies only for initial state, so the key of the widget should be changed to apply changes. Now to change key a workaround is:

ExpansionTile(
     key: PageStorageKey("${DateTime.now().millisecondsSinceEpoch}"),
     initiallyExpanded: ....
     onExpansionChanged: ....
     .
     .
     .
)
Syed Umair
  • 1,480
  • 1
  • 13
  • 14
1

There is now an ExpansionTileController which can be used to and close the tile programmatically, as explained in the docs.

user3185563
  • 1,314
  • 2
  • 15
  • 22
0

initiallyExpanded = true , this answer is correct but if we have a TextFiled inside the ExpansionTile's children , then the keyboard automatically hides(bug). So my solution is wrap the children with Visibility widget and control visibilty. initially declare bool _expansionVisibility = false;

ExpansionTile(
            onExpansionChanged: (changed) {
              setState(() {
                print("changed $changed");
                if (changed) {
                  _expansionVisibility = true;
                } else {
                  _expansionVisibility = false;
                }
              });
            },
            title: Text(
              "Change Password",
            ),
            children: <Widget>[
              Visibility(
                visible: _expansionVisibility,
                child: Container(),
              ),
            ],
          ),
Ilyas Arafath
  • 511
  • 7
  • 13