Flutter 与 OpenHarmony 应用功能深化:构建独立任务表单页面与完善编辑体验

构建独立任务表单页面与完善编辑体验

    • 引言:从"能用"到"好用",再到"专业"
    • 一、为什么需要独立表单页面?
    • [二、架构扩展:支持编辑模式的 ViewModel 设计](#二、架构扩展:支持编辑模式的 ViewModel 设计)
      • [1. 扩展 `Todo` 模型(隐含变更)](#1. 扩展 Todo 模型(隐含变更))
      • [2. ViewModel 新增 `updateTodo` 方法](#2. ViewModel 新增 updateTodo 方法)
    • [三、表单页面实现:`TodoFormScreen` 的核心逻辑](#三、表单页面实现:TodoFormScreen 的核心逻辑)
      • [1. 页面结构与状态管理](#1. 页面结构与状态管理)
      • [2. 表单验证与实时反馈](#2. 表单验证与实时反馈)
      • [3. 日期与时间选择器集成](#3. 日期与时间选择器集成)
      • [4. 优先级选择器(Chip 样式)](#4. 优先级选择器(Chip 样式))
    • 四、导航与状态同步:确保体验连贯
      • [1. 从列表页跳转到表单页](#1. 从列表页跳转到表单页)
      • [2. 表单页返回结果](#2. 表单页返回结果)
    • [五、OpenHarmony 适配考量](#五、OpenHarmony 适配考量)
    • 六、工程实践与测试策略
      • [1. 遵循开发指南](#1. 遵循开发指南)
      • [2. 可测试性设计](#2. 可测试性设计)
      • [3. 代码质量](#3. 代码质量)
    • 结语:功能深化是架构韧性的试金石

引言:从"能用"到"好用",再到"专业"

在完成基础交互优化后,待办事项应用已具备良好的视觉表现和流畅的操作反馈。然而,一个真正实用的任务管理工具,不能仅依赖快速输入框------用户需要更精细的控制能力:设置截止时间、指定优先级、编写详细描述。这正是本次迭代的核心目标。

通过引入独立的任务表单页面,我们不仅提升了功能完整性,更进一步验证了 MVVM 架构在复杂场景下的扩展能力。本文将深入剖析表单页面的设计与实现,重点探讨:

  • 如何在保持架构清晰的前提下支持添加与编辑双模式
  • 如何实现跨平台兼容的日期/时间选择器集成
  • 如何通过表单验证保障数据质量
  • 如何设计导航与状态同步机制,确保用户体验连贯

所有实践均以 OpenHarmony 平台兼容性为前提,为未来深度集成奠定坚实基础。


一、为什么需要独立表单页面?

虽然快速输入(一行 TextField + Add 按钮)适合简单场景,但它存在明显局限:

  • 无法设置任务截止时间
  • 无法指定优先级
  • 标题长度受限,无法输入详细说明
  • 缺乏输入验证,易产生无效数据

独立表单页面的价值

  • ✅ 提供完整任务属性编辑能力
  • ✅ 支持复杂输入组件(日期、时间、选择器)
  • ✅ 实现实时表单验证,提升数据质量
  • ✅ 符合用户对"专业工具"的心理预期

更重要的是,它迫使我们思考页面间数据流状态同步问题,这是构建多页面应用的关键挑战。


二、架构扩展:支持编辑模式的 ViewModel 设计

1. 扩展 Todo 模型(隐含变更)

虽然变更记录未明确提及,但为支持新功能,Todo 模型需增强:

dart 复制代码
// lib/models/todo.dart (扩展后)
class Todo {
  final String id;
  final String title;
  final bool completed;
  final DateTime? createdAt;
  final DateTime? dueDate;      // 新增:截止日期
  final int priority;           // 新增:优先级 (0=低, 1=中, 2=高)

  Todo({
    required this.id,
    required this.title,
    this.completed = false,
    this.createdAt,
    this.dueDate,
    this.priority = 1, // 默认中等优先级
  });

  // ... copyWith 需同步更新
  Todo copyWith({
    String? title,
    bool? completed,
    DateTime? dueDate,
    int? priority,
  }) {
    return Todo(
      id: id,
      title: title ?? this.title,
      completed: completed ?? this.completed,
      createdAt: createdAt,
      dueDate: dueDate ?? this.dueDate,
      priority: priority ?? this.priority,
    );
  }
}

设计原则 :模型变更不影响现有 Repository 接口,因 toJson/fromJson 可自动处理新增字段。

2. ViewModel 新增 updateTodo 方法

dart 复制代码
// lib/viewmodels/todo_notifier.dart
Future<void> updateTodo(Todo updatedTodo) async {
  await ref.read(todoRepositoryProvider).updateTodo(updatedTodo);
  // 局部更新状态,避免全量重载
  final todos = state.value!;
  final index = todos.indexWhere((t) => t.id == updatedTodo.id);
  if (index != -1) {
    state = AsyncData([...todos]..[index] = updatedTodo);
  }
}

关键点 :与 addTodo 不同,updateTodo 直接替换列表中的对象,效率更高。


三、表单页面实现:TodoFormScreen 的核心逻辑

1. 页面结构与状态管理

dart 复制代码
// lib/screens/todo_form_screen.dart
class TodoFormScreen extends ConsumerStatefulWidget {
  final Todo? initialTodo; // null 表示新建,非 null 表示编辑

  const TodoFormScreen({super.key, this.initialTodo});

  @override
  ConsumerState<TodoFormScreen> createState() => _TodoFormScreenState();
}

class _TodoFormScreenState extends ConsumerState<TodoFormScreen> {
  late final TextEditingController _titleController;
  late final FocusNode _titleFocusNode;
  late Todo _currentTodo;

  @override
  void initState() {
    super.initState();
    _currentTodo = widget.initialTodo ?? Todo(id: '', title: '');
    _titleController = TextEditingController(text: _currentTodo.title);
    _titleFocusNode = FocusNode()..requestFocus();
  }

  @override
  void dispose() {
    _titleController.dispose();
    _titleFocusNode.dispose();
    super.dispose();
  }

双模式支持 :通过 initialTodo 参数区分新建/编辑,复用同一套 UI 逻辑。

2. 表单验证与实时反馈

dart 复制代码
String? _validateTitle(String? value) {
  if (value == null || value.isEmpty) return 'Title is required';
  if (value.length < 2) return 'Title must be at least 2 characters';
  if (value.length > 200) return 'Title cannot exceed 200 characters';
  return null;
}

// 在 TextFormField 中使用
TextFormField(
  controller: _titleController,
  focusNode: _titleFocusNode,
  validator: _validateTitle,
  onChanged: (value) {
    _currentTodo = _currentTodo.copyWith(title: value);
  },
  decoration: InputDecoration(
    labelText: 'Task Title',
    errorText: _validateTitle(_titleController.text), // 实时显示错误
  ),
)

优势:用户输入时即时反馈,避免提交时才发现错误。

3. 日期与时间选择器集成

dart 复制代码
ElevatedButton.icon(
  onPressed: () async {
    final picked = await showDatePicker(
      context: context,
      initialDate: _currentTodo.dueDate ?? DateTime.now(),
      firstDate: DateTime(2020),
      lastDate: DateTime(2030),
    );
    if (picked != null) {
      // 再选择时间
      final time = await showTimePicker(
        context: context,
        initialTime: TimeOfDay.now(),
      );
      if (time != null) {
        final dateTime = DateTime(
          picked.year,
          picked.month,
          picked.day,
          time.hour,
          time.minute,
        );
        setState(() {
          _currentTodo = _currentTodo.copyWith(dueDate: dateTime);
        });
      }
    }
  },
  icon: Icon(Icons.calendar_today),
  label: Text(_currentTodo.dueDate != null 
      ? '${_currentTodo.dueDate!.month}/${_currentTodo.dueDate!.day} ${_currentTodo.dueDate!.hour}:${_currentTodo.dueDate!.minute.toString().padLeft(2, '0')}'
      : 'Set Due Date'),
)

OpenHarmony 兼容性showDatePickershowTimePicker 是 Flutter 官方组件,在 OpenHarmony 上通过 Skia 渲染,无需原生桥接。

4. 优先级选择器(Chip 样式)

dart 复制代码
Wrap(
  spacing: 8,
  children: [
    for (final level in [0, 1, 2])
      ChoiceChip(
        label: Text(['Low', 'Medium', 'High'][level]),
        selected: _currentTodo.priority == level,
        onSelected: (selected) {
          if (selected) {
            setState(() {
              _currentTodo = _currentTodo.copyWith(priority: level);
            });
          }
        },
        backgroundColor: Colors.grey[200],
        selectedColor: Theme.of(context).colorScheme.primary.withOpacity(0.2),
      ),
  ],
)

UI 一致性:采用 Chip 组件,与主页面的统计芯片风格统一。


四、导航与状态同步:确保体验连贯

1. 从列表页跳转到表单页

dart 复制代码
// lib/screens/todo_screen.dart
// 添加"详细添加"按钮
ElevatedButton.icon(
  onPressed: () async {
    final result = await Navigator.push<bool>(
      context,
      MaterialPageRoute(builder: (_) => const TodoFormScreen()),
    );
    if (result == true) {
      // 刷新列表
      ref.read(todoListProvider.notifier).refresh();
    }
  },
  icon: Icon(Icons.add),
  label: Text('Add Detailed'),
)

// 任务卡片添加编辑按钮
IconButton(
  icon: Icon(Icons.edit),
  onPressed: () async {
    final result = await Navigator.push<bool>(
      context,
      MaterialPageRoute(
        builder: (_) => TodoFormScreen(initialTodo: todo),
      ),
    );
    if (result == true) {
      ref.read(todoListProvider.notifier).refresh();
    }
  },
)

2. 表单页返回结果

dart 复制代码
// lib/screens/todo_form_screen.dart
ElevatedButton(
  onPressed: () async {
    if (_formKey.currentState?.validate() ?? false) {
      final todo = _currentTodo.copyWith(
        id: _currentTodo.id.isEmpty 
            ? DateTime.now().millisecondsSinceEpoch.toString() 
            : _currentTodo.id,
        title: _titleController.text.trim(),
      );
      
      if (widget.initialTodo == null) {
        await ref.read(todoListProvider.notifier).addTodo(todo.title);
      } else {
        await ref.read(todoListProvider.notifier).updateTodo(todo);
      }
      
      if (!mounted) return;
      Navigator.pop(context, true); // 返回 true 表示需要刷新
    }
  },
  child: Text(widget.initialTodo == null ? 'Create' : 'Update'),
)

关键机制

  • 使用 Navigator.push<bool> 传递布尔结果
  • 调用 refresh() 确保列表最新
  • 自动收起键盘(因页面 pop)

五、OpenHarmony 适配考量

尽管当前仍在标准 Flutter 环境开发,但所有设计均考虑 OpenHarmony 特性:

  1. 无平台特定 API:未使用 Android/iOS 原生选择器
  2. 触摸与键盘友好:表单支持外接键盘输入(车机/平板场景)
  3. 响应式布局:在大屏设备上自动调整表单宽度
  4. 无障碍支持:所有交互元素均有语义标签

未来扩展 :若 OpenHarmony 提供专属日期选择器,只需在 RepositoryUtils 层封装调用,不影响表单逻辑。


六、工程实践与测试策略

1. 遵循开发指南

  • 先更新变更记录文档
  • 新增文件严格按 MVVM 分层
  • 运行 build_runner 生成代码

2. 可测试性设计

  • Todo 模型的 copyWith 易于单元测试
  • 表单验证逻辑可独立测试
  • ViewModel 的 updateTodo 方法可通过 Mock Repository 验证

3. 代码质量

  • 使用 flutter analyze 确保无警告
  • 表单状态集中管理,避免分散的 setState

结语:功能深化是架构韧性的试金石

本次任务表单页面的引入,是对项目架构的一次重要考验。令人欣慰的是,Riverpod + MVVM 架构轻松应对了复杂度的提升:

  • 新增功能未破坏原有分层
  • 双模式(新建/编辑)通过参数化实现,避免代码重复
  • 导航与状态同步机制简洁可靠

这证明了前期架构投入的价值------它不仅支撑了当前需求,更为未来集成 Hive 持久化、OpenHarmony 通知、分布式任务同步 等高级功能预留了清晰路径。

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

相关推荐
四谎真好看2 小时前
JavaWeb学习笔记(Day08+Day09)之Mybatis入门+基础操作
笔记·学习·学习笔记·javaweb
2601_949575862 小时前
Flutter for OpenHarmony二手物品置换App实战 - 自定义组件实现
android·javascript·flutter
丝斯20112 小时前
AI学习笔记整理(56)——大模型微调
人工智能·笔记·学习
峥嵘life3 小时前
Android16 EDLA【CTS】CtsNetTestCases存在fail项
android·java·linux·学习·elasticsearch
wqwqweee3 小时前
Flutter for OpenHarmony 看书管理记录App实战:个人中心实现
开发语言·javascript·python·flutter·harmonyos
楼田莉子3 小时前
Linux进程间通信——管道
linux·运维·服务器·c++·学习
理人综艺好会3 小时前
Web学习之网络通信
学习
科技林总3 小时前
【系统分析师】5.4 数据库设计与建模
学习
雨季6663 小时前
构建 OpenHarmony 应用内消息通知模拟器:用纯 UI 演示通知流
flutter·ui·自动化·dart