Flutter 框架跨平台鸿蒙开发 - 待办事项优先级排序开发教程

Flutter待办事项优先级排序开发教程

项目简介

待办事项优先级排序是一个基于Flutter开发的任务管理应用,专注于通过智能优先级算法帮助用户高效管理日常任务。应用支持四级优先级设置,结合截止日期自动计算任务紧急程度,确保重要任务不被遗漏。
运行效果图

核心功能

  • 智能优先级排序:基于优先级和截止日期的综合排序算法
  • 四级优先级管理:紧急、高、中、低四个优先级等级
  • 截止日期提醒:自动识别逾期和即将到期的任务
  • 任务状态管理:支持任务完成状态切换和编辑
  • 统计分析:提供完成率和优先级分布统计
  • 分类筛选:按优先级快速筛选和查看任务

技术特点

  • 单文件架构,代码结构清晰
  • Material Design 3设计风格
  • 智能排序算法,自动计算任务优先级分数
  • 响应式布局适配不同屏幕尺寸

架构设计

整体架构

TodoPriorityApp
TodoHomePage
待办列表页面
优先级页面
统计页面
任务卡片
添加任务对话框
优先级筛选器
分类任务列表
总体统计
优先级分布
完成率图表
TodoItem数据模型
优先级分数算法

核心组件

组件 功能 说明
TodoPriorityApp 应用入口 配置主题和路由
TodoHomePage 主页面 管理页面状态和导航
TodoItem 任务数据模型 封装任务信息和优先级算法
PriorityConfig 优先级配置 定义优先级名称、颜色和权重

数据模型设计

枚举类型

Priority - 优先级枚举
dart 复制代码
enum Priority {
  urgent,    // 紧急
  high,      // 高
  medium,    // 中
  low,       // 低
}

配置类

PriorityConfig - 优先级配置
dart 复制代码
class PriorityConfig {
  static const Map<Priority, String> names = {
    Priority.urgent: '紧急',
    Priority.high: '高',
    Priority.medium: '中',
    Priority.low: '低',
  };

  static const Map<Priority, Color> colors = {
    Priority.urgent: Colors.red,
    Priority.high: Colors.orange,
    Priority.medium: Colors.blue,
    Priority.low: Colors.green,
  };

  static const Map<Priority, int> weights = {
    Priority.urgent: 4,
    Priority.high: 3,
    Priority.medium: 2,
    Priority.low: 1,
  };
}

配置说明:

  • names: 优先级的中文显示名称
  • colors: 每个优先级对应的颜色标识
  • weights: 优先级权重,用于排序算法计算

核心数据类

TodoItem - 待办事项模型
dart 复制代码
class TodoItem {
  final String id;              // 唯一标识
  final String title;           // 任务标题
  final String description;     // 任务描述
  final Priority priority;      // 优先级
  final DateTime createdAt;     // 创建时间
  final DateTime? dueDate;      // 截止日期
  final bool isCompleted;       // 是否完成

  // 计算优先级分数(用于排序)
  int get priorityScore {
    int score = (PriorityConfig.weights[priority] ?? 1) * 100;
    
    // 根据截止日期调整分数
    if (dueDate != null) {
      final now = DateTime.now();
      final daysLeft = dueDate!.difference(now).inDays;
      
      if (daysLeft < 0) {
        score += 200; // 已过期,最高优先级
      } else if (daysLeft == 0) {
        score += 150; // 今天到期
      } else if (daysLeft == 1) {
        score += 100; // 明天到期
      } else if (daysLeft <= 3) {
        score += 50; // 3天内到期
      }
    }
    
    return score;
  }
}

字段说明:

  • id: 任务的唯一标识符
  • title: 任务标题,必填字段
  • description: 任务详细描述,可选
  • priority: 任务优先级等级
  • createdAt: 任务创建时间
  • dueDate: 任务截止日期,可选
  • isCompleted: 任务完成状态

核心功能实现

1. 智能优先级排序算法

优先级分数计算
dart 复制代码
int get priorityScore {
  // 基础分数 = 优先级权重 × 100
  int score = (PriorityConfig.weights[priority] ?? 1) * 100;
  
  // 截止日期加权
  if (dueDate != null) {
    final now = DateTime.now();
    final daysLeft = dueDate!.difference(now).inDays;
    
    if (daysLeft < 0) {
      score += 200; // 已过期任务优先级最高
    } else if (daysLeft == 0) {
      score += 150; // 今天到期
    } else if (daysLeft == 1) {
      score += 100; // 明天到期
    } else if (daysLeft <= 3) {
      score += 50;  // 3天内到期
    }
  }
  
  return score;
}
排序逻辑实现
dart 复制代码
List<TodoItem> _getSortedTodos() {
  final todos = List<TodoItem>.from(_todos);
  todos.sort((a, b) {
    // 已完成的任务排在后面
    if (a.isCompleted != b.isCompleted) {
      return a.isCompleted ? 1 : -1;
    }
    
    // 按优先级分数排序(分数高的在前)
    return b.priorityScore.compareTo(a.priorityScore);
  });
  return todos;
}

