因爲公司需求,對PageView的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
其中這個方法會在父類的_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 繼承自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
↓
ScrollPositionWithSingleContext implements ScrollActivityDelegate
↓
ScrollPosition with ScrollMetrics
↓
ViewportOffset
↓
ChangeNotifier (能夠發現、它的子類是具備向外通知的功能的)
複製代碼
這個類是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是個抽象類、內部有一些分發通知的方法和幾個變量,如:是否滾動、滾動速度等等。 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 } 複製代碼
雖然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,這個仍是有待研究。 你們有啥想法能夠交流一哈。