feat: 重构 Reminder Notification 系统并更新应用包名

This commit is contained in:
qzl
2026-03-30 18:36:57 +08:00
parent 9fb2a6857b
commit 91bf3c3f96
90 changed files with 5133 additions and 3017 deletions
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:drag_and_drop_lists/drag_and_drop_lists.dart';
import 'package:go_router/go_router.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../../../../app/di/injection.dart';
@@ -398,65 +397,6 @@ class _TodoQuadrantsScreenState extends State<TodoQuadrantsScreen> {
Widget _buildDragBoard() {
final colorScheme = Theme.of(context).colorScheme;
final palette = Theme.of(context).extension<AppColorPalette>()!;
final quadrants = [
_QuadrantMeta(
value: 1,
title: context.l10n.todoQuadrantImportantUrgent,
textColor: palette.g1Text,
dividerColor: palette.g1Divider,
borderColor: palette.g1Border,
items: _importantUrgent,
),
_QuadrantMeta(
value: 3,
title: context.l10n.todoQuadrantUrgentNotImportant,
textColor: palette.g3Text,
dividerColor: palette.g3Divider,
borderColor: palette.g3Border,
items: _urgentNotImportant,
),
_QuadrantMeta(
value: 2,
title: context.l10n.todoQuadrantImportantNotUrgent,
textColor: palette.g2Text,
dividerColor: palette.g2Divider,
borderColor: palette.g2Border,
items: _importantNotUrgent,
),
];
final lists = quadrants
.map(
(meta) => DragAndDropList(
canDrag: false,
header: _buildQuadrantHeader(meta),
contentsWhenEmpty: _buildEmptyQuadrant(),
lastTarget: const SizedBox(height: AppSpacing.lg),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: meta.borderColor),
),
children: meta.items
.map(
(item) => DragAndDropItem(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.sm,
),
child: _TodoItemWidget(
key: ValueKey(item.id),
item: item,
onComplete: () => _completeTodo(item),
onTap: () => _navigateToDetail(item),
),
),
),
)
.toList(growable: false),
),
)
.toList(growable: false);
return SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(
@@ -465,36 +405,199 @@ class _TodoQuadrantsScreenState extends State<TodoQuadrantsScreen> {
AppSpacing.lg,
96,
),
child: DragAndDropLists(
children: lists,
onItemReorder: _onItemReorder,
onListReorder: (oldListIndex, newListIndex) {},
listDivider: const SizedBox(height: AppSpacing.md),
itemDivider: Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
child: Container(height: 1, color: colorScheme.surfaceContainerHigh),
),
listPadding: EdgeInsets.zero,
itemDecorationWhileDragging: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: colorScheme.shadow.withValues(alpha: 0.16),
blurRadius: AppRadius.md,
offset: const Offset(0, AppSpacing.xs),
),
],
),
itemGhost: const SizedBox(height: 42),
itemDragOnLongPress: true,
lastItemTargetHeight: AppSpacing.xl,
disableScrolling: true,
child: Column(
children: [
_buildQuadrant(
value: 1,
title: context.l10n.todoQuadrantImportantUrgent,
textColor: palette.g1Text,
dividerColor: palette.g1Divider,
borderColor: palette.g1Border,
items: _importantUrgent,
colorScheme: colorScheme,
),
const SizedBox(height: AppSpacing.md),
_buildQuadrant(
value: 3,
title: context.l10n.todoQuadrantUrgentNotImportant,
textColor: palette.g3Text,
dividerColor: palette.g3Divider,
borderColor: palette.g3Border,
items: _urgentNotImportant,
colorScheme: colorScheme,
),
const SizedBox(height: AppSpacing.md),
_buildQuadrant(
value: 2,
title: context.l10n.todoQuadrantImportantNotUrgent,
textColor: palette.g2Text,
dividerColor: palette.g2Divider,
borderColor: palette.g2Border,
items: _importantNotUrgent,
colorScheme: colorScheme,
),
],
),
);
}
Widget _buildQuadrant({
required int value,
required String title,
required Color textColor,
required Color dividerColor,
required Color borderColor,
required List<TodoResponse> items,
required ColorScheme colorScheme,
}) {
return DragTarget<_TodoDragInfo>(
onWillAcceptWithDetails: (details) => true,
onAcceptWithDetails: (details) {
final info = details.data;
if (info.sourceQuadrant != value) {
_onItemReorder(
info.sourceIndex,
_listIndexByQuadrant(info.sourceQuadrant),
0,
_listIndexByQuadrant(value),
);
}
},
builder: (context, candidateData, rejectedData) {
final isHovering = candidateData.isNotEmpty;
return AnimatedContainer(
duration: const Duration(milliseconds: 150),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: isHovering ? colorScheme.primary : borderColor,
width: isHovering ? 2 : 1,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildQuadrantHeader(
_QuadrantMeta(
value: value,
title: title,
textColor: textColor,
dividerColor: dividerColor,
borderColor: borderColor,
items: items,
),
),
if (items.isEmpty)
_buildEmptyContent(colorScheme)
else
..._buildItemList(items, value, colorScheme),
],
),
);
},
);
}
Widget _buildDraggableItem(
TodoResponse item,
int sourceQuadrant,
ColorScheme colorScheme,
) {
final sourceIndex = _sortedQuadrantTodos(
sourceQuadrant,
).indexWhere((t) => t.id == item.id);
final dragInfo = _TodoDragInfo(
todoId: item.id,
sourceQuadrant: sourceQuadrant,
sourceIndex: sourceIndex,
);
return LongPressDraggable<_TodoDragInfo>(
data: dragInfo,
delay: const Duration(milliseconds: 150),
feedback: Material(
elevation: 4,
borderRadius: BorderRadius.circular(AppRadius.md),
child: Container(
width: 280,
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Text(
item.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: colorScheme.onSurfaceVariant,
),
),
),
),
childWhenDragging: Opacity(
opacity: 0.3,
child: _TodoItemWidget(
key: ValueKey(item.id),
item: item,
onComplete: () => _completeTodo(item),
onTap: () => _navigateToDetail(item),
),
),
child: _TodoItemWidget(
key: ValueKey(item.id),
item: item,
onComplete: () => _completeTodo(item),
onTap: () => _navigateToDetail(item),
),
);
}
Widget _buildEmptyContent(ColorScheme colorScheme) {
return SizedBox(
height: 60,
child: Center(
child: Text(
context.l10n.todoNoItems,
style: TextStyle(
fontFamily: 'Inter',
fontSize: 13,
color: colorScheme.outline,
),
),
),
);
}
List<Widget> _buildItemList(
List<TodoResponse> items,
int quadrant,
ColorScheme colorScheme,
) {
final result = <Widget>[];
for (var i = 0; i < items.length; i++) {
result.add(_buildDraggableItem(items[i], quadrant, colorScheme));
if (i < items.length - 1) {
result.add(
Padding(
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
child: Container(
height: 1,
color: colorScheme.surfaceContainerHigh,
),
),
);
}
}
return result;
}
Widget _buildQuadrantHeader(_QuadrantMeta meta) {
return Column(
mainAxisSize: MainAxisSize.min,
@@ -533,21 +636,17 @@ class _TodoQuadrantsScreenState extends State<TodoQuadrantsScreen> {
);
}
Widget _buildEmptyQuadrant() {
final colorScheme = Theme.of(context).colorScheme;
return SizedBox(
height: 60,
child: Center(
child: Text(
context.l10n.todoNoItems,
style: TextStyle(
fontFamily: 'Inter',
fontSize: 13,
color: colorScheme.outline,
),
),
),
);
int _listIndexByQuadrant(int quadrant) {
switch (quadrant) {
case 1:
return 0;
case 3:
return 1;
case 2:
return 2;
default:
return 0;
}
}
Widget _buildBottomDock() {
@@ -571,6 +670,18 @@ class _TodoQuadrantsScreenState extends State<TodoQuadrantsScreen> {
}
}
class _TodoDragInfo {
final String todoId;
final int sourceQuadrant;
final int sourceIndex;
const _TodoDragInfo({
required this.todoId,
required this.sourceQuadrant,
required this.sourceIndex,
});
}
class _ReorderResult {
final List<TodoResponse> todos;
final List<TodoResponse> changedTodos;