代码编织梦想

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

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

效果图(1.1):

20201211154618470.gif

咋们先来学习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):

20201211160210672.gif

来看看内几种滑动效果:

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

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

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

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)

20201211163807301.gif

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)
20201211164427991.gif

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):
20201216174414415.gif

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

最后再把ListView优化一下

ListView优化

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

ListView优化效果:

效果图(1.10):
20201211171934716.gif

当前有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辅助类

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

在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/weixin_44819566/article/details/111037724

Listview中的getChildAt()方法返回null-爱代码爱编程

今天写代码时候,开发一个ListView的刷新某个item的功能。使用到了listView.getChildAt(position)这么一个方法获取当前点击的item的view。 简单测试发现一个问题,在不上拉加载数据的时候,当前屏幕的item点击刷新是没问题的。但是经过上拉加载下一个屏幕的item的数据时候,getChildAt(position)方法会

【机器学习基础】Scipy(科学计算库) 简易入门-爱代码爱编程

0.导语 Scipy是一个用于数学、科学、工程领域的常用软件包,可以处理插值、积分、优化、图像处理、常微分方程数值解的求解、信号处理等问题。它用于有效计算Numpy矩阵,使Numpy和Scipy协同工作,高效解决问题。 Scipy是由针对特定任务的子模块组成: 模块名应用领域scipy.cluster向量计算/Kmeansscipy.const

Android开发(三):在Listview以列表形式显示数组数据,并实现文本框自动补全-爱代码爱编程

输入框自动补全是一个很有趣的功能,实现起来也非常简单。 控件选择AutoCompleteTextView,completionThreshold属性控制输入几个字开始自动补全。 创建了一个数组用来存储数据,然后把数组数添加到ArrayAdapter适配器,用AutoCompleteTextView加载适配器,实现自动补全。 布局如下: <?xml

android expandlistview三级动态列表使用及各级点击事件-爱代码爱编程

功能(以模拟音乐播放器各级文件夹目录为例): 1、首先通过按键点击事件生成首级列表目录 2、通过点击首级列表进行动态数据更新生成二级目录 3、生成三级目录后,最终在第三级目录上进行条目的点击事件 activity.xml 内容 <?xml version="1.0" encoding="utf-8"?> <LinearLayout

Flutter之listView加载数据 刷新以及加载更多-爱代码爱编程

import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_weight_ui/model/home_article_data

Flutter之banner实现-爱代码爱编程

 先写一个banner控件 import 'dart:async'; import 'package:flutter/material.dart'; import 'package:transparent_image/transparent_image.dart'; //这个是在网络上找的一个例子,练手 const MAX_COUNT = 0x7

flutter快速入门,一周搞定前后端,上线完毕-爱代码爱编程

注意 项目是持续可运行的,起码在服务器有效期内是可运行的,如果运行失败,请相信我,检查自己的flutter环境。如果有关于接口方面的报错,请回复联系我,应该是接口改了,还没来得及更新,看到后会及时更新的。 基于flutter 2.0之前的版本做的开发,最新的版本如需运行请自行兼容适配。 免租说明 项目起源于蛋壳暴雷事件:从目前社会现状来看

Android 计算网络速度&文件下载剩余时间<<最优方案>>-爱代码爱编程

最近在项目开发中遇到了一项功能,需要在下载文件时显示出当前的网络速度和预计剩余时间,在调研中发现使用的比较多的是通过TrafficStats来获取网络使用量然后在间隔每秒后重新获取一次网络使用量,两者相机算既可以得出每秒的网络使用量。 public class FlowStats { private long lastTotalRxByt

flutter webview三指奔溃或者冻屏-爱代码爱编程

是手势冲突: Android有onTouchEvent可以抛异常 flutter需要用GestureDetector 重写 onTapDown:(e){} 虽然看着没啥用 但是解决 多指滑动报错问题 (android 8.1以下) return GestureDetector( onTapDown: (event) {}, child

flutter 中的深拷贝-爱代码爱编程

在web开发中,在遇到基础类型和复杂类型 的深拷贝问题大部分可以通过 let newObj = JSON.parse(JSON.stringify(obj));来完成深拷贝(函数除外  拓展https://www.jianshu.com/p/1c142ec2ca45)   1.flutter 的dart 语法在处理json map 的时候可以通过类似