Flutter ListView 全面解析:从基础用法到性能优化(附鸿蒙List组件对比)
文章目录
Flutter ListView 全面解析:从基础用法到性能优化(附鸿蒙List组件对比)
引言
在移动应用开发中,列表是最核心、最常用的UI组件之一——无论是商品列表、消息流、数据展示,还是设置页面,都离不开列表的支持。Flutter作为跨平台开发的主流框架,其ListView组件凭借灵活的构造方式、优异的性能表现和丰富的扩展能力,成为开发者构建列表UI的首选;而开源鸿蒙(HarmonyOS)则以List+LazyForEach为核心,构建了适配全场景设备的列表体系,原生支持多端部署与高效渲染。
本文将从核心概念、基础用法、高级特性、性能优化四个维度,系统讲解Flutter ListView的使用技巧,并与鸿蒙的列表组件进行深度对比,结合完整代码案例与可视化图表,帮助开发者快速掌握两种框架的列表开发逻辑,轻松应对各类列表场景(如长列表、可交互列表、个性化列表)。文章适合Flutter初学者与鸿蒙生态开发者,内容兼顾实用性与深度,可直接作为项目开发参考。
一、核心概念:Flutter ListView与鸿蒙List的本质
1.1 Flutter ListView:滚动容器的核心逻辑
Flutter的ListView本质是一个可滚动的线性布局容器,继承自BoxScrollView,核心作用是在有限的屏幕空间内展示无限或大量的子组件,并支持垂直/水平滚动、懒加载、样式定制等功能。
其核心设计逻辑如下:
- 布局原理:
ListView通过Sliver机制实现高效滚动——将列表分为“可见区域”和“缓存区域”,仅渲染可见区域及周边少量组件,避免一次性加载所有子组件导致的性能问题。 - 构造方式:提供多种构造函数(
ListView、ListView.builder、ListView.separated等),适配不同数据量场景(少量固定数据、大量动态数据、带分割线数据)。 - 滚动特性:支持弹性滚动(iOS)、阻尼滚动(Android)、滚动监听、滚动控制(如滚动到指定位置)等原生特性。
1.2 鸿蒙List组件:全场景适配的列表体系
鸿蒙的列表组件以List为容器,结合ForEach(遍历数据)、LazyForEach(懒加载遍历)、ListItem(列表项)构成核心体系,核心设计目标是全场景设备适配(手机、平板、PC、智慧屏等)。
其核心设计逻辑如下:
- 布局原理:基于鸿蒙的“弹性布局”和“断点系统”,列表可根据设备屏幕尺寸自动调整布局结构(如单列/多列、横向/纵向)。
- 懒加载机制:通过
LazyForEach实现列表项的懒加载,仅当列表项进入可见区域时才创建并渲染,大幅优化长列表性能。 - 全场景适配:支持自适应屏幕方向、窗口大小变化,原生支持列表项的拖拽排序、侧滑操作等交互特性。
1.3 核心概念对比
| 特性 | Flutter ListView | 鸿蒙List组件 |
|---|---|---|
| 核心定位 | 跨平台线性滚动容器 | 全场景自适应列表容器 |
| 数据遍历方式 | 直接传入子组件列表或通过builder回调 | ForEach(普通遍历)、LazyForEach(懒加载) |
| 懒加载支持 | ListView.builder/separated原生支持 | LazyForEach原生支持 |
| 多设备适配 | 需手动处理屏幕尺寸与方向变化 | 原生支持断点系统与多端布局自适应 |
| 核心依赖 | Sliver滚动体系 | 鸿蒙弹性布局与组件复用机制 |
| 扩展能力 | 依赖第三方库或自定义组件(如下拉刷新) | 原生支持下拉刷新、侧滑操作等特性 |
二、Flutter ListView基础用法:从简单到复杂
2.1 基础构造函数:ListView(适用于少量固定数据)
ListView的最基础构造函数直接接收一个子组件列表(children),适用于数据量少、无需懒加载的场景(如设置页面、功能菜单)。
代码案例:基础设置列表
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter ListView基础示例',
theme: ThemeData(primarySwatch: Colors.blue),
home: const SettingPage(),
);
}
}
class SettingPage extends StatelessWidget {
const SettingPage({super.key});
// 列表项组件
Widget _buildSettingItem(String title, IconData icon) {
return ListTile(
leading: Icon(icon, color: Colors.blue), // 左侧图标
title: Text(title), // 标题
trailing: const Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey), // 右侧箭头
onTap: () {
// 点击事件
print('点击了$title');
},
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('设置')),
body: ListView(
padding: const EdgeInsets.all(0), // 取消默认内边距
// 直接传入固定子组件列表
children: [
_buildSettingItem('账号与安全', Icons.person_outline),
const Divider(height: 1, color: Colors.grey[200]), // 分割线
_buildSettingItem('通知设置', Icons.notifications_outlined),
const Divider(height: 1, color: Colors.grey[200]),
_buildSettingItem('隐私设置', Icons.lock_outlined),
const Divider(height: 1, color: Colors.grey[200]),
_buildSettingItem('帮助与反馈', Icons.help_outline),
const Divider(height: 1, color: Colors.grey[200]),
_buildSettingItem('关于我们', Icons.info_outline),
],
),
);
}
}
关键说明:
ListTile是Flutter提供的封装好的列表项组件,包含leading(左侧)、title(标题)、subtitle(副标题)、trailing(右侧)等属性,快速实现常见列表项样式。Divider用于添加分割线,height属性控制分割线的高度(实际线高由thickness控制,默认1)。- 此构造方式的缺点:所有子组件会一次性创建和渲染,数据量较大(如超过20条)时会导致页面初始化卡顿,不适合长列表。
2.2 懒加载构造函数:ListView.builder(适用于大量动态数据)
ListView.builder是最常用的构造函数,通过itemBuilder回调函数按需创建列表项(仅渲染可见区域及周边少量组件),适用于数据量较大或动态数据场景(如商品列表、消息流)。
代码案例:商品列表(懒加载)
class ProductListPage extends StatefulWidget {
const ProductListPage({super.key});
State<ProductListPage> createState() => _ProductListPageState();
}
class _ProductListPageState extends State<ProductListPage> {
// 模拟商品数据(100条动态数据)
final List<Product> _products = List.generate(
100,
(index) => Product(
id: index,
name: '高性能智能手机 ${index + 1}',
price: 1999 + index * 100,
imageUrl: 'https://example.com/product_$index.jpg',
sales: 1000 + index * 50,
),
);
// 构建商品列表项
Widget _buildProductItem(Product product) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 商品图片
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.grey[100],
image: DecorationImage(
image: NetworkImage(product.imageUrl),
fit: BoxFit.cover,
),
),
),
const SizedBox(width: 12),
// 商品信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
'已售${product.sales}件',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
const SizedBox(height: 8),
Text(
'¥${product.price}',
style: const TextStyle(fontSize: 16, color: Colors.red),
),
],
),
),
],
),
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('商品列表')),
body: ListView.builder(
// 列表项数量
itemCount: _products.length,
// 列表项构造回调(index:当前索引)
itemBuilder: (context, index) {
final product = _products[index];
return _buildProductItem(product);
},
// 列表项之间的间距
padding: const EdgeInsets.symmetric(vertical: 8),
// 滚动物理效果(iOS:弹性滚动,Android:阻尼滚动)
physics: const AlwaysScrollableScrollPhysics(),
),
);
}
}
// 商品模型类
class Product {
final int id;
final String name;
final int price;
final String imageUrl;
final int sales;
Product({
required this.id,
required this.name,
required this.price,
required this.imageUrl,
required this.sales,
});
}
关键说明:
itemCount:指定列表项总数,itemBuilder会根据索引从0到itemCount-1依次回调。- 懒加载机制:
itemBuilder仅在列表项即将进入可见区域时触发,即使数据量为1000条,页面初始化也能瞬间完成。 physics属性控制滚动效果:AlwaysScrollableScrollPhysics()强制可滚动,NeverScrollableScrollPhysics()禁止滚动,BouncingScrollPhysics()(iOS默认)、ClampingScrollPhysics()(Android默认)。
2.3 带分割线构造函数:ListView.separated
ListView.separated在ListView.builder的基础上,增加了separatorBuilder回调,用于在列表项之间添加自定义分割线,适用于需要个性化分割线的场景(如带间距、图标、文字的分割线)。
代码案例:带自定义分割线的消息列表
class MessageListPage extends StatelessWidget {
const MessageListPage({super.key});
// 模拟消息数据
final List<Message> _messages = [
Message(name: '张三', content: '今天的会议几点开始?', time: '09:23', unread: true),
Message(name: '李四', content: '文件已经发送到邮箱,请查收', time: '昨天', unread: false),
Message(name: '王五', content: '这个需求的原型图更新了,见附件', time: '昨天', unread: true),
Message(name: '技术群', content: 'Flutter 3.19版本发布了,新增了很多特性', time: '3天前', unread: false),
];
// 构建消息列表项
Widget _buildMessageItem(Message message) {
return ListTile(
leading: CircleAvatar(
child: Text(message.name.substring(0, 1)),
backgroundColor: Colors.blue[100],
),
title: Text(message.name),
subtitle: Text(
message.content,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
message.time,
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
),
if (message.unread)
const SizedBox(
width: 8,
height: 8,
child: CircleAvatar(
backgroundColor: Colors.red,
),
),
],
),
);
}
// 构建自定义分割线
Widget _buildSeparator(BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
height: 1,
color: Colors.grey[100],
// 分割线中间添加一个小缺口
child: const Align(
alignment: Alignment.centerLeft,
child: SizedBox(width: 40),
),
),
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('消息列表')),
body: ListView.separated(
itemCount: _messages.length,
// 列表项构造
itemBuilder: (context, index) => _buildMessageItem(_messages[index]),
// 分割线构造(索引为index的列表项之后的分割线)
separatorBuilder: _buildSeparator,
padding: const EdgeInsets.all(0),
),
);
}
}
// 消息模型类
class Message {
final String name;
final String content;
final String time;
final bool unread;
Message({
required this.name,
required this.content,
required this.time,
required this.unread,
});
}
关键说明:
separatorBuilder的回调参数index对应“第index个列表项之后的分割线”,分割线数量 = 列表项数量 - 1。- 自定义分割线支持任意Widget,如带间距的线、带图标的分割线、文字分割线等,灵活性远超基础的
Divider。
2.4 自定义布局构造函数:ListView.custom
ListView.custom提供最高级的自定义能力,通过childrenDelegate参数自定义列表项的构建逻辑,适用于复杂场景(如自定义懒加载规则、动态插入/删除列表项)。
代码案例:自定义列表项代理(支持动态添加数据)
class CustomListViewPage extends StatefulWidget {
const CustomListViewPage({super.key});
State<CustomListViewPage> createState() => _CustomListViewPageState();
}
class _CustomListViewPageState extends State<CustomListViewPage> {
final List<int> _data = List.generate(20, (index) => index);
late final SliverChildBuilderDelegate _delegate;
void initState() {
super.initState();
// 自定义列表项代理
_delegate = SliverChildBuilderDelegate(
(context, index) {
// 当索引超过数据长度时,显示“加载更多”
if (index == _data.length) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Center(child: CircularProgressIndicator()),
);
}
return ListTile(
title: Text('自定义列表项 ${_data[index]}'),
leading: Icon(Icons.list, color: Colors.green[500]),
);
},
// 列表项总数(数据长度 + 1个“加载更多”项)
childCount: _data.length + 1,
// 是否需要重建列表项(优化性能)
shouldRebuild: (oldDelegate) => false,
);
}
// 模拟加载更多数据
void _loadMore() {
Future.delayed(const Duration(seconds: 1), () {
setState(() {
_data.addAll(List.generate(10, (index) => _data.length + index));
});
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('自定义ListView')),
body: NotificationListener<ScrollNotification>(
// 监听滚动事件,实现“上拉加载更多”
onNotification: (notification) {
if (notification is ScrollEndNotification &&
notification.metrics.pixels >= notification.metrics.maxScrollExtent - 100) {
_loadMore();
}
return false;
},
child: ListView.custom(
childrenDelegate: _delegate,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 动态插入数据到列表头部
setState(() {
_data.insert(0, _data.length);
});
},
child: const Icon(Icons.add),
),
);
}
}
关键说明:
SliverChildBuilderDelegate是核心代理类,负责列表项的构建、总数计算、重建判断等逻辑。- 通过
NotificationListener监听滚动事件,实现“上拉加载更多”功能,无需依赖第三方库。 shouldRebuild返回false表示当代理实例不变时,不需要重建列表项,优化性能。
三、鸿蒙List组件对应实现:基础与对比
3.1 基础列表:List + ForEach(适用于少量固定数据)
鸿蒙的List组件配合ForEach遍历固定数据,适用于数据量少的场景,与Flutter的基础ListView功能对等。
代码案例:鸿蒙基础设置列表(ArkTS)
import router from '@ohos.router';
import { Column, List, ListItem, Text, Icon, Divider, Scaffold, AppBar } from '@ohos/ui';
@Entry
@Component
struct SettingPage {
// 设置项数据
private settings = [
{ title: '账号与安全', icon: 'person_outline' },
{ title: '通知设置', icon: 'notifications_outlined' },
{ title: '隐私设置', icon: 'lock_outlined' },
{ title: '帮助与反馈', icon: 'help_outline' },
{ title: '关于我们', icon: 'info_outline' },
];
build() {
Scaffold() {
AppBar() {
Text('设置')
.fontSize(20)
.fontWeight(FontWeight.Medium)
}
.backgroundColor('#ffffff')
List() {
ForEach(this.settings, (item, index) => {
ListItem() {
Column() {
// 列表项内容
Row() {
Icon(item.icon)
.size(24)
.color('#2196F3')
Text(item.title)
.fontSize(16)
.marginLeft(12)
Spacer()
Icon('arrow_forward_ios')
.size(16)
.color('#999999')
}
.padding(16)
.onClick(() => {
console.log(`点击了${item.title}`);
})
// 分割线(最后一项不显示)
if (index < this.settings.length - 1) {
Divider()
.height(1)
.backgroundColor('#f5f5f5')
.marginLeft(16)
}
}
}
}, (item) => item.title)
}
.backgroundColor('#ffffff')
}
.backgroundColor('#f5f5f5')
}
}
3.2 懒加载列表:List + LazyForEach(适用于大量动态数据)
鸿蒙的LazyForEach是懒加载遍历组件,与List配合实现长列表懒加载,功能对等Flutter的ListView.builder。
代码案例:鸿蒙商品列表(ArkTS)
import { Column, List, ListItem, Text, Image, Row, Spacer, Scaffold, AppBar } from '@ohos/ui';
// 商品模型类
class Product {
id: number;
name: string;
price: number;
imageUrl: string;
sales: number;
constructor(id: number, name: string, price: number, imageUrl: string, sales: number) {
this.id = id;
this.name = name;
this.price = price;
this.imageUrl = imageUrl;
this.sales = sales;
}
}
@Entry
@Component
struct ProductListPage {
// 模拟100条商品数据
private products: Product[] = [];
aboutToAppear() {
// 初始化数据
for (let i = 0; i < 100; i++) {
this.products.push(new Product(
i,
`高性能智能手机 ${i + 1}`,
1999 + i * 100,
`https://example.com/product_${i}.jpg`,
1000 + i * 50
));
}
}
// 构建商品列表项
buildProductItem(product: Product) {
return Row() {
// 商品图片
Image(product.imageUrl)
.width(80)
.height(80)
.borderRadius(8)
.objectFit(ImageFit.Cover)
.backgroundColor('#f5f5f5')
Column() {
Text(product.name)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.maxLines(2)
.overflow(TextOverflow.Ellipsis)
.width('100%')
Text(`已售${product.sales}件`)
.fontSize(12)
.color('#666666')
.marginTop(4)
Text(`¥${product.price}`)
.fontSize(16)
.color('#FF4444')
.marginTop(8)
}
.marginLeft(12)
.flexGrow(1)
}
.padding(16)
}
build() {
Scaffold() {
AppBar() {
Text('商品列表')
.fontSize(20)
.fontWeight(FontWeight.Medium)
}
.backgroundColor('#ffffff')
List() {
LazyForEach(
// 数据源(需实现IEnumerable接口)
this.products,
// 列表项构造
(product) => {
ListItem() {
this.buildProductItem(product)
}
},
// 唯一标识(用于组件复用)
(product) => product.id.toString()
)
}
.backgroundColor('#ffffff')
.padding(8)
}
.backgroundColor('#f5f5f5')
}
}
3.3 Flutter ListView与鸿蒙List基础用法对比
| 用法场景 | Flutter实现方式 | 鸿蒙实现方式 | 核心差异 |
|---|---|---|---|
| 少量固定数据 | ListView(children: […]) | List() + ForEach() | 鸿蒙的ForEach需指定唯一标识,Flutter直接传入子组件 |
| 大量动态数据(懒加载) | ListView.builder(itemBuilder: …) | List() + LazyForEach() | 鸿蒙的LazyForEach原生支持组件复用,Flutter通过builder回调实现 |
| 带分割线列表 | ListView.separated(separatorBuilder: …) | ListItem内嵌套Divider,通过索引控制显示 | Flutter的separatorBuilder更简洁,鸿蒙需手动控制分割线显示 |
| 自定义列表逻辑 | ListView.custom(childrenDelegate: …) | 自定义DataSource + LazyForEach | Flutter的代理模式更灵活,鸿蒙需实现特定接口 |
四、高级特性:交互与个性化定制
4.1 列表项交互:点击、滑动删除、长按
Flutter实现
// 滑动删除 + 长按事件
class InteractiveListViewPage extends StatefulWidget {
const InteractiveListViewPage({super.key});
State<InteractiveListViewPage> createState() => _InteractiveListViewPageState();
}
class _InteractiveListViewPageState extends State<InteractiveListViewPage> {
final List<String> _items = List.generate(20, (index) => '列表项 ${index + 1}');
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('交互列表')),
body: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
final item = _items[index];
// 滑动删除
return Dismissible(
// 唯一标识
key: Key(item),
// 滑动方向(左滑)
direction: DismissDirection.endToStart,
// 背景(滑动时显示)
background: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 16),
child: const Icon(Icons.delete, color: Colors.white),
),
// 删除回调
onDismissed: (direction) {
setState(() {
_items.removeAt(index);
});
// 显示 SnackBar 提示
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('已删除 $item')),
);
},
// 长按事件
child: ListTile(
title: Text(item),
onTap: () {
// 点击事件
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('点击了 $item')),
);
},
onLongPress: () {
// 长按事件
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('提示'),
content: Text('是否删除 $item?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
setState(() {
_items.removeAt(index);
});
Navigator.pop(context);
},
child: const Text('删除'),
),
],
),
);
},
),
);
},
),
);
}
}
鸿蒙实现(ArkTS)
import { Column, List, ListItem, Text, Icon, Scaffold, AppBar, SwipeItem, Dialog, Button } from '@ohos/ui';
@Entry
@Component
struct InteractiveListPage {
private items: string[] = [];
aboutToAppear() {
this.items = Array.from({ length: 20 }, (_, i) => `列表项 ${i + 1}`);
}
build() {
Scaffold() {
AppBar() {
Text('交互列表')
.fontSize(20)
.fontWeight(FontWeight.Medium)
}
.backgroundColor('#ffffff')
List() {
LazyForEach(
this.items,
(item, index) => {
ListItem() {
// 侧滑删除
SwipeItem(
// 右侧操作区
end: [
Button('删除')
.backgroundColor('#FF4444')
.color('#ffffff')
.onClick(() => {
this.items.splice(index, 1);
// 刷新列表
this.items = [...this.items];
})
]
) {
// 列表项内容
Text(item)
.fontSize(16)
.padding(16)
.width('100%')
.onClick(() => {
// 点击事件
prompt.showToast({ message: `点击了 ${item}` });
})
.onLongPress(() => {
// 长按事件
Dialog() {
Column() {
Text('提示')
.fontSize(18)
.fontWeight(FontWeight.Medium)
.marginBottom(16)
Text(`是否删除 ${item}?`)
.fontSize(16)
.marginBottom(24)
Row() {
Button('取消')
.backgroundColor('#f5f5f5')
.color('#333333')
.flexGrow(1)
.marginRight(8)
.onClick(() => {
DialogController.close();
})
Button('删除')
.backgroundColor('#FF4444')
.color('#ffffff')
.flexGrow(1)
.marginLeft(8)
.onClick(() => {
this.items.splice(index, 1);
this.items = [...this.items];
DialogController.close();
})
}
}
.padding(24)
}
.show();
})
}
}
},
(item) => item
)
}
.backgroundColor('#ffffff')
}
.backgroundColor('#f5f5f5')
}
}
4.2 下拉刷新与上拉加载
Flutter实现
class RefreshLoadListViewPage extends StatefulWidget {
const RefreshLoadListViewPage({super.key});
State<RefreshLoadListViewPage> createState() => _RefreshLoadListViewPageState();
}
class _RefreshLoadListViewPageState extends State<RefreshLoadListViewPage> {
final List<int> _items = List.generate(20, (index) => index);
bool _isLoading = false;
// 下拉刷新
Future<void> _onRefresh() async {
await Future.delayed(const Duration(seconds: 1));
setState(() {
// 清空数据,重新添加20条
_items.clear();
_items.addAll(List.generate(20, (index) => index));
});
}
// 上拉加载
Future<void> _onLoadMore() async {
if (_isLoading) return;
setState(() => _isLoading = true);
await Future.delayed(const Duration(seconds: 1));
setState(() {
// 添加10条新数据
final start = _items.length;
_items.addAll(List.generate(10, (index) => start + index));
_isLoading = false;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('下拉刷新与上拉加载')),
body: RefreshIndicator(
// 下拉刷新回调
onRefresh: _onRefresh,
// 列表内容
child: ListView.builder(
itemCount: _items.length + (_isLoading ? 1 : 0),
itemBuilder: (context, index) {
if (index == _items.length) {
// 加载中提示
return const Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Center(child: CircularProgressIndicator()),
);
}
return ListTile(
title: Text('列表项 ${_items[index] + 1}'),
);
},
// 监听滚动事件
controller: ScrollController()
..addListener(() {
final scrollController = ScrollController();
if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 100 &&
!_isLoading) {
_onLoadMore();
}
}),
),
),
);
}
}
鸿蒙实现(ArkTS)
import { Column, List, ListItem, Text, Scaffold, AppBar, Refresh, LoadingProgress } from '@ohos/ui';
@Entry
@Component
struct RefreshLoadListPage {
private items: number[] = [];
private isLoading = false;
aboutToAppear() {
this.items = Array.from({ length: 20 }, (_, i) => i);
}
// 下拉刷新
private async onRefresh() {
await new Promise((resolve) => setTimeout(resolve, 1000));
this.items = Array.from({ length: 20 }, (_, i) => i);
}
// 上拉加载
private async onLoadMore() {
if (this.isLoading) return;
this.isLoading = true;
await new Promise((resolve) => setTimeout(resolve, 1000));
const start = this.items.length;
this.items.push(...Array.from({ length: 10 }, (_, i) => start + i));
this.isLoading = false;
}
build() {
Scaffold() {
AppBar() {
Text('下拉刷新与上拉加载')
.fontSize(20)
.fontWeight(FontWeight.Medium)
}
.backgroundColor('#ffffff')
// 下拉刷新组件
Refresh(
onRefresh: this.onRefresh.bind(this),
refreshing: false,
offset: 60,
) {
List() {
LazyForEach(
this.items,
(item) => {
ListItem() {
Text(`列表项 ${item + 1}`)
.fontSize(16)
.padding(16)
.width('100%')
}
},
(item) => item.toString()
)
// 加载中提示
if (this.isLoading) {
ListItem() {
LoadingProgress()
.size({ width: 30, height: 30 })
.margin(16)
}
}
}
.backgroundColor('#ffffff')
// 监听滚动到底部
.onReachEnd(() => {
this.onLoadMore();
})
}
}
.backgroundColor('#f5f5f5')
}
}
五、性能优化策略:Flutter与鸿蒙对比
5.1 Flutter ListView性能优化
1. 合理选择构造函数
- 少量固定数据:使用
ListView(children: ...),简单高效。 - 大量动态数据:优先使用
ListView.builder或ListView.separated,避免一次性创建所有组件。 - 复杂自定义场景:使用
ListView.custom,通过SliverChildBuilderDelegate优化构建逻辑。
2. 减少不必要的重建
- 使用
const构造函数:对于静态列表项(如无状态的ListTile),添加const关键字,编译期缓存组件实例。// 优化前 ListTile(title: Text('静态列表项')) // 优化后 const ListTile(title: const Text('静态列表项')) - 提取列表项为独立无状态组件:避免在
itemBuilder中创建匿名函数或临时变量,减少重建开销。
3. 优化滚动性能
- 设置
itemExtent:对于固定高度的列表项,指定itemExtent,避免Flutter实时计算列表项高度,提升滚动流畅度。ListView.builder( itemExtent: 56, // 固定列表项高度 itemCount: 1000, itemBuilder: (context, index) => ListTile(title: Text('列表项 $index')), ) - 使用
RepaintBoundary隔离动态区域:对于包含动画或频繁刷新的列表项,用RepaintBoundary包裹,避免整列表重绘。RepaintBoundary( child: AnimatedContainer( duration: const Duration(milliseconds: 300), color: _isSelected ? Colors.blue : Colors.white, ), )
4. 避免布局嵌套过深
- 简化列表项布局结构,避免多层
Row、Column嵌套(建议不超过3层)。 - 使用
Padding、Margin替代嵌套Container,减少布局计算量。
5.2 鸿蒙List组件性能优化
1. 优先使用LazyForEach
- 长列表必须使用
LazyForEach而非ForEach,避免一次性渲染所有列表项,导致内存溢出。 - 为
LazyForEach指定合理的cachedCount(默认5),控制缓存的列表项数量,平衡内存与性能。LazyForEach( this.items, (item) => ListItem(...), (item) => item.id.toString() ) .cachedCount(10) // 缓存10个列表项
2. 减少布局嵌套与硬编码
- 使用
Flex、Spacer等组件简化布局,避免多层Row、Column嵌套。 - 避免硬编码尺寸,使用
vp单位或百分比,结合弹性布局适配不同屏幕。
3. 组件复用与状态管理
- 列表项组件尽量设计为无状态组件,通过参数传递数据,避免状态冗余。
- 动态数据更新时,使用
Immutable数据结构,减少组件重建范围。
4. 图片优化
- 列表项中的图片使用
objectFit控制缩放方式,避免图片拉伸导致的性能损耗。 - 根据设备分辨率加载不同尺寸的图片,减少网络传输与渲染开销。
5.3 性能优化对比总结
| 优化方向 | Flutter优化方案 | 鸿蒙优化方案 |
|---|---|---|
| 懒加载 | ListView.builder/separated | LazyForEach + cachedCount |
| 减少重建 | const构造函数、独立无状态组件 | 无状态组件、Immutable数据结构 |
| 滚动性能 | itemExtent、RepaintBoundary | 简化布局、避免硬编码尺寸 |
| 布局优化 | 减少嵌套、使用Padding替代Container | Flex+Spacer组件、弹性布局 |
| 图片优化 | CachedNetworkImage缓存、图片压缩 | 多分辨率图片、objectFit控制 |
六、总结与展望
Flutter的ListView与鸿蒙的List组件是各自生态中最核心的列表解决方案,两者在基础功能、高级特性和性能优化上各有侧重:
- Flutter ListView:以“跨平台一致性”为核心,提供了灵活的构造函数(
builder、separated、custom)和丰富的第三方生态支持,适合追求iOS/Android UI统一的跨平台应用开发。其优势在于布局灵活性高、滚动性能优异,缺点是多设备适配需手动处理。 - 鸿蒙List组件:以“全场景适配”为目标,原生支持多端布局自适应、懒加载、侧滑操作等特性,大幅降低了全场景应用的开发成本。其优势在于全场景适配能力强、原生交互特性丰富,缺点是生态成熟度略逊于Flutter。
从开发实践来看,两者的核心逻辑高度相似:均通过懒加载机制优化长列表性能,均支持列表项交互、下拉刷新、上拉加载等高级特性,开发者可快速实现技术迁移。在实际项目选型时,建议根据核心需求决策:若需优先保证iOS/Android跨平台一致性,选择Flutter ListView;若需面向手机、平板、PC等全场景设备,鸿蒙的List组件是更高效的选择。
未来,随着跨平台技术与全场景生态的融合,两种列表体系也在相互借鉴:Flutter正通过插件增强鸿蒙生态适配能力,而鸿蒙也在持续优化声明式UI的灵活性。对于开发者而言,掌握两种列表组件的核心用法与优化技巧,将能更好地应对不同场景下的应用开发需求。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐


所有评论(0)