Flutter ListView使用

灵魂烈焰中死神降临,生命不断流逝,枯萎,只求不要带走那对你最后的一丝思念。——沙漠死神

先来看看今天的最终效果吧:

效果图(1.1):

咋们先来学习ListView,若果你对ListView比较熟悉,直接跳过这段吧~

ListView参数介绍

加粗是必须传参数

ListView参数类型说明
scrollDirectionAxis滑动方向
Axis.vertical(默认)
Axis.horizontal
itemExtentdouble表示的是子Widget之间的距离
当scrollDirection = Axis.vertical itemExtent代表的是高度
当scrollDirection = Axis.horizontal itemExtent代表的是宽度
paddingEdgeInsetsGeometry内边距
primarybool当条目不足时 true可以尝试滚动 false不可以滚动
physicsScrollPhysics滑动类型
BouncingScrollPhysics() 拉到ListView最底部有回弹效果
ClampingScrollPhysics() 包裹内容不会回弹
NeverScrollableScrollPhysics() 滑动禁止
cacheExtentdouble预加载区域
reversebool是否倒序显示 默认正序 false 倒序true
controllerScrollController滑动控制器,监听ListView滑动距离等
childrenList<Widget>子Widget

在来看看代码:

Bean辅助类:

class ListViewBean{
  //是否选中
  bool ischeck = false;
  //标题
  String title;
  ListViewBean(this.ischeck, this.title,);
}

ListViewTextWidget类:



class ListViewTextWidget extends StatefulWidget {
  @override
  _ListViewTextWidgetState createState() => _ListViewTextWidgetState();
}

class _ListViewTextWidgetState extends State<ListViewTextWidget> {
 List<ListViewBean> _list = [];

  @override
  void initState() {
    super.initState();
	 //初始化30个条目
    for (int i = 0; i <= 30; i++) {
      _list.add(new ListViewBean(false, "$i"));
    }
  }
  @override
  Widget build(BuildContext context) {
		return child: Container(
            child: _buildListView(),
          );
	}

	Widget _buildListView() {
		return ListView(
			  //子Widget
      		  children: initData(),
		);
	}
	
	List<Widget> initData() {
	    List<Widget> mlist = [];
	     _list.forEach((e) => {
          mlist.add( buildItem(e) )
        });
   		 return mlist;
	}

 ///[e] 当前的Widget
  Widget buildItem(ListViewBean e) {
    return Container(
      decoration: BoxDecoration(
        border: Border(
          bottom: BorderSide(color: Colors.lightBlueAccent,width: 0.5)
        )
      ),
      alignment: Alignment.center,
      child: Row(
        children: [
          Expanded(
            child: Text("第${e.title}个元素"),
          ),
          RaisedButton(
            color: e.ischeck ? Colors.black : Colors.red,
            onPressed: () {
            },
            child: Text(
              e.ischeck ? "已收藏" : "未收藏",
              style: TextStyle(color: Colors.white),
            ),
          )
        ],
      ),
    );
  }
}

这段代码很简单,在定义一个辅助类,在initState()中初始化30条数据,然后赋值给ListView,来看看效果吧:

效果图(1.2):

来看看内几种滑动效果:

  • ClampingScrollPhysics() 拉到ListView最底部有回弹效果
    效果图(1.3):

  • NeverScrollableScrollPhysics()滑动禁止
    效果图(1.4):

  • ClampingScrollPhysics(默认) 包裹内容不会回弹
    效果图(1.5):

ScrollController的使用

 ScrollController scrollController = ScrollController();

 scrollController.addListener(() {
      ///scrollController.position.maxScrollExtent  ListView滑动的最大距离
      ///scrollController.position.pixels  现在滑动的距离
      if (scrollController.position.maxScrollExtent ==
       scrollController.position.pixels) {
        print(scrollController.....maxScrollExtent ..pixels);
      }
    });
    
	  @override
	  void dispose() {
  	 	  scrollController.dispose();
  		  super.dispose();
	  }

  • scrollController.position.maxScrollExtent ListView滑动的最大距离
  • scrollController.position.pixels 现在滑动的距离

