Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically expand ExpansionTile in Flutter

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);
  }
}
like image 692
kira Avatar asked Aug 05 '18 20:08

kira


3 Answers

@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(),
        );
...
like image 191
Buffer Underflow Avatar answered Oct 19 '22 05:10

Buffer Underflow


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 : index==selected, //attention

                                    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; 
                                            });        
                                    })
                                  ),
                             
                          ]
                        );
             
like image 34
Anandh Krishnan Avatar answered Oct 19 '22 04:10

Anandh Krishnan


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(),
like image 33
diegoveloper Avatar answered Oct 19 '22 05:10

diegoveloper