Flutter for OpenHarmony 构建简洁高效的待办事项应用 实战解析

Flutter for OpenHarmony 构建简洁高效的待办事项应用 实战解析

在移动开发的世界中,待办事项(Todo)应用 常被视为"Hello World"级别的入门项目。然而,一个真正优秀的 Todo 应用远不止是简单的增删改查------它应具备清晰的交互逻辑、优雅的视觉反馈和流畅的用户体验。本文将深入剖析一段由 AI 编程助手 Trae 生成的 Flutter 代码,带你从零构建一款符合 Material Design 3 规范、支持滑动删除、状态切换与空状态引导的现代化任务清单应用。

🌐 加入社区 欢迎加入 开源鸿蒙跨平台开发者社区 ,获取最新资源与技术支持: 👉 开源鸿蒙跨平台开发者社区


完整效果

一、整体架构:状态驱动 + 组件化设计

该应用采用经典的 StatefulWidget + 状态管理 模式,结构清晰、职责分明:

组件 职责
TodoItem 数据模型:封装任务标题与完成状态
_TodoListPageState 业务逻辑:管理任务列表、处理增删改操作
Scaffold + ListView.builder UI 层:构建页面骨架与动态列表
Dismissible 交互增强:实现滑动删除手势

💡 整个应用仅 150 行核心代码,却完整覆盖了 Todo 应用的核心功能,体现了 Flutter "少即是多"的开发哲学。


二、数据模型:轻量而明确

dart 复制代码
class TodoItem {
  String title;
  bool isDone;

  TodoItem({required this.title, this.isDone = false});
}
  • 字段精简 :仅保留 title(任务内容)和 isDone(完成状态);
  • 默认值设计 :新任务默认未完成(isDone = false),符合用户心智模型;
  • 可变性 :使用 var 而非 final,便于后续状态更新(通过 setState 触发重绘)。

⚠️ 注意:在大型应用中,建议使用不可变对象(final 字段 + copyWith)配合状态管理库(如 Riverpod),但在此小型 Demo 中,直接修改状态更为简洁高效。


三、核心交互:四大操作全覆盖

1. 添加任务:弹窗输入

dart 复制代码
void _showAddDialog() {
  String newTaskTitle = "";
  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text('新增任务'),
      content: TextField(
        autofocus: true,
        onChanged: (value) => newTaskTitle = value,
      ),
      actions: [
        TextButton(onPressed: Navigator.pop, child: Text('取消')),
        FilledButton(
          onPressed: () {
            _addTask(newTaskTitle);
            Navigator.pop(context);
          },
          child: Text('添加'),
        ),
      ],
    ),
  );
}
  • 自动聚焦autofocus: true 提升输入效率;
  • 防空提交_addTask 内部校验 title.isNotEmpty
  • Material 3 风格 :使用 FilledButton 替代旧版 FlatButton,更符合现代设计语言。

2. 标记完成:复选框联动

dart 复制代码
Checkbox(
  value: task.isDone,
  onChanged: (value) => _toggleTask(index),
)
  • 视觉反馈 :完成任务后,文字显示删除线(TextDecoration.lineThrough)并变为灰色;
  • 卡片样式变化 :已完成任务使用 surfaceContainerHighest 背景色,形成视觉层次。

3. 删除任务:双路径支持

  • 滑动删除 :通过 Dismissible 实现 iOS/Android 风格的右滑删除;

    dart 复制代码
    background: Container(
      color: Colors.red[100],
      alignment: Alignment.centerRight,
      child: Icon(Icons.delete, color: Colors.red),
    ),
    onDismissed: (direction) => _deleteTask(index),
  • 按钮删除:每项右侧提供 × 按钮,适合精准操作;
  • 操作确认 :删除后通过 SnackBar 提示"已删除",并支持撤销(虽未实现,但预留扩展点)。

4. 清空所有:批量操作

dart 复制代码
IconButton(
  icon: Icon(Icons.delete_outline),
  onPressed: () => setState(() => _tasks.clear()),
)
  • AppBar 集成:置于右上角,符合 Material 设计规范;
  • 即时生效:无二次确认(因有 SnackBar 撤销提示,可接受)。

四、UI/UX 设计亮点