算法特点:

  • 综合考虑优先级和时间因素
  • 已过期任务自动提升到最高优先级
  • 即将到期的任务获得额外权重
  • 已完成任务自动排到列表末尾

2. 任务管理功能

添加任务对话框
dart 复制代码
void _showTodoDialog({TodoItem? todo}) {
  final isEdit = todo != null;
  final titleController = TextEditingController(text: todo?.title ?? '');
  final descController = TextEditingController(text: todo?.description ?? '');
  Priority selectedPriority = todo?.priority ?? Priority.medium;
  DateTime? selectedDueDate = todo?.dueDate;

  showDialog(
    context: context,
    builder: (context) => StatefulBuilder(
      builder: (context, setDialogState) => AlertDialog(
        title: Text(isEdit ? '编辑任务' : '添加任务'),
        content: SingleChildScrollView(
          child: Column(
            children: [
              // 任务标题输入
              TextField(
                controller: titleController,
                decoration: const InputDecoration(
                  labelText: '任务标题',
                  border: OutlineInputBorder(),
                ),
              ),
              
              // 任务描述输入
              TextField(
                controller: descController,
                decoration: const InputDecoration(
                  labelText: '任务描述(可选)',
                  border: OutlineInputBorder(),
                ),
                maxLines: 3,
              ),
              
              // 优先级选择
              DropdownButtonFormField<Priority>(
                value: selectedPriority,
                items: Priority.values.map((priority) {
                  return DropdownMenuItem(
                    value: priority,
                    child: Row(
                      children: [
                        Container(
                          width: 12,
                          height: 12,
                          decoration: BoxDecoration(
                            color: PriorityConfig.colors[priority],
                            shape: BoxShape.circle,
                          ),
                        ),
                        const SizedBox(width: 8),
                        Text(PriorityConfig.names[priority] ?? ''),
                      ],
                    ),
                  );
                }).toList(),
                onChanged: (value) {
                  setDialogState(() {
                    selectedPriority = value!;
                  });
                },
              ),
              
              // 截止日期选择
              ListTile(
                title: const Text('截止日期'),
                subtitle: Text(selectedDueDate != null 
                    ? _formatDate(selectedDueDate!) 
                    : '未设置'),
                trailing: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    IconButton(
                      onPressed: () async {
                        final date = await showDatePicker(
                          context: context,
                          initialDate: selectedDueDate ?? DateTime.now(),
                          firstDate: DateTime.now(),
                          lastDate: DateTime.now().add(const Duration(days: 365)),
                        );
                        if (date != null) {
                          setDialogState(() {
                            selectedDueDate = date;
                          });
                        }
                      },
                      icon: const Icon(Icons.calendar_today),
                    ),
                    if (selectedDueDate != null)
                      IconButton(
                        onPressed: () {
                          setDialogState(() {
                            selectedDueDate = null;
                          });
                        },
                        icon: const Icon(Icons.clear),
                      ),
                  ],
                ),
              ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('取消'),
          ),
          ElevatedButton(
            onPressed: () => _saveTodo(),
            child: Text(isEdit ? '更新' : '添加'),
          ),
        ],
      ),
    ),
  );
}
任务状态切换
dart 复制代码
Widget _buildTodoCard(TodoItem todo) {
  return Card(
    child: ListTile(
      leading: Checkbox(
        value: todo.isCompleted,
        onChanged: (value) {
          setState(() {
            final index = _todos.indexWhere((t) => t.id == todo.id);
            _todos[index] = todo.copyWith(isCompleted: value);
          });
        },
      ),
      title: Text(
        todo.title,
        style: TextStyle(
          decoration: todo.isCompleted ? TextDecoration.lineThrough : null,
          color: todo.isCompleted ? Colors.grey : null,
        ),
      ),
      // 其他UI组件...
    ),
  );
}

3. 日期处理功能

日期格式化
dart 复制代码
String _formatDate(DateTime date) {
  return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
}

String _formatDueDate(DateTime dueDate) {
  final now = DateTime.now();
  final today = DateTime(now.year, now.month, now.day);
  final due = DateTime(dueDate.year, dueDate.month, dueDate.day);
  final difference = due.difference(today).inDays;

  if (difference < 0) {
    return '已逾期';
  } else if (difference == 0) {
    return '今天';
  } else if (difference == 1) {
    return '明天';
  } else {
    return '${difference}天后';
  }
}
逾期检测
dart 复制代码
bool get isOverdue {
  return dueDate != null && 
         dueDate!.isBefore(DateTime.now()) && 
         !isCompleted;
}

UI组件设计

1. 主页面布局

主页面采用底部导航栏设计,包含三个主要页面:

