1. 程式人生 > >Flutter 滾動監聽及實戰appBar滾動漸變

Flutter 滾動監聽及實戰appBar滾動漸變

介紹

在 Flutter 中滾動監聽一般可以採用兩種方式來實現,分別是 ScrollControllerNotificationListener 這兩種方式。

ScrollController介紹

ScrollController

介紹一下ScrollController常用的屬性和方法:

  • offset:可滾動元件當前的滾動位置。
  • jumpTo(double offset) 跳轉到指定位置,offset 為滾動偏移量。
  • animateTo(double offset,@required Duration duration,@required Curve curve)jumpTo(double offset)
    一樣,不同的是 animateTo 跳轉時會執行一個動畫,需要傳入執行動畫需要的時間和動畫曲線。

ScrollPosition

ScrollPosition是用來儲存可滾動元件的滾動位置的。一個 ScrollController 物件可能會被多個可滾動的元件使用,

ScrollController 會為每一個滾動元件建立一個 ScrollPosition 物件來儲存位置資訊。ScrollPosition 中儲存的是在 ScrollController 的 positions 屬性裡面,他是一個 List<ScrollPosition> 陣列,在 ScrollController 中真正儲存位置資訊的就是 ScrollPosition,而 offset 只是一個便捷使用的屬性。檢視原始碼中可以發現 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,則需要一對一讀取。在一對多的情況下,我們可以使用其他方法來實現讀取滾動位置。假設現在一個 ScrollController 對應了兩個可以滾動的元件,那麼可以通過 position.elementAt(index) 來獲取 ScrollPosition,從而獲得 offset

controller.positions.elementAt(0).pixels
controller.positions.elementAt(1).pixels

ScrollPosition的方法

ScrollPosition 有兩個常用方法:分別是 animateTo()jumpTo(),他們才是真正控制跳轉到滾動位置的方法,在 ScrollController 中這兩個同名方法,內部最終都會呼叫 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 和可滾動元件關聯時,可滾動元件首先會調 ScrollControllercreateScrollPosition 方法來建立一個ScrollPosition來儲存滾動位置資訊。
ScrollPosition createScrollPosition(
    ScrollPhysics physics,
    ScrollContext context,
    ScrollPosition oldPosition);

  • 在滾動元件呼叫 createScrollPosition 方法之後,接著會呼叫 attach 方法來將建立號的 ScrollPosition 資訊新增到 positions 屬性中,這一步稱為“註冊位置”,只有註冊後animateTo()jumpTo()才可以被呼叫。
void attach(ScrollPosition position);

  • 最後當可滾動元件被銷燬時,會呼叫 detach()方法,將其 ScrollPosition 物件從 ScrollControllerpositions 屬性中移除,這一步稱為“登出位置”,登出後 animateTo()jumpTo() 將不能再被呼叫。
void detach(ScrollPosition position);

NotificationListener介紹

通知冒泡

Flutter Widget 樹中子 Widge t可以通過傳送通知(Notification)與父(包括祖先) Widget 進行通訊,父級元件可以通過 NotificationListener 元件來監聽自己關注的通知,這種通訊方式類似於 Web 開發中瀏覽器的事件冒泡,在 Flutter 中就沿用了“冒泡”這個術語,稱為通知冒泡

通知冒泡和使用者觸控事件冒泡是相似的,但有一點不同:通知冒泡可以中止,但使用者觸控事件不行。

滾動通知

Flutter 中很多地方使用了通知,如可滾動元件(Scrollable Widget)滑動時就會分發滾動通知(ScrollNotification),而 Scrollbar 正是通過監聽 ScrollNotification 來確定滾動條位置的。

switch (notification.runtimeType){
  case ScrollStartNotification: print("開始滾動"); break;
  case ScrollUpdateNotification: print("正在滾動"); break;
  case ScrollEndNotification: print("滾動停止"); break;
  case OverscrollNotification: print("滾動到邊界"); break;
}

其中 ScrollStartNotificationScrollUpdateNotification等都是繼承 ScrollNotification 類的,不同型別的通知子類會包含不同的資訊,ScrollUpdateNotification 有一個 scrollDelta 屬性,它記錄了移動的位移。

NotificationListener 時繼承 StatelessWidget 類的額,左右我們可以直接在放置在Widget 數中,通過裡面的 onNotification 可以指定一個模板引數,該模板引數型別必須是繼承自Notification,可以顯式指定模板引數時,比如通知的型別為滾動結束通知:

NotificationListener<ScrollEndNotification>

這個時候 NotificationListener 便只會接收該引數型別的通知。

onNotification 回撥為通知處理回撥,他的返回值時布林型別(bool),當返回值為 true 時,阻止冒泡,其父級 Widget 將再也收不到該通知;當返回值為 false 時繼續向上冒泡通知。

兩者區別

首先這兩種方式都可以實現對滾動的監聽,但是他們還是有一些區別:

  1. ScrollController 可以控制滾動控制元件的滾動,而 NotificationListener 是不可以的。
  2. 通過 NotificationListener 可以在從可滾動元件到widget樹根之間任意位置都能監聽,而ScrollController只能和具體的可滾動元件關聯後才可以。
  3. 收到滾動事件後獲得的資訊不同;NotificationListener在收到滾動事件時,通知中會攜帶當前滾動位置和ViewPort的一些資訊,而ScrollController只能獲取當前滾動位置。

ScrollController例項

效果圖

程式碼實現步驟

1.建立滾動所需的介面,一個 Scaffold 元件 body 裡面方式一個 Stack 的層疊小部件,裡面放置一個listview,和自定義的 appBarfloatingActionButton 放置一個返回頂部的懸浮按鈕。

Scaffold(
      body: Stack(
        children: <Widget>[
          MediaQuery.removePadding(
            removeTop: true,
            context: context,
            child: ListView.builder(
              // ScrollController 關聯滾動元件
              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);
              },
            ),
    )

2.建立 ScrollController 物件,在初始化中新增對滾動的監聽,並和 ListView 這個可滾動小部件進行關聯:

ScrollController _controller = new ScrollController();

@override
void initState() {
  _controller.addListener(() {
    print(_controller.offset); //列印滾動位置
  })
}

3.在 _controller.addListener 中新增相關業務程式碼,根據滾動的偏移量計算出透明度,實現appBar滾動漸變:

  double t = _controller.offset / DEFAULT_SCROLLER;
  if (t < 0.0) {
    t = 0.0;
  } else if (t > 1.0) {
    t = 1.0;
  }
  setState(() {
    toolbarOpacity = t;
  });

4.更具滾動的高度和當前 floatingActionButton 的現實狀態,判斷 floatingActionButton 是否需要展示:

if(_controller.offset < DEFAULT_SHOW_TOP_BTN && showToTopBtn){
  setState(() {
  showToTopBtn = false;
	});
}else if(_controller.offset >= DEFAULT_SHOW_TOP_BTN && !showToTopBtn){
  setState(() {
    showToTopBtn = true;
  });
}

5.點選 floatingActionButton 返回到頂部:

 _controller.animateTo(0.0, duration: Duration(milliseconds: 500), curve: Curves.ease);

NotificationListener例項

效果圖

程式碼實現步驟

在 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); //列印滾動位置
}

最後

如果你看到了這裡,覺得文章寫得不錯就給個讚唄!歡迎大家評論討論!如果你覺得那裡值得改進的,請給我留言。一定會認真查詢,修正不足,定期免費分享技術乾貨。喜歡的小夥伴可以關注一下哦。謝謝!

相關推薦

Flutter 滾動實戰appBar滾動漸變

介紹 在 Flutter 中滾動監聽一般可以採用兩種方式來實現,分別是 ScrollController 和 Notificati

滾動

位置 position 用法 href data 也會 lis bar target 滾動監聽插件是用來根據滾動條所處的位置來自動更新導航項的。如下所示,滾動導航條下面的區域並關註導航項的變化。下拉菜單中的條目也會自動高亮顯示。 用法 依賴 Bootstrap 的導航組件

Jquery滾動和附加導航

