Flutter for OpenHarmony 第三方库实战:使用 flutter_slidable 构建待办事项滑动操作应用
在应用开发中,网络请求是非常基础也非常重要的功能。很多页面都需要从服务器获取数据,例如新闻列表、商品列表、课程列表、用户信息、待办任务等。如果只使用系统原生网络请求接口,代码会相对分散。使用可以让请求写法更接近前端常见的 Axios 风格,方便进行 GET、POST、错误处理、请求配置等操作。本篇文章选择使用 OpenHarmony 三方库来实现网络请求功能。通过该库,可以在 ArkTS 页面中发
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
项目效果
本文实现的是一个基于 Flutter for OpenHarmony 的待办事项滑动操作应用。项目中使用 Flutter 第三方库 flutter_slidable 实现列表项左右滑动操作,让用户可以通过滑动待办事项完成标记、收藏和删除等操作。
最终运行效果如下:

页面主要包含以下内容:
- 顶部标题栏;
- 待办事项统计卡片;
- 筛选按钮;
- 可左右滑动的待办事项列表;
- 滑动完成任务;
- 滑动收藏任务;
- 滑动删除任务;
- 第三方库使用说明;
- 页面整体采用 Flutter Material 风格布局。
本文重点是演示如何在 Flutter for OpenHarmony 项目中使用 Flutter 第三方库 flutter_slidable。项目代码写在 lib/main.dart 中,依赖配置写在 pubspec.yaml 中,符合 Flutter for OpenHarmony 第三方库实践方向。
前言
在移动应用开发中,列表操作非常常见。例如待办事项、消息列表、邮件列表、收藏列表、购物车列表等,都可能需要对某一项进行删除、完成、收藏、归档等操作。
如果只在每一项后面放一排按钮,页面会显得很拥挤。尤其是在手机端,屏幕空间本来就不富裕,再硬塞按钮,用户看着累,开发者写着也累,大家一起为 UI 设计的失败买单。
滑动操作是一种比较自然的交互方式。用户向左或向右滑动列表项,就可以看到对应操作按钮。
本文选择使用 Flutter 第三方库 flutter_slidable 来实现滑动列表项。它可以快速创建可滑动的列表操作区域,支持开始方向和结束方向的操作面板,也可以配合不同动画效果使用。
本项目以“待办事项滑动操作应用”为例,使用 flutter_slidable 实现任务完成、收藏和删除功能,并结合 Flutter 状态更新展示任务统计结果。
一、项目目标
本次实践主要实现以下目标:
- 创建 Flutter for OpenHarmony 项目;
- 在
pubspec.yaml中添加第三方库flutter_slidable; - 使用
flutter pub get获取依赖; - 在
lib/main.dart中引入flutter_slidable; - 使用
Slidable构建可滑动列表项; - 使用
ActionPane设置滑动操作区域; - 使用
SlidableAction实现完成、收藏和删除按钮; - 使用
setState()更新待办事项状态; - 使用 Flutter Material 组件构建完整页面;
- 将应用运行到 OpenHarmony 设备或模拟器中。
二、技术栈
| 类型 | 内容 |
|---|---|
| 开发方向 | Flutter for OpenHarmony |
| 开发语言 | Dart |
| UI 框架 | Flutter |
| 第三方库 | flutter_slidable |
| 功能场景 | 滑动列表 / 待办事项 / 列表操作 |
| 核心组件 | Slidable / ActionPane / SlidableAction |
| 项目入口 | lib/main.dart |
| 依赖配置 | pubspec.yaml |
| 运行平台 | OpenHarmony 设备或模拟器 |
三、为什么选择 flutter_slidable
在实际应用中,滑动列表项可以用于很多场景,例如:
- 待办事项完成;
- 消息已读和删除;
- 邮件归档;
- 收藏内容;
- 清理购物车;
- 删除浏览记录;
- 文件列表管理;
- 课程任务管理。
如果自己使用 Flutter 原生手写滑动操作,需要处理手势监听、滑动距离、按钮显示、动画回弹、列表状态更新等逻辑。能写,但没有必要把简单列表操作写成一场人类耐力测试。
flutter_slidable 已经封装好了列表滑动操作组件,可以让开发者直接把重点放在业务功能上。
在本项目中,flutter_slidable 主要完成以下工作:
- 为待办事项列表项添加滑动交互;
- 向右滑动显示“完成”操作;
- 向左滑动显示“收藏”和“删除”操作;
- 配合 Flutter 状态更新刷新页面;
- 提升列表操作的交互体验。
四、创建 Flutter for OpenHarmony 项目
在已经配置好 Flutter for OpenHarmony 开发环境的前提下,可以创建一个 Flutter 项目。
示例项目名称:
flutter create slidable_todo_demo
进入项目目录:
cd slidable_todo_demo
项目创建完成后,主要关注两个文件:
slidable_todo_demo
├── pubspec.yaml
└── lib
└── main.dart
其中:
| 文件 | 作用 |
|---|---|
| pubspec.yaml | 配置 Flutter 项目依赖 |
| lib/main.dart | 编写 Flutter 页面和业务逻辑 |
五、添加 flutter_slidable 第三方库
打开项目根目录下的 pubspec.yaml 文件,在 dependencies 中添加 flutter_slidable。
示例配置如下:
dependencies:
flutter:
sdk: flutter
flutter_slidable: ^4.0.3
完整结构大致如下:
name: slidable_todo_demo
description: A Flutter for OpenHarmony slidable todo demo.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=3.4.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
flutter_slidable: ^4.0.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
添加完成后,在终端执行:
flutter pub get
执行成功后,就可以在 Dart 代码中使用 flutter_slidable 了。
六、项目结构
本项目主要修改 lib/main.dart 文件:
lib
└── main.dart
本项目不需要编写 OpenHarmony 原生 ArkTS 页面,也不需要修改 Index.ets。
因为这是 Flutter for OpenHarmony 项目,页面主体应该是 Flutter 代码。审核重点会看:
- 是否使用
pubspec.yaml添加 Flutter 第三方库; - 是否在 Dart 文件中
import package; - 是否在
lib/main.dart中实际调用第三方库; - 是否属于 Flutter for OpenHarmony 项目。
看到 pubspec.yaml、lib/main.dart、import 'package:flutter_slidable/flutter_slidable.dart';,这才是正确方向。别拿 ArkTS 硬凑 Flutter,代码不是换个标题就能转世投胎的。
七、核心实现思路
本项目的核心流程如下:
- 在
pubspec.yaml中添加flutter_slidable; - 在
main.dart中引入第三方库; - 定义待办事项数据模型;
- 使用
ListView展示任务列表; - 使用
Slidable包裹每一个任务项; - 使用
startActionPane设置右滑操作; - 使用
endActionPane设置左滑操作; - 使用
SlidableAction实现完成、收藏和删除; - 操作后使用
setState()更新页面。
第三方库引入代码如下:
import 'package:flutter_slidable/flutter_slidable.dart';
滑动列表项核心代码如下:
Slidable(
startActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
onPressed: (context) {},
icon: Icons.done,
label: '完成',
),
],
),
endActionPane: ActionPane(
motion: const DrawerMotion(),
children: [
SlidableAction(
onPressed: (context) {},
icon: Icons.star,
label: '收藏',
),
SlidableAction(
onPressed: (context) {},
icon: Icons.delete,
label: '删除',
),
],
),
child: ListTile(
title: Text('待办事项'),
),
)
这段代码是本文的重点,说明项目确实使用了 Flutter 第三方库实现滑动操作。
八、main.dart 完整代码
打开文件:
lib/main.dart
将其中内容替换为下面代码:
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
void main() {
runApp(const SlidableTodoApp());
}
class SlidableTodoApp extends StatelessWidget {
const SlidableTodoApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Slidable Todo Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.teal,
brightness: Brightness.light,
),
useMaterial3: true,
),
home: const SlidableTodoHomePage(),
);
}
}
class TodoItem {
TodoItem({
required this.title,
required this.category,
required this.description,
required this.icon,
required this.color,
this.isDone = false,
this.isStarred = false,
});
final String title;
final String category;
final String description;
final IconData icon;
final Color color;
bool isDone;
bool isStarred;
}
class SlidableTodoHomePage extends StatefulWidget {
const SlidableTodoHomePage({super.key});
State<SlidableTodoHomePage> createState() => _SlidableTodoHomePageState();
}
class _SlidableTodoHomePageState extends State<SlidableTodoHomePage> {
final List<String> _filters = const [
'全部',
'未完成',
'已完成',
'收藏',
];
final List<TodoItem> _todos = [
TodoItem(
title: '完成 Flutter 第三方库文章',
category: '项目',
description: '整理 pubspec.yaml、main.dart、运行截图和文章说明。',
icon: Icons.article,
color: Colors.teal,
),
TodoItem(
title: '复习 C 语言指针',
category: '学习',
description: '重点看指针变量、地址、数组和函数参数传递。',
icon: Icons.code,
color: Colors.blue,
),
TodoItem(
title: '整理桌面文件',
category: '生活',
description: '清理截图、压缩包和临时文件,不然电脑迟早变成电子垃圾场。',
icon: Icons.folder,
color: Colors.orange,
isDone: true,
),
TodoItem(
title: '准备项目运行截图',
category: '项目',
description: '运行 Flutter for OpenHarmony 项目,截图首页效果图。',
icon: Icons.screenshot_monitor,
color: Colors.deepPurple,
),
TodoItem(
title: '英语口语练习',
category: '学习',
description: '朗读一段英文材料,注意语音语调和停顿。',
icon: Icons.language,
color: Colors.green,
isStarred: true,
),
TodoItem(
title: '检查依赖是否写对',
category: '项目',
description: '确认 pubspec.yaml 中第三方库名称和缩进都正确。',
icon: Icons.fact_check,
color: Colors.redAccent,
),
];
String _selectedFilter = '全部';
List<TodoItem> get _filteredTodos {
if (_selectedFilter == '未完成') {
return _todos.where((todo) => !todo.isDone).toList();
}
if (_selectedFilter == '已完成') {
return _todos.where((todo) => todo.isDone).toList();
}
if (_selectedFilter == '收藏') {
return _todos.where((todo) => todo.isStarred).toList();
}
return _todos;
}
int get _doneCount {
return _todos.where((todo) => todo.isDone).length;
}
int get _starredCount {
return _todos.where((todo) => todo.isStarred).length;
}
int get _unfinishedCount {
return _todos.where((todo) => !todo.isDone).length;
}
void _selectFilter(String filter) {
setState(() {
_selectedFilter = filter;
});
}
void _toggleDone(TodoItem todo) {
setState(() {
todo.isDone = !todo.isDone;
});
_showMessage(todo.isDone ? '已完成:${todo.title}' : '已取消完成:${todo.title}');
}
void _toggleStar(TodoItem todo) {
setState(() {
todo.isStarred = !todo.isStarred;
});
_showMessage(todo.isStarred ? '已收藏:${todo.title}' : '已取消收藏:${todo.title}');
}
void _deleteTodo(TodoItem todo) {
setState(() {
_todos.remove(todo);
});
_showMessage('已删除:${todo.title}');
}
void _resetTodos() {
setState(() {
for (final TodoItem todo in _todos) {
todo.isDone = false;
todo.isStarred = false;
}
_selectedFilter = '全部';
});
_showMessage('已重置任务状态');
}
void _showMessage(String message) {
if (!mounted) {
return;
}
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(milliseconds: 1200),
),
);
}
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final List<TodoItem> todos = _filteredTodos;
return Scaffold(
appBar: AppBar(
title: const Text('待办滑动操作'),
centerTitle: true,
),
body: SafeArea(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildOverviewCard(theme),
const SizedBox(height: 16),
_buildFilterCard(theme),
const SizedBox(height: 16),
_buildTodoListCard(theme, todos),
const SizedBox(height: 16),
_buildActionCard(theme),
const SizedBox(height: 16),
_buildLibraryCard(theme),
],
),
),
);
}
Widget _buildOverviewCard(ThemeData theme) {
return Card(
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(22),
),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
children: [
Container(
width: 76,
height: 76,
decoration: BoxDecoration(
color: theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(24),
),
child: Icon(
Icons.swipe,
size: 42,
color: theme.colorScheme.onPrimaryContainer,
),
),
const SizedBox(height: 18),
Text(
'Flutter for OpenHarmony',
style: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'使用 flutter_slidable 构建可左右滑动操作的待办事项列表',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
height: 1.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
Row(
children: [
_buildStatItem(
theme,
title: '总任务',
value: '${_todos.length}',
icon: Icons.list_alt,
),
_buildStatItem(
theme,
title: '未完成',
value: '$_unfinishedCount',
icon: Icons.pending_actions,
),
_buildStatItem(
theme,
title: '已完成',
value: '$_doneCount',
icon: Icons.check_circle,
),
_buildStatItem(
theme,
title: '收藏',
value: '$_starredCount',
icon: Icons.star,
),
],
),
],
),
),
);
}
Widget _buildStatItem(
ThemeData theme, {
required String title,
required String value,
required IconData icon,
}) {
return Expanded(
child: Column(
children: [
Icon(
icon,
color: theme.colorScheme.primary,
),
const SizedBox(height: 6),
Text(
value,
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
),
const SizedBox(height: 2),
Text(
title,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
);
}
Widget _buildFilterCard(ThemeData theme) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Wrap(
spacing: 10,
runSpacing: 10,
children: _filters.map((filter) {
final bool selected = filter == _selectedFilter;
return ChoiceChip(
label: Text(filter),
selected: selected,
onSelected: (_) {
_selectFilter(filter);
},
selectedColor: theme.colorScheme.primaryContainer,
labelStyle: TextStyle(
color: selected
? theme.colorScheme.onPrimaryContainer
: theme.colorScheme.onSurface,
fontWeight: selected ? FontWeight.bold : FontWeight.normal,
),
);
}).toList(),
),
),
);
}
Widget _buildTodoListCard(ThemeData theme, List<TodoItem> todos) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 20, 16, 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 4, right: 4, bottom: 14),
child: Row(
children: [
Expanded(
child: Text(
'待办事项列表',
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
Text(
'${todos.length} 条',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
if (todos.isEmpty)
Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(
Icons.inbox,
color: theme.colorScheme.onSurfaceVariant,
),
const SizedBox(width: 12),
Expanded(
child: Text(
'当前筛选条件下没有任务。',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
],
),
)
else
...todos.map((todo) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: _buildSlidableTodoItem(theme, todo),
);
}),
],
),
),
);
}
Widget _buildSlidableTodoItem(ThemeData theme, TodoItem todo) {
return ClipRRect(
borderRadius: BorderRadius.circular(18),
child: Slidable(
key: ValueKey(todo.title),
startActionPane: ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.28,
children: [
SlidableAction(
onPressed: (_) {
_toggleDone(todo);
},
backgroundColor: todo.isDone ? Colors.orange : Colors.green,
foregroundColor: Colors.white,
icon: todo.isDone ? Icons.undo : Icons.done,
label: todo.isDone ? '取消' : '完成',
),
],
),
endActionPane: ActionPane(
motion: const DrawerMotion(),
extentRatio: 0.56,
children: [
SlidableAction(
onPressed: (_) {
_toggleStar(todo);
},
backgroundColor: Colors.amber,
foregroundColor: Colors.white,
icon: todo.isStarred ? Icons.star : Icons.star_border,
label: todo.isStarred ? '取消' : '收藏',
),
SlidableAction(
onPressed: (_) {
_deleteTodo(todo);
},
backgroundColor: Colors.redAccent,
foregroundColor: Colors.white,
icon: Icons.delete,
label: '删除',
),
],
),
child: Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: todo.isDone
? theme.colorScheme.surfaceContainerHighest
: todo.color.withOpacity(0.12),
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: todo.isStarred
? Colors.amber
: todo.color.withOpacity(0.20),
width: todo.isStarred ? 1.4 : 1,
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: todo.color.withOpacity(0.16),
borderRadius: BorderRadius.circular(16),
),
child: Icon(
todo.icon,
color: todo.color,
),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
todo.title,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
decoration: todo.isDone
? TextDecoration.lineThrough
: TextDecoration.none,
color: todo.isDone
? theme.colorScheme.onSurfaceVariant
: theme.colorScheme.onSurface,
),
),
const SizedBox(height: 6),
Text(
todo.category,
style: theme.textTheme.bodySmall?.copyWith(
color: todo.color,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
todo.description,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
height: 1.5,
),
),
const SizedBox(height: 10),
Row(
children: [
Icon(
todo.isDone
? Icons.check_circle
: Icons.radio_button_unchecked,
size: 18,
color: todo.isDone
? Colors.green
: theme.colorScheme.onSurfaceVariant,
),
const SizedBox(width: 6),
Text(
todo.isDone ? '已完成' : '未完成',
style: theme.textTheme.bodySmall?.copyWith(
color: todo.isDone
? Colors.green
: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(width: 14),
if (todo.isStarred) ...[
const Icon(
Icons.star,
size: 18,
color: Colors.amber,
),
const SizedBox(width: 6),
Text(
'已收藏',
style: theme.textTheme.bodySmall?.copyWith(
color: Colors.amber,
),
),
],
],
),
],
),
),
],
),
),
),
);
}
Widget _buildActionCard(ThemeData theme) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: OutlinedButton.icon(
onPressed: _resetTodos,
icon: const Icon(Icons.refresh),
label: const Text('重置任务状态'),
),
),
);
}
Widget _buildLibraryCard(ThemeData theme) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'第三方库说明',
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
_buildInfoRow(
theme,
title: '库名称',
value: 'flutter_slidable',
),
_buildInfoRow(
theme,
title: '配置文件',
value: 'pubspec.yaml',
),
_buildInfoRow(
theme,
title: '导入方式',
value: "import 'package:flutter_slidable/flutter_slidable.dart';",
),
_buildInfoRow(
theme,
title: '核心组件',
value: 'Slidable / ActionPane / SlidableAction',
),
_buildInfoRow(
theme,
title: '应用场景',
value: '待办事项、消息列表、邮件列表、购物车、文件管理',
),
],
),
),
);
}
Widget _buildInfoRow(
ThemeData theme, {
required String title,
required String value,
}) {
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 82,
child: Text(
title,
style: theme.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
Expanded(
child: Text(
value,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
],
),
);
}
}
九、代码实现说明
1. 引入 flutter_slidable 第三方库
代码开头引入第三方库:
import 'package:flutter_slidable/flutter_slidable.dart';
这说明项目确实使用了 Flutter 第三方库,而不是 OpenHarmony 原生库。
本项目中主要使用以下组件:
Slidable
ActionPane
SlidableAction
ScrollMotion
DrawerMotion
其中:
| 组件 | 作用 |
|---|---|
| Slidable | 包裹普通列表项,让它支持滑动操作 |
| ActionPane | 定义滑动后显示的操作区域 |
| SlidableAction | 定义具体操作按钮 |
| ScrollMotion | 滑动操作区域跟随滚动的动画效果 |
| DrawerMotion | 类似抽屉展开的动画效果 |
2. 定义待办事项数据模型
项目中定义了待办事项模型:
class TodoItem {
TodoItem({
required this.title,
required this.category,
required this.description,
required this.icon,
required this.color,
this.isDone = false,
this.isStarred = false,
});
final String title;
final String category;
final String description;
final IconData icon;
final Color color;
bool isDone;
bool isStarred;
}
字段说明如下:
| 字段 | 作用 |
|---|---|
| title | 任务标题 |
| category | 任务分类 |
| description | 任务说明 |
| icon | 任务图标 |
| color | 任务主题色 |
| isDone | 是否完成 |
| isStarred | 是否收藏 |
其中 isDone 和 isStarred 是可变化状态,用于响应用户滑动操作。
3. 使用 Slidable 包裹列表项
滑动操作的核心是:
Slidable(
child: Container(...),
)
child 就是平时显示出来的列表项内容。
用户没有滑动时,只能看到普通任务卡片。
用户向左或向右滑动时,就会显示对应操作按钮。
4. 使用 startActionPane 设置右滑操作
本项目中,右滑显示“完成”操作:
startActionPane: ActionPane(
motion: const ScrollMotion(),
children: [
SlidableAction(
onPressed: (_) {
_toggleDone(todo);
},
icon: Icons.done,
label: '完成',
),
],
)
startActionPane 表示开始方向的操作区域。在常见从左到右的语言环境中,它通常对应右滑后显示的左侧操作。
这里用于完成或取消完成任务。
5. 使用 endActionPane 设置左滑操作
本项目中,左滑显示“收藏”和“删除”操作:
endActionPane: ActionPane(
motion: const DrawerMotion(),
children: [
SlidableAction(
onPressed: (_) {
_toggleStar(todo);
},
icon: Icons.star,
label: '收藏',
),
SlidableAction(
onPressed: (_) {
_deleteTodo(todo);
},
icon: Icons.delete,
label: '删除',
),
],
)
endActionPane 表示结束方向的操作区域。
这里用于收藏任务和删除任务。
这样一个任务项就可以同时支持多个操作,不需要在页面上摆一堆按钮。终于让列表看起来不像小广告栏了。
6. 使用 SlidableAction 实现操作按钮
每一个滑动按钮都是一个 SlidableAction:
SlidableAction(
onPressed: (_) {
_deleteTodo(todo);
},
backgroundColor: Colors.redAccent,
foregroundColor: Colors.white,
icon: Icons.delete,
label: '删除',
)
参数说明如下:
| 参数 | 作用 |
|---|---|
| onPressed | 点击按钮后的回调 |
| backgroundColor | 按钮背景色 |
| foregroundColor | 按钮文字和图标颜色 |
| icon | 按钮图标 |
| label | 按钮文字 |
7. 完成任务状态切换
完成任务方法如下:
void _toggleDone(TodoItem todo) {
setState(() {
todo.isDone = !todo.isDone;
});
}
如果任务未完成,滑动后会改为已完成。
如果任务已完成,再次滑动可以取消完成。
页面会根据 isDone 改变文字样式,例如添加删除线:
decoration: todo.isDone
? TextDecoration.lineThrough
: TextDecoration.none
这样用户可以直观看到任务是否完成。
8. 收藏任务状态切换
收藏任务方法如下:
void _toggleStar(TodoItem todo) {
setState(() {
todo.isStarred = !todo.isStarred;
});
}
收藏后,任务卡片边框会变成黄色,并显示“已收藏”。
这类状态反馈很重要。不然用户点了半天不知道有没有成功,最后开始怀疑软件、手机、人生和开发者。
9. 删除任务
删除任务方法如下:
void _deleteTodo(TodoItem todo) {
setState(() {
_todos.remove(todo);
});
}
删除后,该任务会从列表中移除,统计数据也会自动更新。
10. 任务筛选功能
页面提供了四种筛选条件:
final List<String> _filters = const [
'全部',
'未完成',
'已完成',
'收藏',
];
筛选逻辑如下:
List<TodoItem> get _filteredTodos {
if (_selectedFilter == '未完成') {
return _todos.where((todo) => !todo.isDone).toList();
}
if (_selectedFilter == '已完成') {
return _todos.where((todo) => todo.isDone).toList();
}
if (_selectedFilter == '收藏') {
return _todos.where((todo) => todo.isStarred).toList();
}
return _todos;
}
这样用户可以快速查看不同状态的任务。
十、运行项目
完成代码后,在终端执行:
flutter pub get
然后连接 OpenHarmony 设备或启动 OpenHarmony 模拟器。
查看设备:
flutter devices
运行项目:
flutter run
如果环境配置正确,应用会运行到 OpenHarmony 设备或模拟器中。
运行成功后,页面会显示“待办滑动操作”。用户可以右滑任务完成事项,也可以左滑任务进行收藏或删除。
十一、开发中遇到的问题
1. flutter_slidable 依赖没有生效
如果代码中出现找不到 flutter_slidable 的问题,可以检查 pubspec.yaml 中是否添加了:
flutter_slidable: ^4.0.3
然后重新执行:
flutter pub get
如果还是不行,可以重启编辑器。编辑器有时候像没睡醒,依赖装好了它还装作没看见,经典软件行为。
2. import 导入报错
如果下面代码报错:
import 'package:flutter_slidable/flutter_slidable.dart';
通常有几种原因:
pubspec.yaml中没有添加依赖;- 没有执行
flutter pub get; - YAML 缩进错误;
- 包名写错;
- 编辑器没有刷新依赖。
其中 YAML 缩进最容易出问题。依赖必须写在 dependencies 下面,并且缩进要正确。一个空格就能让项目不认依赖,编程世界真是贴心到令人窒息。
3. 滑动后没有操作按钮
如果滑动列表项后没有按钮,可以检查:
- 是否使用了
Slidable; - 是否设置了
startActionPane或endActionPane; ActionPane中是否添加了SlidableAction;- 列表项外层是否遮挡了滑动区域;
- 设备或模拟器是否支持正常拖动操作。
最基础结构应该类似:
Slidable(
startActionPane: ActionPane(...),
endActionPane: ActionPane(...),
child: ListTile(...),
)
4. 点击操作后页面没有刷新
如果点击“完成”“收藏”“删除”后页面没有变化,可以检查是否调用了:
setState(() {
...
});
Flutter 中状态变化后必须调用 setState(),否则页面不会重新构建。它不是读心框架,不会根据你的眼神更新 UI。
5. 删除任务后列表显示异常
如果删除任务后列表异常,可以检查 Slidable 是否设置了唯一 key:
key: ValueKey(todo.title),
列表中每个可滑动项最好有稳定的 key,这样 Flutter 能正确识别每一项的状态。
6. 滑动区域太窄或太宽
可以通过 extentRatio 调整滑动操作区域宽度:
extentRatio: 0.56
数值越大,滑出的按钮区域越宽。
如果按钮太多但区域太窄,文字可能显示不完整。UI 不会因为你塞得多就自动变聪明,它只会挤成一团。
7. 运行不到 OpenHarmony 设备
如果项目无法运行到 OpenHarmony 设备或模拟器,可以检查:
- Flutter for OpenHarmony 环境是否配置完成;
- 设备是否连接成功;
flutter devices是否能识别设备;- 是否执行了
flutter pub get; - 是否选择了正确的运行设备;
- 项目是否为 Flutter 项目,而不是原生鸿蒙项目。
如果 flutter devices 都识别不到设备,那应该先处理环境问题,而不是盯着滑动代码怀疑人生。列表很无辜,至少这次大概率是。
十二、本文和原生鸿蒙项目的区别
本文是 Flutter for OpenHarmony 第三方库实践,不是 OpenHarmony 原生 ArkTS 项目。
主要区别如下:
| 对比项 | 本文写法 | 原生鸿蒙写法 |
|---|---|---|
| UI 技术 | Flutter | ArkUI |
| 主要语言 | Dart | ArkTS |
| 页面入口 | lib/main.dart | Index.ets |
| 依赖配置 | pubspec.yaml | oh-package.json5 |
| 依赖安装 | flutter pub get | ohpm install |
| 第三方库 | flutter_slidable | OpenHarmony 原生库 |
| 页面组件 | MaterialApp / Scaffold / Slidable | @Entry / @Component |
因此本文符合 Flutter for OpenHarmony 第三方库实践方向。
十三、总结
本篇完成了一个基于 flutter_slidable 的 Flutter for OpenHarmony 待办事项滑动操作应用。项目通过 Flutter 第三方库实现列表项左右滑动操作,并结合待办事项状态更新实现完成、收藏和删除功能。
通过本次实践,我主要完成了以下内容:
- 创建 Flutter for OpenHarmony 项目;
- 在
pubspec.yaml中添加flutter_slidable依赖; - 使用
flutter pub get获取第三方库; - 在
lib/main.dart中引入flutter_slidable; - 使用
Slidable构建可滑动列表项; - 使用
ActionPane设置滑动操作区域; - 使用
SlidableAction实现完成、收藏和删除按钮; - 使用
ChoiceChip实现任务筛选; - 使用
setState()更新任务状态; - 使用 Flutter Material 组件构建完整页面;
- 将项目运行到 OpenHarmony 设备或模拟器中。
这个项目虽然只是一个基础待办事项应用,但完整展示了 Flutter for OpenHarmony 项目中第三方库的使用流程。
后续可以在这个基础上继续扩展,例如:
- 添加新建任务功能;
- 添加编辑任务功能;
- 添加任务优先级;
- 添加任务截止时间;
- 添加本地数据保存;
- 添加任务提醒;
- 添加分类管理;
- 添加搜索功能;
- 添加暗色主题;
- 添加云端同步。
整体来看,flutter_slidable 可以帮助 Flutter 开发者快速实现列表项滑动操作。通过这个项目,可以理解 Flutter for OpenHarmony 中第三方库依赖配置、滑动列表组件使用和页面状态更新之间的基本关系。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)