dart 复制代码
Widget build(BuildContext context) {
  return Scaffold(
    body: [
      _buildTodoListPage(),    // 待办列表
      _buildPriorityPage(),    // 优先级管理
      _buildStatsPage(),       // 统计分析
    ][_selectedIndex],
    bottomNavigationBar: NavigationBar(
      selectedIndex: _selectedIndex,
      destinations: const [
        NavigationDestination(
          icon: Icon(Icons.list_outlined),
          selectedIcon: Icon(Icons.list),
          label: '待办',
        ),
        NavigationDestination(
          icon: Icon(Icons.priority_high_outlined),
          selectedIcon: Icon(Icons.priority_high),
          label: '优先级',
        ),
        NavigationDestination(
          icon: Icon(Icons.analytics_outlined),
          selectedIcon: Icon(Icons.analytics),
          label: '统计',
        ),
      ],
    ),
    floatingActionButton: _selectedIndex == 0
        ? FloatingActionButton(
            onPressed: _showAddTodoDialog,
            child: const Icon(Icons.add),
          )
        : null,
  );
}

2. 任务卡片设计

任务卡片组件
dart 复制代码
Widget _buildTodoCard(TodoItem todo) {
  final isOverdue = todo.dueDate != null && 
      todo.dueDate!.isBefore(DateTime.now()) && 
      !todo.isCompleted;

  return Card(
    margin: const EdgeInsets.only(bottom: 8),
    child: ListTile(
      // 完成状态复选框
      leading: Checkbox(
        value: todo.isCompleted,
        onChanged: (value) => _toggleTodoStatus(todo, value),
      ),
      
      // 任务标题
      title: Text(
        todo.title,
        style: TextStyle(
          decoration: todo.isCompleted ? TextDecoration.lineThrough : null,
          color: todo.isCompleted ? Colors.grey : null,
        ),
      ),
      
      // 任务详情
      subtitle: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          if (todo.description.isNotEmpty)
            Text(todo.description),
          const SizedBox(height: 4),
          Row(
            children: [
              // 优先级标签
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
                decoration: BoxDecoration(
                  color: PriorityConfig.colors[todo.priority],
                  borderRadius: BorderRadius.circular(12),
                ),
                child: Text(
                  PriorityConfig.names[todo.priority] ?? '',
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 10,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
              
              // 截止日期显示
              if (todo.dueDate != null) ...[
                const SizedBox(width: 8),
                Icon(
                  isOverdue ? Icons.warning : Icons.schedule,
                  size: 14,
                  color: isOverdue ? Colors.red : Colors.grey,
                ),
                const SizedBox(width: 2),
                Text(
                  _formatDueDate(todo.dueDate!),
                  style: TextStyle(
                    fontSize: 12,
                    color: isOverdue ? Colors.red : Colors.grey,
                    fontWeight: isOverdue ? FontWeight.bold : null,
                  ),
                ),
              ],
            ],
          ),
        ],
      ),
      
      // 操作菜单
      trailing: PopupMenuButton(
        itemBuilder: (context) => [
          PopupMenuItem(
            value: 'edit',
            child: const Row(
              children: [
                Icon(Icons.edit, size: 18),
                SizedBox(width: 8),
                Text('编辑'),
              ],
            ),
          ),
          PopupMenuItem(
            value: 'delete',
            child: const Row(
              children: [
                Icon(Icons.delete, size: 18, color: Colors.red),
                SizedBox(width: 8),
                Text('删除', style: TextStyle(color: Colors.red)),
              ],
            ),
          ),
        ],
        onSelected: (value) {
          if (value == 'edit') {
            _showEditTodoDialog(todo);
          } else if (value == 'delete') {
            _deleteTodo(todo.id);
          }
        },
      ),
    ),
  );
}

3. 优先级筛选器

dart 复制代码
Widget _buildPriorityFilter() {
  return Card(
    child: Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            '选择优先级',
            style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 12),
          Wrap(
            spacing: 8,
            children: Priority.values.map((priority) {
              final isSelected = _filterPriority == priority;
              final count = _getTodosByPriority(priority).length;
              
              return FilterChip(
                selected: isSelected,
                label: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text(PriorityConfig.names[priority] ?? ''),
                    const SizedBox(width: 4),
                    Container(
                      padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
                      decoration: BoxDecoration(
                        color: isSelected ? Colors.white : PriorityConfig.colors[priority],
                        borderRadius: BorderRadius.circular(10),
                      ),
                      child: Text(
                        count.toString(),
                        style: TextStyle(
                          fontSize: 10,
                          color: isSelected ? PriorityConfig.colors[priority] : Colors.white,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ],
                ),
                selectedColor: PriorityConfig.colors[priority]?.withOpacity(0.2),
                onSelected: (selected) {
                  setState(() {
                    _filterPriority = priority;
                  });
                },
              );
            }).toList(),
          ),
        ],
      ),
    ),
  );
}

4. 统计图表组件