jquery 導航 滾動監聽初學jquery,多多指教導航思路:設定nav導航的類。設定索引值。點擊導航內容,導航的索引和內容的索引一一對應。點擊導航欄,內容滑動一段距離。監聽: 1.滑動內容文檔時,索引值變動。 2.導航對應的索引所在內容高亮。下面是代碼<!--導航-->$(doc

關於vue中滾動失效問題

top scrolltop ont bsp .cn vue 資料 index document 在vue項目中, 監聽window滾動失效;並且document.body.scrollTop一直是0的情況! 查找了許多資料;並沒有找到合理的解決方案; 最中發現,在in

Bootstrap 下拉菜單和滾動插件

bootstrap 下拉菜單和滾動監聽插件 一.下拉菜單 常規使用中,和組件方法一樣,代碼如下: //聲明式用法 <div class="dropdown"> <button class="btn btn-primary" data-toggle="dropdown"> 下拉菜

Bootstrap 學習筆記8 下拉菜單滾動

bsp http drop htm bar 下拉 overflow inf flow 代碼部分: <nav class="navbar navbar-default"> <a href="#" class="navba

vue給當前頁面加滾動怎麼樣不影響其他頁面

最近利用vue寫了一個網站,在網站某一個頁面滾動到某一個地方後執行某一動化,所以要獲取拿一個div到頂部的距離,所以我在頁面載入的時候給window加了一個滾動的監聽 mounted(){ window.addEventListener('scroll', this.handleScro

導航的滾動實現步驟

<!DOCTYPE html> <html>     <head>         <meta charset="UTF-8">    &nb

bootstrap的滾動

<!DOCTYPE html> <html> <head>     <meta charset="utf-8">     <title>bootstrap中滾動導航監聽主要的就是在

滾動(上面導航下面錨點)

就是點選導航一個欄目,會顯示對應的內容 基本使用方法 ** 讓欄目有選中的效果? ** data-spy=“scroll” style=“position: relative” ** 當導航欄和內容框的距離達到多少時,導航欄選中效果會發生切換? **

Bootstrap -- 外掛: 模態框、滾動、標籤頁

Bootstrap -- 外掛: 模態框、滾動監聽、標籤頁 1. 模態框(Modal): 覆蓋在父窗體上的子窗體。 使用模態框: <!DOCTYPE html> <html> <head> <meta http-equiv="Content

Android 對ScrollView滾動,實現美團 大眾點評的購買懸浮效果

                隨著移動網際網路的快速發展,它已經和我們的生活息息相關了,在公交地鐵裡面都能看到很多人的人低頭看著自己的手機螢幕,從此“低頭族”一詞就產生了,作為一名移動行業的開發人員,我自己也是一名“低頭族”,上下班時間在公交地鐵上看看新聞來打發下時間,有時候也會看看那些受歡迎的App的一些

支付寶小程序滾動吸頂效果

頂部 style fix pan menu 小程序 定位 bsp color axml部分 <view class=" {{menuFixed ? ‘fixed‘: ‘‘}}" id="affix">菜單欄</view> axss部分 .fix

Bootstrap 滾動(Scrollspy)

  body {  position: relative;       }  </style></head><body data-spy="scroll" data-target=".navbar" data-offset="50"><

滾動條的 與內容隨著滾動條動態載入

<!DOCTYPE html> <html> <head> <title>當滾動條滑到底部時自動載入內容</title> <meta http-equiv="Content-Type" con

bootstrap滾動

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"&g

Bootstrap3.0學習第二十輪(JavaScript外掛——滾動)

前言 本文主要來學習一下JavaScript外掛--下拉選單。 滾動監聽 案例 滾動監聽外掛可以根據滾動條的位置自動更新所對應的導航標記。你可以試試滾動這個頁面,看看左側導航的變化。 先把實現的程式碼上了,你可以通過測試程式碼先來看看效果。 View Code 然後執行後,在內容下,也就是有滾動條哪裡滾

Bootstrap 滾動Scrollspy 呼叫方式

呼叫方式1、data屬性呼叫只需給監聽物件定義 data-spy="scroll" 屬性及 data-target 屬性,就可以啟用滾動監聽外掛。data-target 屬性的值是一個選擇器,它指向擁有滾動監聽功能的頁面元素。本例項中,擁有滾動監聽功能的頁面元素為 id="n

css實現返回首部和滾動的原理

 在頁面中實現返回首部  原理將<a>連線到頂部的其中一個標籤 當點選這個連結時  可以實現返回頂部 js 在裡面實現返回圖示的出現 注意的是實現的邏輯為  考察滾動條距離頂部的距離 方法

Bootstrap外掛(二)——滾動(scrollspy.js)

         滾動監聽就不細緻的將他的方法屬性或者事件之類的了,我們實現一個根據滑動位置動態改變導航選中選項和根據不同的導航選單選中跳轉到相應的位置的案例 先上效果圖: