對于滾動的視圖,我們經常需要監(jiān)聽它的一些滾動事件,在監(jiān)聽到的時候去做對應的一些事情。
比如視圖滾動到底部時,我們可能希望做上拉加載更多;
比如滾動到一定位置時顯示一個回到頂部的按鈕,點擊回到頂部的按鈕,回到頂部;
比如監(jiān)聽滾動什么時候開始,什么時候結束;
在Flutter中監(jiān)聽滾動相關的內容由兩部分組成:ScrollController和ScrollNotification。
1. ScrollController
在Flutter中,Widget并不是最終渲染到屏幕上的元素(真正渲染的是RenderObject),因此通常這種監(jiān)聽事件以及相關的信息并不能直接從Widget中獲取,而是必須通過對應的Widget的Controller來實現(xiàn)。
ListView、GridView的組件控制器是ScrollController,我們可以通過它來獲取視圖的滾動信息,并且可以調用里面的方法來更新視圖的滾動位置。
另外,通常情況下,我們會根據(jù)滾動的位置來改變一些Widget的狀態(tài)信息,所以ScrollController通常會和StatefulWidget一起來使用,并且會在其中控制它的初始化、監(jiān)聽、銷毀等事件。
我們來做一個案例,當滾動到1000位置的時候,顯示一個回到頂部的按鈕:
- jumpTo(double offset)、animateTo(double offset,...):這兩個方法用于跳轉到指定的位置,它們不同之處在于,后者在跳轉時會執(zhí)行一個動畫,而前者不會。
- ScrollController間接繼承自Listenable,我們可以根據(jù)ScrollController來監(jiān)聽滾動事件。
class MyHomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage> {
ScrollController _controller;
bool _isShowTop = false;
@override
void initState() {
//初始化ScrollController
_controller = ScrollController();
//監(jiān)聽滾動
_controller.addListener((){
var tempShowTop = _controller.offset >= 1000;
if(tempShowTop != _isShowTop){
setState(() {
_isShowTop = tempShowTop;
});
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ListView展示"),
),
body: ListView.builder(
itemCount: 100,
itemExtent: 60,
controller: _controller,
itemBuilder: (BuildContext context,int index){
return ListTile(
title: Text("item $index"),
);
},
),
floatingActionButton: !_isShowTop?null:FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: (){
_controller.animateTo(0, duration: Duration(milliseconds: 1000), curve: Curves.ease);
},
),
);
}
}

image.png
2. NotificationListener
如果我們希望監(jiān)聽什么時候開始滾動,什么時候結束滾動,這個時候我們可以通過NotificationListener。
- NotificationListener是一個Widget,模板參數(shù)T是想監(jiān)聽的通知類型,如果省略,則所有類型通知都會被監(jiān)聽,如果指定特定類型,則只有該類型的通知會被監(jiān)聽。
- NotificationListener需要一個onNotification回調函數(shù),用于實現(xiàn)監(jiān)聽處理邏輯。
- 該回調可以返回一個布爾值,代表是否阻止該事件繼續(xù)向上冒泡,如果為true時,則冒泡終止,事件停止向上傳播,如果不返回或者返回值為false 時,則冒泡繼續(xù)。
案例: 列表滾動, 并且在中間顯示滾動進度
class MyHomeNotificationDemo extends StatefulWidget {
@override
_MyHomeNotificationDemoState createState() => _MyHomeNotificationDemoState();
}
class _MyHomeNotificationDemoState extends State<MyHomeNotificationDemo> {
int _progress = 0;
@override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: (ScrollNotification notification){
//1.監(jiān)聽事件的類型
if(notification is ScrollStartNotification){
print("開始滾動...");
}else if(notification is ScrollUpdateNotification){
//當前滾動的位置和總長度
final currentPixel = notification.metrics.pixels;
final totalPixel = notification.metrics.maxScrollExtent;
double progress = currentPixel/totalPixel;
setState(() {
_progress = (progress*100).toInt();
});
print("正在滾動:${notification.metrics.pixels} - ${notification.metrics.maxScrollExtent}");
}else if(notification is ScrollEndNotification){
print("滾動結束....");
}
return false;
},
child: Stack(
alignment: Alignment(.9,.9),
children: <Widget>[
ListView.builder(
itemCount: 100,
itemExtent: 80,
itemBuilder: (BuildContext context,int index){
return ListTile(title: Text("item $index"),);
},
),
CircleAvatar(
radius: 30,
child: Text("$_progress%"),
backgroundColor: Colors.black54,
),
],
),
);
}
}

image.png
學習內容來自Flutter從入門到實戰(zhàn)