当前位置: 首页 > 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…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

uniapp手机号一键登录保姆级教程(包含前端和后端)

目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号&#xff08;第三种&#xff09;后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

【C++】纯虚函数类外可以写实现吗?

1. 答案 先说答案&#xff0c;可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...

Python实现简单音频数据压缩与解压算法

Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中&#xff0c;压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言&#xff0c;提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...