對(duì)于滾動(dòng)的視圖,我們經(jīng)常需要監(jiān)聽(tīng)它的一些滾動(dòng)事件,在監(jiān)聽(tīng)到的時(shí)候去做對(duì)應(yīng)的一些事情。
比如視圖滾動(dòng)到底部時(shí),我們可能希望做上拉加載更多;
比如滾動(dòng)到一定位置時(shí)顯示一個(gè)回到頂部的按鈕,點(diǎn)擊回到頂部的按鈕,回到頂部;
比如監(jiān)聽(tīng)滾動(dòng)什么時(shí)候開(kāi)始,什么時(shí)候結(jié)束;
在Flutter中監(jiān)聽(tīng)滾動(dòng)相關(guān)的內(nèi)容由兩部分組成:ScrollController和ScrollNotification。
ScrollController
在Flutter中,Widget并不是最終渲染到屏幕上的元素(真正渲染的是RenderObject),因此通常這種監(jiān)聽(tīng)事件以及相關(guān)的信息并不能直接從Widget中獲取,而是必須通過(guò)對(duì)應(yīng)的Widget的Controller來(lái)實(shí)現(xiàn)。
ListView、GridView的組件控制器是ScrollController,我們可以通過(guò)它來(lái)獲取視圖的滾動(dòng)信息,并且可以調(diào)用里面的方法來(lái)更新視圖的滾動(dòng)位置。
另外,通常情況下,我們會(huì)根據(jù)滾動(dòng)的位置來(lái)改變一些Widget的狀態(tài)信息,所以ScrollController通常會(huì)和StatefulWidget一起來(lái)使用,并且會(huì)在其中控制它的初始化、監(jiān)聽(tīng)、銷毀等事件。
我們來(lái)做一個(gè)案例,當(dāng)滾動(dòng)到1000位置的時(shí)候,顯示一個(gè)回到頂部的按鈕:
jumpTo(double offset)、animateTo(double offset,...):這兩個(gè)方法用于跳轉(zhuǎn)到指定的位置,它們不同之處在于,后者在跳轉(zhuǎn)時(shí)會(huì)執(zhí)行一個(gè)動(dòng)畫,而前者不會(huì)。
ScrollController間接繼承自Listenable,我們可以根據(jù)ScrollController來(lái)監(jiān)聽(tīng)滾動(dòng)事件。
classMyHomePageextendsStatefulWidget{
? @override
? State<StatefulWidget> createState() => MyHomePageState();
}
classMyHomePageStateextendsState{
? ScrollController _controller;
? bool _isShowTop = false;
? @override
? void initState() {
? ? // 初始化ScrollController
? ? _controller = ScrollController();
? ? // 監(jiān)聽(tīng)滾動(dòng)
? ? _controller.addListener(() {
? ? ? var tempSsShowTop = _controller.offset >= 1000;
? ? ? if (tempSsShowTop != _isShowTop) {
? ? ? ? setState(() {
? ? ? ? ? _isShowTop = tempSsShowTop;
? ? ? ? });
? ? ? }
? ? });
? ? 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);
? ? ? ? },
? ? ? ),
? ? );
? }
}