完成率圆形图表
dart 复制代码
Widget _buildCompletionChart(int completed, int total) {
  return Center(
    child: SizedBox(
      width: 120,
      height: 120,
      child: Stack(
        children: [
          CircularProgressIndicator(
            value: total > 0 ? completed / total : 0,
            strokeWidth: 12,
            backgroundColor: Colors.grey.shade300,
            valueColor: const AlwaysStoppedAnimation<Color>(Colors.green),
          ),
          Center(
            child: Text(
              '${total > 0 ? (completed / total * 100).toStringAsFixed(1) : 0}%',
              style: const TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
          ),
        ],
      ),
    ),
  );
}
优先级分布条形图
dart 复制代码
Widget _buildPriorityDistribution() {
  return Column(
    children: Priority.values.map((priority) {
      final count = _getTodosByPriority(priority).length;
      final total = _todos.length;
      final percentage = total > 0 ? (count / total * 100) : 0.0;
      
      return Padding(
        padding: const EdgeInsets.only(bottom: 12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(PriorityConfig.names[priority] ?? ''),
                Text('$count (${percentage.toStringAsFixed(1)}%)'),
              ],
            ),
            const SizedBox(height: 4),
            LinearProgressIndicator(
              value: total > 0 ? count / total : 0,
              backgroundColor: Colors.grey.shade300,
              valueColor: AlwaysStoppedAnimation<Color>(
                PriorityConfig.colors[priority]!,
              ),
            ),
          ],
        ),
      );
    }).toList(),
  );
}

状态管理

1. 状态变量设计

dart 复制代码
class _TodoHomePageState extends State<TodoHomePage> {
  int _selectedIndex = 0;              // 当前选中的页面索引
  List<TodoItem> _todos = [];          // 待办事项列表
  Priority _filterPriority = Priority.urgent; // 优先级筛选条件
}

2. 状态更新机制

任务状态切换
dart 复制代码
void _toggleTodoStatus(TodoItem todo, bool? isCompleted) {
  setState(() {
    final index = _todos.indexWhere((t) => t.id == todo.id);
    if (index != -1) {
      _todos[index] = todo.copyWith(isCompleted: isCompleted);
    }
  });
}
任务添加和编辑
dart 复制代码
void _saveTodo(TodoItem todo, {bool isEdit = false}) {
  setState(() {
    if (isEdit) {
      final index = _todos.indexWhere((t) => t.id == todo.id);
      if (index != -1) {
        _todos[index] = todo;
      }
    } else {
      _todos.add(todo);
    }
  });
}
任务删除
dart 复制代码
void _deleteTodo(String id) {
  setState(() {
    _todos.removeWhere((todo) => todo.id == id);
  });
}

3. 数据持久化

虽然当前版本使用内存存储,但可以轻松扩展到本地存储:

dart 复制代码
// 保存到本地存储
Future<void> _saveTodosToStorage() async {
  final prefs = await SharedPreferences.getInstance();
  final todosJson = _todos.map((todo) => todo.toJson()).toList();
  await prefs.setString('todos', jsonEncode(todosJson));
}

// 从本地存储加载
Future<void> _loadTodosFromStorage() async {
  final prefs = await SharedPreferences.getInstance();
  final todosString = prefs.getString('todos');
  if (todosString != null) {
    final todosJson = jsonDecode(todosString) as List;
    setState(() {
      _todos = todosJson.map((json) => TodoItem.fromJson(json)).toList();
    });
  }
}

工具方法实现

1. 日期处理工具

dart 复制代码
class DateUtils {
  // 格式化日期为 YYYY-MM-DD 格式
  static String formatDate(DateTime date) {
    return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
  }

  // 计算相对日期描述
  static String getRelativeDateDescription(DateTime dueDate) {
    final now = DateTime.now();
    final today = DateTime(now.year, now.month, now.day);
    final due = DateTime(dueDate.year, dueDate.month, dueDate.day);
    final difference = due.difference(today).inDays;

    if (difference < 0) {
      return '已逾期';
    } else if (difference == 0) {
      return '今天';
    } else if (difference == 1) {
      return '明天';
    } else if (difference <= 7) {
      return '${difference}天后';
    } else {
      return formatDate(dueDate);
    }
  }

  // 检查是否逾期
  static bool isOverdue(DateTime? dueDate, bool isCompleted) {
    if (dueDate == null || isCompleted) return false;
    return dueDate.isBefore(DateTime.now());
  }

  // 获取今天的开始时间
  static DateTime getStartOfDay(DateTime date) {
    return DateTime(date.year, date.month, date.day);
  }

  // 获取今天的结束时间
  static DateTime getEndOfDay(DateTime date) {
    return DateTime(date.year, date.month, date.day, 23, 59, 59);
  }
}

2. 优先级工具

dart 复制代码
class PriorityUtils {
  // 获取优先级颜色
  static Color getPriorityColor(Priority priority) {
    return PriorityConfig.colors[priority] ?? Colors.grey;
  }

  // 获取优先级名称
  static String getPriorityName(Priority priority) {
    return PriorityConfig.names[priority] ?? '未知';
  }

  // 获取优先级权重
  static int getPriorityWeight(Priority priority) {
    return PriorityConfig.weights[priority] ?? 1;
  }

