📱 鸿蒙+Flutter 跨平台开发——从0到1打造一款人生清单管理工具

🚀运行效果展示

在这里插入图片描述
在这里插入图片描述

🌟 前言

在数字化时代,我们每天都面临着无数的待办事项,从工作任务到个人目标,从短期计划到长期梦想。如何高效地管理这些事项,成为了现代人面临的重要挑战。与此同时,跨平台开发技术也在快速发展,为开发者提供了一次编写、多端运行的便利。

技术选型思考

技术平台 优势 劣势
鸿蒙OS 分布式生态、强大的安全机制、国产自主可控 生态相对年轻
Flutter 跨平台兼容性好、高性能UI渲染、热重载 包体积较大

为什么选择鸿蒙+Flutter?

  • 互补优势:鸿蒙提供底层安全和分布式能力,Flutter提供优秀的跨平台UI体验
  • 开发效率:一次编写,多端运行,大幅降低开发成本
  • 未来潜力:鸿蒙作为国产操作系统,拥有广阔的发展前景
  • 用户体验:Flutter的Material Design和Cupertino风格,提供原生级别的UI体验

人生清单管理工具的价值

人生清单管理工具,不仅仅是一个简单的待办事项应用,更是一个帮助用户实现人生目标的助手。它可以:

  • 🎯 帮助用户清晰地规划人生目标
  • 📊 可视化展示任务进度
  • ⏰ 智能提醒重要事项
  • 💪 激励用户坚持完成目标

🎮 应用介绍

应用定位

人生清单管理工具是一款基于鸿蒙+Flutter开发的跨平台应用,旨在帮助用户记录、管理和实现人生中的各种目标和梦想。

核心功能

功能模块 主要功能
📝 任务管理 添加、编辑、删除任务
🏷️ 任务分类 支持按优先级、状态、标签分类
📅 时间管理 支持设置截止日期、提醒
📊 进度追踪 可视化展示任务完成进度
🔄 状态切换 支持任务状态的切换(待办、进行中、已完成)
📤 数据同步 支持本地存储(未来可扩展到云同步)

应用界面设计

应用采用了简洁、现代的设计风格,主要包含以下页面:

  1. 任务列表页:展示所有任务,支持分类和筛选
  2. 任务详情页:展示任务的详细信息
  3. 任务添加/编辑页:用于添加新任务或编辑现有任务
  4. 统计分析页:展示任务完成情况的统计数据

🏗️ 技术架构

应用架构图

用户界面层
Flutter Widget

业务逻辑层
Service

数据模型层
Model

存储层
Storage

内存存储
In-Memory

本地存储
SharedPreferences

鸿蒙安全存储
Harmony OS Secure Storage

状态管理
Provider/Bloc

项目结构

lib/
├── models/          # 数据模型
│   └── task.dart    # 任务数据模型
├── services/        # 业务逻辑服务
│   └── task_storage_service.dart  # 任务存储服务
├── pages/           # 页面组件
│   ├── task_list_page.dart        # 任务列表页
│   └── task_add_edit_page.dart    # 任务添加/编辑页
├── widgets/         # 通用组件
│   └── task_item.dart             # 任务项组件
└── main.dart        # 应用入口

核心技术栈

技术 版本 用途
Flutter 3.6.2 跨平台UI框架
Dart 3.6.2 开发语言
intl 0.19.0 日期时间格式化
uuid 4.4.0 生成唯一标识符
shared_preferences 2.3.2 本地存储(备选方案)

🔧 核心功能实现

1. 任务数据模型设计

import 'dart:convert';
import 'package:intl/intl.dart';

/// 任务状态枚举
enum TaskStatus {
  todo,       // 待办
  inProgress, // 进行中
  completed,  // 已完成
}

/// 任务优先级枚举
enum TaskPriority {
  low,
  medium,
  high,
}

/// 任务数据模型
class Task {
  final String id;
  final String title;
  final String description;
  final DateTime createdAt;
  final TaskStatus status;
  final TaskPriority priority;
  final DateTime? dueDate;

  Task({
    required this.id,
    required this.title,
    required this.description,
    required this.createdAt,
    this.status = TaskStatus.todo,
    this.priority = TaskPriority.medium,
    this.dueDate,
  });

  // 从JSON创建Task实例
  factory Task.fromJson(Map<String, dynamic> json) {
    return Task(
      id: json['id'],
      title: json['title'],
      description: json['description'],
      createdAt: DateTime.parse(json['createdAt']),
      status: TaskStatus.values[json['status']],
      priority: TaskPriority.values[json['priority']],
      dueDate: json['dueDate'] != null ? DateTime.parse(json['dueDate']) : null,
    );
  }

  // 转换为JSON格式
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'description': description,
      'createdAt': createdAt.toIso8601String(),
      'status': status.index,
      'priority': priority.index,
      'dueDate': dueDate?.toIso8601String(),
    };
  }

  // 复制方法,用于更新任务
  Task copyWith({
    String? id,
    String? title,
    String? description,
    DateTime? createdAt,
    TaskStatus? status,
    TaskPriority? priority,
    DateTime? dueDate,
  }) {
    return Task(
      id: id ?? this.id,
      title: title ?? this.title,
      description: description ?? this.description,
      createdAt: createdAt ?? this.createdAt,
      status: status ?? this.status,
      priority: priority ?? this.priority,
      dueDate: dueDate ?? this.dueDate,
    );
  }
}

2. 任务存储服务

import 'dart:convert';
import '../models/task.dart';

/// 任务存储服务
class TaskStorageService {
  /// 内存存储的任务列表
  static List<Task> _memoryTasks = [];

  /// 保存任务列表
  Future<void> saveTasks(List<Task> tasks) async {
    _memoryTasks = tasks;
  }

  /// 加载任务列表
  Future<List<Task>> loadTasks() async {
    return _memoryTasks;
  }

  /// 添加任务
  Future<void> addTask(Task task) async {
    final tasks = await loadTasks();
    tasks.add(task);
    await saveTasks(tasks);
  }

  /// 更新任务
  Future<void> updateTask(Task task) async {
    final tasks = await loadTasks();
    final index = tasks.indexWhere((t) => t.id == task.id);
    if (index != -1) {
      tasks[index] = task;
      await saveTasks(tasks);
    }
  }

  /// 删除任务
  Future<void> deleteTask(String taskId) async {
    final tasks = await loadTasks();
    tasks.removeWhere((t) => t.id == taskId);
    await saveTasks(tasks);
  }

  /// 切换任务状态
  Future<void> toggleTaskStatus(String taskId, TaskStatus newStatus) async {
    final tasks = await loadTasks();
    final index = tasks.indexWhere((t) => t.id == taskId);
    if (index != -1) {
      tasks[index] = tasks[index].copyWith(status: newStatus);
      await saveTasks(tasks);
    }
  }
}

3. 任务列表页面

import 'package:flutter/material.dart';
import '../models/task.dart';
import '../services/task_storage_service.dart';
import 'task_add_edit_page.dart';

/// 任务列表页面
class TaskListPage extends StatefulWidget {
  const TaskListPage({Key? key}) : super(key: key);

  
  State<TaskListPage> createState() => _TaskListPageState();
}

class _TaskListPageState extends State<TaskListPage> {
  List<Task> _tasks = [];
  bool _isLoading = true;
  final _taskStorageService = TaskStorageService();

  
  void initState() {
    super.initState();
    _loadTasks();
  }

  /// 加载任务列表
  Future<void> _loadTasks() async {
    setState(() {
      _isLoading = true;
    });
    try {
      final tasks = await _taskStorageService.loadTasks();
      setState(() {
        _tasks = tasks;
      });
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('加载任务失败: $e')),
      );
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  /// 导航到添加任务页面
  void _navigateToAddTask() async {
    final result = await Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => const TaskAddEditPage()),
    );
    if (result == true) {
      _loadTasks();
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('人生清单'),
        centerTitle: true,
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : _buildTaskList(),
      floatingActionButton: FloatingActionButton(
        onPressed: _navigateToAddTask,
        tooltip: '添加任务',
        child: const Icon(Icons.add),
      ),
    );
  }

  /// 构建任务列表
  Widget _buildTaskList() {
    if (_tasks.isEmpty) {
      return const Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.task_alt, size: 64, color: Colors.grey),
            SizedBox(height: 16),
            Text('还没有任务', style: TextStyle(fontSize: 18, color: Colors.grey)),
            SizedBox(height: 8),
            Text('点击右下角按钮添加第一个任务', style: TextStyle(fontSize: 14, color: Colors.grey)),
          ],
        ),
      );
    }

    return ListView.builder(
      itemCount: _tasks.length,
      padding: const EdgeInsets.all(8.0),
      itemBuilder: (context, index) {
        final task = _tasks[index];
        return TaskItem(
          task: task,
          onStatusChange: (status) => _handleStatusChange(task.id, status),
          onEdit: () => _navigateToEditTask(task),
          onDelete: () => _handleDeleteTask(task.id),
        );
      },
    );
  }

  // 其他辅助方法...
}

4. 任务添加/编辑页面

import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
import '../models/task.dart';
import '../services/task_storage_service.dart';

/// 任务添加/编辑页面
class TaskAddEditPage extends StatefulWidget {
  final Task? task;

  const TaskAddEditPage({Key? key, this.task}) : super(key: key);

  
  State<TaskAddEditPage> createState() => _TaskAddEditPageState();
}

class _TaskAddEditPageState extends State<TaskAddEditPage> {
  final _formKey = GlobalKey<FormState>();
  final _titleController = TextEditingController();
  final _descriptionController = TextEditingController();
  TaskPriority _priority = TaskPriority.medium;
  DateTime? _dueDate;
  final _taskStorageService = TaskStorageService();

  
  void initState() {
    super.initState();
    if (widget.task != null) {
      _titleController.text = widget.task!.title;
      _descriptionController.text = widget.task!.description;
      _priority = widget.task!.priority;
      _dueDate = widget.task!.dueDate;
    }
  }

  /// 保存任务
  Future<void> _saveTask() async {
    if (_formKey.currentState!.validate()) {
      FocusScope.of(context).unfocus();
      try {
        final task = widget.task != null
            ? widget.task!.copyWith(
                title: _titleController.text,
                description: _descriptionController.text,
                priority: _priority,
                dueDate: _dueDate,
              )
            : Task(
                id: const Uuid().v4(),
                title: _titleController.text,
                description: _descriptionController.text,
                createdAt: DateTime.now(),
                priority: _priority,
                dueDate: _dueDate,
              );

        if (widget.task != null) {
          await _taskStorageService.updateTask(task);
        } else {
          await _taskStorageService.addTask(task);
        }

        Navigator.pop(context, true);
      } catch (e) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('保存任务失败: $e')),
        );
      }
    }
  }

  
  Widget build(BuildContext context) {
    final isEditing = widget.task != null;
    return Scaffold(
      appBar: AppBar(
        title: Text(isEditing ? '编辑任务' : '添加任务'),
        centerTitle: true,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(20.0),
        child: Form(
          key: _formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              // 任务标题输入
              TextFormField(
                controller: _titleController,
                decoration: const InputDecoration(
                  labelText: '任务标题',
                  hintText: '请输入任务标题',
                  border: OutlineInputBorder(),
                  prefixIcon: Icon(Icons.title),
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return '请输入任务标题';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              // 任务描述输入
              TextFormField(
                controller: _descriptionController,
                decoration: const InputDecoration(
                  labelText: '任务描述',
                  hintText: '请输入任务描述',
                  border: OutlineInputBorder(),
                  prefixIcon: Icon(Icons.description),
                ),
                maxLines: 4,
              ),
              const SizedBox(height: 20),
              // 优先级选择
              _buildPrioritySelector(),
              const SizedBox(height: 20),
              // 截止日期选择
              _buildDueDateSelector(),
              const SizedBox(height: 30),
              // 保存按钮
              ElevatedButton(
                onPressed: _saveTask,
                style: ElevatedButton.styleFrom(
                  padding: const EdgeInsets.symmetric(vertical: 16),
                  textStyle: const TextStyle(fontSize: 18),
                ),
                child: Text(isEditing ? '更新任务' : '添加任务'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  // 其他辅助方法...
}

📊 流程图设计

任务添加流程图

验证失败

验证成功

用户点击添加按钮

打开任务添加页面

填写任务信息

信息验证

显示错误提示

创建任务对象

保存到存储服务

返回任务列表页面

刷新任务列表

任务状态流转图

开始执行

完成任务

直接完成

放弃执行

重新开启

待办

进行中

已完成

🔍 核心功能详解

1. 任务优先级管理

应用支持三种优先级:低、中、高,通过不同的颜色进行区分:

  • 🔴 高优先级:红色
  • 🟡 中优先级:黄色
  • 🟢 低优先级:绿色
/// 获取优先级对应的颜色
Color getPriorityColor(TaskPriority priority) {
  switch (priority) {
    case TaskPriority.low:
      return Colors.green;
    case TaskPriority.medium:
      return Colors.yellow;
    case TaskPriority.high:
      return Colors.red;
  }
}

2. 截止日期提醒

应用会自动检查任务的截止日期,并在截止日期临近时显示提醒:

  • ⚠️ 截止日期当天:显示黄色提醒
  • 🔴 已过期:显示红色提醒
  • ✅ 未过期:显示正常颜色
/// 获取截止日期显示样式
TextStyle getDueDateStyle(DateTime? dueDate) {
  if (dueDate == null) {
    return const TextStyle(color: Colors.grey);
  }
  
  final now = DateTime.now();
  final difference = dueDate.difference(now);
  
  if (difference.isNegative) {
    // 已过期
    return const TextStyle(color: Colors.red, fontWeight: FontWeight.bold);
  } else if (difference.inDays <= 1) {
    // 即将过期
    return const TextStyle(color: Colors.orange, fontWeight: FontWeight.bold);
  } else {
    // 未过期
    return const TextStyle(color: Colors.grey);
  }
}

3. 任务状态管理

应用支持三种任务状态:

  • 📝 待办:任务尚未开始
  • 🔄 进行中:任务正在执行
  • ✅ 已完成:任务已经完成
/// 获取状态对应的图标
Icon getStatusIcon(TaskStatus status) {
  switch (status) {
    case TaskStatus.todo:
      return const Icon(Icons.radio_button_unchecked);
    case TaskStatus.inProgress:
      return const Icon(Icons.pending);
    case TaskStatus.completed:
      return const Icon(Icons.check_circle);
  }
}

🎉 总结与展望

项目成果

通过本次开发,我们成功打造了一款功能完整的人生清单管理工具,具有以下特点:

  1. 跨平台兼容:支持鸿蒙及其他Flutter平台
  2. 功能完整:包含任务管理的核心功能
  3. UI美观:采用现代简洁的设计风格
  4. 性能优良:流畅的用户体验
  5. 易于扩展:模块化的代码结构

未来规划

  1. 云同步功能:支持多设备数据同步
  2. 数据分析功能:提供任务完成情况的数据分析和可视化
  3. 社交分享功能:支持任务的分享和协作
  4. 语音输入功能:支持语音添加任务
  5. 智能推荐功能:根据用户习惯推荐任务管理策略
  6. 鸿蒙分布式特性:充分利用鸿蒙的分布式能力,实现多设备协同

💡 技术感悟

鸿蒙+Flutter的组合,为跨平台开发提供了新的可能性。鸿蒙的安全机制和分布式能力,结合Flutter的优秀UI体验,能够打造出既安全又易用的跨平台应用。

在开发过程中,我们遇到了一些挑战,特别是在平台兼容性方面,但通过合理的设计和适配,最终成功实现了目标。这也让我们深刻体会到,跨平台开发不仅仅是技术的堆叠,更是需要对不同平台的深入理解和合理适配。


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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