diff --git a/lib/models/todo.dart b/lib/models/todo.dart index 6906c52..476fa8e 100644 --- a/lib/models/todo.dart +++ b/lib/models/todo.dart @@ -111,3 +111,5 @@ enum TodoTab { const TodoTab(this.label); } + +enum TodoSortMode { time, priority } diff --git a/lib/pages/todo_page.dart b/lib/pages/todo_page.dart index 22be58b..882540c 100644 --- a/lib/pages/todo_page.dart +++ b/lib/pages/todo_page.dart @@ -26,6 +26,7 @@ class TodoPageState extends State { late List _todos; TodoTab _currentTab = TodoTab.active; + late TodoSortMode _selectedMode = TodoSortMode.time; // 获取过滤后的待办事项 List get _activeTodos => getActiveTodos(_currentTab, _todos); @@ -37,7 +38,13 @@ class TodoPageState extends State { @override void initState() { super.initState(); - _todos = todoService.getAllTodos(); + _refreshTodos(); + } + + void _refreshTodos() { + setState(() { + _todos = todoService.getAllTodos(_selectedMode); + }); } @override @@ -45,6 +52,7 @@ class TodoPageState extends State { return Column( children: [ _buildTodoTabs(), + _buildTodoSegment(), Expanded(child: _buildActiveTodoList(context)), ], ); @@ -62,6 +70,36 @@ class TodoPageState extends State { ); } + Widget _buildTodoSegment() { + final colors = Theme.of(context).colorScheme; + + return SegmentedButton( + showSelectedIcon: false, + emptySelectionAllowed: false, + multiSelectionEnabled: false, + segments: [ + ButtonSegment( + value: TodoSortMode.time, + icon: Icon(Icons.access_time_filled, size: 16), + label: const Text('时间', style: TextStyle(fontSize: 12)), + ), + ButtonSegment( + value: TodoSortMode.priority, + icon: Icon(Icons.flag_outlined, size: 16,), + label: const Text('优先级', style: TextStyle(fontSize: 12)), + ), + ], + selected: {_selectedMode}, + onSelectionChanged: (Set newSelection) { + setState(() { + _selectedMode = newSelection.first; + }); + _refreshTodos(); + }, + style: buildSegmentStyle(colors), + ); + } + Widget _buildActiveTodoList(BuildContext context) { return _activeTodos.isEmpty ? buildEmptyState(context, _currentTab) @@ -142,9 +180,7 @@ class TodoPageState extends State { ); } - setState(() { - _todos = todoService.getAllTodos(); - }); + _refreshTodos(); if (isSuccess) { showSuccessDialog(context, isEditing ? '更新成功' : '添加成功'); @@ -157,9 +193,7 @@ class TodoPageState extends State { await notifyService.cancelNotification(todo.id); bool isSuccess = await todoService.deleteTodo(todo); - setState(() { - _todos = todoService.getAllTodos(); - }); + _refreshTodos(); if (isSuccess) { showSuccessDialog(context, '删除成功'); diff --git a/lib/service/todo_service.dart b/lib/service/todo_service.dart index d8a059b..6bf1a84 100644 --- a/lib/service/todo_service.dart +++ b/lib/service/todo_service.dart @@ -15,9 +15,29 @@ class TodoService { } } - List getAllTodos() { - return box.values.toList() - ..sort((a, b) => b.updateTime.compareTo(a.updateTime)); + List getAllTodos(TodoSortMode mode) { + if (mode == TodoSortMode.time) { + return box.values.toList() + ..sort((a, b) { + // 先按是否有 dueDate 分组:有时间的排前面 + if (a.dueDate != null && b.dueDate == null) { + return -1; // a有时间,b没有时间,a排在前面 + } else if (a.dueDate == null && b.dueDate != null) { + return 1; // a没有时间,b有时间,b排在前面 + } + // 都有 dueDate,按 dueDate 排序 + else if (a.dueDate != null && b.dueDate != null) { + return a.dueDate!.compareTo(b.dueDate!); // 最近的在前面 + } + // 都没有 dueDate,按 updateTime 排序 + else { + return b.updateTime.compareTo(a.updateTime); // 最近的在前面 + } + }); + } else { + return box.values.toList() + ..sort((a, b) => b.priorityIndex.compareTo(a.priorityIndex)); + } } List getAllTodosByYear(int year) { diff --git a/lib/widgets/common.dart b/lib/widgets/common.dart index 9784ce4..3df4663 100644 --- a/lib/widgets/common.dart +++ b/lib/widgets/common.dart @@ -213,6 +213,41 @@ Widget buildToggleSwitch({ ); } +ButtonStyle buildSegmentStyle(ColorScheme colors) { + return ButtonStyle( + side: WidgetStateProperty.all( + const BorderSide(color: Colors.transparent, width: 0), + ), + iconColor: WidgetStateProperty.resolveWith( + (Set states) { + if (states.contains(WidgetState.selected)) { + return colors.onPrimary; + } + return Colors.grey; + }, + ), + backgroundColor: WidgetStateProperty.resolveWith(( + Set states, + ) { + if (states.contains(WidgetState.selected)) { + return colors.primary; + } + return Colors.grey.shade200; + }), + foregroundColor: WidgetStateProperty.resolveWith(( + Set states, + ) { + if (states.contains(WidgetState.selected)) { + return colors.onPrimary; + } + return colors.onSurface; + }), + shape: WidgetStateProperty.all( + RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + ); +} + Widget circleIconButton({ required IconData icon, required VoidCallback onPressed,