文章目录

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机制实现高效滚动——将列表分为“可见区域”和“缓存区域”,仅渲染可见区域及周边少量组件,避免一次性加载所有子组件导致的性能问题。
  • 构造方式:提供多种构造函数(ListViewListView.builderListView.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.separatedListView.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.builderListView.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. 避免布局嵌套过深
  • 简化列表项布局结构,避免多层RowColumn嵌套(建议不超过3层)。
  • 使用PaddingMargin替代嵌套Container,减少布局计算量。

5.2 鸿蒙List组件性能优化

1. 优先使用LazyForEach
  • 长列表必须使用LazyForEach而非ForEach,避免一次性渲染所有列表项,导致内存溢出。
  • LazyForEach指定合理的cachedCount(默认5),控制缓存的列表项数量,平衡内存与性能。
    LazyForEach(
      this.items,
      (item) => ListItem(...),
      (item) => item.id.toString()
    )
    .cachedCount(10) // 缓存10个列表项
    
2. 减少布局嵌套与硬编码
  • 使用FlexSpacer等组件简化布局,避免多层RowColumn嵌套。
  • 避免硬编码尺寸,使用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:以“跨平台一致性”为核心,提供了灵活的构造函数(builderseparatedcustom)和丰富的第三方生态支持,适合追求iOS/Android UI统一的跨平台应用开发。其优势在于布局灵活性高、滚动性能优异,缺点是多设备适配需手动处理。
  • 鸿蒙List组件:以“全场景适配”为目标,原生支持多端布局自适应、懒加载、侧滑操作等特性,大幅降低了全场景应用的开发成本。其优势在于全场景适配能力强、原生交互特性丰富,缺点是生态成熟度略逊于Flutter。

从开发实践来看,两者的核心逻辑高度相似:均通过懒加载机制优化长列表性能,均支持列表项交互、下拉刷新、上拉加载等高级特性,开发者可快速实现技术迁移。在实际项目选型时,建议根据核心需求决策:若需优先保证iOS/Android跨平台一致性,选择Flutter ListView;若需面向手机、平板、PC等全场景设备,鸿蒙的List组件是更高效的选择。

未来,随着跨平台技术与全场景生态的融合,两种列表体系也在相互借鉴:Flutter正通过插件增强鸿蒙生态适配能力,而鸿蒙也在持续优化声明式UI的灵活性。对于开发者而言,掌握两种列表组件的核心用法与优化技巧,将能更好地应对不同场景下的应用开发需求。

欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