Flutter+开源鸿蒙实战:列表下拉刷新与上滑加载更多完整实现
Flutter内置的/// 自定义鸿蒙风格下拉刷新头部// 鸿蒙风格加载动画(旋转圆环)valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF007DFF)), // 鸿蒙主题蓝),Text("正在刷新...",),],// 自定义下拉刷新组件super.key,});@override// 替换默认指示器// 使用自定义组件替换原RefreshI
文章目录
Flutter+开源鸿蒙实战:列表下拉刷新与上滑加载更多完整实现
在移动应用开发中,列表是承载数据展示的核心组件,而下拉刷新(Pull-to-Refresh)和上滑加载更多(Load More)则是提升用户体验的关键交互。Flutter凭借跨平台特性与自绘引擎,能快速实现统一UI;开源鸿蒙(OpenHarmony)则以全场景分布式能力赋能应用,二者结合可打造高效兼容的列表交互体验。本文基于Flutter 3.24+与鸿蒙NEXT API 9,从环境搭建到高级优化,手把手教你实现生产级列表交互功能,代码可直接用于项目开发或课程实践。
一、技术选型与核心价值
1.1 为什么选择Flutter+开源鸿蒙?
Flutter的自绘引擎(Skia)不依赖平台原生控件,能在鸿蒙全场景设备(手机、平板、智慧屏)上保持UI一致性,避免多设备适配冗余;而鸿蒙的分布式能力可让Flutter列表数据跨设备同步,比如手机上的列表刷新状态能无缝流转到智慧屏。相比传统方案,优势显著:
| 方案 | 鸿蒙适配痛点 | Flutter+鸿蒙优势 |
|---|---|---|
| 鸿蒙原生开发(ArkTS) | 多设备需写多套布局代码 | 一套代码覆盖鸿蒙全设备,适配成本降低80% |
| React Native | 依赖Android兼容层,性能损耗 | 直接调用鸿蒙图形API,帧率稳定58-60FPS |
| 原生Android+鸿蒙 | 双端开发维护成本高 | 单工程开发,同时支持鸿蒙与Android/iOS |
1.2 核心组件选型
- 下拉刷新:使用Flutter内置
RefreshIndicator组件,原生支持Material Design风格,无需额外依赖,适配鸿蒙系统交互规范。 - 上滑加载更多:通过
ScrollController监听滚动位置,结合状态控制避免重复请求,兼容鸿蒙设备的滑动物理特性。 - 列表渲染:采用
ListView.builder实现懒加载,仅构建可见区域列表项,降低鸿蒙设备内存占用。 - 鸿蒙适配:集成官方
harmonyos_flutter库,实现Flutter与鸿蒙系统的底层通信,确保交互响应及时。
二、开发环境搭建(避坑指南)
2.1 工具清单与版本要求
开发前需确保工具版本对齐,避免兼容性问题:
| 工具 | 推荐版本 | 作用 | 避坑提醒 |
|---|---|---|---|
| Flutter SDK | 3.24.0+ | Flutter核心开发工具 | 低于3.20版本不支持鸿蒙NEXT图形API |
| DevEco Studio | 4.1.0+ | 鸿蒙应用打包与调试 | 需安装鸿蒙SDK 6.0+(API 9) |
| JDK | 11 | 支持DevEco Studio编译 | 禁止使用JDK 17,会导致Gradle冲突 |
| 鸿蒙模拟器 | API 9(HarmonyOS 6) | 测试应用表现 | 分配至少4GB内存,否则运行卡顿 |
| Android Studio | 2023.1.1+ | 辅助Flutter项目构建 | 仅需安装Android SDK Build-Tools 34 |
2.2 环境配置步骤
步骤1:创建Flutter项目并集成鸿蒙依赖
# 创建支持Android/iOS的Flutter项目(后续关联鸿蒙)
flutter create --platforms=android,ios flutter_harmony_list
cd flutter_harmony_list
# 在pubspec.yaml中添加鸿蒙适配依赖
flutter pub add harmonyos_flutter:^1.2.0
flutter pub add harmonyos_platform_channels:^0.5.0
flutter pub get
步骤2:配置DevEco Studio关联Flutter项目
- 打开DevEco Studio,选择「Import Project」,导入Flutter项目的
android目录(鸿蒙通过Android模块间接集成Flutter); - 进入「File → Project Structure」,指定鸿蒙SDK路径(选择API 9+版本);
- 安装鸿蒙模拟器:「Tools → Device Manager」,创建Phone类型模拟器(推荐HarmonyOS 6.0+2K分辨率);
- 配置Flutter SDK路径:「Settings → Flutter」,手动指定SDK位置,验证版本≥3.24。
步骤3:验证环境可用性
修改lib/main.dart,添加鸿蒙设备信息获取功能,测试适配是否成功:
import 'package:flutter/material.dart';
import 'package:harmonyos_platform_channels/harmonyos_platform_channels.dart';
void main() {
// 初始化鸿蒙平台通道
HarmonyOSPlatformChannels.init();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter鸿蒙列表Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const ListDemoPage(),
);
}
}
class ListDemoPage extends StatefulWidget {
const ListDemoPage({super.key});
State<ListDemoPage> createState() => _ListDemoPageState();
}
class _ListDemoPageState extends State<ListDemoPage> {
String _deviceInfo = "获取中...";
void initState() {
super.initState();
_getHarmonyDeviceInfo();
}
// 获取鸿蒙设备信息
Future<void> _getHarmonyDeviceInfo() async {
try {
final result = await HarmonyOSPlatformChannels.invokeMethod(
"getDeviceInfo",
{},
);
setState(() {
_deviceInfo = "鸿蒙设备:${result['deviceName']}(API ${result['apiVersion']})";
});
} catch (e) {
setState(() {
_deviceInfo = "获取失败:$e";
});
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("环境验证")),
body: Center(child: Text(_deviceInfo, style: const TextStyle(fontSize: 18))),
);
}
}
运行后若能显示鸿蒙设备信息,说明环境配置成功
三、基础功能实现:下拉刷新+上滑加载
3.1 核心逻辑设计
- 状态管理:维护列表数据、当前页码、加载状态(是否正在刷新/加载)、是否已加载全部数据;
- 下拉刷新:通过
RefreshIndicator的onRefresh回调,重置页码并重新请求第一页数据; - 上滑加载:通过
ScrollController监听滚动到底部事件,请求下一页数据; - 加载状态展示:下拉时显示原生刷新指示器,上滑时显示加载中动画,无更多数据时显示提示文本。
3.2 完整代码实现
import 'package:flutter/material.dart';
import 'package:harmonyos_flutter/harmonyos_flutter.dart';
class RefreshLoadMoreList extends StatefulWidget {
const RefreshLoadMoreList({super.key});
State<RefreshLoadMoreList> createState() => _RefreshLoadMoreListState();
}
class _RefreshLoadMoreListState extends State<RefreshLoadMoreList> {
// 列表数据
final List<String> _listData = [];
// 滚动控制器
final ScrollController _scrollController = ScrollController();
// 当前页码
int _currentPage = 1;
// 每页数据量
static const int _pageSize = 10;
// 最大页数(模拟)
static const int _maxPage = 5;
// 加载状态
bool _isRefreshing = false;
bool _isLoadingMore = false;
// 是否已加载全部
bool _hasMoreData = true;
void initState() {
super.initState();
// 初始化加载第一页数据
_loadData(page: 1);
// 监听滚动事件
_scrollController.addListener(_onScroll);
}
void dispose() {
// 释放控制器
_scrollController.dispose();
super.dispose();
}
/// 滚动监听:判断是否需要加载更多
void _onScroll() {
// 滚动到底部、不在加载中、还有更多数据
if (_scrollController.position.pixels >=
_scrollController.position.maxScrollExtent - 100 &&
!_isLoadingMore &&
_hasMoreData) {
_loadMoreData();
}
}
/// 模拟网络请求:获取列表数据
Future<List<String>> _fetchData(int page) async {
// 模拟网络延迟(鸿蒙设备上建议延迟不超过1.5秒,避免交互卡顿)
await Future.delayed(const Duration(milliseconds: 1200));
// 生成模拟数据
final List<String> newData = List.generate(_pageSize, (index) {
final itemIndex = (page - 1) * _pageSize + index + 1;
return "鸿蒙列表项 $itemIndex(第$page页)- 基于Flutter跨平台实现";
});
return newData;
}
/// 加载数据(通用方法)
Future<void> _loadData({required int page}) async {
if (page == 1) {
setState(() => _isRefreshing = true);
} else {
setState(() => _isLoadingMore = true);
}
try {
final newData = await _fetchData(page);
setState(() {
if (page == 1) {
// 下拉刷新:重置数据
_listData.clear();
_listData.addAll(newData);
_currentPage = 1;
// 重置是否有更多数据
_hasMoreData = true;
} else {
// 上滑加载:追加数据
_listData.addAll(newData);
_currentPage = page;
// 判断是否已加载全部
if (_currentPage >= _maxPage) {
_hasMoreData = false;
}
}
});
} catch (e) {
// 异常处理:显示错误提示
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("加载失败:$e"),
backgroundColor: Colors.redAccent,
),
);
}
} finally {
// 结束加载状态
setState(() {
_isRefreshing = false;
_isLoadingMore = false;
});
}
}
/// 下拉刷新回调
Future<void> _onRefresh() async {
await _loadData(page: 1);
}
/// 上滑加载更多
Future<void> _loadMoreData() async {
await _loadData(page: _currentPage + 1);
}
/// 构建列表项
Widget _buildListItem(int index) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
blurRadius: 4,
offset: const Offset(0, 2),
)
],
),
child: Text(
_listData[index],
style: const TextStyle(fontSize: 16, color: Colors.black87),
),
);
}
/// 构建加载更多底部组件
Widget _buildLoadMoreFooter() {
if (!_hasMoreData) {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Center(
child: Text(
"已加载全部数据",
style: TextStyle(color: Colors.grey, fontSize: 14),
),
),
);
}
return _isLoadingMore
? const Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(strokeWidth: 2),
SizedBox(width: 12),
Text("加载中...", style: TextStyle(color: Colors.grey, fontSize: 14)),
],
),
),
)
: const SizedBox.shrink();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Flutter鸿蒙列表(下拉刷新+上滑加载)"),
// 适配鸿蒙系统状态栏
systemOverlayStyle: HarmonyOSFlutter.getSystemOverlayStyle(),
),
body: RefreshIndicator(
// 下拉刷新颜色(适配鸿蒙主题色)
color: Theme.of(context).primaryColor,
// 下拉刷新背景色
backgroundColor: Colors.white,
// 下拉刷新回调
onRefresh: _onRefresh,
// 列表内容
child: ListView.builder(
// 滚动控制器
controller: _scrollController,
// 列表项数量(数据量+底部加载组件)
itemCount: _listData.length + 1,
// 构建列表项
itemBuilder: (context, index) {
if (index < _listData.length) {
return _buildListItem(index);
} else {
return _buildLoadMoreFooter();
}
},
// 优化:固定列表项高度(鸿蒙设备上提升渲染性能)
itemExtent: 100,
// 滚动物理效果(适配鸿蒙设备滑动特性)
physics: const AlwaysScrollableScrollPhysics(
parent: BouncingScrollPhysics(),
),
),
),
);
}
}
// 主函数入口
void main() {
HarmonyOSPlatformChannels.init();
runApp(const MaterialApp(
home: RefreshLoadMoreList(),
debugShowCheckedModeBanner: false,
));
}
3.3 功能效果演示
3.3.1 下拉刷新效果
在鸿蒙模拟器中,从列表顶部向下拉动,会显示与系统主题匹配的刷新指示器,刷新完成后更新为最新数据
3.3.2 上滑加载效果
当滚动到列表底部附近时,自动触发加载更多,显示加载中动画;加载完成后追加新数据;当达到最大页数时,显示"已加载全部数据"提示
四、高级优化:适配鸿蒙全场景特性
4.1 自定义刷新/加载动画(鸿蒙风格)
Flutter内置的RefreshIndicator可自定义,结合鸿蒙设计规范(圆角、淡蓝色调),实现更贴合系统的动画效果:
/// 自定义鸿蒙风格下拉刷新头部
Widget _buildCustomRefreshHeader() {
return const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 鸿蒙风格加载动画(旋转圆环)
CircularProgressIndicator(
strokeWidth: 2.5,
valueColor: AlwaysStoppedAnimation<Color>(Color(0xFF007DFF)), // 鸿蒙主题蓝
),
SizedBox(height: 8),
Text(
"正在刷新...",
style: TextStyle(fontSize: 14, color: Color(0xFF666666)),
),
],
);
}
// 自定义下拉刷新组件
class HarmonyRefreshIndicator extends StatelessWidget {
final Widget child;
final Future<void> Function() onRefresh;
const HarmonyRefreshIndicator({
super.key,
required this.child,
required this.onRefresh,
});
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: onRefresh,
color: const Color(0xFF007DFF),
backgroundColor: Colors.white,
displacement: 40,
child: child,
// 替换默认指示器
notificationPredicate: (notification) => notification.depth == 0,
refreshIndicatorLayout: RefreshIndicatorLayout.stack,
);
}
}
// 使用自定义组件替换原RefreshIndicator
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(...),
body: HarmonyRefreshIndicator(
onRefresh: _onRefresh,
child: ListView.builder(...),
),
);
}
4.2 鸿蒙分布式数据同步
利用鸿蒙的分布式能力,可实现列表数据跨设备同步。例如,在手机上下拉刷新后,平板上的同一列表自动更新:
// 监听鸿蒙分布式数据变更
void _listenDistributedData() {
HarmonyOSFlutter.registerDistributedDataListener(
"flutter_harmony_list_key", // 数据同步key
(data) {
if (mounted && data != null) {
setState(() {
_listData.clear();
_listData.addAll(List<String>.from(data));
});
}
},
);
}
// 下拉刷新成功后,同步数据到分布式存储
Future<void> _onRefresh() async {
await _loadData(page: 1);
// 同步到鸿蒙分布式存储
HarmonyOSFlutter.setDistributedData(
"flutter_harmony_list_key",
_listData,
);
}
// 在initState中初始化监听
void initState() {
super.initState();
_loadData(page: 1);
_scrollController.addListener(_onScroll);
_listenDistributedData(); // 开启分布式数据监听
}
4.3 性能优化(鸿蒙设备重点)
4.3.1 列表渲染优化
- 使用
itemExtent固定列表项高度,减少鸿蒙设备布局计算耗时; - 列表项使用
const构造函数,避免不必要的重建; - 图片资源使用
CachedNetworkImage缓存,减少鸿蒙设备网络请求。
4.3.2 内存管理优化
// 1. 避免内存泄漏:使用WeakReference持有上下文
final WeakReference<BuildContext> _contextRef;
void initState() {
super.initState();
_contextRef = WeakReference(context);
}
// 2. 数据分页加载时释放旧数据(适用于大数据量场景)
if (page == 1 && _listData.length > 100) {
_listData.clear(); // 刷新时释放历史数据
}
4.3.3 异常处理优化
针对鸿蒙设备的网络不稳定场景,增加重试机制:
/// 带重试机制的网络请求
Future<List<String>> _fetchDataWithRetry(int page, {int retryCount = 3}) async {
try {
return await _fetchData(page);
} catch (e) {
if (retryCount > 0) {
await Future.delayed(const Duration(milliseconds: 500));
return _fetchDataWithRetry(page, retryCount: retryCount - 1);
} else {
throw Exception("网络异常,已重试3次");
}
}
}
五、实战案例:鸿蒙新闻列表(完整Demo)
5.1 需求设计
实现一个鸿蒙风格的新闻列表,支持:
- 下拉刷新获取最新新闻;
- 上滑加载更多历史新闻;
- 新闻卡片显示标题、发布时间、缩略图;
- 适配鸿蒙手机与平板设备。
5.2 完整代码
import 'package:flutter/material.dart';
import 'package:harmonyos_flutter/harmonyos_flutter.dart';
import 'package:cached_network_image/cached_network_image.dart';
// 新闻数据模型
class NewsModel {
final String id;
final String title;
final String time;
final String imageUrl;
NewsModel({
required this.id,
required this.title,
required this.time,
required this.imageUrl,
});
}
class HarmonyNewsList extends StatefulWidget {
const HarmonyNewsList({super.key});
State<HarmonyNewsList> createState() => _HarmonyNewsListState();
}
class _HarmonyNewsListState extends State<HarmonyNewsList> {
final List<NewsModel> _newsList = [];
final ScrollController _scrollController = ScrollController();
int _currentPage = 1;
static const int _pageSize = 8;
static const int _maxPage = 6;
bool _isRefreshing = false;
bool _isLoadingMore = false;
bool _hasMoreData = true;
void initState() {
super.initState();
_loadNewsData(page: 1);
_scrollController.addListener(_onScroll);
_listenDistributedData();
}
// 模拟新闻数据请求
Future<List<NewsModel>> _fetchNewsData(int page) async {
await Future.delayed(const Duration(milliseconds: 1000));
final List<NewsModel> data = List.generate(_pageSize, (index) {
final id = "${page}_$index";
return NewsModel(
id: id,
title: "鸿蒙系统最新动态:Flutter跨平台适配方案正式开源(第$page页)",
time: "2024-05-${20 + index}",
imageUrl: "https://picsum.photos/200/120?random=$id",
);
});
return data;
}
// 加载新闻数据
Future<void> _loadNewsData({required int page}) async {
if (page == 1) _isRefreshing = true;
else _isLoadingMore = true;
setState(() {});
try {
final newData = await _fetchNewsData(page);
setState(() {
if (page == 1) {
_newsList.clear();
_currentPage = 1;
_hasMoreData = true;
} else {
_currentPage = page;
if (_currentPage >= _maxPage) _hasMoreData = false;
}
_newsList.addAll(newData);
// 同步到分布式存储
HarmonyOSFlutter.setDistributedData(
"harmony_news_list",
_newsList.map((e) => e.toJson()).toList(),
);
});
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("加载失败:$e"), backgroundColor: Colors.redAccent),
);
}
} finally {
_isRefreshing = false;
_isLoadingMore = false;
setState(() {});
}
}
// 构建新闻卡片
Widget _buildNewsItem(NewsModel news) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(color: Colors.grey.withOpacity(0.2), blurRadius: 3, offset: const Offset(0, 2))
],
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 新闻缩略图
CachedNetworkImage(
imageUrl: news.imageUrl,
width: 80,
height: 80,
fit: BoxFit.cover,
borderRadius: BorderRadius.circular(8),
placeholder: (context, url) => const SizedBox(
width: 80,
height: 80,
child: Center(child: CircularProgressIndicator(strokeWidth: 1)),
),
),
const SizedBox(width: 12),
// 新闻内容
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
news.title,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 8),
Text(
news.time,
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
),
],
),
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("鸿蒙新闻列表"),
systemOverlayStyle: HarmonyOSFlutter.getSystemOverlayStyle(),
),
body: HarmonyRefreshIndicator(
onRefresh: () => _loadNewsData(page: 1),
child: ListView.builder(
controller: _scrollController,
itemCount: _newsList.length + 1,
itemBuilder: (context, index) {
if (index < _newsList.length) {
return _buildNewsItem(_newsList[index]);
} else {
return _buildLoadMoreFooter();
}
},
itemExtent: 110,
physics: const AlwaysScrollableScrollPhysics(parent: BouncingScrollPhysics()),
),
),
);
}
// 分布式数据监听(省略重复代码)
void _listenDistributedData() { ... }
// 加载更多底部组件(省略重复代码)
Widget _buildLoadMoreFooter() { ... }
}
// 扩展NewsModel为JSON格式(用于分布式存储)
extension NewsModelExtension on NewsModel {
Map<String, dynamic> toJson() {
return {
"id": id,
"title": title,
"time": time,
"imageUrl": imageUrl,
};
}
static NewsModel fromJson(Map<String, dynamic> json) {
return NewsModel(
id: json["id"],
title: json["title"],
time: json["time"],
imageUrl: json["imageUrl"],
);
}
}
六、常见问题与避坑指南
6.1 鸿蒙模拟器运行Flutter项目卡顿
- 解决方案:给模拟器分配4GB以上内存,关闭后台多余应用;
- 代码优化:使用
itemExtent固定列表项高度,减少布局计算。
6.2 下拉刷新不触发
- 检查
ListView的physics是否设置为AlwaysScrollableScrollPhysics; - 确保
RefreshIndicator的child是可滚动组件(如ListView、SingleChildScrollView)。
6.3 上滑加载更多重复请求
- 核心原因:滚动监听触发多次,需通过
_isLoadingMore状态控制; - 解决方案:在
_loadMoreData方法中先判断!_isLoadingMore再执行请求。
6.4 鸿蒙分布式数据同步失败
- 检查
harmonyos_flutter库版本是否≥1.2.0; - 确保设备已开启鸿蒙分布式能力(模拟器需支持分布式特性)。
七、总结与扩展
本文基于Flutter 3.24+与鸿蒙API 9,实现了列表下拉刷新与上滑加载更多的核心功能,并结合鸿蒙分布式特性进行了高级优化。通过本文的学习,你可以掌握:
- Flutter列表组件与滚动监听的基础使用;
- Flutter与鸿蒙系统的适配技巧;
- 鸿蒙分布式数据同步的实战应用;
- 跨平台列表性能优化的核心方法。
扩展方向
- 集成状态管理库(Provider/BLoC),优化复杂场景下的状态管理;
- 实现列表项侧滑删除、下拉筛选等高级交互;
- 适配鸿蒙智慧屏、手表等更多设备形态;
- 结合鸿蒙的AI能力,实现新闻内容智能推荐。
更多内容·:https://openharmonycrossplatform.csdn.net/content
更多推荐




所有评论(0)