Flutter——PageView的PageController源碼分析筆記

前言

因爲公司需求,對PageView的PageController的進行了拆解,這裏將所記得筆記分享一哈。
複製代碼

PageController

構造函數 簡介

PageController繼承自ScrollController extends ChangeNotifier(pageController並無作什麼擴展、只是複寫了一些方法),由ChangeNotifier可見controller中的屬性是能夠向外通知的。(具體可見Provider) 構造函數:緩存

PageController({
    this.initialPage = 0,   //初始顯示的頁面
    this.keepPage = true,   //是否保持頁面
    this.viewportFraction = 1.0, //viewport 係數
  })
複製代碼

viewportFraction是一個係數,決定pageview的page的大小,即(假設是橫向):外層容器寬度*viewportFraction。 同時在滾動的位置計算也會用到這個變量,具體後面會見到bash

PageController 內部結構圖

createScrollPosition()

其中這個方法會在父類的_updatePosition()中調用,而 _updatePosition()在這裏兩個生命週期中會調用:ide

didChangeDependencies(),didUpdateWidget(),函數

因而可知,createScrollPosition()在頁面建立和刷新時是會調用。 它會返回一個 _PagePosition():post

return _PagePosition(
      physics: physics,
      context: context,
      initialPage: initialPage,
      keepPage: keepPage,
      viewportFraction: viewportFraction,
      oldPosition: oldPosition,
    )
複製代碼

其中physics,咱們沒傳的話,系統會默認傳一個_kPagePhysics(類PageScrollPhysics),另外在使用pageview時,若是咱們不傳controller,系統也會傳一個默認的 _defaultPageController(類PageController)。ui

接下來咱們看一下PageScrollPhysics,(以後再回來看_PagePosition)this

PageScrollPhysics 及結構圖

PageScrollPhysics 繼承自ScrollPhysics,用來決定滾動時的物理特性。若是你對pageview、listview等等的滾動效果不滿意,就能夠從這裏入手,定義本身的physics。spa

它主要有4個方法,分別是:debug

_getPage(ScrollMetrics position)code

_getPixels(ScrollMetrics position, double page)

_getTargetPixels(ScrollMetrics position, Tolerance tolerance, double velocity)

以上三個是用於根據頁面取得滾動位置或者根據位置取得頁面的,他們三個組合到一塊兒,就能夠實現pageview 的一次滾動一個頁面、滑動過半自動滾動到下一頁的效果。

第4個方法createBallisticSimulation(ScrollMetrics position, double velocity)

該方法會組裝一個含有起點和終點的,滑動時帶有彈簧阻尼效果的Simulation,並返回。 具體哪裏用到,後面會說,這裏咱們回到_PagePosition

_PagePosition

這裏結構比較複雜,較深的類我只貼結構圖(內帶註釋),儘可能減小文字。
繼承關係(上到下:子到父):
_PagePosition
    ↓
ScrollPositionWithSingleContext implements ScrollActivityDelegate
    ↓
ScrollPosition with ScrollMetrics
    ↓
ViewportOffset
    ↓
ChangeNotifier (能夠發現、它的子類是具備向外通知的功能的)
複製代碼

ViewportOffset

ScrollPosition

ScrollMetrics

ScrollPositionWithSingleContext

這個類是pagePosition的直接父類,它實現了接口ScrollActivityDelegate(抽象類。dart實際沒有接口的概念),ScrollActivityDelegate定義了幾個方法其中經常使用的是:

void goIdle();  //中止滾動

void goBallistic(double velocity);//彈性滾動
複製代碼

咱們就來看一看goBallistic的具體實現吧:

@override
  void goBallistic(double velocity) {
    assert(pixels != null);
    final Simulation simulation = physics.createBallisticSimulation(this, velocity);
    if (simulation != null) {
      beginActivity(BallisticScrollActivity(this, simulation, context.vsync));
    } else {
      goIdle();
    }
  }
複製代碼

注意這句:physics.createBallisticSimulation(this, velocity); 這裏就是調用咱們上面說的PageScrollPhysics中的方法createBallisticSimulation,它返回的simulation用於生成一個BallisticScrollActivity會交給beginActivity來處理(沒有返回的話就呆住啦~)。

咱們先來了解一下ScrollActivity
複製代碼

ScrollActivity

ScrollActivity是個抽象類、內部有一些分發通知的方法和幾個變量,如:是否滾動、滾動速度等等。 flutter裏,據我所知各類的滾動樣式活動(drag、fling等)都是繼承自ScrollActivity。如上面的BallisticScrollActivity。對於它的理解我是這樣想的: Simulation是描述一個物理效果、ScrollActivity的子類來展示這個效果,咱們看一下他的構造函數就一目瞭然了:

BallisticScrollActivity(
    ScrollActivityDelegate delegate,
    Simulation simulation,
    TickerProvider vsync,
  ) : super(delegate) {
    _controller = AnimationController.unbounded(
      debugLabel: kDebugMode ? objectRuntimeType(this, 'BallisticScrollActivity') : null,
      vsync: vsync,
    )
      ..addListener(_tick)
      ..animateWith(simulation)
       .whenComplete(_end); // won't trigger if we dispose _controller first } 複製代碼

回到ScrollPositionWithSingleContext

雖然pagePosition 繼承自ScrollPositionWithSingleContext,可是pagePosition裏的方法並很少、主要是加了這兩個方法:

getPageFromPixels(),getPixelsFromPage()
複製代碼

這些方法在PageScrollPhysics中會用到(輔助達到一次一頁的效果)。

父類ScrollPositionWithSingleContext內部定義了咱們常見animateTo(),jumpTo()等等、對應的會生成一些列scrollActivity的子類用於驅動,如:DrivenScrollActivity\BallisticScrollActivity\IdleScrollActivity

這裏再放一下controller的結構圖(注意左側):
複製代碼

到這裏pagePosition的內部結構就基本解剖完了,同時也回到了最初的controller的createScrollPosition,即整個控制流程的分析就結束了。

文章比較長,謝謝你們觀看。如有錯誤的地方或者沒說明白的,還請指正,感謝。
複製代碼

意外發現

扒源碼的時候,我發現官方的scrollcontroller 其實是支持指定item進行滾動的,咱們能夠看一下上面scrollPosition的結構圖(圖內底部),其中有一個方法ensureVisible(),能夠滾動到指定的RenderObject,目前我試了一下scrollView等(非回收的)效果完美。可是listview不行(屏幕內能夠),應該是因爲listview的屏外child(超出緩存)會被回收的緣由的,拿不到RenderObject,這個仍是有待研究。 你們有啥想法能夠交流一哈。

相關文章

PageView源碼和Gesture競技場消歧的淺析

相關文章
相關標籤/搜索