这个属性常用于监听ListView是否滑动到最底部,从而加载更多属性.

效果图(1.6):
在这里插入图片描述

其他属性都非常简单,大家自己动手试一下吧~

ListView中还有自带的ListTile

ListTile

ListTile参数类型说明
tileColorColor背景色会把圆角覆盖掉!!
subtitleWidget副标题
densebool将字体缩小
leadingWidget左边图标
trailingWdiget末尾图标设置
contentPaddingEdgeInsetsGeometry内边距
titleWidget主标题
onTapGestureTapCallback单击事件
onLongPressGestureLongPressCallback长按事件
selectedbool如果选中,则颜色会跟随主题颜色
enabledbool禁止点击事件
_item("拍照",  "拍照哦"),
_item("从相册选择",  "选取照片哦"),
/**
   *  title 主标题
   *  subTitle 副标题
   */
  _item(String title,  String subTitle) {
    return ListTile(
        tileColor: Colors.teal,//背景色会把圆角覆盖掉!!
        //背景色
        subtitle: Text(subTitle),
        //副标题
        dense: true,
        //将字体缩小
        leading: Icon(Icons.ac_unit_outlined, size: 25,),
        //左边显示图片
        trailing: Icon(Icons.android),
        //末尾显示图片
        contentPadding: EdgeInsets.all(3),
        //内边距 默认16
        title: Text( title,),
        selected: true,//如果选中,则颜色会跟随主题颜色
        enabled: true,//禁止点击事件
      );
  }

效果图(1.7):
在这里插入图片描述

走到这里的话ListView已经了解的差不多了,接下来完成今天的效果吧:

ListView全选

ListViewBean辅助类:

class ListViewBean{
  //是否选中
  bool ischeck = false;
  //标题
  String title;
  ListViewBean(this.ischeck, this.title,);
}

收藏按钮 e为当前点击的Widget

 List<ListViewBean> _list = [];
 
///[e] 为当前点击的Widget
 RaisedButton( color: e.ischeck ? Colors.black : Colors.red,
            onPressed: () {
              setState(() {
                  //全选
                  _list.forEach((element) {
                     e.ischeck = element.ischeck = !element.ischeck;
                  });
              });
            },
            child: Text(
              e.ischeck ? "已收藏" : "未收藏",
              style: TextStyle(color: Colors.white),
            ),
          )

全选最为简单,当点击任意一个的时候,控制所有的按钮,实现颜色的转换.

//循环所有的Widget
 _list.forEach((element) {
 		//  e.ischeck 为当前Widget
 		// element.ischeck 为_list循环的Widget
       e.ischeck = element.ischeck = !element.ischeck;
});

这段代码不理解的记得在评论区留言哦~

效果图(1.7)

ListView多选

 List<ListViewBean> _list = [];
 
///[e] 为当前点击的Widget
 RaisedButton( color: e.ischeck ? Colors.black : Colors.red,
            onPressed: () {
              setState(() {
                //多选
                  initMultipleChoice(e);
              });
            },
            child: Text(
              e.ischeck ? "已收藏" : "未收藏",
              style: TextStyle(color: Colors.white),
            ),
          )

//用来存储当前点击值
List<String> _lsitcheck = [];

 //多选
  void initMultipleChoice(ListViewBean e) {
    //多选
    if (!e.ischeck) {
      _lsitcheck.add("${e.title}");
    } else {
      _lsitcheck.remove("${e.title}");
    }
    Toast.toast(context, msg: "${_lsitcheck.toString()}");

    e.ischeck = !e.ischeck;
  }

多选也比较好理解,先判断当前是否选中

  • 如果选中的话就添加到_lsitcheck集合里面
  • 如果再点击当前选中的话就从_lsitcheck集合中删除掉在Toast出来即可

这段代码不理解的记得在评论区留言哦~

效果图(1.8)

ListView单选

代码:

RaisedButton(
            color: e.ischeck ? Colors.black : Colors.red,
            onPressed: () {
              setState(() {
                  //单选
                  initSingleChoice(e);
              });
            },
            child: Text(
              e.ischeck ? "已收藏" : "未收藏",
              style: TextStyle(color: Colors.white),
            ),
          )

 //单选
  void initSingleChoice(ListViewBean e) {
    e.ischeck = !e.ischeck;
    _list.forEach((element) {
      //若当前有选中的按钮
      if (element.ischeck == e.ischeck && e.ischeck == true) {
        //无论有没有选中 都将改成未选中
        element.ischeck = false;
        //选中
        e.ischeck = true;
        Toast.toast(context, msg: e.title);
      }
    });
  }

这段代码相对于这篇文章我觉得是最难的了

   e.ischeck = !e.ischeck;
   _list.forEach((element) {
      //若当前有选中的按钮
      if (element.ischeck == e.ischeck && e.ischeck == true) {
        //无论有没有选中 都将改成未选中
        element.ischeck = false;
        //选中
        e.ischeck = true;
        Toast.toast(context, msg: e.title);
      }

如果当前有选中的

则将选中的都改成未选中的(element.ischeck = false;)

然后在设置当前选中的(e.ischeck = true;)

这段话太绕口了,我给大家举一个例子:

如果当前选中的为1,3,6,那么循环的时候,第1,3,6个元素就会走到if()

if (element.ischeck == e.ischeck && e.ischeck == true) {
        //无论有没有选中 都将改成未选中
        element.ischeck = false;
        //选中
        e.ischeck = true;
        Toast.toast(context, msg: e.title);
      }

方法里面,将当前的1,3,6号都设置为false, 当前界面上都是未选中,之后在将当前点击的设置为false这样的话就达到了一个单选的效果.


要想看这是为什么我把这两行代码注释掉线看看这样的效果:

   e.ischeck = !e.ischeck;
   _list.forEach((element) {
      //若当前有选中的按钮
      if (element.ischeck == e.ischeck && e.ischeck == true) {
        //无论有没有选中 都将改成未选中
        //element.ischeck = false;
        //选中
       // e.ischeck = true;
        Toast.toast(context, msg: e.title);
      }

效果图(1.9):

好了,走到这里今天的效果就完成了~

最后再把ListView优化一下

ListView优化

使用场景,当网络请求特别多的时候,优化的话会提高用户使用体验

ListView优化效果:

效果图(1.10):

当前有100条数据,当用户滑动时,显示的是loading

代码:


 bool isLoadingImage = true; //是否滑动
 
NotificationListener(
          child: Container(
            child: ListView(
            .....
            ),
          ),
          onNotification: (notification) {
            ///通知类型
            switch (notification.runtimeType) {
              case ScrollStartNotification:
                print("开始滚动");
                ///在这里更新标识 刷新页面 不加载图片
                isLoadingImage = false;
                break;
              case ScrollUpdateNotification:
                print("正在滚动");
                break;
              case ScrollEndNotification:
                print("滚动停止");

                ///在这里更新标识 刷新页面 加载图片
                setState(() {
                  isLoadingImage = true;
                });
                break;
              case OverscrollNotification:
                print("滚动到边界");
                break;
            }
            return true;
          },
        )

isLoadingImage这个变量就是当前是否在滑动

在ListView设置数据时判断显示的布局:

ListView(
 children: initData(),
)

 List<Widget> initData() {
    List<Widget> mlist = [];

    mlist.add(initButton("单选", 1));
    mlist.add(initButton("多选", 2));
    mlist.add(initButton("全选", 3));

    _list.forEach((e) => {
          mlist.add(
            isLoadingImage
                ? buildItem(e)//显示正常展示布局
                :
                //滑动时显示的LoadingUtil
                Container(
                    alignment: Alignment.center,
                    child: LoadingUtil.loading(context),
                  ),
          )
        });
    return mlist;
  }

这里还是比较简单的,使用NotificationListener对ListView滑动监听,然后通过判断现在是否滑动,设置对应的布局

完整项目

ListView完整代码

loadUtil辅助类

Toast辅助类

这一篇写的有点长了,如果您看到这里,麻烦给点个赞呗,您的点赞就是对我最大的支持,留下您的点赞吧~

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s10g

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值