灵魂烈焰中死神降临,生命不断流逝,枯萎,只求不要带走那对你最后的一丝思念。——沙漠死神
先来看看今天的最终效果吧:
效果图(1.1)
:
咋们先来学习ListView,若果你对ListView比较熟悉,直接跳过这段吧~
加粗是必须传参数
ListView参数 | 类型 | 说明 |
---|---|---|
scrollDirection | Axis | 滑动方向 Axis.vertical(默认) Axis.horizontal |
itemExtent | double | 表示的是子Widget之间的距离 当scrollDirection = Axis.vertical itemExtent代表的是高度 当scrollDirection = Axis.horizontal itemExtent代表的是宽度 |
padding | EdgeInsetsGeometry | 内边距 |
primary | bool | 当条目不足时 true可以尝试滚动 false不可以滚动 |
physics | ScrollPhysics | 滑动类型 BouncingScrollPhysics() 拉到ListView最底部有回弹效果 ClampingScrollPhysics() 包裹内容不会回弹 NeverScrollableScrollPhysics() 滑动禁止 |
cacheExtent | double | 预加载区域 |
reverse | bool | 是否倒序显示 默认正序 false 倒序true |
controller | ScrollController | 滑动控制器,监听ListView滑动距离等 |
children | List |
子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)
:
ClampingScrollPhysics(默认) 包裹内容不会回弹
效果图(1.5)
:
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();
}
这个属性常用于监听ListView是否滑动到最底部,从而加载更多属性.
其他属性都非常简单,大家自己动手试一下吧~
ListView中还有自带的ListTile
ListTile参数 | 类型 | 说明 |
---|---|---|
tileColor | Color | 背景色会把圆角覆盖掉!! |
subtitle | Widget | 副标题 |
dense | bool | 将字体缩小 |
leading | Widget | 左边图标 |
trailing | Wdiget | 末尾图标设置 |
contentPadding | EdgeInsetsGeometry | 内边距 |
title | Widget | 主标题 |
onTap | GestureTapCallback | 单击事件 |
onLongPress | GestureLongPressCallback | 长按事件 |
selected | bool | 如果选中,则颜色会跟随主题颜色 |
enabled | bool | 禁止点击事件 |
_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,//禁止点击事件
);
}
走到这里的话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)
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;
}
多选也比较好理解,先判断当前是否选中
这段代码不理解的记得在评论区留言哦~
代码:
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);
}
好了,走到这里今天的效果就完成了~
最后再把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辅助类
这一篇写的有点长了,如果您看到这里,麻烦给点个赞呗,您的点赞就是对我最大的支持,留下您的点赞吧~