Flutter + OpenHarmony 实战:通用列表页开发(从 0 到 1 实现可复用列表组件)
本文介绍了如何使用Flutter框架在OpenHarmony平台上开发一个通用的列表页组件。主要内容包括: 开发背景:列表页是移动应用中的高频场景,本文实现了一个高性能、可复用的通用列表组件,适配鸿蒙设备交互规范。 开发环境配置:使用Flutter创建项目,并添加pull_to_refresh、fluttertoast等核心依赖库。 核心实现步骤: 定义列表数据模型ListItemModel 封装
·
Flutter + OpenHarmony 实战:通用列表页开发(从 0 到 1 实现可复用列表组件)
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
一、开发背景与场景
在移动应用开发中,列表页是高频核心场景(如商品列表、消息列表、数据展示列表等),也是 Flutter 跨平台开发的基础能力。本文以 OpenHarmony 平台为落地载体,基于 Flutter 框架实现一套「高性能、可复用、交互友好」的通用列表页组件,适配鸿蒙设备的交互规范,同时兼顾跨平台兼容性,代码可直接迁移至 Android/iOS 平台。
核心目标
掌握 Flutter 在 OpenHarmony 平台的列表渲染核心逻辑
实现列表「下拉刷新、上拉加载、空数据占位、侧滑操作」等通用功能
适配鸿蒙设备的 UI/UX 规范,保证交互体验一致性
提供可直接复用的列表组件模板
二、开发环境与前置准备
- 环境配置

