前言
在上一篇中,我们学习了controller
、点击事件onPressed
和GestureDetector
、TabBar
和PageView
联动的使用,这一篇,我们来说说ListView
的上拉刷新、下拉加载和轮播图。
- 【Flutter】开发之目录
刷新控件
下拉刷新
官方为我们提供了RefreshIndicator
,主要有color
、backgroundColor
、displacement
、onRefresh
等属性
RefreshIndicator(
//刷新进度条颜色
color: Colors.black45,
//背景色
backgroundColor: Colors.blue,
//触发下拉刷新的距离 默认40
displacement: 40,
//下拉回调方法,方法需要有async和await关键字,没有await,刷新图标立马消失,没有async,刷新图标不会消失
onRefresh: refresh,
child: ListView.separated(
itemBuilder: ((context, index) {
return MoveItem();
}),
separatorBuilder: (context, index) {
return Divider(
color: Colors.black45,
height: 10,
);
},
itemCount: count,
),
),
int count = 2;
Future refresh() async {
await Future.delayed(Duration(seconds: 3), () {
setState(() {
count = 10;
});
});
}
效果图如下
上拉加载
上拉加载的话,需要使用ListView
的controller
属性
final ScrollController _scrollController = new ScrollController();
@override
void initState() {
///增加滑动监听
_scrollController.addListener(() {
///判断当前滑动位置是不是到达底部,触发加载更多回调
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
setState(() {
count += 5;
});
}
});
super.initState();
}
@override
void dispose() {
super.dispose();
_scrollController.dispose();
}
然后将_scrollController
设置给ListView
当我们运行时却发现,不仅不能上拉加载,连下拉刷新也失效了!
这是因为,RefreshIndicator
和ScrollController
有兼容性问题,当然官方也给出了解决办法,给ListView
添加如下代码:
physics: const AlwaysScrollableScrollPhysics()
效果图如下
虽然说功能实现了,但是感觉效果有点怪怪的,新的item出现前应该有个过渡。
思路如下,当触发上拉加载时,给ListView
添加一个加载中的item,当加载完成后,再移除。
先定义个变量,bool loadMore = false;
当触发上拉加载时,设置为true
@override
void initState() {
///增加滑动监听
_scrollController.addListener(() {
///判断当前滑动位置是不是到达底部,触发加载更多回调
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
setState(() {
loadMore = true;
});
getMore();
}
});
super.initState();
}
加载完成时,设置为false
Future getMore() async {
await Future.delayed(Duration(seconds: 3), () {
setState(() {
loadMore = false;
count += 5;
});
});
}
这时,item就不能是固定的了
Widget getItem(int index) {
if (loadMore && index == count) {
return LoadMoreItem();
} else {
return MoveItem();
}
}
包括count
int getItemCount() {
if (loadMore) {
return count + 1;
} else {
return count;
}
}
效果图如下
完整代码如下
class ListViewDemo extends StatefulWidget {
@override
_ReListViewDemoState createState() => _ReListViewDemoState();
}
class _ReListViewDemoState extends State {
int count = 2;
final ScrollController _scrollController = new ScrollController();
bool loadMore = false;
Future refresh() async {
await Future.delayed(Duration(seconds: 3), () {
setState(() {
count = 10;
});
});
}
Future getMore() async {
await Future.delayed(Duration(seconds: 3), () {
setState(() {
loadMore = false;
count += 5;
});
});
}
@override
void initState() {
///增加滑动监听
_scrollController.addListener(() {
///判断当前滑动位置是不是到达底部,触发加载更多回调
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
setState(() {
loadMore = true;
});
getMore();
}
});
super.initState();
}
Widget getItem(int index) {
if (loadMore && index == count) {
return LoadMoreItem();
} else {
return MoveItem();
}
}
int getItemCount() {
if (loadMore) {
return count + 1;
} else {
return count;
}
}
@override
void dispose() {
super.dispose();
_scrollController.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListViewDemo'),
centerTitle: true,
brightness: Brightness.dark,
),
body: RefreshIndicator(
//刷新进度条颜色
color: Colors.black45,
//背景色
backgroundColor: Colors.blue,
////触发下拉刷新的距离 默认40
displacement: 40,
//下拉回调方法,方法需要有async和await关键字,没有await,刷新图标立马消失,没有async,刷新图标不会消失
onRefresh: refresh,
child: ListView.separated(
itemBuilder: ((context, index) {
return getItem(index);
}),
separatorBuilder: (context, index) {
return Divider(
color: Colors.black45,
height: 10,
);
},
itemCount: getItemCount(),
controller: _scrollController,
//保持ListView任何情况都能滚动,解决在RefreshIndicator的兼容问题。
physics: const AlwaysScrollableScrollPhysics(),
),
),
);
}
}
轮播图
首先,用到的是PageView
,以及PageController
,这些之前已经说过,就不在细说
Widget _pageView() {
return PageView(
children: [
Image.network(
'http://img4.imgtn.bdimg.com/it/u=1621655683,865218969&fm=200&gp=0.jpg',
height: 150,
fit: BoxFit.fitWidth,
),
Image.network(
'http://img1.imgtn.bdimg.com/it/u=1901690610,3955011377&fm=200&gp=0.jpg',
height: 150,
fit: BoxFit.fitWidth,
),
Image.network(
'http://img3.imgtn.bdimg.com/it/u=1546158593,2358526642&fm=200&gp=0.jpg',
height: 150,
fit: BoxFit.fitWidth,
),
],
controller: pageController,
);
}
其次,需要一个定时器,别忘记取消
int count = 3;
int currentPosition = 0;
@override
void initState() {
super.initState();
_timer = new Timer.periodic(Duration(seconds: 2), (time) {
//每2秒执行一次
changePage();
});
}
@override
void dispose() {
super.dispose();
_timer.cancel();
pageController.dispose();
}
void changePage() {
pageController.animateToPage(currentPosition % count,
duration: Duration(milliseconds: 200), curve: Curves.fastOutSlowIn);
currentPosition++;
}
效果图如下
当我们手动滑动时,currentPosition
会错乱,我们需要对其进行调整,需要用到的是onPageChanged
属性
onPageChanged: (index) {
_timer.cancel();
currentPosition = index;
_timer = new Timer.periodic(Duration(seconds: 2), (time) {
changePage();
});
},
当然,这么常用的控件,已经有造好的轮子了 flutter_swiper
首先添加依赖flutter_swiper: ^1.1.6
下面是一些常用属性
Widget _swiper() {
return new Swiper(
itemBuilder: (BuildContext context, int index) {
return new Image.network(
"http://img4.imgtn.bdimg.com/it/u=1621655683,865218969&fm=200&gp=0.jpg",
fit: BoxFit.fitWidth,
height: 150,
);
},
itemCount: 3,
//动画时间,默认300.0毫秒
duration: 300,
//初始位置
index: 0,
//无限轮播模式开关
loop: true,
//是否自动播放,默认false
autoplay: true,
layout: SwiperLayout.DEFAULT,
//滚动方式
scrollDirection: Axis.horizontal,
//点击轮播的事件
onTap: (index) {},
//用户拖拽的时候,是否停止自动播放
autoplayDisableOnInteraction: true,
//指示器
pagination: new SwiperPagination(),
//左右箭头
control: null,
);
}