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

flutter ListView Item复用源码解析

Flutter 的 ListView 的 Item 复用机制是其高性能列表渲染的核心,底层实现依赖于 Flutter 的渲染管线、Element 树和 Widget 树的协调机制。以下是 ListView 复用机制的源码级解析,结合关键类和核心逻辑进行分析。


1. ListView 的底层结构

ListView 的复用机制是通过 Sliver 系列组件实现的,具体在 SliverList 和 SliverChildBuilderDelegate 中完成。以下是关键类的关系:

  • ListView:基于 ScrollView,内部使用 SliverList 实现滚动布局。

  • SliverList:继承自 SliverMultiBoxAdaptorWidget,负责管理子组件的布局和复用。

  • SliverChildBuilderDelegate:实现 SliverChildDelegate 接口,负责按需构建子组件(即 itemBuilder)。


2. 复用机制的核心:SliverChildDelegate

ListView.builder 使用的 SliverChildBuilderDelegate 是复用机制的核心。其核心逻辑在以下两个方法中:

(1) build 方法:按需构建子组件

当用户滚动时,SliverList 会通过 SliverChildDelegate 的 build 方法请求可见区域的子组件。例如:

// SliverChildBuilderDelegate 的 build 方法
Widget build(BuildContext context, int index) {return widget.itemBuilder(context, index); // 调用用户定义的 itemBuilder
}

关键点build 方法仅在需要显示某个子组件时被调用(懒加载)。


(2) createChild 和 didFinishLayout:复用子组件

在 RenderSliverListSliverList 的渲染对象)中,滚动时会触发以下逻辑:

// RenderSliverList 的 performLayout 方法
void performLayout() {// 计算可见区域的子组件范围final int firstIndex = _calculateFirstIndex();final int lastIndex = _calculateLastIndex();// 回收不可见的子组件collectGarbage(...);// 按需创建或复用子组件for (int index = firstIndex; index <= lastIndex; index++) {if (child == null) {child = createChild(index); // 创建或复用子组件}// 布局子组件...}
}
  • createChild:尝试从缓存池(childManager)中获取可复用的子组件。

  • collectGarbage:将滑出屏幕的子组件放入缓存池,供后续复用。


3. Element 树的复用

Flutter 的复用机制不仅仅是对 Widget 的复用,更重要的是对 Element 树的复用。Element 是 Widget 和 RenderObject 之间的桥梁,负责管理状态和生命周期。

(1) Element 的复用条件

当 ListView 滚动时,Flutter 会检查新旧 Widget 的类型和 Key 是否一致:

// Element 的 updateChild 方法
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {if (newWidget == null) {// 移除旧 Elementreturn null;}if (child != null) {// 如果新旧 Widget 类型和 Key 匹配,复用 Elementif (child.widget == newWidget) {return child;}if (Widget.canUpdate(child.widget, newWidget)) {child.update(newWidget);return child;}}// 创建新的 Elementreturn inflateWidget(newWidget, newSlot);
}

关键点:如果新旧 Widget 的 runtimeType 和 Key 相同,则复用 Element,否则销毁旧的并创建新的。


(2) Widget 的不可变性

由于 Widget 是不可变的(immutable),复用时 Element 会直接更新到新的 Widget 实例,但底层的 RenderObject(如布局和绘制信息)可能被复用。


4. 缓存池:ChildVendor

在 SliverMultiBoxAdaptorWidget 中,ChildVendor 负责管理子组件的缓存池:

// ChildVendor 的 collectGarbage 方法
void collectGarbage(int leadingGarbage, int trailingGarbage) {// 将超出可见范围的子组件放入缓存池while (leadingGarbage > 0) {final Element element = _children.removeAt(0);_detachChild(element);leadingGarbage--;}// 类似处理 trailingGarbage...
}
  • 缓存策略:缓存池的大小受 cacheExtent 参数控制(默认缓存一屏外的子组件)。


5. 性能优化关键点

(1) Key 的作用

如果列表项的顺序可能变化,或需要保持状态,必须为每个子组件指定唯一的 Key。例如:

ListView.builder(itemBuilder: (context, index) {return MyItem(key: ValueKey(items[index].id)); // 唯一 Key},
);
  • 源码中的 Key 匹配:在 Element 的 updateChild 方法中,Key 是判断是否复用的关键条件。

 static bool canUpdate(Widget oldWidget, Widget newWidget) {return oldWidget.runtimeType == newWidget.runtimeType&& oldWidget.key == newWidget.key;}
(2) 避免不必要的重建
  • 使用 const 构造函数减少 Widget 实例的创建。

  • 避免在 itemBuilder 中创建新的闭包或对象。


6. 源码流程总结

  1. 滚动触发布局:用户滚动时,RenderSliverList 的 performLayout 被调用。

  2. 计算可见范围:确定需要显示的子组件索引(firstIndex 和 lastIndex)。

  3. 回收不可见子组件:通过 collectGarbage 将滑出屏幕的子组件放入缓存池。

  4. 复用或创建子组件:通过 createChild 从缓存池获取或新建子组件。

  5. 更新 Element 树:检查新旧 Widget 是否可复用,更新 Element 和 RenderObject


总结

Flutter 的 ListView 复用机制通过以下核心设计实现高性能:

  • 懒加载:仅构建可见区域的子组件。

  • Element 复用:通过 Key 和 Widget 类型匹配复用 Element

  • 缓存池:通过 ChildVendor 管理滑出屏幕的子组件。

  • 不可变 Widget:快速更新 Element,复用底层的 RenderObject

通过理解源码逻辑,开发者可以更好地优化列表性能(如合理使用 Key、避免不必要的重建)。

相关文章:

flutter ListView Item复用源码解析

Flutter 的 ListView 的 Item 复用机制是其高性能列表渲染的核心&#xff0c;底层实现依赖于 Flutter 的渲染管线、Element 树和 Widget 树的协调机制。以下是 ListView 复用机制的源码级解析&#xff0c;结合关键类和核心逻辑进行分析。 1. ListView 的底层结构 ListView 的复…...

Spring Boot 配置 Mybatis 读写分离

JPA 的读写分离配置不能应用在 Mybatis 上, 所以 Mybatis 要单独处理 为了不影响原有代码, 使用了增加拦截器的方式, 在拦截器里根据 SQL 的 CRUD 来路由到不同的数据源 需要单独增加Mybatis的配置 Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) t…...

网络初识-

网络的相关概念 一、局域网和广域网 将各种计算机、外部设备等相互连接起来&#xff0c;实现在这个范围内数据通信和资源共享的计算机网络。它的覆盖范围通常在几百米到几公里之内。例如&#xff0c;一个小型企业的办公室&#xff0c;通过交换机将多台电脑连接在一起&#xf…...

DNS污染:网络世界的“隐形劫持”与防御

在互联网的底层架构中&#xff0c;DNS&#xff08;域名系统&#xff09;如同数字世界的“导航员”&#xff0c;将用户输入的域名翻译成机器可读的IP地址。然而&#xff0c;DNS污染&#xff08;DNS Poisoning&#xff09;正像一场无声的“地址篡改”危机&#xff0c;威胁着全球网…...

MQTT(Message Queuing Telemetry Transport)协议(三)

主题是什么 2. TCP 协议封装 tcp.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h>// 建立 TCP 连接 int tcp_connect(const char *server_ip, int s…...

多核cpu与时间片多线程的问题

在多核处理器中&#xff0c;每个核心可以独立运行一个线程。操作系统负责管理和调度这些线程&#xff0c;以确保高效利用处理器资源。下面详细解释如何获取时间片以及四个线程如何在四个核心上同时工作。 ### 时间片和调度 #### 1. 时间片&#xff08;Time Slice&#xff09;…...

电脑出现蓝屏英文怎么办?查看修复过程

电脑出现蓝屏英文是一种常见的电脑故障&#xff0c;它通常表示电脑遇到了严重的错误&#xff0c;需要停止运行以防止进一步的损坏。电脑蓝屏英文的原因可能有很多&#xff0c;比如硬件故障、驱动程序错误、系统文件损坏、病毒感染等。那么&#xff0c;当电脑出现蓝屏英文时&…...

安卓基础(第一集)

SharedPreferences&#xff08;本地存储简单数据&#xff09; 在 Android 中&#xff0c;SharedPreferences 用于存储小型数据。 &#xff08;1&#xff09;存储数据 // 获取 SharedPreferences 对象 SharedPreferences sharedPreferences getSharedPreferences("MyPre…...

【从零开始入门unity游戏开发之——C#篇56】C#补充知识点——模式匹配

考虑到每个人基础可能不一样,且并不是所有人都有同时做2D、3D开发的需求,所以我把 【零基础入门unity游戏开发】 分为成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要讲解C#的基础语法,包括变量、数据类型、运算符、流程控制、面向对象等,适合没有编程基础的…...

【数据可视化-16】珍爱网上海注册者情况分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…...

c/c++蓝桥杯经典编程题100道(21)背包问题

背包问题 ->返回c/c蓝桥杯经典编程题100道-目录 目录 背包问题 一、题型解释 二、例题问题描述 三、C语言实现 解法1&#xff1a;0-1背包&#xff08;基础动态规划&#xff0c;难度★&#xff09; 解法2&#xff1a;0-1背包&#xff08;空间优化版&#xff0c;难度★…...

电赛DEEPSEEK

以下是针对竞赛题目的深度优化方案&#xff0c;重点解决频率接近时的滤波难题和相位测量精度问题&#xff1a; 以下是使用NI Multisim 14.3实现本项目的详细解决方案&#xff1a; 一、基础要求实现方案&#xff08;模块化设计&#xff09; 1. 双频信号发生电路 电路结构&…...

VSOMEIP ROUTING应用和CLIENT应用之间交互的消息

#define VSOMEIP_ASSIGN_CLIENT 0x00 // client应用请求分配client_id #define VSOMEIP_ASSIGN_CLIENT_ACK 0x01 // routing应用返回分配的client_id #define VSOMEIP_REGISTER_APPLICATION 0x02 // client应用注册someip应用 #…...

HTML之基本布局div|span

HTML基本布局使用 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"width<device-width>, initial-scale1.0"><title>布局</title> <…...

Linux下学【MySQL】常用函数助你成为数据库大师~(配sql+实操图+案例巩固 通俗易懂版~)

绪论​ 每日激励&#xff1a;“唯有努力&#xff0c;才能进步” 绪论​&#xff1a; 本章是MySQL中常见的函数&#xff0c;利用好函数能很大的帮助我们提高MySQL使用效率&#xff0c;也能很好处理一些情况&#xff0c;如字符串的拼接&#xff0c;字符串的获取&#xff0c;进制…...

【Rabbitmq篇】高级特性----TTL,死信队列,延迟队列

目录 一.TTL ???1.设置消息的TTL 2.设置队列的TTL 3.俩者区别? 二.死信队列 定义&#xff1a; 消息成为死信的原因&#xff1a; 1.消息被拒绝&#xff08;basic.reject 或 basic.nack&#xff09; 2.消息过期&#xff08;TTL&#xff09; 3.队列达到最大长度? …...

机器学习赋能的智能光子学器件系统研究与应用

机器学习赋能的智能光子学器件系统研究与应用 时间&#xff1a; 2025年03月29日-03月30日 2025年04月05日-04月06日 机器学习赋能的光子学器件与系统&#xff1a;从创新设计到前沿应用 课程针对光子学方面的从业科研人员及开发者&#xff0c;希望了解和实践在集成光学/空间…...

尚硅谷课程【笔记】——大数据之Linux【三】

课程视频链接&#xff1a;尚硅谷大数据Linux课程 七、定时任务调度 任务调度&#xff1a;指系统在某个时间执行的特定的命令或程序。 1&#xff09;系统工作&#xff1a;有些重要的工作必须周而复始地执行。 2&#xff09;个别用户工作&#xff1a;用户可能希望在某些特定的时…...

Visual Studio踩过的坑

统计Unity项目代码行数 编辑-查找和替换-在文件中查找 查找内容输入 b*[^:b#/].*$ 勾选“使用正则表达式” 文件类型留空 也有网友做了指定&#xff0c;供参考 !*\bin\*;!*\obj\*;!*\.*\*!*.meta;!*.prefab;!*.unity 打开Unity的项目 注意&#xff1a;只是看&#xff0…...

教程 | MySQL 基本指令指南(附MySQL软件包)

此前已经发布了安装教程安装教程&#xff0c;现在让我们来学习一下MySQL的基本指令。 一、数据库连接与退出 连接本地数据库 mysql -uroot -p # 输入后回车&#xff0c;按提示输入密码&#xff08;密码输入不可见&#xff09;若需隐藏密码显示&#xff0c;可使用&#xff1…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

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…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...