Flutter 滾動監(jiān)聽及實戰(zhàn)appBar滾動漸變的實現(xiàn)
介紹
在 Flutter 中滾動監(jiān)聽一般可以采用兩種方式來實現(xiàn),分別是ScrollController
和NotificationListener
這兩種方式。
ScrollController介紹
ScrollController
介紹一下ScrollController
常用的屬性和方法:
offset
:可滾動組件當前的滾動位置。jumpTo(double offset)
跳轉到指定位置,offset
為滾動偏移量。animateTo(double offset,@required Duration duration,@required Curve curve)
同jumpTo(double offset)
一樣,不同的是animateTo
跳轉時會執(zhí)行一個動畫,需要傳入執(zhí)行動畫需要的時間和動畫曲線。
ScrollPosition
ScrollPosition是用來保存可滾動組件的滾動位置的。一個 ScrollController 對象可能會被多個可滾動的組件使用,
ScrollController 會為每一個滾動組件創(chuàng)建一個 ScrollPosition 對象來存儲位置信息。ScrollPosition 中存儲的是在 ScrollController 的 positions 屬性里面,他是一個List<ScrollPosition>
數(shù)組,在 ScrollController 中真正保存位置信息的就是 ScrollPosition,而 offset 只是一個便捷使用的屬性。查看源碼中可以發(fā)現(xiàn) offset 獲取就是從 ScrollPosition 中獲取的。
/// Returns the attached [ScrollPosition], from which the actual scroll offset /// of the [ScrollView] can be obtained. /// Calling this is only valid when only a single position is attached. ScrollPosition get position { assert(_positions.isNotEmpty, 'ScrollController not attached to any scroll views.'); assert(_positions.length == 1, 'ScrollController attached to multiple scroll views.'); return _positions.single; } /// The current scroll offset of the scrollable widget. /// Requires the controller to be controlling exactly one scrollable widget. double get offset => position.pixels;
一個ScrollController
雖然可以對應多個可滾動組件,但是讀取滾動位置offset
,則需要一對一讀取。在一對多的情況下,我們可以使用其他方法來實現(xiàn)讀取滾動位置。假設現(xiàn)在一個ScrollController
對應了兩個可以滾動的組件,那么可以通過position.elementAt(index)
來獲取ScrollPosition
,從而獲得offset
:
controller.positions.elementAt(0).pixels controller.positions.elementAt(1).pixels
ScrollPosition的方法
ScrollPosition
有兩個常用方法:分別是animateTo()
和jumpTo()
,他們才是真正控制跳轉到滾動位置的方法,在 ScrollController 中這兩個同名方法,內(nèi)部最終都會調用 ScrollPosition 這兩個方法。
Future<void> animateTo( double offset, { @required Duration duration, @required Curve curve, }) { assert(_positions.isNotEmpty, 'ScrollController not attached to any scroll views.'); final List<Future<void>> animations = List<Future<void>>(_positions.length); for (int i = 0; i < _positions.length; i += 1) // 調用 ScrollPosition 中的 animateTo 方法 animations[i] = _positions[i].animateTo(offset, duration: duration, curve: curve); return Future.wait<void>(animations).then<void>((List<void> _) => null); }
ScrollController控制原理
ScrollController
還有其他比較重要的三個方法:
1、createScrollPosition
:當ScrollController
和可滾動組件關聯(lián)時,可滾動組件首先會調ScrollController
的createScrollPosition
方法來創(chuàng)建一個ScrollPosition
來存儲滾動位置信息。
ScrollPosition createScrollPosition( ScrollPhysics physics, ScrollContext context, ScrollPosition oldPosition);
2、在滾動組件調用createScrollPosition
方法之后,接著會調用attach
方法來將創(chuàng)建號的ScrollPosition
信息添加到positions
屬性中,這一步稱為“注冊位置”,只有注冊后animateTo()
和jumpTo()
才可以被調用。
void attach(ScrollPosition position);
3、最后當可滾動組件被銷毀時,會調用detach()
方法,將其ScrollPosition
對象從ScrollController
的positions
屬性中移除,這一步稱為“注銷位置”,注銷后animateTo()
和jumpTo()
將不能再被調用。
void detach(ScrollPosition position);
NotificationListener介紹
通知冒泡
Flutter Widget 樹中子 Widge t可以通過發(fā)送通知(Notification)與父(包括祖先) Widget 進行通信,父級組件可以通過NotificationListener
組件來監(jiān)聽自己關注的通知,這種通信方式類似于 Web 開發(fā)中瀏覽器的事件冒泡,在 Flutter 中就沿用了“冒泡”這個術語,稱為通知冒泡
通知冒泡和用戶觸摸事件冒泡是相似的,但有一點不同:通知冒泡可以中止,但用戶觸摸事件不行。
滾動通知
Flutter 中很多地方使用了通知,如可滾動組件(Scrollable Widget)滑動時就會分發(fā)滾動通知(ScrollNotification),而Scrollbar
正是通過監(jiān)聽ScrollNotification
來確定滾動條位置的。
switch (notification.runtimeType){ case ScrollStartNotification: print("開始滾動"); break; case ScrollUpdateNotification: print("正在滾動"); break; case ScrollEndNotification: print("滾動停止"); break; case OverscrollNotification: print("滾動到邊界"); break; }
其中ScrollStartNotification
和ScrollUpdateNotification
等都是繼承ScrollNotification
類的,不同類型的通知子類會包含不同的信息,ScrollUpdateNotification
有一個scrollDelta
屬性,它記錄了移動的位移。
NotificationListener
時繼承StatelessWidget
類的額,左右我們可以直接在放置在Widget 數(shù)中,通過里面的onNotification
可以指定一個模板參數(shù),該模板參數(shù)類型必須是繼承自Notification
,可以顯式指定模板參數(shù)時,比如通知的類型為滾動結束通知:
NotificationListener<ScrollEndNotification>
這個時候NotificationListener
便只會接收該參數(shù)類型的通知。
onNotification
回調為通知處理回調,他的返回值時布爾類型(bool),當返回值為true
時,阻止冒泡,其父級 Widget 將再也收不到該通知;當返回值為false
時繼續(xù)向上冒泡通知。
兩者區(qū)別
首先這兩種方式都可以實現(xiàn)對滾動的監(jiān)聽,但是他們還是有一些區(qū)別:
ScrollController
可以控制滾動控件的滾動,而NotificationListener
是不可以的。- 通過
NotificationListener
可以在從可滾動組件到widget樹根之間任意位置都能監(jiān)聽,而ScrollController
只能和具體的可滾動組件關聯(lián)后才可以。 - 收到滾動事件后獲得的信息不同;
NotificationListener
在收到滾動事件時,通知中會攜帶當前滾動位置和ViewPort的一些信息,而ScrollController
只能獲取當前滾動位置。ScrollController實例效果圖
代碼實現(xiàn)步驟
創(chuàng)建滾動所需的界面,一個Scaffold
組件body
里面方式一個Stack
的層疊小部件,里面放置一個listview
,和自定義的appBar
;floatingActionButton
放置一個返回頂部的懸浮按鈕。
Scaffold( body: Stack( children: <Widget>[ MediaQuery.removePadding( removeTop: true, context: context, child: ListView.builder( // ScrollController 關聯(lián)滾動組件 controller: _controller, itemCount: 100, itemBuilder: (context, index) { if (index == 0) { return Container( height: 200, child: Swiper( itemBuilder: (BuildContext context, int index) { return new Image.network( "http://via.placeholder.com/350x150", fit: BoxFit.fill, ); }, itemCount: 3, autoplay: true, pagination: new SwiperPagination(), ), ); } return ListTile( title: Text("ListTile:$index"), ); }, ), ), Opacity( opacity: toolbarOpacity, child: Container( height: 98, color: Colors.blue, child: Padding( padding: const EdgeInsets.only(top: 30.0), child: Center( child: Text( "ScrollerDemo", style: TextStyle(color: Colors.white, fontSize: 20.0), ), ), ), ), ) ], ), floatingActionButton: !showToTopBtn ? null : FloatingActionButton( child: Icon(Icons.keyboard_arrow_up), onPressed: () { _controller.animateTo(0.0, duration: Duration(milliseconds: 500), curve: Curves.ease); }, ), )
創(chuàng)建ScrollController
對象,在初始化中添加對滾動的監(jiān)聽,并和ListView
這個可滾動小部件進行關聯(lián):
double t = _controller.offset / DEFAULT_SCROLLER; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } setState(() { toolbarOpacity = t; });
在 _controller.addListener 中添加相關業(yè)務代碼,根據(jù)滾動的偏移量計算出透明度,實現(xiàn)appBar滾動漸變:
if(_controller.offset < DEFAULT_SHOW_TOP_BTN && showToTopBtn){ setState(() { showToTopBtn = false; }); }else if(_controller.offset >= DEFAULT_SHOW_TOP_BTN && !showToTopBtn){ setState(() { showToTopBtn = true; }); }
更具滾動的高度和當前floatingActionButton
的現(xiàn)實狀態(tài),判斷floatingActionButton
是否需要展示:
if(_controller.offset < DEFAULT_SHOW_TOP_BTN && showToTopBtn){ setState(() { showToTopBtn = false; }); }else if(_controller.offset >= DEFAULT_SHOW_TOP_BTN && !showToTopBtn){ setState(() { showToTopBtn = true; }); }
點擊floatingActionButton
返回到頂部:
_controller.animateTo(0.0, duration: Duration(milliseconds: 500), curve: Curves.ease);
完整代碼請參考下方GitHub項目中/demo/scroller_demo.dart
文件。
NotificationListener實例
效果圖
代碼實現(xiàn)步驟
在 NotificationListener 實例中布局基本上和 ScrollController 一致,不同的地方在于 ListView 需要包裹在 NotificationListener 中作為 child,然后 NotificationListener 在 onNotification 中判斷滾動偏移量:
if (notification is ScrollUpdateNotification && notification.depth == 0) { double t = notification.metrics.pixels / DEFAULT_SCROLLER; if (t < 0.0) { t = 0.0; } else if (t > 1.0) { t = 1.0; } setState(() { toolbarOpacity = t; }); print(notification.metrics.pixels); //打印滾動位置 }
完整代碼請參考下方GitHub項目中/demo/notification_listener_demo.dart
文件
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持我們。
欄 目:Android
下一篇:Android使用MediaCodec將攝像頭采集的視頻編碼為h264
本文標題:Flutter 滾動監(jiān)聽及實戰(zhàn)appBar滾動漸變的實現(xiàn)
本文地址:http://mengdiqiu.com.cn/a1/Android/9166.html
您可能感興趣的文章
- 01-10如何給Flutter界面切換實現(xiàn)點特效
- 01-10Flutter適配深色模式的方法(DarkMode)
- 01-10Flutter里面錯誤捕獲的正確方法
- 01-10Android DSelectorBryant 單選滾動選擇器的實例代碼
- 01-10如何使用Flutter實現(xiàn)58同城中的加載動畫詳解
- 01-10android監(jiān)聽器實例代碼
- 01-10Flutter 假異步的實現(xiàn)示例
- 01-10使用Flutter實現(xiàn)一個走馬燈布局的示例代碼
- 01-10Flutter中如何加載并預覽本地的html文件的方法
- 01-10SurfaceView 視頻發(fā)送彈幕并實現(xiàn)滾動歌詞


閱讀排行
本欄相關
- 01-10Android自定義View之繪制圓形頭像功能
- 01-10Android實現(xiàn)雙擊返回鍵退出應用實現(xiàn)方
- 01-10android實現(xiàn)簡單計算器功能
- 01-10android實現(xiàn)記住用戶名和密碼以及自動
- 01-10C++自定義API函數(shù)實現(xiàn)大數(shù)相乘算法
- 01-10Android 友盟第三方登錄與分享的實現(xiàn)代
- 01-10android實現(xiàn)指紋識別功能
- 01-10如何給Flutter界面切換實現(xiàn)點特效
- 01-10Android實現(xiàn)圓形漸變加載進度條
- 01-10Emoji表情在Android JNI中的兼容性問題詳
隨機閱讀
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 04-02jquery與jsp,用jquery
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10C#中split用法實例總結
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-10delphi制作wav文件的方法
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10使用C語言求解撲克牌的順子及n個骰子