1. Material Design 3 全面应用

  • 主题色ColorScheme.fromSeed(seedColor: Colors.indigo) 自动生成和谐配色;
  • 卡片样式
    • 未完成:surface 背景 + outlineVariant 边框;
    • 已完成:surfaceContainerHighest 深色背景,突出状态差异;
  • 圆角与间距borderRadius: 12 + vertical: 4 margin,营造呼吸感。

2. 空状态友好引导

dart 复制代码
_tasks.isEmpty
  ? Center(
      child: Column(
        children: [
          Icon(Icons.task_alt, size: 80, color: Colors.grey[300]),
          Text('暂无任务,轻松一下!', style: TextStyle(fontSize: 18)),
        ],
      ),
    )
  : ListView.builder(...)
  • 情感化文案:"轻松一下!"缓解用户焦虑;
  • 大图标+浅灰色:弱化空状态,避免页面显得"空洞"。

3. 交互动效与反馈

  • 滑动删除动画Dismissible 自带平滑过渡;
  • 删除提示SnackBar 显示操作结果;
  • FAB 悬浮按钮FloatingActionButton.extended 提供明确的新增入口。

五、代码质量与最佳实践

1. 健壮性保障

  • 输入校验_addTask 拒绝空字符串;
  • Key 唯一性Dismissible 使用 task.title + index 作为 Key,避免重建错乱;
  • 资源清理:无需要 dispose 的控制器,结构轻量。

2. 可维护性设计

  • 方法拆分:每个操作(add/toggle/delete)独立为私有方法;
  • 逻辑集中 :所有状态变更通过 setState 包裹,便于追踪;
  • 注释清晰:关键部分配有中文注释,降低理解成本。

3. 性能优化

  • ListView.builder:仅构建可见项,支持长列表;
  • 无冗余 Widget:避免在 build 中创建不必要的对象。

六、扩展建议:从 Demo 到产品

当前代码已是一个功能完整的 MVP,若要推向生产,可考虑:

方向 实现思路
数据持久化 集成 shared_preferenceshive 保存任务
撤销删除 SnackBar 中添加"撤销"按钮,恢复刚删除的任务
任务分类 添加标签或过滤器(如"全部/未完成/已完成")
截止日期 扩展 TodoItem 模型,支持设置提醒时间
云同步 接入 Firebase Firestore 实现多设备同步

结语:小应用,大智慧

这个由 Trae 生成的 Todo 应用,虽功能简单,却处处体现着 用户中心的设计思维Flutter 开发的最佳实践 。它没有炫技的动画,也没有复杂的架构,而是专注于解决一个具体问题:帮助用户高效管理日常任务

完整代码展示

bash 复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(const TraeDemoApp());
}

class TraeDemoApp extends StatelessWidget {
  const TraeDemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Trae Flutter Demo',
      debugShowCheckedModeBanner: false, // 去除右上角的 Debug 标签
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
        useMaterial3: true,
      ),
      home: const TodoListPage(),
    );
  }
}

// 待办事项的数据模型
class TodoItem {
  String title;
  bool isDone;

  TodoItem({required this.title, this.isDone = false});
}

class TodoListPage extends StatefulWidget {
  const TodoListPage({super.key});

  @override
  State<TodoListPage> createState() => _TodoListPageState();
}

class _TodoListPageState extends State<TodoListPage> {
  // 存储任务的列表
  final List<TodoItem> _tasks = [
    TodoItem(title: '欢迎使用 Trae 进行开发', isDone: true),
    TodoItem(title: '运行这个 Flutter 程序'),
    TodoItem(title: '尝试添加一个新的任务'),
  ];

  // 添加任务的方法
  void _addTask(String title) {
    if (title.isNotEmpty) {
      setState(() {
        _tasks.add(TodoItem(title: title));
      });
    }
  }

  // 切换任务完成状态
  void _toggleTask(int index) {
    setState(() {
      _tasks[index].isDone = !_tasks[index].isDone;
    });
  }

  // 删除任务
  void _deleteTask(int index) {
    setState(() {
      _tasks.removeAt(index);
    });
  }

