前言
在上一篇中,我們學習了controller
、點選事件onPressed
和GestureDetector
、TabBar
和PageView
聯動的使用,這一篇,我們來說說ListView
的上拉重新整理、下拉載入和輪播圖。
重新整理控制元件
下拉重新整理
官方為我們提供了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<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,
);
}
複製程式碼