<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 滾動監聽及控制 在前幾節中,我們介紹了Flutter中常用的可滾動Widget,也說過可以用ScrollController來控制可滾動widget的滾動位置,本節先介紹一下ScrollController,然后以ListView為例,展示一下ScrollController的具體用法。最后,再介紹一下路由切換時如何來保存滾動位置。 ### ScrollController 構造函數: ``` ScrollController({ double initialScrollOffset = 0.0, //初始滾動位置 this.keepScrollOffset = true,//是否保存滾動位置 ... }) ``` 我們介紹一下ScrollController常用的屬性和方法: - `offset`:可滾動Widget當前滾動的位置。 - `jumpTo(double offset)`、`animateTo(double offset,...)`:這兩個方法用于跳轉到指定的位置,它們不同之處在于,后者在跳轉時會執行一個動畫,而前者不會。 ScrollController還有一些屬性和方法,我們將在后面原理部分解釋。 #### 滾動監聽 ScrollController間接繼承自Listenable,我們可以根據ScrollController來監聽滾動事件。如: ``` controller.addListener(()=>print(controller.offset)) ``` ### 示例 我們創建一個ListView,當滾動位置發生變化時,我們先打印出當前滾動位置,然后判斷當前位置是否超過1000像素,如果超過則在屏幕右下角顯示一個“返回頂部”的按鈕,該按鈕點擊后可以使ListView恢復到初始位置;如果沒有超過1000像素,則隱藏“返回頂部”按鈕。代碼如下: ``` class ScrollControllerTestRoute extends StatefulWidget { @override ScrollControllerTestRouteState createState() { return new ScrollControllerTestRouteState(); } } class ScrollControllerTestRouteState extends State<ScrollControllerTestRoute> { ScrollController _controller = new ScrollController(); bool showToTopBtn = false; //是否顯示“返回到頂部”按鈕 @override void initState() { //監聽滾動事件,打印滾動位置 _controller.addListener(() { print(_controller.offset); //打印滾動位置 if (_controller.offset < 1000 && showToTopBtn) { setState(() { showToTopBtn = false; }); } else if (_controller.offset >= 1000 && showToTopBtn == false) { setState(() { showToTopBtn = true; }); } }); } @override void dispose() { //為了避免內存泄露,需要調用_controller.dispose _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("滾動控制")), body: Scrollbar( child: ListView.builder( itemCount: 100, itemExtent: 50.0, //列表項高度固定時,顯式指定高度是一個好習慣(性能消耗小) controller: _controller, itemBuilder: (context, index) { return ListTile(title: Text("$index"),); } ), ), floatingActionButton: !showToTopBtn ? null : FloatingActionButton( child: Icon(Icons.arrow_upward), onPressed: () { //返回到頂部時執行動畫 _controller.animateTo(.0, duration: Duration(milliseconds: 200), curve: Curves.ease ); } ), ); } } ``` 代碼說明已經包含在注釋里,下面我們看看運行效果: ![](https://box.kancloud.cn/1b612437ff62291ca3f41269f04bf8d0_320x569.png)![](https://box.kancloud.cn/00ac68b69e124ec38c8faf68949b1921_320x569.png) 由于列表項高度為50像素,當滑動到第20個列表項后,右下角“返回頂部”按鈕會顯示,點擊該按鈕,ListView會在返回頂部的過程中執行一個滾動動畫,動畫時間是200毫秒,動畫曲線是Curves.ease,關于動畫的詳細內容我們將在后面“動畫”一章中詳細介紹。 ### 滾動位置恢復 PageStorage是一個用于保存頁面(路由)相關數據的Widget,它并不會影響子樹的UI外觀,其實,PageStorage是一個功能型Widget,它擁有一個存儲桶(bucket),子樹中的Widget可以通過指定不同的PageStorageKey來存儲各自的數據或狀態。 每次滾動結束,Scrollable Widget都會將滾動位置`offset`存儲到PageStorage中,當Scrollable Widget 重新創建時再恢復。如果`ScrollController.keepScrollOffset`為`false`,則滾動位置將不會被存儲,Scrollable Widget重新創建時會使用`ScrollController.initialScrollOffset`;`ScrollController.keepScrollOffset`為`true`時,Scrollable Widget在**第一次**創建時,會滾動到`initialScrollOffset`處,因為這時還沒有存儲過滾動位置。在接下來的滾動中就會存儲、恢復滾動位置,而`initialScrollOffset`會被忽略。 當一個路由中包含多個Scrollable Widget時,如果你發現在進行一些跳轉或切換操作后,滾動位置不能正確恢復,這時你可以通過顯式指定PageStorageKey來分別跟蹤不同Scrollable Widget的位置,如: ``` ListView(key: PageStorageKey(1), ... ); ... ListView(key: PageStorageKey(2), ... ); ``` 不同的PageStorageKey,需要不同的值,這樣才可以區分為不同Scrollable Widget保存的滾動位置。 > 注意:一個路由中包含多個Scrollable Widget時,如果要分別跟蹤它們的滾動位置,并非一定就得給他們分別提供PageStorageKey。這是因為Scrollable本身是一個StatefulWidget,它的狀態中也會保存當前滾動位置,所以,只要Scrollable Widget本身沒有被從樹上detach掉,那么其State就不會銷毀(dispose),滾動位置就不會丟失。只有當Widget發生結構變化,導致Scrollable Widget的State銷毀或重新構建時才會丟失狀態,這種情況就需要顯式指定PageStorageKey,通過PageStorage來存儲滾動位置,一個典型的場景是在使用TabBarView時,在Tab發生切換時,Tab頁中的Scrollable Widget的State就會銷毀,這時如果想恢復滾動位置就需要指定PageStorageKey。 ### ScrollPosition 一個ScrollController可以同時被多個Scrollable Widget使用,ScrollController會為每一個Scrollable Widget創建一個ScrollPosition對象,這些ScrollPosition保存在ScrollController的`positions`屬性中(List)。ScrollPosition是真正保存滑動位置信息的對象,`offset`只是一個便捷屬性: ``` double get offset => position.pixels; ``` 一個ScrollController雖然可以對應多個Scrollable Widget,但是有一些操作,如讀取滾動位置`offset`,則需要一對一,但是我們仍然可以在一對多的情況下,通過其它方法讀取滾動位置,舉個例子,假設一個ScrollController同時被兩個Scrollable Widget使用,那么我們可以通過如下方式分別讀取他們的滾動位置: ``` ... controller.positions.elementAt(0).pixels controller.positions.elementAt(1).pixels ... ``` 我們可以通過`controller.positions.length`來確定`controller`被幾個Scrollable Widget使用。 #### 方法 ScrollPosition有兩個常用方法:`animateTo()` 和 `jumpTo()`,它們是真正來控制跳轉滾動位置的方法,ScrollController的這兩個同名方法,內部最終都會調用ScrollPosition的。 ### ScrollController控制原理 我們來介紹一下ScrollController的另外三個方法: ``` ScrollPosition createScrollPosition( ScrollPhysics physics, ScrollContext context, ScrollPosition oldPosition); void attach(ScrollPosition position) ; void detach(ScrollPosition position) ; ``` 當ScrollController和Scrollable Widget關聯時,Scrollable Widget首先會調用ScrollController的`createScrollPosition()`方法來創建一個ScrollPosition來存儲滾動位置信息,接著,Scrollable Widget會調用`attach()`方法,將創建的ScrollPosition添加到ScrollController的`positions`屬性中,這一步稱為“注冊位置”,只有注冊后`animateTo()` 和 `jumpTo()`才可以被調用。當Scrollable Widget銷毀時,會調用ScrollController的`detach()`方法,將其ScrollPosition對象從ScrollController的`positions`屬性中移除,這一步稱為“注銷位置”,注銷后`animateTo()` 和 `jumpTo()` 將不能再被調用。 需要注意的是,ScrollController的`animateTo()` 和 `jumpTo()`內部會調用所有ScrollPosition的`animateTo()` 和 `jumpTo()`,以實現所有和該ScrollController關聯的Scrollable Widget都滾動到指定的位置。 ## 滾動監聽 Flutter Widget樹中子Widget可以通過發送通知(Notification)與父(包括祖先)Widget通信。父Widget可以通過NotificationListener Widget來監聽自己關注的通知,這種通信方式類似于Web開發中瀏覽器的事件冒泡,我們在Flutter中沿用“冒泡”這個術語。Scrollable Widget在滾動時會發送ScrollNotification類型的通知,ScrollBar正是通過監聽滾動通知來實現的。通過NotificationListener監聽滾動事件和通過ScrollController有兩個主要的不同: 1. 通過NotificationListener可以在從Scrollable Widget到Widget樹根之間任意位置都能監聽。而ScrollController只能和具體的Scrollable Widget關聯后才可以。 2. 收到滾動事件后獲得的信息不同;NotificationListener在收到滾動事件時,通知中會攜帶當前滾動位置和ViewPort的一些信息,而ScrollController只能獲取當前滾動位置。 ### NotificationListener NotificationListener是一個Widget,模板參數T是想監聽的通知類型,如果省略,則所有類型通知都會被監聽,如果指定特定類型,則只有該類型的通知會被監聽。NotificationListener需要一個onNotification回調函數,用于實現監聽處理邏輯,該回調可以返回一個布爾值,代表是否阻止該事件繼續向上冒泡,如果為`true`時,則冒泡終止,事件停止向上傳播,如果不返回或者返回值為`false` 時,則冒泡繼續。 ### 示例 下面,我們監聽ListView的滾動通知,然后顯示當前滾動進度百分比: ``` import 'package:flutter/material.dart'; class ScrollNotificationTestRoute extends StatefulWidget { @override _ScrollNotificationTestRouteState createState() => new _ScrollNotificationTestRouteState(); } class _ScrollNotificationTestRouteState extends State<ScrollNotificationTestRoute> { String _progress = "0%"; //保存進度百分比 @override Widget build(BuildContext context) { return Scrollbar( //進度條 // 監聽滾動通知 child: NotificationListener<ScrollNotification>( onNotification: (ScrollNotification notification) { double progress = notification.metrics.pixels / notification.metrics.maxScrollExtent; //重新構建 setState(() { _progress = "${(progress * 100).toInt()}%"; }); print("BottomEdge: ${notification.metrics.extentAfter == 0}"); //return true; //放開此行注釋后,進度條將失效 }, child: Stack( alignment: Alignment.center, children: <Widget>[ ListView.builder( itemCount: 100, itemExtent: 50.0, itemBuilder: (context, index) { return ListTile(title: Text("$index")); } ), CircleAvatar( //顯示進度百分比 radius: 30.0, child: Text(_progress), backgroundColor: Colors.black54, ) ], ), ), ); } } ``` 我們看一看運行結果: ![](https://box.kancloud.cn/20d0839c24043fc105bfe3114633a80f_320x492.png) 在接收到滾動事件時,參數類型為ScrollNotification,它包括一個`metrics`屬性,它的類型是ScrollMetrics,該屬性包含當前ViewPort及滾動位置等信息: - pixels:當前滾動位置。 - maxScrollExtent:最大可滾動長度。 - extentBefore:滑出ViewPort頂部的長度;此示例中相當于頂部滑出屏幕上方的列表長度。 - extentInside:ViewPort內部長度;此示例中屏幕顯示的列表部分的長度。 - extentAfter:列表中未滑入ViewPort部分的長度;此示例中列表底部未顯示到屏幕范圍部分的長度。 - atEdge:是否滑到了Scrollable Widget的邊界(此示例中相當于列表頂或底部)。 ScrollMetrics還有一些其它屬性,讀者可以自行查閱API文檔。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看