  // 显示添加任务的弹窗
  void _showAddDialog() {
    String newTaskTitle = "";
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: const Text('新增任务'),
          content: TextField(
            autofocus: true,
            decoration: const InputDecoration(
              hintText: '请输入要做的事情...',
              border: OutlineInputBorder(),
            ),
            onChanged: (value) {
              newTaskTitle = value;
            },
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('取消'),
            ),
            FilledButton(
              onPressed: () {
                _addTask(newTaskTitle);
                Navigator.pop(context);
              },
              child: const Text('添加'),
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('我的任务清单'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        centerTitle: true,
        actions: [
          IconButton(
            icon: const Icon(Icons.delete_outline),
            tooltip: '清空所有',
            onPressed: () {
              setState(() {
                _tasks.clear();
              });
            },
          )
        ],
      ),
      body: _tasks.isEmpty
          ? Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(
                    Icons.task_alt,
                    size: 80,
                    color: Colors.grey[300],
                  ),
                  const SizedBox(height: 16),
                  Text(
                    '暂无任务,轻松一下!',
                    style: TextStyle(fontSize: 18, color: Colors.grey[600]),
                  ),
                ],
              ),
            )
          : ListView.builder(
              padding: const EdgeInsets.all(16),
              itemCount: _tasks.length,
              itemBuilder: (context, index) {
                final task = _tasks[index];
                return Dismissible(
                  key: Key(task.title + index.toString()),
                  background: Container(
                    color: Colors.red[100],
                    alignment: Alignment.centerRight,
                    padding: const EdgeInsets.only(right: 20),
                    margin: const EdgeInsets.symmetric(vertical: 4),
                    child: const Icon(Icons.delete, color: Colors.red),
                  ),
                  direction: DismissDirection.endToStart,
                  onDismissed: (direction) {
                    _deleteTask(index);
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(content: Text('${task.title} 已删除')),
                    );
                  },
                  child: Card(
                    elevation: 0,
                    color: task.isDone
                        ? Theme.of(context).colorScheme.surfaceContainerHighest
                        : Theme.of(context).colorScheme.surface,
                    shape: RoundedRectangleBorder(
                      side: BorderSide(
                        color: Theme.of(context).colorScheme.outlineVariant,
                      ),
                      borderRadius: BorderRadius.circular(12),
                    ),
                    margin: const EdgeInsets.symmetric(vertical: 4),
                    child: ListTile(
                      leading: Checkbox(
                        value: task.isDone,
                        onChanged: (value) => _toggleTask(index),
                        shape: const CircleBorder(),
                      ),
                      title: Text(
                        task.title,
                        style: TextStyle(
                          decoration: task.isDone
                              ? TextDecoration.lineThrough
                              : TextDecoration.none,
                          color: task.isDone ? Colors.grey : Colors.black87,
                        ),
                      ),
                      trailing: IconButton(
                        icon: const Icon(Icons.close, size: 18),
                        onPressed: () => _deleteTask(index),
                      ),
                    ),
                  ),
                );
              },
            ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _showAddDialog,
        icon: const Icon(Icons.add),
        label: const Text('新任务'),
      ),
    );
  }
}
相关推荐
ZH154558913111 小时前
Flutter for OpenHarmony Python学习助手实战:Web开发框架应用的实现
python·学习·flutter
百思可瑞教育11 小时前
构建自己的Vue UI组件库:从设计到发布
前端·javascript·vue.js·ui·百思可瑞教育·北京百思教育
百锦再11 小时前
Vue高阶知识:利用 defineModel 特性开发搜索组件组合
前端·vue.js·学习·flutter·typescript·前端框架
廖松洋(Alina)12 小时前
【收尾以及复盘】flutter开发鸿蒙APP之成就徽章页面
flutter·华为·开源·harmonyos·鸿蒙
这儿有一堆花12 小时前
Vue 是什么:一套为「真实业务」而生的前端框架
前端·vue.js·前端框架
方见华Richard12 小时前
世毫九实验室技术优势拆解与对比分析(2026)
人工智能·交互·学习方法·原型模式·空间计算
ZH154558913112 小时前
Flutter for OpenHarmony Python学习助手实战:机器学习算法实现的实现
python·学习·flutter
哈__12 小时前
CANN加速多模态融合推理:跨模态对齐与特征交互优化
人工智能·交互
廖松洋(Alina)12 小时前
【收尾以及复盘】flutter开发鸿蒙APP之打卡日历页面
flutter·华为·开源·harmonyos·鸿蒙