  // 根据字符串获取优先级
  static Priority? getPriorityFromString(String priorityString) {
    for (final priority in Priority.values) {
      if (PriorityConfig.names[priority] == priorityString) {
        return priority;
      }
    }
    return null;
  }

  // 获取下一个优先级
  static Priority getNextPriority(Priority current) {
    final index = Priority.values.indexOf(current);
    final nextIndex = (index + 1) % Priority.values.length;
    return Priority.values[nextIndex];
  }

  // 获取上一个优先级
  static Priority getPreviousPriority(Priority current) {
    final index = Priority.values.indexOf(current);
    final prevIndex = (index - 1 + Priority.values.length) % Priority.values.length;
    return Priority.values[prevIndex];
  }
}

3. 统计工具

dart 复制代码
class StatisticsUtils {
  // 计算完成率
  static double calculateCompletionRate(List<TodoItem> todos) {
    if (todos.isEmpty) return 0.0;
    final completed = todos.where((todo) => todo.isCompleted).length;
    return completed / todos.length;
  }

  // 获取优先级分布
  static Map<Priority, int> getPriorityDistribution(List<TodoItem> todos) {
    final distribution = <Priority, int>{};
    for (final priority in Priority.values) {
      distribution[priority] = todos.where((todo) => todo.priority == priority).length;
    }
    return distribution;
  }

  // 获取逾期任务数量
  static int getOverdueCount(List<TodoItem> todos) {
    return todos.where((todo) => 
        todo.dueDate != null && 
        todo.dueDate!.isBefore(DateTime.now()) && 
        !todo.isCompleted
    ).length;
  }

  // 获取今日到期任务数量
  static int getTodayDueCount(List<TodoItem> todos) {
    final today = DateTime.now();
    final startOfDay = DateTime(today.year, today.month, today.day);
    final endOfDay = DateTime(today.year, today.month, today.day, 23, 59, 59);
    
    return todos.where((todo) => 
        todo.dueDate != null && 
        todo.dueDate!.isAfter(startOfDay) && 
        todo.dueDate!.isBefore(endOfDay) && 
        !todo.isCompleted
    ).length;
  }

  // 获取本周完成的任务数量
  static int getWeeklyCompletedCount(List<TodoItem> todos) {
    final now = DateTime.now();
    final weekStart = now.subtract(Duration(days: now.weekday - 1));
    final weekStartDay = DateTime(weekStart.year, weekStart.month, weekStart.day);
    
    return todos.where((todo) => 
        todo.isCompleted && 
        todo.createdAt.isAfter(weekStartDay)
    ).length;
  }
}

功能扩展建议

1. 数据持久化

dart 复制代码
// 使用 SharedPreferences 实现本地存储
class TodoStorage {
  static const String _todosKey = 'todos';

  static Future<void> saveTodos(List<TodoItem> todos) async {
    final prefs = await SharedPreferences.getInstance();
    final todosJson = todos.map((todo) => todo.toJson()).toList();
    await prefs.setString(_todosKey, jsonEncode(todosJson));
  }

  static Future<List<TodoItem>> loadTodos() async {
    final prefs = await SharedPreferences.getInstance();
    final todosString = prefs.getString(_todosKey);
    if (todosString == null) return [];
    
    final todosJson = jsonDecode(todosString) as List;
    return todosJson.map((json) => TodoItem.fromJson(json)).toList();
  }
}

// TodoItem 添加序列化方法
extension TodoItemSerialization on TodoItem {
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'description': description,
      'priority': priority.index,
      'createdAt': createdAt.millisecondsSinceEpoch,
      'dueDate': dueDate?.millisecondsSinceEpoch,
      'isCompleted': isCompleted,
    };
  }

  static TodoItem fromJson(Map<String, dynamic> json) {
    return TodoItem(
      id: json['id'],
      title: json['title'],
      description: json['description'] ?? '',
      priority: Priority.values[json['priority']],
      createdAt: DateTime.fromMillisecondsSinceEpoch(json['createdAt']),
      dueDate: json['dueDate'] != null 
          ? DateTime.fromMillisecondsSinceEpoch(json['dueDate'])
          : null,
      isCompleted: json['isCompleted'] ?? false,
    );
  }
}

2. 通知提醒功能

dart 复制代码
class NotificationService {
  static Future<void> scheduleTaskReminder(TodoItem todo) async {
    if (todo.dueDate == null) return;

    final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
    
    // 在截止日期前一天提醒
    final reminderTime = todo.dueDate!.subtract(const Duration(days: 1));
    
    await flutterLocalNotificationsPlugin.schedule(
      todo.id.hashCode,
      '任务提醒',
      '任务"${todo.title}"将在明天到期',
      reminderTime,
      const NotificationDetails(
        android: AndroidNotificationDetails(
          'task_reminder',
          '任务提醒',
          channelDescription: '待办事项到期提醒',
          importance: Importance.high,
          priority: Priority.high,
        ),
      ),
    );
  }

  static Future<void> cancelTaskReminder(String todoId) async {
    final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
    await flutterLocalNotificationsPlugin.cancel(todoId.hashCode);
  }
}

