【开源鸿蒙跨平台开发先锋训练营】Flutter框架下鸿蒙应用完善和异常修复
摘要:本文记录了开源鸿蒙跨平台社区项目中的四个页面功能完善点,包括消息页添加会话、设置页三项功能、发布页发布按钮和退出登录功能。重点解决了添加会话弹窗关闭时的Flutter断言错误,通过延迟dispose TextEditingController至下一帧,避免了_dependents.isEmpty断言失败。其他功能完善方向包括持久化设置、实际API对接和登录态管理。
继续完善项目,把未完善的功能和页面继续开发完成。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
文章目录
完善页面
1、消息页「添加会话」
问题描述
- 页面:
lib/pages/messages_page.dart— 消息页 AppBar 右侧「添加会话」图标按钮。 - 原行为:点击后仅弹出 SnackBar:「添加会话功能开发中」。
关键代码(修复前)
// lib/pages/messages_page.dart(约 84-91 行,修复前)
actions: [
IconButton(
icon: appUserPlusIcon(size: 24),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('添加会话功能开发中')),
);
},
),
],
- 点击「添加会话」改为打开新建会话弹窗:输入会话名称 → 确认后向列表头部插入一条新会话,并给出「已添加会话:xxx」的 SnackBar,不再出现「待开发中」。
- 新增方法:
_showAddConversationDialog(BuildContext context):弹出AlertDialog,内含TextField与取消/添加按钮。_submitAddConversation(BuildContext dialogContext, TextEditingController controller):校验非空、生成id、setState插入ConversationModel、关闭弹窗并显示 SnackBar。
关键代码位置:
- 按钮绑定:
lib/pages/messages_page.dart中 AppBaractions的IconButton的onPressed: () => _showAddConversationDialog(context)。 - 弹窗与提交逻辑:同文件内
_showAddConversationDialog与_submitAddConversation方法(在_openChat之后)。
2. 我的 — 设置页内三项
- 页面:
lib/pages/profile_page.dart→ 由「设置」进入的二级设置页。 - 入口:设置页
ListView中的三个ListTile:「通知设置」「隐私与安全」「清除缓存」。 - 当前行为:点击后仅通过
_showSnackOn(settingsContext, msg)显示与项同名的 SnackBar(如「通知设置」「隐私与安全」「已清除缓存」),无实际持久化或系统设置/清理逻辑。
关键代码:
// lib/pages/profile_page.dart 约 84-99 行
ListTile(
title: const Text('通知设置'),
trailing: appChevronRightIcon(size: 22, interactive: false),
onTap: () => _showSnackOn(settingsContext, '通知设置'),
),
ListTile(
title: const Text('隐私与安全'),
...
onTap: () => _showSnackOn(settingsContext, '隐私与安全'),
),
ListTile(
title: const Text('清除缓存'),
...
onTap: () => _showSnackOn(settingsContext, '已清除缓存'),
),
// lib/pages/profile_page.dart 约 111-113 行
void _showSnackOn(BuildContext scaffoldContext, String msg) {
ScaffoldMessenger.of(scaffoldContext).showSnackBar(SnackBar(content: Text(msg)));
}
建议完善方向:通知设置 → 持久化开关 + 系统通知权限;隐私与安全 → 隐私政策/权限说明页;清除缓存 → 调用图片/临时文件清理并刷新统计。
3. 发布页 — 发布按钮
- 页面:
lib/pages/upload_page.dart。 - 当前行为:表单校验通过后延迟 1 秒,然后显示 SnackBar「发布成功(模拟)」并清空标题/描述,无真实上传 API。
关键代码:
// lib/pages/upload_page.dart 约 16-24 行
Future<void> _mockUpload() async {
if (!_formKey.currentState!.validate()) return;
setState(() => _uploading = true);
await Future.delayed(const Duration(seconds: 1));
setState(() => _uploading = false);
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('发布成功(模拟)')));
_title.clear();
_desc.clear();
}
这个项目没有后端,后端接口就暂时不做,后续可以对接实际上传接口,成功后提示改为「发布成功」并可选跳转首页/个人页。
4. 我的 — 退出登录
- 页面:
lib/pages/profile_page.dart。 - 当前行为:弹窗确认后仅执行
_showSnack('已退出登录'),未清除登录态或跳转登录页。
关键代码:
// lib/pages/profile_page.dart 约 155-176 行
void _logout() {
showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: const Text('退出登录'),
content: const Text('确定要退出当前账号吗?'),
actions: [
...
FilledButton(
onPressed: () {
Navigator.of(context).pop();
_showSnack('已退出登录');
},
child: const Text('退出'),
),
],
),
);
}
建议完善方向:清除本地 token/用户信息、调用登出 API(若有)、并跳转至登录页或重置为未登录状态。
以上修改后运行发现了下面的报错,报错截图如下
二、报错处理:添加会话弹窗关闭时 _dependents.isEmpty 断言失败
- 界面:整屏红底,Flutter 调试态断言失败 overlay。
- 来源:
package:flutter/src/widgets/framework.dart,约 line 6079 pos 14。 - 断言内容:
'_dependents.isEmpty': is not true. - 含义:框架期望某对象在此时没有依赖者(
_dependents为空),但实际仍有依赖。常见于控件/元素正在被移除或 dispose 时,仍有子节点或监听方在引用它。
复现
在消息页点击「添加会话」→ 在弹窗中输入名称并点击「添加」(或取消关闭弹窗)后,弹窗关闭时触发上述断言。
原因分析
新建会话弹窗中使用了 TextEditingController,并在 showDialog(...).then((_) => controller.dispose()) 里在 dialog 的 Future 完成时立即 dispose 了 controller。
showDialog的 Future 在Navigator.pop()被调用时即完成。- 此时弹窗的 widget 树可能仍在当前帧内拆解,
TextField仍持有对 controller 的依赖。 - 立即
controller.dispose()会在「仍有 dependents」的情况下销毁 controller,触发框架内部_dependents.isEmpty的断言。

