Flutter中编程扩展ExpansionTile

16
我只是在尝试在Flutter中使用ExpansionTile,我修改了示例代码,使其变成了这样:

enter image description here

我想隐藏箭头并使用Switch来展开瓷砖,这可能吗?还是我需要自定义小部件以编程方式呈现子级?基本上,我只需要显示/隐藏子级。
这是我的代码:
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);
  }
}

2
点赞 https://github.com/flutter/flutter/issues/7024 - Günter Zöchbauer
6个回答

8
@diegoveloper的答案几乎正确,只有一个小问题没有解决:它不会将对“Switch”的点击传播到“ExpansionTile”,因此如果您在开关之外单击,则会展开,而单击“Switch”则不会发生任何事情。用IgnorePointer将其包装起来,并在展开事件中设置开关的值。这是一个有点反向的逻辑,但效果很好。
...
        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(),
        );
...

6

我认为这会对您有所帮助 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; 
                                            });        
                                    })
                                  ),
                             
                          ]
                        );
             

是的,兄弟 @IlyasArafath - Anand

1

是的,这是可能的,我稍微修改了你的代码:

        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);
          }
        }

基本上,我从无状态(Stateless)改为有状态(Stateful),因为您需要处理您的Switch小部件的状态。
有一个trailing属性来自于ExpansionTile,在其中我将开关放置在默认情况下删除arrow小部件。
监听onExpansionChanged: _onExpansionChanged,以更改开关的状态。
最后,构建子项作为新的小部件:
children: root.children.map((entry) => EntryItem(entry)).toList(),

使用这种方法来扩展面板时,我不能使用开关,而是需要点击面板上的文本。 - kira
你是什么意思?我不明白。它对你有效吗? - diegoveloper
我猜他想在开关启用时也显示选项,而不仅仅是通过单击ExpansionTile来实现,但恐怕这还不可能(目前):https://github.com/flutter/flutter/issues/7024 - Dani

1

现在有一个 ExpansionTileController 可以在编程中打开和关闭瓷贴,详细说明在 docs 中。


1

简短回答:根据onExpansionChanged的帮助,设置initiallyExpanded为true或false。但请记住,initiallyExpanded仅适用于初始状态,因此小部件的键应更改以应用更改。现在要更改键,可以使用以下解决方法:

ExpansionTile(
     key: PageStorageKey("${DateTime.now().millisecondsSinceEpoch}"),
     initiallyExpanded: ....
     onExpansionChanged: ....
     .
     .
     .
)

0

initiallyExpanded = true,这个答案是正确的,但如果我们在ExpansionTile的children中有一个TextFiled,则键盘会自动隐藏(bug)。 因此,我的解决方案是使用Visibility小部件包装children并控制可见性。 最初声明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(),
              ),
            ],
          ),

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接