3. 任务分类功能

dart 复制代码
enum TaskCategory {
  work,     // 工作
  personal, // 个人
  study,    // 学习
  health,   // 健康
  shopping, // 购物
  other,    // 其他
}

class CategoryConfig {
  static const Map<TaskCategory, String> names = {
    TaskCategory.work: '工作',
    TaskCategory.personal: '个人',
    TaskCategory.study: '学习',
    TaskCategory.health: '健康',
    TaskCategory.shopping: '购物',
    TaskCategory.other: '其他',
  };

  static const Map<TaskCategory, IconData> icons = {
    TaskCategory.work: Icons.work,
    TaskCategory.personal: Icons.person,
    TaskCategory.study: Icons.school,
    TaskCategory.health: Icons.favorite,
    TaskCategory.shopping: Icons.shopping_cart,
    TaskCategory.other: Icons.more_horiz,
  };
}

// 扩展 TodoItem 添加分类字段
class TodoItemWithCategory extends TodoItem {
  final TaskCategory category;

  const TodoItemWithCategory({
    required super.id,
    required super.title,
    super.description = '',
    required super.priority,
    required super.createdAt,
    super.dueDate,
    super.isCompleted = false,
    this.category = TaskCategory.other,
  });
}

4. 搜索和筛选功能

dart 复制代码
class TodoFilter {
  final String? searchText;
  final Priority? priority;
  final TaskCategory? category;
  final bool? isCompleted;
  final DateRange? dateRange;

  const TodoFilter({
    this.searchText,
    this.priority,
    this.category,
    this.isCompleted,
    this.dateRange,
  });

  List<TodoItem> apply(List<TodoItem> todos) {
    return todos.where((todo) {
      // 文本搜索
      if (searchText != null && searchText!.isNotEmpty) {
        final searchLower = searchText!.toLowerCase();
        if (!todo.title.toLowerCase().contains(searchLower) &&
            !todo.description.toLowerCase().contains(searchLower)) {
          return false;
        }
      }

      // 优先级筛选
      if (priority != null && todo.priority != priority) {
        return false;
      }

      // 完成状态筛选
      if (isCompleted != null && todo.isCompleted != isCompleted) {
        return false;
      }

      // 日期范围筛选
      if (dateRange != null && todo.dueDate != null) {
        if (todo.dueDate!.isBefore(dateRange!.start) ||
            todo.dueDate!.isAfter(dateRange!.end)) {
          return false;
        }
      }

      return true;
    }).toList();
  }
}

class DateRange {
  final DateTime start;
  final DateTime end;

  const DateRange({required this.start, required this.end});
}

5. 数据导入导出功能

dart 复制代码
class TodoExportService {
  // 导出为 JSON 格式
  static String exportToJson(List<TodoItem> todos) {
    final todosJson = todos.map((todo) => todo.toJson()).toList();
    return jsonEncode({
      'version': '1.0',
      'exportDate': DateTime.now().toIso8601String(),
      'todos': todosJson,
    });
  }

  // 从 JSON 导入
  static List<TodoItem> importFromJson(String jsonString) {
    final data = jsonDecode(jsonString) as Map<String, dynamic>;
    final todosJson = data['todos'] as List;
    return todosJson.map((json) => TodoItem.fromJson(json)).toList();
  }

  // 导出为 CSV 格式
  static String exportToCsv(List<TodoItem> todos) {
    final buffer = StringBuffer();
    buffer.writeln('标题,描述,优先级,创建时间,截止时间,完成状态');
    
    for (final todo in todos) {
      buffer.writeln([
        todo.title,
        todo.description,
        PriorityConfig.names[todo.priority],
        DateUtils.formatDate(todo.createdAt),
        todo.dueDate != null ? DateUtils.formatDate(todo.dueDate!) : '',
        todo.isCompleted ? '已完成' : '未完成',
      ].join(','));
    }
    
    return buffer.toString();
  }
}

性能优化策略

1. 列表优化

dart 复制代码
// 使用 ListView.builder 优化长列表性能
Widget _buildOptimizedTodoList(List<TodoItem> todos) {
  return ListView.builder(
    itemCount: todos.length,
    itemBuilder: (context, index) {
      final todo = todos[index];
      return _buildTodoCard(todo);
    },
  );
}

// 使用 AutomaticKeepAliveClientMixin 保持页面状态
class _TodoListPageState extends State<TodoListPage> 
    with AutomaticKeepAliveClientMixin {
  
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context); // 必须调用
    return _buildTodoList();
  }
}

2. 状态管理优化

dart 复制代码
// 使用 ValueNotifier 优化局部更新
class TodoNotifier extends ValueNotifier<List<TodoItem>> {
  TodoNotifier(List<TodoItem> todos) : super(todos);

  void addTodo(TodoItem todo) {
    value = [...value, todo];
  }

  void updateTodo(String id, TodoItem updatedTodo) {
    final index = value.indexWhere((todo) => todo.id == id);
    if (index != -1) {
      final newList = [...value];
      newList[index] = updatedTodo;
      value = newList;
    }
  }

