前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Flutter Widget]ExpansionPanelList

[Flutter Widget]ExpansionPanelList

作者头像
flyou
发布2018-10-16 11:03:13
4K0
发布2018-10-16 11:03:13
举报
文章被收录于专栏:flutter开发者

前言


在前面的文章中我们介绍了可以展开的带标题控件ExpansionTile的用法,在文章的最后还是按照惯例给大家留下了一个问题。

实现如下效果:

可以看到界面整体上是一个listView,在ListView的第二例是一个ExpansionTile,ExpansionTile的内部是多个ListTile,trailing结合自定义动画将“+”icon旋转22.5°变成了一个“×”,并且在ExpansionTile展开时改变了icon的颜色。

当然,代码实现起来也是非常的简单,不熟悉动画的童鞋可以看下公众号前面的文章.

代码:


代码语言:javascript
复制
import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    home: MyApp(),
  ));
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  Animation animation;
  AnimationController animationController;
  Color iconColors = Colors.grey;

  @override
  void initState() {
    super.initState();
    animationController = new AnimationController(
        vsync: this, duration: Duration(milliseconds: 200));
    animation = new Tween(begin: 0.0, end: 0.125).animate(animationController);
  }

  _changeOpacity(bool expand) {
    setState(() {
      if (expand) {
        animationController.forward();
        iconColors = Colors.redAccent;
      } else {
        animationController.reverse();
        iconColors = Colors.grey;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: const Text('ExpandTitle')),
        body: ListView(children: <Widget>[
          const ListTile(title: Text('第一列')),
          ExpansionTile(
              title: const Text('第二列'),
              backgroundColor: Theme.of(context).accentColor.withOpacity(0.025),
              trailing: RotationTransition(
                turns: animation,
                child: Icon(
                  Icons.add,
                  color: iconColors,
                ),
              ),
              onExpansionChanged: (bool) {
                _changeOpacity(bool);
              },
              children: const <Widget>[
                ListTile(title: Text('One')),
                ListTile(title: Text('Two')),
                ListTile(title: Text('Free')),
                ListTile(title: Text('Four'))
              ]),
          const ListTile(title: Text('第三列')),
          const ListTile(title: Text('第四列')),
          const ListTile(title: Text('第五列')),
          const ListTile(title: Text('第六列')),
          const ListTile(title: Text('第七列')),
          const ListTile(title: Text('第八列')),
        ]));
  }
}

既然我们看过了ExpansionTile 的用法,那么我们今天再来看下ExpansionPanelList的用法吧

ExpansionPanelList

ExpansionPanel从单词的字面意思可以翻译为一个可以展开的面板,那么加上List就是包含多个可展开面板的列表啰。那么它又和前面讲过的ExpansionTile有什么区别,其实长得还是挺想的但是ExpansionPanelList在展开和关闭的时候是有动画的,比较不那么突兀。

ExpansionPanelList的构造方法:

代码:


代码语言:javascript
复制
ExpansionPanelList({
    Key key,
    this.children = const <ExpansionPanel>[],
    this.expansionCallback,//展开关闭回调
    this.animationDuration = kThemeAnimationDuration,//展开进行时间
  })

构造方法很简单,接收ExpansionPanel类型的List集合,展开关闭的回调和展开时间三个参数。

ExpansionPanel的构造方法:

代码:


代码语言:javascript
复制
ExpansionPanel({
    @required this.headerBuilder,//标题构造器
    @required this.body,//内容区域
    this.isExpanded = false//是否展开
  })

构造方法非常的简单,但是在这里需要注意的是ExpansionPanel不是一个Widget它是一个单独类,只能配合ExpansionPanelList使用。

国际惯例,看下最基本的用法。

代码:


代码语言:javascript
复制
import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    home: ExpansionPanelListDemo(),
  ));
}

class ExpansionPanelListDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return ExpansionPanelListDemoState();
  }
}

