当前位置: 首页 > news >正文

Flutter Scrollable 中ViewPort滚动原理

关于Flutter Sliver组件内容可以参考下面这位博主博客,写的已经非常好了,这里就不再赘述。

38、Flutter之 可滚动组件简介_flutter 可滑动_风雨「83」的博客-CSDN博客

通过阅读上面的博客,我们已经知道了Scrollable和Viewport基础概念,接下来跟随作者一起,结合Flutter源码分析下ViewPort是如何滚动。

先看一个简单的demo

MaterialApp(home: Scaffold(body: Center(child: Container(height: 300,child: Scrollable(viewportBuilder: (BuildContext context, ViewportOffset position) {return Viewport(offset: position,slivers: [SliverToBoxAdapter(child: Container(width: 100,height: 100,color: Colors.lightBlue,),),SliverToBoxAdapter(child: Container(width: 100,height: 100,color: Colors.pink,),),SliverToBoxAdapter(child: Container(width: 100,height: 100,color: Colors.green,),),SliverToBoxAdapter(child: Container(width: 100,height: 100,color: Colors.black,),),SliverToBoxAdapter(child: Container(width: 100,height: 100,color: Colors.red,),)],);},))),),)

就是一个简单的滚动列表,可以上下滚动。

 上面是一个简单的滚动列表,当手指触摸屏幕时,内容随之发上移动(滚动)。

下面我们从Flutter刷新机制来梳理下列表里面的组件是在什么时机,由谁触发重新布局的(组件的位移就是重新布局的体现)。

class Viewport extends MultiChildRenderObjectWidget {......@overrideRenderViewport createRenderObject(BuildContext context) {return RenderViewport(axisDirection: axisDirection,crossAxisDirection: crossAxisDirection ?? Viewport.getDefaultCrossAxisDirection(context, axisDirection),anchor: anchor,offset: offset,cacheExtent: cacheExtent,cacheExtentStyle: cacheExtentStyle,clipBehavior: clipBehavior,);}@overridevoid updateRenderObject(BuildContext context, RenderViewport renderObject) {renderObject..axisDirection = axisDirection..crossAxisDirection = crossAxisDirection ?? Viewport.getDefaultCrossAxisDirection(context, axisDirection)..anchor = anchor..offset = offset..cacheExtent = cacheExtent..cacheExtentStyle = cacheExtentStyle..clipBehavior = clipBehavior;}

Viewport 继承MultiChildRenderObjectWidget,与之对应的RenderObject是RenderViewport,相关布局逻辑肯定是在RenderViewport 内部实现。

class RenderViewport extends RenderViewportBase<SliverPhysicalContainerParentData> {RenderViewport({super.axisDirection,required super.crossAxisDirection,required super.offset,double anchor = 0.0,List<RenderSliver>? children,RenderSliver? center,super.cacheExtent,super.cacheExtentStyle,super.clipBehavior,})

这里我们重点关注offset这个参数(其他参数不是本篇内容考虑的范围),这个参数在刚刚Viewport 里面赋值,这个参数是重点,后面会用到。

我们知道RenderViewport管理一个List<RenderSliver> 列表,列表内容滚动就是该父组件对子组件列表重新布局的结果,我们直接找到其performLayou方法

  @overridevoid performLayout() {......int count = 0;do {assert(offset.pixels != null);correction = _attemptLayout(mainAxisExtent, crossAxisExtent, offset.pixels + centerOffsetAdjustment);if (correction != 0.0) {offset.correctBy(correction);} else {if (offset.applyContentDimensions(math.min(0.0, _minScrollExtent + mainAxisExtent * anchor),math.max(0.0, _maxScrollExtent - mainAxisExtent * (1.0 - anchor)),)) {break;}}count += 1;} while (count < _maxLayoutCycles);......}());}

performLayout方法中 

correction = _attemptLayout(mainAxisExtent, crossAxisExtent, offset.pixels + centerOffsetAdjustment);