  void deleteTodo(String id) {
    value = value.where((todo) => todo.id != id).toList();
  }
}

// 在 Widget 中使用
class TodoListWidget extends StatelessWidget {
  final TodoNotifier todoNotifier;

  const TodoListWidget({super.key, required this.todoNotifier});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<List<TodoItem>>(
      valueListenable: todoNotifier,
      builder: (context, todos, child) {
        return ListView.builder(
          itemCount: todos.length,
          itemBuilder: (context, index) => _buildTodoCard(todos[index]),
        );
      },
    );
  }
}

3. 内存管理

dart 复制代码
class TodoMemoryManager {
  static const int maxTodosInMemory = 1000;

  // 清理已完成的旧任务
  static List<TodoItem> cleanupOldCompletedTodos(List<TodoItem> todos) {
    final completedTodos = todos.where((todo) => todo.isCompleted).toList();
    final pendingTodos = todos.where((todo) => !todo.isCompleted).toList();

    // 保留最近30天内完成的任务
    final thirtyDaysAgo = DateTime.now().subtract(const Duration(days: 30));
    final recentCompletedTodos = completedTodos.where((todo) => 
        todo.createdAt.isAfter(thirtyDaysAgo)
    ).toList();

    return [...pendingTodos, ...recentCompletedTodos];
  }

  // 限制内存中的任务数量
  static List<TodoItem> limitTodosInMemory(List<TodoItem> todos) {
    if (todos.length <= maxTodosInMemory) return todos;

    // 优先保留未完成的任务
    final pendingTodos = todos.where((todo) => !todo.isCompleted).toList();
    final completedTodos = todos.where((todo) => todo.isCompleted).toList();

    if (pendingTodos.length >= maxTodosInMemory) {
      return pendingTodos.take(maxTodosInMemory).toList();
    }

    final remainingSlots = maxTodosInMemory - pendingTodos.length;
    final recentCompletedTodos = completedTodos
        .take(remainingSlots)
        .toList();

    return [...pendingTodos, ...recentCompletedTodos];
  }
}

测试指南

1. 单元测试

dart 复制代码
import 'package:flutter_test/flutter_test.dart';

void main() {
  group('TodoItem Tests', () {
    test('priorityScore calculation should be correct', () {
      final now = DateTime.now();
      
      // 测试基础优先级分数
      final urgentTodo = TodoItem(
        id: '1',
        title: 'Test',
        priority: Priority.urgent,
        createdAt: now,
      );
      expect(urgentTodo.priorityScore, 400); // 4 * 100

      // 测试截止日期影响
      final overdueTodo = TodoItem(
        id: '2',
        title: 'Test',
        priority: Priority.medium,
        createdAt: now,
        dueDate: now.subtract(const Duration(days: 1)),
      );
      expect(overdueTodo.priorityScore, 400); // 2 * 100 + 200
    });

    test('copyWith should update specific fields', () {
      final original = TodoItem(
        id: '1',
        title: 'Original',
        priority: Priority.low,
        createdAt: DateTime.now(),
      );

      final updated = original.copyWith(
        title: 'Updated',
        priority: Priority.high,
      );

      expect(updated.title, 'Updated');
      expect(updated.priority, Priority.high);
      expect(updated.id, original.id); // 不变的字段
    });
  });

  group('Sorting Tests', () {
    test('todos should be sorted by priority score', () {
      final now = DateTime.now();
      final todos = [
        TodoItem(
          id: '1',
          title: 'Low Priority',
          priority: Priority.low,
          createdAt: now,
        ),
        TodoItem(
          id: '2',
          title: 'Urgent',
          priority: Priority.urgent,
          createdAt: now,
        ),
        TodoItem(
          id: '3',
          title: 'Medium',
          priority: Priority.medium,
          createdAt: now,
        ),
      ];

      todos.sort((a, b) => b.priorityScore.compareTo(a.priorityScore));

      expect(todos[0].priority, Priority.urgent);
      expect(todos[1].priority, Priority.medium);
      expect(todos[2].priority, Priority.low);
    });
  });
}

2. Widget 测试

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

void main() {
  group('TodoCard Widget Tests', () {
    testWidgets('should display todo information correctly', (tester) async {
      final todo = TodoItem(
        id: '1',
        title: 'Test Todo',
        description: 'Test Description',
        priority: Priority.high,
        createdAt: DateTime.now(),
      );

      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: TodoCard(todo: todo),
          ),
        ),
      );

      expect(find.text('Test Todo'), findsOneWidget);
      expect(find.text('Test Description'), findsOneWidget);
      expect(find.text('高'), findsOneWidget);
    });

    testWidgets('should toggle completion status', (tester) async {
      bool isCompleted = false;
      final todo = TodoItem(
        id: '1',
        title: 'Test Todo',
        priority: Priority.medium,
        createdAt: DateTime.now(),
        isCompleted: isCompleted,
      );

      await tester.pumpWidget(
        MaterialApp(
          home: Scaffold(
            body: StatefulBuilder(
              builder: (context, setState) {
                return TodoCard(
                  todo: todo,
                  onToggle: (value) {
                    setState(() {
                      isCompleted = value ?? false;
                    });
                  },
                );
              },
            ),
          ),
        ),
      );

      // 点击复选框
      await tester.tap(find.byType(Checkbox));
      await tester.pump();

      expect(isCompleted, true);
    });
  });
}

