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

前言

网络功能开发完成后,接下来就是最常用的列表下拉刷新功能,这是所有数据类应用的标配功能。Flutter 原生的下拉刷新组件在鸿蒙端基本可以正常使用,但在实际开发过程中还是会遇到一些鸿蒙端特有的适配问题,需要做少量调整才能达到完美的效果。

本文将完整记录 Flutter 鸿蒙应用列表下拉刷新功能的开发全流程,从组件选型、列表搭建、刷新集成到状态管理,每一步都附带完整可复用的代码,同时整理开发过程中遇到的所有问题和对应的解决方案,每个问题都附带完整解决代码,帮大家快速完成下拉刷新功能的开发。

一、下拉刷新组件选型

Flutter 开发下拉刷新有多种方案,经过实际测试,推荐使用官方的RefreshIndicator组件,这是对鸿蒙端兼容性最好的方案,不需要额外引入第三方依赖,性能稳定,适配成本最低。

组件方案 鸿蒙兼容性 优点 缺点
官方 RefreshIndicator 99% 兼容 无依赖、性能好、适配成本低 样式自定义程度一般
第三方 flutter_easyrefresh 80% 兼容 样式丰富 鸿蒙端存在手势冲突问题
自定义下拉刷新 100% 兼容 完全自定义 开发成本高

综合考虑,优先选择官方RefreshIndicator组件,完全满足常规业务需求,适配成本最低。

二、基础列表页面搭建

先搭建基础的列表页面,实现静态列表展示,为后续添加下拉刷新做准备。

2.1 列表数据实体类

创建列表数据的实体类,用于解析接口返回的数据:

class ListItem {
  int id;
  String title;
  String content;

  ListItem({
    required this.id,
    required this.title,
    required this.content,
  });

  factory ListItem.fromJson(Map<String, dynamic> json) {
    return ListItem(
      id: json["id"],
      title: json["title"],
      content: json["content"],
    );
  }
}

2.2 基础列表页面实现

实现基础的列表页面,使用ListView.builder构建列表:

import 'package:flutter/material.dart';

class RefreshListPage extends StatefulWidget {
  const RefreshListPage({super.key});

  @override
  State<RefreshListPage> createState() => _RefreshListPageState();
}

class _RefreshListPageState extends State<RefreshListPage> {
  List<ListItem> dataList = [];

  @override
  void initState() {
    super.initState();
    // 初始化模拟数据
    initMockData();
  }

  // 初始化模拟数据
  void initMockData() {
    for (int i = 0; i < 10; i++) {
      dataList.add(ListItem(
        id: i,
        title: "列表标题$i",
        content: "这是列表的第$i条内容",
      ));
    }
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("下拉刷新列表"),
        centerTitle: true,
      ),
      body: ListView.builder(
        itemCount: dataList.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(dataList[index].title),
            subtitle: Text(dataList[index].content),
          );
        },
      ),
    );
  }
}

三、下拉刷新功能集成

在基础列表的基础上,集成RefreshIndicator组件,实现下拉刷新功能。

3.1 刷新组件集成

RefreshIndicator包裹 ListView,添加下拉刷新功能:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text("下拉刷新列表"),
      centerTitle: true,
    ),
    body: RefreshIndicator(
      onRefresh: _onRefresh,
      child: ListView.builder(
        physics: const AlwaysScrollableScrollPhysics(),
        itemCount: dataList.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(dataList[index].title),
            subtitle: Text(dataList[index].content),
          );
        },
      ),
    ),
  );
}

3.2 刷新回调方法实现

实现刷新回调方法,模拟网络请求刷新数据:

Future<void> _onRefresh() async {
  // 模拟网络请求延迟
  await Future.delayed(const Duration(seconds: 2));
  // 清空原有数据,添加新数据
  dataList.clear();
  for (int i = 0; i < 10; i++) {
    dataList.add(ListItem(
      id: i,
      title: "刷新后的标题$i",
      content: "这是刷新后的第$i条内容",
    ));
  }
  setState(() {});
}

四、刷新状态管理

添加刷新状态管理,优化用户体验,避免重复刷新。

4.1 刷新状态变量定义

定义刷新状态变量,控制刷新状态:

bool isRefreshing = false;

4.2 状态控制逻辑优化

优化刷新回调,添加状态控制:

Future<void> _onRefresh() async {
  if (isRefreshing) return;
  isRefreshing = true;
  setState(() {});

  try {
    await Future.delayed(const Duration(seconds: 2));
    dataList.clear();
    for (int i = 0; i < 10; i++) {
      dataList.add(ListItem(
        id: i,
        title: "刷新后的标题$i",
        content: "这是刷新后的第$i条内容",
      ));
    }
  } finally {
    isRefreshing = false;
    setState(() {});
  }
}

五、数据刷新逻辑实现

对接之前开发的网络请求功能,实现真实接口数据的刷新。

5.1 接口数据刷新实现

对接网络请求,获取真实数据刷新列表:

Future<void> _onRefresh() async {
  if (isRefreshing) return;
  isRefreshing = true;
  setState(() {});

  try {
    // 调用网络请求获取最新数据
    var response = await HttpRequest().get("/list", params: {"page": 1, "size": 10});
    dataList.clear();
    for (var item in response.data) {
      dataList.add(ListItem.fromJson(item));
    }
  } catch (e) {
    print("刷新失败:$e");
  } finally {
    isRefreshing = false;
    setState(() {});
  }
}

六、开发过程中遇到的问题与解决方案

6.1 问题:鸿蒙端下拉刷新手势不触发

  • 问题现象:在鸿蒙真机上,下拉列表没有反应,无法触发刷新回调
  • 解决方案: ① 给 ListView 添加AlwaysScrollableScrollPhysics物理滚动属性,确保列表始终可以滚动 ② 确保 ListView 的内容高度超过屏幕高度,内容不足时无法触发下拉 ③ 不要给 ListView 添加shrinkWrap: true属性,会导致滚动失效
  • 完整解决代码
ListView.builder(
  // 关键配置:添加始终可滚动的物理属性
  physics: const AlwaysScrollableScrollPhysics(),
  // 禁止添加shrinkWrap: true
  // shrinkWrap: true, ❌ 不要加这个
  itemCount: dataList.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(dataList[index].title),
      subtitle: Text(dataList[index].content),
    );
  },
)

6.2 问题:刷新指示器不显示

  • 问题现象:下拉时没有显示刷新的加载指示器
  • 解决方案
  • ① 确保RefreshIndicator是 ListView 的直接父组件,不要嵌套过多层级
  • ② 不要修改RefreshIndicator的默认颜色和高度,鸿蒙端自定义样式会有兼容问题
  • ③ 确保刷新回调方法是异步方法,返回 Future 类型
  • 完整解决代码
RefreshIndicator(
  // 不要自定义颜色和样式,使用默认样式
  // color: Colors.red, ❌ 鸿蒙端自定义颜色会有兼容问题
  // strokeWidth: 4, ❌ 不要修改默认线宽
  onRefresh: _onRefresh, // 必须是返回Future的异步方法
  child: ListView.builder(
    physics: const AlwaysScrollableScrollPhysics(),
    itemCount: dataList.length,
    itemBuilder: (context, index) {
      return ListTile(
        title: Text(dataList[index].title),
        subtitle: Text(dataList[index].content),
      );
    },
  ),
)

6.3 问题:刷新完成后列表不更新

  • 问题现象:刷新完成后,列表内容没有变化
  • 解决方案
  • ① 刷新数据后必须调用setState更新状态
  • ② 确保数据列表是重新赋值,而不是只修改列表内的元素
  • ③ 不要使用常量列表,必须是可变的 List 对象
  • 完整解决代码
Future<void> _onRefresh() async {
  await Future.delayed(const Duration(seconds: 2));
  // 正确做法:清空后重新添加数据,然后调用setState
  dataList.clear();
  for (int i = 0; i < 10; i++) {
    dataList.add(ListItem(
      id: i,
      title: "刷新后的标题$i",
      content: "这是刷新后的第$i条内容",
    ));
  }
  // 必须调用setState更新UI
  setState(() {});
}

6.4 问题:下拉刷新和鸿蒙系统手势冲突

  • 问题现象:下拉时触发了系统的返回手势,无法触发刷新
  • 解决方案
  • ① 调整下拉触发的距离,修改RefreshIndicatordisplacement属性为 50
  • ② 不要在页面边缘添加下拉刷新,避免和系统边缘返回手势冲突
  • ③ 优先使用官方组件,第三方组件的手势冲突问题更严重
  • 完整解决代码
RefreshIndicator(
  // 调整下拉触发距离,避免和系统手势冲突
  displacement: 50,
  onRefresh: _onRefresh,
  child: ListView.builder(
    physics: const AlwaysScrollableScrollPhysics(),
    itemCount: dataList.length,
    itemBuilder: (context, index) {
      return ListTile(
        title: Text(dataList[index].title),
        subtitle: Text(dataList[index].content),
      );
    },
  ),
)

七、真机功能测试

功能开发完成后,在真机上进行完整测试:

  1. 测试下拉手势是否正常触发刷新
  2. 测试刷新指示器显示是否正常
  3. 测试刷新完成后列表数据是否更新
  4. 测试快速重复下拉是否会出现重复刷新
  5. 测试网络异常情况下的刷新容错

所有测试项通过即代表下拉刷新功能开发完成。

八、功能开发总结

Flutter 鸿蒙端下拉刷新功能的开发,核心注意点总结如下:

  1. 优先使用官方RefreshIndicator组件,兼容性最好,不要盲目使用第三方组件
  2. 必须给 ListView 添加AlwaysScrollableScrollPhysics属性,否则鸿蒙端无法触发刷新
  3. 尽量不要自定义刷新指示器样式,默认样式在鸿蒙端的兼容性最好
  4. 添加刷新状态管理,避免用户快速重复下拉导致的重复请求问题
  5. 所有功能必须在真机上测试,模拟器上的手势和真机有差异

下拉刷新是最常用的基础功能,封装好之后可以在所有列表页面复用,后续的上拉加载功能也可以基于此扩展。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