是我们关注的重点。

  double _attemptLayout(double mainAxisExtent, double crossAxisExtent, double correctedOffset) {......// positive scroll offsetsreturn layoutChildSequence(child: center,scrollOffset: math.max(0.0, -centerOffset),overlap: leadingNegativeChild == null ? math.min(0.0, -centerOffset) : 0.0,layoutOffset: centerOffset >= mainAxisExtent ? centerOffset: reverseDirectionRemainingPaintExtent,remainingPaintExtent: forwardDirectionRemainingPaintExtent,mainAxisExtent: mainAxisExtent,crossAxisExtent: crossAxisExtent,growthDirection: GrowthDirection.forward,advance: childAfter,remainingCacheExtent: forwardDirectionRemainingCacheExtent,cacheOrigin: clampDouble(centerOffset, -_calculatedCacheExtent!, 0.0),);}

我们找到了layoutChildSequence方法,从方法名就能知道,这个方法功能就是对子组件列表按照顺序重新布局的。

  @protecteddouble layoutChildSequence({required RenderSliver? child,required double scrollOffset,required double overlap,required double layoutOffset,required double remainingPaintExtent,required double mainAxisExtent,required double crossAxisExtent,required GrowthDirection growthDirection,required RenderSliver? Function(RenderSliver child) advance,required double remainingCacheExtent,required double cacheOrigin,}) {......while (child != null) {......child.layout(SliverConstraints(axisDirection: axisDirection,growthDirection: growthDirection,userScrollDirection: adjustedUserScrollDirection,scrollOffset: sliverScrollOffset,precedingScrollExtent: precedingScrollExtent,overlap: maxPaintOffset - layoutOffset,remainingPaintExtent: math.max(0.0, remainingPaintExtent - layoutOffset + initialLayoutOffset),crossAxisExtent: crossAxisExtent,crossAxisDirection: crossAxisDirection,viewportMainAxisExtent: mainAxisExtent,remainingCacheExtent: math.max(0.0, remainingCacheExtent + cacheExtentCorrection),cacheOrigin: correctedCacheOrigin,), parentUsesSize: true);......// move on to the next childchild = advance(child);}// we made it without a correction, whee!return 0.0;}

 这个方法就是在不停循环获取下一个child,直到最后一个需要布局的child组件。

大体流程就是这样的,细心的你一定发现,上面没有说明组件是如何根据手指滑动滚动的,也就是如何触发RenderViewport 执行performLayout方法。

不要急,下面就来说明这一点。

还记得上面提到的offset这个参数吗?重头戏就是它。

  set offset(ViewportOffset value) {assert(value != null);if (value == _offset) {return;}if (attached) {_offset.removeListener(markNeedsLayout);}_offset = value;if (attached) {_offset.addListener(markNeedsLayout);}// We need to go through layout even if the new offset has the same pixels// value as the old offset so that we will apply our viewport and content// dimensions.markNeedsLayout();}

当上面我们给RenderViewport 配置offset参数是,offset是一个ChangeNotifer ,可以添加变化监听

abstract class ViewportOffset extends ChangeNotifier {/// Default constructor.////// Allows subclasses to construct this object directly.ViewportOffset();
......
}

当offset 只有的变量更新后,通知监听它的回调函数markNeedsLayout,这里就回到了Flutter组件渲染大流程里面了。

这个流程下来是不是非常巧妙,希望大家阅读完后,也能写出如此巧妙的架构。

相关文章:

Flutter Scrollable 中ViewPort滚动原理

关于Flutter Sliver组件内容可以参考下面这位博主博客&#xff0c;写的已经非常好了&#xff0c;这里就不再赘述。 38、Flutter之 可滚动组件简介_flutter 可滑动_风雨「83」的博客-CSDN博客 通过阅读上面的博客&#xff0c;我们已经知道了Scrollable和Viewport基础概念&#…...

多目标粒子群结合极限学习机ELM求解帕累托前沿,MOPSO-ELM

目录 背影 parte前沿的定义 注意事项 基于多目标粒子群结合极限学习机的帕累托前沿求解帕累托前沿 主要参数 MATLAB代码 效果图 结果分析 展望 背影 在目标优化过程种,很多时候都两个或者多个目标,并且目标函数不能同时达到最优,鱼与熊掌不可兼得,这个时候可以通过求解帕…...

(二十)操作系统-信号量机制

文章目录一、知识预览二、前篇文章知识点回顾三、信号量机制四、信号量机制—整形信号量五、信号量机制—记录型信号量六、总结一、知识预览 二、前篇文章知识点回顾 进程互斥的四种软件实现方式&#xff1a;单标志法、双标志先检查、双标志后检查、Peterson算法。&#xff08;…...

ceph osd slow ops 检测

目的 常用的方法检测 ceph slow 问题 参考 yceph -scluster:id: 22908555-e596-4c2d-a1f6-34fcf4d3e935health: HEALTH_WARNDegraded data redundancy: 46384/12805029 objects degraded (0.362%), 145 pgs degraded, 122 pgs undersized309 slow ops, oldest one blocked…...

百度CTO王海峰:深度学习平台+大模型,夯实产业智能化基座

2月27日&#xff0c;中国人工智能学会首届智能融合产业论坛在成都顺利举办。本届论坛由中国人工智能学会&#xff08;CAAI&#xff09;主办&#xff0c;中国人工智能学会智能融合专委会、百度公司、深度学习技术及应用国家工程研究中心和电子科技大学联合承办。中国工程院多名院…...

【C++】vector的基本使用

难道向上攀爬的那条路&#xff0c;不是比站在顶峰更让人热血沸腾吗&#xff1f; 文章目录一、vector和string的联系与不同二、vector的扩容操作1.resize() &#xff08;缺省值为匿名对象&#xff09;&& reserve()2.reserve在g和vs上的扩容机制3.reserve异地扩容和shri…...

社交媒体营销的5个好处

有些人认为&#xff0c;社交媒体营销不能直接与销售挂钩。这就是为什么在制定营销策略时&#xff0c;社交媒体营销会被部分人忽视的原因。然而&#xff0c;与其他广告渠道不同&#xff0c;社交媒体是双向渠道。忽视社交媒体营销将影响与客户的关系。最重要的是&#xff0c;它将…...

飞行机器人专栏(十)-- 异构多视角视觉系统

感知系统架构为满足天空端主控制器的诸如RGB-D图像处理等大容量数据吞吐、高速并行计算、实时运动控制以及通信和可视化任务的计算算力需求&#xff0c;同时优化功耗表现&#xff0c;采用了结构紧凑、功耗表现优异的边缘计算硬件NVIDA IJetson AGXOrin 。该开发者套件包含高性能…...

2023年湖北住建厅八大员各岗位题库精准小题库-启程别

2023年湖北住建厅八大员各岗位题库精准小题库-启程别 住建厅八大员&#xff08;施工员、质量员、资料员、材料员、机械员、标准员、劳务员&#xff09; 各岗位题库分2种&#xff1a; 1.住建厅八大员报名之后会有培训任务&#xff0c;完成培训任务学习才能安排考试&#xff0c;…...

志愿者招募令|来!一起Build OceanBase第一次开发者大会

2023 年 3 月 25 日&#xff0c;我们将开启第一次 OceanBase 开发者大会&#xff0c;走近开发者&#xff0c;共同探讨单机分布式、云原生、HTAP 等数据库前沿趋势&#xff0c;分享全新的产品 Roadmap&#xff0c;交流场景探索和最佳实践。 为了让活动现场更有活力&#xff0c;…...

java 元数据 和 元注解

基本介绍三种基本注解OverrideDeprecatedSuppressWarnings四种元注解RetentionTargetDocumentedInherited一、基本介绍1.概述java注解&#xff08;Annotation&#xff09;[ˌ nəˈ teɪʃn]&#xff0c;又称java标注&#xff0c;也被称为元数据&#xff08;关于数据的数据&…...

RFID射频卡写入手机NFC心路小记

声明&#xff1a; 本文仅是作者学习探索的心里路程日记&#xff0c;如果您看完以后&#xff0c;从中获得了一些知识&#xff0c;作者不胜荣幸。科技是一把双刃剑&#xff0c;利用好了&#xff0c;可以方便生活&#xff0c;利用不当也肯能扰乱公共管理秩序&#xff0c;造成不必要…...

【C++】STL 模拟实现之 list

文章目录一、list 的常用接口及其使用1、list 一般接口2、list 特殊接口3、list 排序的性能分析二、list 迭代器的实现1、迭代器的分类2、list 迭代器失效问题3、list 迭代器源码分析4、list 迭代器模拟实现4.1 普通迭代器4.2 const 迭代器4.3 完整版迭代器三、list 的模拟实现…...

20230228----重返学习-数组-引用数据类型的转换-基础调试用方法-对象检测-各数据转布尔值及相等运算符-条件语句-循环语句

day-017-seventeen-20230228-数组-引用数据类型的转换-基础调试用方法-对象检测-各数据转布尔值及相等运算符-条件语句-循环语句 数组 字面量表示法 [数组成员0,数组成员1,数组成员2]用中括号语法来取值 var ary [5,6,7] console.log("ary[0]--->", ary[0])数组…...

apscheduler 定时任务框架

Apscheduler 介绍 四大组件 triggers&#xff1a;触发器&#xff0c;用于设定触发任务的条件job stores&#xff1a;作业存储器&#xff0c;用于存放任务&#xff0c;可以存放在数据库或内存&#xff0c;默认内存executors&#xff1a;执行器&#xff0c;用于执行任务&#x…...

Softing OPC Tunnel——绕过DCOM配置实现OPC Classic广域网通信

一 摘要 Softing OPC Tunnel是dataFEED OPC Suite的一个组件&#xff0c;可避免跨设备OPC Classic通信中出现的DCOM配置问题&#xff0c;同时可保证跨网络数据交换的高性能和可靠性。OPC Tunnel内部集成的存储转发功能&#xff0c;可在连接中断时缓存数据&#xff0c;并在重新…...

Java的运算操作

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【JavaSE_primary】 文章目录算术运算符增量运算符注意自增自减运算符关系运算符逻辑运算符逻辑与&&逻辑或||逻辑非&#xff01;…...

基于OBD系统的量产车评估测试(PVE)

在轻型汽车污染物排放限值及测量方法&#xff08;中国第六阶段&#xff09;中&#xff0c;除了对汽车尾气排放等制定了更为严格的限制之外&#xff0c;也在OBD系统认证项目中增加了新的要求——量产车评估&#xff08;Production Vehicle Evaluation&#xff09;测试。该测试由…...

【蓝桥杯集训10】Tire树 字典树 最大异或对专题(3 / 3)

目录 字典树模板 1、插入操作 2、查询操作 143. 最大异或对 - trie 二进制 3485. 最大异或和 - 前缀和Trie滑动窗口 字典树模板 活动 - AcWing 字典树&#xff1a;高效存储和查找字符串集合的数据结构 son[节点1地址][值]节点2地址 —— 节点1的子节点为节点2cnt[节点地…...

docker部署zabbix6.2.7+grafana

目录 1、下载docker 2、下载相关镜像文件 3、创建一个供zabbix系统使用的网络环境 4、创建一个供mysql数据库存放文件的目录 5、启动mysql容器 6、为zabbix-server创建一个持久卷 7、启动zabbix-server容器 8、创建语言存放目录 9、启动zabbix-web容器 10、启动zabbix…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...