3. 集成测试

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

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  group('Todo App Integration Tests', () {
    testWidgets('complete todo workflow', (tester) async {
      await tester.pumpWidget(const TodoPriorityApp());

      // 1. 验证初始状态
      expect(find.text('待办事项优先级排序'), findsOneWidget);

      // 2. 添加新任务
      await tester.tap(find.byType(FloatingActionButton));
      await tester.pumpAndSettle();

      await tester.enterText(find.byType(TextField).first, '测试任务');
      await tester.tap(find.text('添加'));
      await tester.pumpAndSettle();

      // 3. 验证任务已添加
      expect(find.text('测试任务'), findsOneWidget);

      // 4. 切换到统计页面
      await tester.tap(find.text('统计'));
      await tester.pumpAndSettle();

      // 5. 验证统计信息
      expect(find.text('总体统计'), findsOneWidget);
      expect(find.text('1'), findsOneWidget); // 总任务数
    });
  });
}

部署指南

1. Android 部署

权限配置 (android/app/src/main/AndroidManifest.xml)
xml 复制代码
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
构建配置 (android/app/build.gradle)
gradle 复制代码
android {
    compileSdkVersion 34
    
    defaultConfig {
        applicationId "com.example.todo_priority"
        minSdkVersion 21
        targetSdkVersion 34
        versionCode 1
        versionName "1.0.0"
    }
    
    buildTypes {
        release {
            signingConfig signingConfigs.release
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

2. iOS 部署

权限配置 (ios/Runner/Info.plist)
xml 复制代码
<key>NSUserNotificationUsageDescription</key>
<string>需要通知权限来提醒您的待办事项</string>

3. 构建命令

bash 复制代码
# 清理项目
flutter clean

# 获取依赖
flutter pub get

# Android APK (调试版)
flutter build apk --debug

# Android APK (发布版)
flutter build apk --release

# Android App Bundle (推荐用于 Google Play)
flutter build appbundle --release

# iOS (发布版)
flutter build ios --release

4. 性能优化构建

bash 复制代码
# 启用代码混淆和压缩
flutter build apk --release --obfuscate --split-debug-info=build/debug-info

# 构建不同架构的 APK
flutter build apk --release --split-per-abi

项目总结

待办事项优先级排序应用是一个功能完整的任务管理工具,展示了以下技术要点:

技术亮点

  1. 智能排序算法:综合考虑优先级和时间因素的动态排序
  2. Material Design 3:现代化的UI设计和交互体验
  3. 状态管理:高效的状态更新和数据流管理
  4. 日期处理:完善的日期格式化和相对时间计算
  5. 数据建模:清晰的数据结构和业务逻辑封装

学习价值

  • 算法设计:学习优先级排序算法的设计和实现
  • 状态管理:掌握Flutter中的状态管理最佳实践
  • UI设计:了解现代移动应用的界面设计原则
  • 数据处理:学习日期时间处理和数据格式化技巧

扩展方向

  1. 数据持久化:集成本地数据库存储
  2. 云端同步:支持多设备数据同步
  3. 通知提醒:添加本地通知功能
  4. 主题定制:支持多种主题和个性化设置
  5. 数据分析:提供更详细的任务完成统计

这个项目为学习Flutter应用开发提供了完整的实践案例,涵盖了状态管理、UI设计、算法实现等多个方面,是一个优秀的学习和参考项目。

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

相关推荐
鸣弦artha2 小时前
Flutter框架跨平台鸿蒙开发——Text样式详解
flutter
小白阿龙2 小时前
鸿蒙+Flutter 跨平台开发——防止预测的真随机密码生成器设计
flutter·华为·harmonyos·鸿蒙
南村群童欺我老无力.3 小时前
Flutter 框架跨平台鸿蒙开发 - 打造手写签名板应用
flutter·华为·harmonyos
哈哈你是真的厉害3 小时前
基础入门 React Native 鸿蒙跨平台开发:AnimatedXY 动画插值
react native·react.js·harmonyos
2501_944526424 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 抽牌游戏实现
android·开发语言·python·flutter·游戏
夜雨声烦丿4 小时前
Flutter 框架跨平台鸿蒙开发 - 游戏存档管理器应用开发教程
flutter·游戏·华为·harmonyos
[H*]5 小时前
Flutter框架跨平台鸿蒙开发——资源图片加载
flutter
2501_944526425 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 笑话生成器实现
android·javascript·python·flutter·游戏
2501_944526425 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 21点游戏实现
android·javascript·flutter·游戏·harmonyos