class ExpansionPanelListDemoState extends State<ExpansionPanelListDemo> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ExpansionPanelListDemo"),
      ),
      body: SingleChildScrollView(
        child: ExpansionPanelList(
          children: [
            ExpansionPanel(
              headerBuilder: (index, opened) {
                return ListTile(
                  title: Text("更多内容"),
                );
              },
              body: Padding(
                padding: EdgeInsets.symmetric(horizontal: 5.0),
                child: Container(
                  height: 100.0,
                  color: Colors.blue,
                  alignment: Alignment.center,
                  child: Icon(
                    Icons.security,
                    size: 35.0,
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

代码很简单,我们在ExpansionPanelList中仅仅放置了一个元素ExpansionPanel,对ExpansionPanel分别设置了它的标题和内容。

注意:ExpansionPanelList必须配合可以滑动的组件才可以使用

效果如下:

但是这个时候无论我们怎么点击右侧的图标都没有任何的反应,那是因为这个ExpansionPanel我们默认设置的是关闭的状态,而且我们也并没有对ExpansionPanelList的点击事件做处理。

下面简单改动下代码,在ExpansionPanelListDemoState中增加如下代码:

代码语言:javascript
复制
var isExpanded;

_expansionCallback(index ,isExpanded){
  setState(() {
    if(this.isExpanded==isExpanded){
      this.isExpanded=! this.isExpanded;
    }
  });
}

并且将isExpanded的值设置给ExpansionPanel的isExpanded属性。

接下来再来看下效果:

嗯,就是这个效果,可以看到在点击右侧按钮的同时下面body的展开时有动画的哦。

接下来我们试试多个ExpansionPanel的效果

代码:


代码语言:javascript
复制
import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    home: ExpansionPanelListDemo(),
  ));
}

class ExpansionPanelListDemo extends StatefulWidget {
  @override
  _ExpansionPanelListDemoState createState() => _ExpansionPanelListDemoState();
}

class ExpandStateBean{
  var isOpen;
  var index;
  ExpandStateBean(this.index,this.isOpen);
}

class _ExpansionPanelListDemoState extends State<ExpansionPanelListDemo> {
  var currentPanelIndex = -1;
  List<int> mList;
   List<ExpandStateBean> expandStateList;
  _ExpansionPanelListDemoState() {
    mList = new List();
    expandStateList=new List();
    for (int i = 0; i < 10; i++) {
      mList.add(i);
      expandStateList.add(ExpandStateBean(i, false));
    }
  }


  _setCurrentIndex(int index,isExpand) {
    setState(() {
      expandStateList.forEach((item){
        if (item.index==index) {
          item.isOpen=!isExpand;
        }
      });

    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("ExpansionPanelList"),
        ),
        body: SingleChildScrollView(child: ExpansionPanelList(
          children: mList.map((index) {
            return new ExpansionPanel(
              headerBuilder: (context, isExpanded) {
                return new ListTile(
                  title: new Text('我是第$index个标题'),
                );
              },
              body: new Padding(
                padding: EdgeInsets.symmetric(horizontal: 5.0),
                child: Container(height: 100.0,
                  color: Colors.blue,
                  alignment: Alignment.center,
                  child:Icon(Icons.security,size: 35.0,),),
              ),
              isExpanded: expandStateList[index].isOpen,
            );
          }).toList(),

          expansionCallback: (index, bol) {
            _setCurrentIndex(index,bol);
          },

        ),));
  }
}

代码虽然有一点多,但是还是非常的简单的,因为是多个ExpansionPanel,所以我们要记录每个Item打开和关闭的状态来做处理,其他和上面的基本一致。

看下效果:

看第一种做法,使用ExpansionPanelList.radio()来实现,看名字就很容易知道,radio单选的意思嘛,也就是说每次只能打开一个ExpansionPanelRadio,只要ExpansionPanelList有已经打开的ExpansionPanelRadio就无法再打开其他的ExpansionPanelRadio。

用法跟ExpansionPanelList类似,只是把children替换成了ExpansionPanelRadio而已,不再做具体的介绍了,看代码吧

代码:


代码语言:javascript
复制
import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    home: ExpansionPanelListDemo(),
  ));
}

class ExpansionPanelListDemo extends StatefulWidget {
  @override
  _ExpansionPanelListDemoState createState() => _ExpansionPanelListDemoState();
}

class _ExpansionPanelListDemoState extends State<ExpansionPanelListDemo> {
  var currentPanelIndex = -1;
  List<int> mList;

  _ExpansionPanelListDemoState() {
    mList = new List();
    for (int i = 0; i < 10; i++) {
      mList.add(i);
    }
  }


  _setCurrentIndex(int index) {
    setState(() {

      if(currentPanelIndex==index){
        index=-1;
      }
      currentPanelIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("ExpansionPanelList"),
        ),
        body: SingleChildScrollView(child: ExpansionPanelList.radio(
          children: mList.map((index) {
            return new ExpansionPanelRadio(
              headerBuilder: (context, isExpanded) {
                return new ListTile(
                  title: new Text('我是第$index个标题'),
                );
              },
              body: new Padding(
                padding: EdgeInsets.symmetric(horizontal: 5.0),
                child: Container(height: 100.0,
                  color: Colors.blue,
                  alignment: Alignment.center,
                  child:Icon(Icons.security,size: 35.0,),),
              ),
              value: index,
            );
          }).toList(),
        initialOpenPanelValue:currentPanelIndex ,
          expansionCallback: (index, bol) {
            _setCurrentIndex(index);
          },

        ),));
  }
}

效果如下:

实现起来还是非常的简单的,但是大家可能会发现一个问题,当有一个ExpansionPanelRadio打开时我们就没办法再去打开其他的ExpansionPanelRadio,除非先关闭这个打开的ExpansionPanelRadio。

那么如果你有这样的需求就还是要借助于ExpansionPanelList()了。

小结


  • 使用ExpansionPanelList可以实现带动画的展开布局效果
  • ExpansionPanelList中的ExpansionPanel是需要受ExpansionPanelList的点击事件处理的
  • 使用ExpansionPanelList.radio()限制每次只能有一个打开的Item

试一试


在前面已经提到了,使用ExpansionPanelList.radio()每次只能打开一个Item,当有一个item处于打开状态时在点击其他item就没有效果了,但是我想要当我点击其他Item关于之前的Item打开现在Item如何做呢?

试一试吧

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-10-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 flutter开发者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • ExpansionPanelList
  • 小结
  • 试一试
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档