当前位置: 首页 > 科技观察

这篇文章让你明确PrimaryScrollController

时间:2023-03-22 16:41:33 科技观察

PrimaryScrollController的作用对于苹果用户来说,大家基本都知道iOS手机应用有一个比较常见的功能:点击状态栏,列表会滚动到顶部。在iOS原生代码中,我们可以通过原生框架已有的特性或者自己添加监听来实现这个功能。那么有没有flutter?答案当然是肯定的。Flutter专门针对iOS端做了这个支持,可以让我们快速实现点击状态栏返回顶部的效果。是围绕PrimaryScrollController的数据传输方式进行的一系列设计。根据我们早期的flutter开发经验,如果不详细了解PrimaryScrollController及相关类的实现,在构建结构复杂的页面时难免会出现各种奇怪的问题。PrimaryScrollController的定义PrimaryScrollController的源码内容不多,主要包括两部分。ExtendedInheritedWidgettoholdScrollControllertypevariables下面是源码部分:超级(钥匙:钥匙,孩子:孩子);constPrimaryScrollController.none({Key?key,requiredWidgetchild,}):controller=null,super(key:key,child:child);最终滚动控制器?控制器;静态滚动控制器?of(BuildContextcontext){finalPrimaryScrollController?结果=context.dependOnInheritedWidgetOfExactType();返回结果?.控制器;}...}关于InheritedWidgetInheritedWidget可以说是flutter框架中比较常见的数据传输设计抽象。我简单介绍一下。?每个Element实例都包含一个_inheritedWidgets。每当将特定类型的依赖项添加到Widget时,都会从集合中获取相关类型的InheritedElement实例。元素的_inheritedWidgets每次挂载元素并重新启用时,元素将从其上层元素获取它持有的所有_inheritedWidgets。还有一个特殊的InheritedElement继承了Element。与普通Element相比,InheritedElement不仅会获取其上层元素的所有_inheritedWidgets,还会将自身作为元素添加到集合中。自定义InheritedWidgetA:classInheritedWidgetAextendsInheritedWidget{Valuea;...静态值?of(BuildContextcontext){finalInheritedWidgetA?结果=context.dependOnInheritedWidgetOfExactType();返回结果?.a;如图上图所示:childA和childB都可以共享父树的数据。ScrollControllerScrollController间接继承自Listenable,主要有两个函数监听滚动事件控制列表scrollingScrollController部分实现:附加到任何滚动视图。');for(List.of(_positions))position.jumpTo(value)中的最终ScrollPosition位置;}voidattach(ScrollPositionposition){assert(!_positions.contains(position));_positions.add(位置);position.addListener(notifyListeners);}voiddetach(ScrollPositionposition){assert(_positions.contains(position));position.removeListener(notifyListeners);_positions.remove(位置);提供绑定和解除绑定ScrollPosition。每个ScrollPosition对应一个Scrollable滚动视图,注意ScrollController可以绑定多个ScrollPosition。所以,直接取scrollController.position的值报错,可能是大多数朋友都会踩到的陷阱。ScrollPositiongetposition{assert(_positions.isNotEmpty,'ScrollController未附加到任何滚动视图。');assert(_positions.length==1,'附加到多个滚动视图的ScrollController。');返回_positions.single;}ScrollViewwithConnectionofScrollController:ScrollView在创建的时候需要controller和primary两个参数,主要用来判断绑定的scrollController是使用controller还是最近的parentPrimaryScrollController中的scrollController。抽象类ScrollView扩展StatelessWidget{finalScrollController?控制器;最终布尔初级;@overrideWidgetbuild(BuildContextcontext){finalListslivers=buildSlivers(context);}最终的AxisDirectionaxisDirection=getDirectler(上下文);最终=滚动控件?基本的?PrimaryScrollController.of(context):控制器;finalScrollablescrollable=Scrollable(controller:scrollController,);...返回可滚动;可以看到在ScrollView中会创建Scrollable,而Scrollable会在_updatePosition和ScrollController绑定,然后ScrollController就可以控制视图的滚动,或者监听视图的滚动。classScrollableStateextendsStatewithTickerProviderStateMixin,RestorationMixinimplementsScrollContext{ScrollPositiongetposition=>_position!;滚动位置?_位置;最后_RestorableScrollOffset_persistedScrollOffset=_RestorableScrollOffset();@overrideAxisDirectiongetaxisDirection=>widget.axisDirection;后期ScrollBehavior_configuration;滚动物理学?_物理;滚动控制器?_fallbackScrollController;媒体查询数据?_mediaQueryData;ScrollControllerget_effectiveScrollController=>widget.controller??_fallbackScrollController!;void_updatePosition(){_configuration=widget.scrollBehavior??ScrollConfiguration.of(上下文);_physics=_configuration.getScrollPhysics(上下文);如果(widget.physics!=null){_physics=widget.physics!.applyTo(_physics);}elseif(widget.scrollBehavior!=null){_physics=widget.scrollBehavior!.getScrollPh物理(上下文).applyTo(_physics);最终滚动位置?旧位置=_位置;如果(oldPosition!=null){_effectiveScrollController.detach(oldPosition);scheduleMicrotask(oldPosition.dispose);}_position=_effectiveScrollController.createScrollysPosition,_position(this,oldPosition);断言(_position!=null);_effectiveScrollController.attach(位置);}}至此,我们已经介绍了PrimaryScrollController的实现以及相关类与它的关系。接下来看看Flutter官方是如何使用PrimaryScrollController来设计点击状态栏返回顶部的功能的。我们来看看Flutter在PrimaryScrollController的处理过程中埋藏了哪些内部组件。它在哪里实施?它如何对应于每个特定页面?你猜对了,在Scaffold中。Scaffold是一个基于Material的可视化脚手架,可以轻松创建类似iOS的交互和UI。Flutter官方在Scaffold中的状态栏区域添加了手势,并处理了点击事件。查看源码:classScaffoldStateextendsStatewithTickerProviderStateMixin,RestorationMixin{@overrideWidgetbuild(BuildContextcontext){...switch(themeData.platform){caseTargetPlatform.iOS:caseTargetPlatform.macOS:_addIfNonNull(children,GestureDetector(行为:HitTestBehavior.opaque,onTap:_handleStatusBarTap,excludeFromSemantics:true,),_ScaffoldSlot.statusBar,removeLeftPadding:false,removeTopPadding:true,removeRightPadding:false,removeBottomPadding:true,);休息;caseTargetPlatform.android:caseTargetPlatform.fuchsia:caseTargetPlatform.linux:caseTargetPlatform.windows:break;}...}void_handleStatusBarTap(){finalScrollController?_primaryScrollController=PrimaryScrollController.of(上下文);如果(_pprimaryScrollController!=null&&_primaryScrollController.hasClients){_primaryScrollController.animateTo(0.0,duration:constDuration(milliseconds:300),curve:Curves.linear,);}}}}可以看到,状态栏位置的点击添加到Scaffold中,点击后通过PrimaryScrollController.of(context)获取scrollController,最后调整滚动位置。至此我们已经知道状态栏监听是使用PrimaryScrollController.of(context)来控制滚动的,ScrollView绑定了PrimaryScrollController.of(context)。好了,至此,我们可以看下面的例子:一般情况下,我们的项目代码如下initialRoute:"/",));classPageAState{Widgetbuild(BuildContextcontext){super.build(context);返回脚手架(孩子:ListView(主要:真正的控制器:空...));}}当你push到PageA,然后点击状态栏,PageA中的列表回到顶部。感觉没什么不对,但又好像少了点什么对不对?正确的!