import 'package:flisp_app/models/todo.dart'; import 'package:flisp_app/utils/todo_utils.dart'; import 'package:flisp_app/widgets/common.dart'; import 'package:flutter/material.dart'; import 'package:flutter_common/widget/common_widget.dart'; import 'package:intl/intl.dart'; // 统计卡片 Widget buildTodoStatsCard(BuildContext context, List todos) { int totalCount = todos.length; int activeCount = todos.where((todo) => !todo.isCompleted).length; int completedCount = todos.where((todo) => todo.isCompleted).length; return CommonCard( child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: [ _buildStatItem(context, '总计', totalCount), _buildStatItem(context, '待完成', activeCount), _buildStatItem(context, '已完成', completedCount), ], ), ); } Widget _buildStatItem(BuildContext context, String label, int count) { return Column( children: [ Text( count.toString(), style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.primary, ), ), const SizedBox(height: 4), Text(label, style: const TextStyle(fontSize: 12, color: Colors.grey)), ], ); } // Tab页 Widget buildTabs({ required BuildContext context, required TodoTab currentTab, required ValueChanged onTabChanged, }) { return Container( padding: EdgeInsets.all(8), child: buildToggleSwitch( context: context, currentTab: currentTab, tabValues: TodoTab.values, labels: TodoTab.values.map((e) => e.label).toList(), onTabChanged: onTabChanged, ), ); } Widget buildTodoList({ required BuildContext context, required List todos, required ValueChanged onToggleTodo, required ValueChanged onEditTodo, required ValueChanged onDelete, }) { return ListView.separated( itemCount: todos.length, separatorBuilder: (context, index) => SizedBox(height: 8), itemBuilder: (context, index) { final todo = todos[index]; return _buildTodoItem( context: context, todo: todo, onToggle: onToggleTodo, onEdit: onEditTodo, onDelete: onDelete, ); }, ); } Widget _buildTodoItem({ required BuildContext context, required Todo todo, required ValueChanged onToggle, required ValueChanged onEdit, required ValueChanged onDelete, }) { return buildDismissible( context: context, id: todo.id, onDelete: () => onDelete(todo), child: CommonCard( child: ListTile( contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 0), leading: SizedBox( width: 24, child: Checkbox( value: todo.isCompleted, onChanged: (value) => onToggle(todo), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), title: _buildTodoTitle(todo), subtitle: _buildTodoSubtitle(todo), trailing: _buildPriorityBadge(todo.priority), onTap: () => onEdit(todo), // onLongPress: () => onShowOptions(todo), ), ), ); } Widget _buildTodoTitle(Todo todo) { return Text( todo.title, style: TextStyle( decoration: todo.isCompleted ? TextDecoration.lineThrough : null, color: todo.isCompleted ? Colors.grey : null, fontSize: 16, fontWeight: FontWeight.w500, ), ); } Widget _buildContent(Todo todo) { return Padding( padding: const EdgeInsets.only(bottom: 8), child: Text( todo.content, maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 13, color: Colors.grey[600], height: 1.3), ), ); } Widget _buildDueDateChip(Todo todo) { final isOverdue = todo.dueDate != null && todo.dueDate!.isBefore(DateTime.now()) && !todo.isCompleted; final isDueToday = todo.dueDate != null && isSameDay(todo.dueDate!, DateTime.now()); final isDueTomorrow = todo.dueDate != null && isSameDay(todo.dueDate!, DateTime.now().add(Duration(days: 1))); return _buildInfoChip( icon: Icons.access_time_rounded, text: formatDueDate(todo.dueDate!, isDueToday, isDueTomorrow), backgroundColor: isOverdue ? Colors.red[50]! : (isDueToday ? Colors.orange[50]! : Colors.grey[50]!), borderColor: isOverdue ? Colors.red[200]! : (isDueToday ? Colors.orange[200]! : Colors.grey[300]!), textColor: isOverdue ? Colors.red[700]! : (isDueToday ? Colors.orange[700]! : Colors.grey[700]!), iconColor: isOverdue ? Colors.red[500]! : (isDueToday ? Colors.orange[500]! : Colors.grey[500]!), ); } Widget _buildScheduledTimeChip(Todo todo) { return _buildInfoChip( icon: Icons.notifications_active_rounded, text: DateFormat("MM-dd HH:mm").format(todo.scheduledTime!), backgroundColor: Colors.blue[50]!, borderColor: Colors.blue[200]!, textColor: Colors.blue[700]!, iconColor: Colors.blue[500]!, ); } // 构建待办事项副标题 Widget? _buildTodoSubtitle(Todo todo) { final hasContent = todo.content.isNotEmpty == true || todo.dueDate != null || todo.scheduledTime != null; if (!hasContent) return null; return Padding( padding: const EdgeInsets.only(top: 6), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 内容文本 if (todo.content.isNotEmpty == true) _buildContent(todo), // 标签容器 Wrap( spacing: 6, runSpacing: 6, children: [ // 截止日期标签 if (todo.dueDate != null) _buildDueDateChip(todo), // 提醒时间标签 if (todo.scheduledTime != null) _buildScheduledTimeChip(todo), ], ), ], ), ); } // 构建信息标签 Widget _buildInfoChip({ required IconData icon, required String text, required Color backgroundColor, required Color borderColor, required Color textColor, required Color iconColor, }) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(12), border: Border.all(color: borderColor, width: 1), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 12, color: iconColor), const SizedBox(width: 4), Text( text, style: TextStyle( fontSize: 10, color: textColor, fontWeight: FontWeight.w500, height: 1.2, ), ), ], ), ); } // 构建优先级徽章 Widget _buildPriorityBadge(TodoPriority priority) { return Chip( label: Text(priority.label), backgroundColor: priority.color, labelStyle: TextStyle(color: Colors.white, fontWeight: FontWeight.bold), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: Colors.white), ), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, visualDensity: VisualDensity.compact, ); } // 空状态 Widget buildEmptyState(BuildContext context, TodoTab tab) { final messages = { TodoTab.all: '📝 还没有待办事项\n点击➕号添加第一个任务吧~', TodoTab.active: '🎯 没有待完成的任务\n享受轻松时光吧!✨', TodoTab.completed: '🎉 还没有完成的任务\n加油哦!💪', TodoTab.today: '📅 今天没有安排任务\n好好放松一下吧~😊', }; final colors = Theme.of(context).colorScheme; return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.checklist, size: 80, color: colors.primary), const SizedBox(height: 16), Text( messages[tab] ?? '暂无数据', textAlign: TextAlign.center, style: TextStyle(color: colors.onSurface, fontSize: 16), ), ], ), ); }