Flutter for OpenHarmony二手物品置换App实战 - 列表性能优化实现

列表是App中最常见的UI组件,商品列表、消息列表、搜索结果等都是列表。当列表数据量大时,性能优化就变得很重要。今天我们来讲解列表性能优化的实现方式。

列表性能问题的来源

列表性能问题主要来自两个方面:一是创建了太多Widget,二是图片加载消耗资源。优化的核心思路是懒加载和缓存。Flutter的渲染机制决定了Widget数量直接影响内存占用和渲染速度,所以控制Widget数量是优化的关键。

使用ListView.builder

dart 复制代码
// 不推荐:一次性创建所有Widget
ListView(
  children: products.map((p) => ProductCard(product: p)).toList(),
)

// 推荐:懒加载,只创建可见区域的Widget
ListView.builder(
  itemCount: products.length,
  itemBuilder: (context, index) => ProductCard(product: products[index]),
)

普通ListView会一次性创建所有子Widget,如果有1000条数据就创建1000个Widget,内存直接爆炸。ListView.builder采用懒加载策略,只创建屏幕可见区域的Widget,滚动时动态创建新的、销毁离开屏幕的。这是列表优化最基本也是最重要的一步。

使用GridView.builder

商品列表用网格布局:

dart 复制代码
GridView.builder(
  padding: const EdgeInsets.all(12),
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
    childAspectRatio: 0.65,
    crossAxisSpacing: 10,
    mainAxisSpacing: 10,
  ),
  itemCount: _products.length,
  itemBuilder: (context, index) => _buildProductCard(_products[index]),
)

GridView.builder和ListView.builder原理一样,都是懒加载。gridDelegate定义网格的布局规则,crossAxisCount设置每行显示2个,childAspectRatio是宽高比,spacing是间距。这些参数都用const修饰,避免每次build都创建新对象。

使用SliverGrid

CustomScrollView中使用SliverGrid

dart 复制代码
CustomScrollView(
  slivers: [
    SliverToBoxAdapter(child: _buildBanner()),
    SliverToBoxAdapter(child: _buildCategories()),
    SliverPadding(
      padding: const EdgeInsets.all(12),
      sliver: SliverGrid(
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          childAspectRatio: 0.65,
          crossAxisSpacing: 10,
          mainAxisSpacing: 10,
        ),
        delegate: SliverChildBuilderDelegate(
          (context, index) => _buildProductCard(_products[index]),
          childCount: _products.length,
        ),
      ),
    ),
  ],
)

首页通常有banner、分类导航、商品列表等多个部分,用CustomScrollView可以把它们组合成一个可滚动的整体。SliverChildBuilderDelegate实现懒加载,效果和GridView.builder一样。这种写法更灵活,可以在列表前后插入任意内容。

图片缓存

使用cached_network_image缓存网络图片:

dart 复制代码
CachedNetworkImage(
  imageUrl: product['image'],
  fit: BoxFit.cover,
  placeholder: (context, url) => Container(
    color: Colors.grey[200],
    child: const Center(child: CircularProgressIndicator()),
  ),
  errorWidget: (context, url, error) => Container(
    color: Colors.grey[200],
    child: Icon(Icons.image, size: 60, color: Colors.grey[400]),
  ),
)

网络图片是列表性能的大敌,每次滚动都重新下载会很卡。CachedNetworkImage会把下载的图片缓存到本地,下次显示直接从缓存读取。placeholder在加载时显示占位内容,errorWidget在加载失败时显示错误提示,用户体验更好。

图片尺寸优化

加载适合显示尺寸的图片,不要加载原图:

dart 复制代码
// 假设服务器支持图片尺寸参数
String getImageUrl(String url, {int width = 200}) {
  return '$url?w=$width';
}

CachedNetworkImage(
  imageUrl: getImageUrl(product['image'], width: 200),
  // ...
)

商品卡片可能只有200像素宽,但原图可能是2000像素。加载原图不仅浪费流量,还会占用大量内存。让服务器返回合适尺寸的图片,或者使用CDN的图片处理功能,能大幅减少资源消耗。

使用const构造函数

能用const的地方都用const

dart 复制代码
// 不推荐
Container(
  padding: EdgeInsets.all(16),
  child: Text('文字'),
)

// 推荐
Container(
  padding: const EdgeInsets.all(16),
  child: const Text('文字'),
)

const对象在编译期就创建好了,运行时直接复用,不需要每次build都重新创建。这个优化看起来微不足道,但在列表中累积起来效果很明显。养成习惯,能加const的地方都加上。

避免在build中创建对象

dart 复制代码
// 不推荐:每次build都创建新的列表
@override
Widget build(BuildContext context) {
  final categories = [
    {'icon': Icons.phone_android, 'name': '数码'},
    {'icon': Icons.checkroom, 'name': '服饰'},
    // ...
  ];
  return // ...
}

