前言
在上一篇中趋艘,我們學(xué)習(xí)了controller
、點(diǎn)擊事件onPressed
和GestureDetector
、TabBar
和PageView
聯(lián)動(dòng)的使用苹祟,這一篇,我們來(lái)說(shuō)說(shuō)ListView
的上拉刷新、下拉加載和輪播圖苔咪。
刷新控件
下拉刷新
官方為我們提供了RefreshIndicator
锰悼,主要有color
、backgroundColor
团赏、displacement
箕般、onRefresh
等屬性
RefreshIndicator(
//刷新進(jìn)度條顏色
color: Colors.black45,
//背景色
backgroundColor: Colors.blue,
//觸發(fā)下拉刷新的距離 默認(rèn)40
displacement: 40,
//下拉回調(diào)方法,方法需要有async和await關(guān)鍵字,沒(méi)有await舔清,刷新圖標(biāo)立馬消失丝里,沒(méi)有async,刷新圖標(biāo)不會(huì)消失
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() {
///增加滑動(dòng)監(jiān)聽(tīng)
_scrollController.addListener(() {
///判斷當(dāng)前滑動(dòng)位置是不是到達(dá)底部杯聚,觸發(fā)加載更多回調(diào)
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
setState(() {
count += 5;
});
}
});
super.initState();
}
@override
void dispose() {
super.dispose();
_scrollController.dispose();
}
然后將_scrollController
設(shè)置給ListView
當(dāng)我們運(yùn)行時(shí)卻發(fā)現(xiàn),不僅不能上拉加載抒痒,連下拉刷新也失效了幌绍!
這是因?yàn)椋?code>RefreshIndicator和ScrollController
有兼容性問(wèn)題,當(dāng)然官方也給出了解決辦法故响,給ListView
添加如下代碼:
physics: const AlwaysScrollableScrollPhysics()
效果圖如下
雖然說(shuō)功能實(shí)現(xiàn)了傀广,但是感覺(jué)效果有點(diǎn)怪怪的,新的item出現(xiàn)前應(yīng)該有個(gè)過(guò)渡彩届。
思路如下伪冰,當(dāng)觸發(fā)上拉加載時(shí),給ListView
添加一個(gè)加載中的item樟蠕,當(dāng)加載完成后贮聂,再移除。
先定義個(gè)變量寨辩,bool loadMore = false;
當(dāng)觸發(fā)上拉加載時(shí)吓懈,設(shè)置為true
@override
void initState() {
///增加滑動(dòng)監(jiān)聽(tīng)
_scrollController.addListener(() {
///判斷當(dāng)前滑動(dòng)位置是不是到達(dá)底部,觸發(fā)加載更多回調(diào)
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
setState(() {
loadMore = true;
});
getMore();
}
});
super.initState();
}
加載完成時(shí)靡狞,設(shè)置為false
Future getMore() async {
await Future.delayed(Duration(seconds: 3), () {
setState(() {
loadMore = false;
count += 5;
});
});
}
這時(shí)骄瓣,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() {
///增加滑動(dòng)監(jiān)聽(tīng)
_scrollController.addListener(() {
///判斷當(dāng)前滑動(dòng)位置是不是到達(dá)底部,觸發(fā)加載更多回調(diào)
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(
//刷新進(jìn)度條顏色
color: Colors.black45,
//背景色
backgroundColor: Colors.blue,
////觸發(fā)下拉刷新的距離 默認(rèn)40
displacement: 40,
//下拉回調(diào)方法,方法需要有async和await關(guān)鍵字耍攘,沒(méi)有await,刷新圖標(biāo)立馬消失畔勤,沒(méi)有async蕾各,刷新圖標(biāo)不會(huì)消失
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任何情況都能滾動(dòng),解決在RefreshIndicator的兼容問(wèn)題庆揪。
physics: const AlwaysScrollableScrollPhysics(),
),
),
);
}
}
輪播圖
首先式曲,用到的是PageView
,以及PageController
,這些之前已經(jīng)說(shuō)過(guò)吝羞,就不在細(xì)說(shuō)
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,
);
}
其次兰伤,需要一個(gè)定時(shí)器,別忘記取消
int count = 3;
int currentPosition = 0;
@override
void initState() {
super.initState();
_timer = new Timer.periodic(Duration(seconds: 2), (time) {
//每2秒執(zhí)行一次
changePage();
});
}
@override
void dispose() {
super.dispose();
_timer.cancel();
pageController.dispose();
}
void changePage() {
pageController.animateToPage(currentPosition % count,
duration: Duration(milliseconds: 200), curve: Curves.fastOutSlowIn);
currentPosition++;
}
效果圖如下
當(dāng)我們手動(dòng)滑動(dòng)時(shí)钧排,currentPosition
會(huì)錯(cuò)亂敦腔,我們需要對(duì)其進(jìn)行調(diào)整,需要用到的是onPageChanged
屬性
onPageChanged: (index) {
_timer.cancel();
currentPosition = index;
_timer = new Timer.periodic(Duration(seconds: 2), (time) {
changePage();
});
},
當(dāng)然恨溜,這么常用的控件符衔,已經(jīng)有造好的輪子了 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,
//動(dòng)畫(huà)時(shí)間,默認(rèn)300.0毫秒
duration: 300,
//初始位置
index: 0,
//無(wú)限輪播模式開(kāi)關(guān)
loop: true,
//是否自動(dòng)播放糟袁,默認(rèn)false
autoplay: true,
layout: SwiperLayout.DEFAULT,
//滾動(dòng)方式
scrollDirection: Axis.horizontal,
//點(diǎn)擊輪播的事件
onTap: (index) {},
//用戶拖拽的時(shí)候判族,是否停止自動(dòng)播放
autoplayDisableOnInteraction: true,
//指示器
pagination: new SwiperPagination(),
//左右箭頭
control: null,
);
}