在上一篇中,咱们学习了controller
、点击事件onPressed
和GestureDetector
、TabBar
和PageView
联动的使用,这一篇,咱们来讲说ListView
的上拉刷新、下拉加载和轮播图。java
官方为咱们提供了RefreshIndicator
,主要有color
、backgroundColor
、displacement
、onRefresh
等属性git
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;
});
});
}
复制代码
效果图以下 github
上拉加载的话,须要使用ListView
的controller
属性app
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
async
当咱们运行时却发现,不只不能上拉加载,连下拉刷新也失效了!ide
这是由于,RefreshIndicator
和ScrollController
有兼容性问题,固然官方也给出了解决办法,给ListView
添加以下代码: physics: const AlwaysScrollableScrollPhysics()
oop
效果图以下 学习
虽说功能实现了,可是感受效果有点怪怪的,新的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
ui
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<ListViewDemo> {
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: <Widget>[
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,
);
}
复制代码