// 推荐:在State中定义,只创建一次
class _HomePageState extends State<HomePage> {
  final List<Map<String, dynamic>> _categories = [
    {'icon': Icons.phone_android, 'name': '数码'},
    {'icon': Icons.checkroom, 'name': '服饰'},
    // ...
  ];
  
  @override
  Widget build(BuildContext context) {
    return // ...
  }
}

build方法可能会被频繁调用,每次调用都创建新对象会产生大量垃圾回收压力。把不变的数据定义在State中,或者用final修饰,只创建一次就够了。

使用RepaintBoundary

对于复杂的Widget,用RepaintBoundary隔离重绘:

dart 复制代码
RepaintBoundary(
  child: _buildProductCard(product),
)

Flutter默认会把相邻的Widget合并到一个图层一起绘制,一个Widget变化可能导致整个区域重绘。RepaintBoundary创建独立的图层,内部变化不会影响外部,外部变化也不会导致内部重绘。对于复杂的商品卡片,这个优化很有效。

使用AutomaticKeepAliveClientMixin

PageViewTabBarView中保持页面状态:

dart 复制代码
class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;
  
  @override
  Widget build(BuildContext context) {
    super.build(context);  // 必须调用
    return // ...
  }
}

默认情况下,切换Tab时不可见的页面会被销毁,再切回来要重新加载。使用AutomaticKeepAliveClientMixin可以保持页面状态,滚动位置、加载的数据都会保留。注意build方法里必须调用super.build(context)。

分页加载

不要一次加载所有数据,分页加载:

dart 复制代码
class _HomePageState extends State<HomePage> {
  List<Map<String, dynamic>> _products = [];
  int _page = 1;
  bool _hasMore = true;
  bool _isLoadingMore = false;

  Future<void> _loadMoreProducts() async {
    if (_isLoadingMore || !_hasMore) return;
    
    setState(() => _isLoadingMore = true);
    
    try {
      _page++;
      final data = await Api.getProducts(page: _page);
      setState(() {
        _products.addAll(data);
        _hasMore = data.length >= 20;
        _isLoadingMore = false;
      });
    } catch (e) {
      _page--;
      setState(() => _isLoadingMore = false);
    }
  }
}

一次加载1000条数据,不管是网络传输还是内存占用都是灾难。分页加载每次只请求20条,滚动到底部时再加载下一页。_isLoadingMore防止重复请求,_hasMore判断是否还有更多数据,加载失败时回退页码。

使用itemExtent

如果列表项高度固定,指定itemExtent

dart 复制代码
ListView.builder(
  itemExtent: 100,  // 每项高度100
  itemCount: products.length,
  itemBuilder: (context, index) => ProductCard(product: products[index]),
)

指定itemExtent后,Flutter不需要调用每个子Widget的layout来计算高度,可以直接通过数学计算确定哪些项在可见区域。这对于长列表的滚动性能提升很明显。

小结

这篇讲解了列表性能优化的实现方式,包括使用builder懒加载、图片缓存、const构造函数、RepaintBoundary、分页加载等。性能优化能让App更流畅,提升用户体验。记住优化的核心思路:减少Widget数量、减少重绘、减少内存占用。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
Utopia^35 分钟前
Flutter 框架跨平台鸿蒙开发 - 旅行预算管家
flutter·华为·harmonyos
李李李勃谦1 小时前
Flutter 框架跨平台鸿蒙开发 - 星空识别助手
flutter·华为·harmonyos
李李李勃谦1 小时前
Flutter 框架跨平台鸿蒙开发 - 本地生活服务预约
flutter·华为·生活·harmonyos
架构师老Y1 小时前
013、数据库性能优化:索引、查询与连接池
数据库·python·oracle·性能优化·架构
2301_822703201 小时前
开源鸿蒙跨平台Flutter开发:幼儿疫苗全生命周期追踪系统:基于 Flutter 的免疫接种档案与状态机设计
算法·flutter·华为·开源·harmonyos·鸿蒙
2301_822703202 小时前
鸿蒙flutter三方库实战——教育与学习平台:Flutter Markdown
学习·算法·flutter·华为·harmonyos·鸿蒙
阿华田5124 小时前
MySQL性能优化大全
数据库·mysql·性能优化
2301_822703204 小时前
开源鸿蒙跨平台Flutter开发:蛋白质序列特征提取:氨基酸组成与理化性质计算
flutter·华为·开源·harmonyos·鸿蒙
钛态5 小时前
Flutter 三方库 ethereum_addresses 的鸿蒙化适配指南 - 掌控区块链地址资产、精密校验治理实战、鸿蒙级 Web3 专家
flutter·harmonyos·鸿蒙·openharmony·ethereum_addresses
提子拌饭1335 小时前
开源鸿蒙跨平台Flutter开发:中小学百米跑信息记录表:基于 Flutter 的高精计时与运动学曲线引擎
flutter·华为·开源·harmonyos