- 项目初始化
# 1. 创建 Flutter 项目(支持 OpenHarmony)
flutter create flutter_ohos_list_demo
# 2. 进入项目目录
cd flutter_ohos_list_demo
# 3. 安装核心依赖
flutter pub add pull_to_refresh # 下拉刷新/上拉加载
flutter pub add fluttertoast # 交互提示
flutter pub add dio # 可选,模拟网络请求加载列表数据
- 关键依赖说明
pull_to_refresh:Flutter 生态成熟的刷新列表组件,适配鸿蒙设备滑动逻辑
fluttertoast:轻量级提示组件,符合鸿蒙系统 Toast 样式规范
dio:网络请求库,用于模拟列表数据的远程加载(也可替换为本地静态数据)
三、核心实现步骤(附完整代码)
步骤 1:定义列表数据模型
// lib/models/list_item_model.dart
/// 通用列表项数据模型(可根据业务扩展字段)
class ListItemModel {
final String id; // 列表项唯一标识
final String title; // 标题
final String subTitle; // 副标题/描述
final String time; // 时间字段(可选)
final String? icon; // 图标/图片地址(可选)
ListItemModel({
required this.id,
required this.title,
required this.subTitle,
required this.time,
this.icon,
});
// 模拟数据转换(从接口/本地数据转模型)
factory ListItemModel.fromJson(Map<String, dynamic> json) {
return ListItemModel(
id: json['id'],
title: json['title'],
subTitle: json['subTitle'],
time: json['time'],
icon: json['icon'],
);
}
}
步骤 2:封装列表数据服务(模拟加载 / 刷新)
// lib/services/list_service.dart
import '../models/list_item_model.dart';
/// 列表数据服务(模拟网络/本地数据加载)
class ListService {
// 模拟初始数据
static List<ListItemModel> _mockData = [
ListItemModel(
id: '1',
title: '列表项1',
subTitle: '这是列表项1的描述信息',
time: '2024-05-01 10:00',
),
ListItemModel(
id: '2',
title: '列表项2',
subTitle: '这是列表项2的描述信息',
time: '2024-05-01 10:30',
),
ListItemModel(
id: '3',
title: '列表项3',
subTitle: '这是列表项3的描述信息',
time: '2024-05-01 11:00',
),
];
// 加载列表数据(模拟异步请求)
static Future<List<ListItemModel>> loadListData({bool isRefresh = false}) async {
// 模拟网络延迟
await Future.delayed(const Duration(milliseconds: 800));
// 刷新时重置数据,加载更多时追加数据
if (isRefresh) {
return _mockData;
} else {
// 模拟加载更多数据
int nextId = _mockData.length + 1;
_mockData.add(ListItemModel(
id: nextId.toString(),
title: '列表项$nextId',
subTitle: '这是列表项$nextId的描述信息(加载更多)',
time: '2024-05-01 11:${30 + nextId % 60}',
));
return _mockData;
}
}
// 模拟删除列表项
static Future<bool> deleteItem(String id) async {
await Future.delayed(const Duration(milliseconds: 300));
_mockData.removeWhere((item) => item.id == id);
return true;
}
}
步骤 3:实现通用列表页(核心页面)
// lib/pages/general_list_page.dart
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import '../models/list_item_model.dart';
import '../services/list_service.dart';
class GeneralListPage extends StatefulWidget {
const GeneralListPage({super.key, required this.title});
final String title;
@override
State<GeneralListPage> createState() => _GeneralListPageState();
}
class _GeneralListPageState extends State<GeneralListPage> {
// 列表数据
List<ListItemModel> _listData = [];
// 刷新控制器
final RefreshController _refreshController =
RefreshController(initialRefresh: false);
// 加载状态
bool _isLoading = true;
@override
void initState() {
super.initState();
// 初始化加载列表数据
_loadListData(isRefresh: true);
}
/// 加载列表数据
Future<void> _loadListData({bool isRefresh = false}) async {
try {
List<ListItemModel> data = await ListService.loadListData(isRefresh: isRefresh);
setState(() {
_listData = data;
_isLoading = false;
});
// 结束刷新/加载状态
if (isRefresh) {
_refreshController.refreshCompleted();
} else {
_refreshController.loadComplete();
}
} catch (e) {
Fluttertoast.showToast(msg: '数据加载失败:$e');
setState(() => _isLoading = false);
if (isRefresh) {
_refreshController.refreshFailed();
} else {
_refreshController.loadFailed();
}
}
}
/// 下拉刷新
void _onRefresh() {
_loadListData(isRefresh: true);
}
/// 上拉加载更多
void _onLoading() {
_loadListData(isRefresh: false);
}
/// 删除列表项
Future<void> _deleteItem(String id) async {
bool success = await ListService.deleteItem(id);
if (success) {
setState(() {
_listData.removeWhere((item) => item.id == id);
});
Fluttertoast.showToast(msg: '删除成功');
} else {
Fluttertoast.showToast(msg: '删除失败');
}
}
/// 列表项构建
Widget _buildListItem(ListItemModel item) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
Text(
item.title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
const SizedBox(height: 8),
// 副标题
Text(
item.subTitle,
style: const TextStyle(
fontSize: 14,
color: Color(0xFF666666),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(height: 8),
// 时间
Align(
alignment: Alignment.centerRight,
child: Text(
item.time,
style: const TextStyle(
fontSize: 12,
color: Color(0xFF999999),
),
),
),
],
),
);
}
/// 空数据占位
Widget _buildEmptyWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.list_alt_outlined,
size: 64,
color: Color(0xFFE0E0E0),
),
SizedBox(height: 16),
Text(
'暂无数据,点击刷新重试',
style: TextStyle(
fontSize: 16,
color: Color(0xFF999999),
),
),
],
),
);
}
/// 加载中占位
Widget _buildLoadingWidget() {
return const Center(
child: CircularProgressIndicator(
color: Color(0xFF007DFF),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color(0xFF007DFF),
title: Text(
widget.title,
style: const TextStyle(color: Colors.white),
),
iconTheme: const IconThemeData(color: Colors.white),
),
body: SmartRefresher(
enablePullDown: true,
enablePullUp: true,
controller: _refreshController,
onRefresh: _onRefresh,
onLoading: _onLoading,
header: const ClassicHeader(
refreshingText: '正在刷新...',
completeText: '刷新完成',
failedText: '刷新失败',
idleText: '下拉刷新',
releaseText: '释放刷新',
textStyle: TextStyle(color: Color(0xFF666666)),
),
footer: const ClassicFooter(
loadingText: '加载更多...',
noDataText: '暂无更多数据',
failedText: '加载失败',
idleText: '上拉加载',
releaseText: '释放加载',
textStyle: TextStyle(color: Color(0xFF666666)),
),
child: _isLoading
? _buildLoadingWidget()
: _listData.isEmpty
? _buildEmptyWidget()
: ListView.builder(
itemCount: _listData.length,
itemBuilder: (context, index) {
ListItemModel item = _listData[index];
// 侧滑删除
return Dismissible(
key: Key(item.id),
direction: DismissDirection.endToStart,
background: Container(
color: const Color(0xFFFF4D4F),
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
child: const Text(
'删除',
style: TextStyle(color: Colors.white),
),
),
confirmDismiss: (direction) async {
// 弹出确认删除对话框
return await showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('确认删除'),
content: const Text('是否确定删除该列表项?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('取消'),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: const Text(
'删除',
style: TextStyle(color: Color(0xFFFF4D4F)),
),
),
],
),
);
},
onDismissed: (direction) => _deleteItem(item.id),
child: _buildListItem(item),
);
},
),
),
);
}
@override
void dispose() {
_refreshController.dispose();
super.dispose();
}
}
步骤 4:应用入口配置
// lib/main.dart
import 'package:flutter/material.dart';
import 'pages/general_list_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter OpenHarmony 列表示例',
theme: ThemeData(
primarySwatch: Colors.blue,
// 适配鸿蒙系统字体/样式规范
fontFamily: 'HarmonyOS_Sans',
),
home: const GeneralListPage(title: 'Flutter 通用列表页'),
debugShowCheckedModeBanner: false,
);
}
}
四、OpenHarmony 平台适配要点
- UI 规范适配
配色:主色调采用鸿蒙系统推荐的 #007DFF,文字色值遵循「深灰 #333、中灰 #666、浅灰 #999」层级
圆角:列表项圆角设置为 12dp,符合鸿蒙组件圆角规范
阴影:使用低透明度浅阴影,避免过度拟物
字体:引入鸿蒙系统字体 HarmonyOS_Sans,保证文字显示一致性 - 交互适配
滑动逻辑:pull_to_refresh 组件调整滑动阻尼,匹配鸿蒙设备触控反馈
Toast 提示:fluttertoast 适配鸿蒙系统 Toast 位置(底部居中,距底部 48dp)
侧滑删除:滑动阈值调整为鸿蒙标准 80dp,删除按钮样式与系统保持一致 - 编译运行(关键步骤)
打开 DevEco Studio,导入 Flutter 项目
配置 OpenHarmony 设备 / 模拟器(API 9+)
执行命令编译:flutter build ohos --release
安装应用到设备:hdc install app/build/outputs/flutter/ohos/release/app-ohos.apk
五、功能测试与验证
六、拓展与优化 - 功能拓展
列表项点击事件:添加跳转详情页逻辑,适配 Flutter 路由
多类型列表:扩展 ListItemModel,支持图文、纯文字、带图标等多类型列表项
数据缓存:结合 hive 数据库,实现列表数据本地持久化
分页加载:增加页码参数,适配后端分页接口 - 性能优化
列表项复用:使用 ListView.builder 懒加载,避免一次性渲染所有项
图片缓存:若列表项含图片,集成 cached_network_image 优化加载
状态管理:引入 Provider/Bloc 分离列表数据状态,适配复杂业务场景
内存优化:及时释放刷新控制器、取消网络请求等资源
七、总结
本文基于 Flutter 框架实现了 OpenHarmony 平台的通用列表页组件,核心亮点:
跨平台兼容:代码无需大幅修改即可运行在 Android/iOS/OpenHarmony 多端
鸿蒙适配:UI / 交互完全遵循 OpenHarmony 设计规范,保证原生体验
高复用性:数据模型、列表服务、UI 组件解耦,可快速适配各类业务列表场景
完整闭环:覆盖「加载 - 刷新 - 加载更多 - 删除 - 异常处理」全流程,满足生产环境使用
Flutter 作为跨平台框架,在 OpenHarmony 生态中具备极高的开发效率和落地价值,本文提供的列表组件模板可直接应用于各类鸿蒙应用开发,帮助开发者快速完成核心页面搭建。
运行实例

更多推荐




所有评论(0)