专栏首页flutter开发者[Flutter Widget]ExpansionPanelList

[Flutter Widget]ExpansionPanelList

前言


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

实现如下效果:

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

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

代码:


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的构造方法:

代码:


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

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

ExpansionPanel的构造方法:

代码:


ExpansionPanel({
    @required this.headerBuilder,//标题构造器
    @required this.body,//内容区域
    this.isExpanded = false//是否展开
  })

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

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

代码:


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中增加如下代码:

var isExpanded;

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

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

接下来再来看下效果:

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

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

代码:


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而已,不再做具体的介绍了,看代码吧

代码:


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如何做呢?

试一试吧

本文分享自微信公众号 - flutter开发者(Flutter_Developers),作者:Flutter开发者

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-10-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ListView&GirdView

    在前面的的文章中我们了解了Flutter中操作提示的用法,包括SnackBar、Dialog、以及BottomSheet,通过这些Widget我们可以很方便的...

    flyou
  • [Flutter Widget] TabBar

    TabBar在客户端的表现形式是一般展示在appBar的下面,做同一级别不同类型界面的展示工作。

    flyou
  • Flutter中的操作提示

    在前面的文章中我们学习了Flutter中输入以及选择控件的用法,借助于这些组件大家可以完成很多常用的功能,但是他不能及时在用户操作后完成相应的界面提示,所以今天...

    flyou
  • Hulu大数据架构与应用经验

    内容来源:2017年8月12日,Hulu大数据架构部门负责人董西成在“网易博学实践日:大数据与人工智能技术大会”进行《Hulu大数据架构与应用经验》演讲分享。I...

    企鹅号小编
  • 加州无人车路测新添Lyft,中国公司Roadstar和长安也要去跑一跑

    李根 发自 LHY 量子位 报道 | 公众号 QbitAI 美国加州交通管理局(DMV)最新更新的文件显示,又有6家公司获得路测牌照,获准在加州进行无人车路测...

    量子位
  • IDEA Activiti 画图中文乱码

    画流程图时,如果节点的name填写的是中文,再次打开流程图时中文会显示乱码,如下图:

    彼岸舞
  • 仅用19天,中芯国际或创下科创板快速上会记录

    6月10日,科创板上市委2020年第47次审议会议发布公告,将于6月19日上午9时对中芯国际进行上市委审议。

    镁客网
  • Kettle 7.1 连接HBase数据表

    在Tools -> Hadoop Distribution 中选择 “HortonWorks HDP 2.5.x”。

    kongxx
  • 中芯国际的奋力进击

    作为内地最大、最先进的晶圆代工企业,很大程度上,中芯国际代表了中国芯片产业的未来,承载了很多的希望。

    刘旷
  • 知识图谱与机器学习

    如果你在网上搜索机器学习,你会找到大约20500万个结果。确实是这样,但是要找到适合每个用例的描述或定义并不容易,然而会有一些非常棒的描述或定义。在这里,我将提...

    商业新知

扫码关注云+社区

领取腾讯云代金券