处理步骤与关键代码
- 问题定位:确认断言来自
framework.dart的_dependents.isEmpty,并结合最近改动锁定为「添加会话」弹窗关闭时的TextEditingController.dispose()时机。 - 修复思路:不在 dialog 关闭的同一时刻 dispose controller,而是延后到下一帧再 dispose,确保弹窗树已完全移除、TextField 不再依赖该 controller。
- 代码修改(
lib/pages/messages_page.dart,_showAddConversationDialog方法末尾):
修改前:
).then((_) => controller.dispose());
修改后:
).then((_) {
// 延后到下一帧再 dispose,避免弹窗树尚未完全移除时 TextField 仍依赖 controller 导致 _dependents.isEmpty 断言失败
WidgetsBinding.instance.addPostFrameCallback((_) {
controller.dispose();
});
});
- 验证:再次在消息页打开「添加会话」→ 输入并点击「添加」或点击「取消」关闭弹窗,确认红屏断言不再出现,且新会话列表与 SnackBar 行为正常。
小结
| 项目 | 说明 |
|---|---|
| 报错类型 | Flutter 断言失败 _dependents.isEmpty(framework.dart) |
| 触发位置 | 消息页「添加会话」弹窗关闭时 |
| 根本原因 | 在 dialog 的 Future 完成时立即 dispose TextEditingController,此时 TextField 仍为依赖方 |
| 修复方式 | 在 .then 中使用 WidgetsBinding.instance.addPostFrameCallback 将 controller.dispose() 推迟到下一帧执行 |
| 相关文件 | lib/pages/messages_page.dart — _showAddConversationDialog |
结束语
感谢阅读本帖,如对贴中内容有意见和建议的,欢迎与我联系交流,也欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
更多推荐




所有评论(0)