From 0e6fe4ad4b7355ba0b225fc28b9cafd686a7425a Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Wed, 29 Apr 2026 19:21:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E5=BE=85=E5=8A=9E?= =?UTF-8?q?=E6=8F=90=E9=86=92=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/icons/app_icon.ico | Bin 0 -> 171755 bytes assets/icons/task.ico | Bin 4286 -> 0 bytes assets/icons/task.png | Bin 1321 -> 0 bytes lib/main.dart | 19 +++++++- lib/models/schedule.dart | 6 +-- lib/models/task.dart | 9 ++-- lib/models/task.g.dart | 65 +++++++++++++++++++++++++++ lib/notice.dart | 31 ------------- lib/pages/task_page.dart | 44 ++++++++++++++----- lib/utils/common.dart | 3 ++ lib/utils/notice.dart | 82 +++++++++++++++++++++++++++++++++++ lib/widgets/common.dart | 3 ++ lib/widgets/task_dialog.dart | 19 +++++++- lib/widgets/task_item.dart | 20 ++++----- pubspec.lock | 58 ++++++++++++++++++++++++- pubspec.yaml | 2 +- 16 files changed, 297 insertions(+), 64 deletions(-) create mode 100644 assets/icons/app_icon.ico delete mode 100644 assets/icons/task.ico delete mode 100644 assets/icons/task.png create mode 100644 lib/models/task.g.dart delete mode 100644 lib/notice.dart create mode 100644 lib/utils/common.dart create mode 100644 lib/utils/notice.dart diff --git a/assets/icons/app_icon.ico b/assets/icons/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..fe61b28f504ed8d03c03162d9a03761d691fbacc GIT binary patch literal 171755 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 diff --git a/lib/main.dart b/lib/main.dart index f9f48ee..fd0b25a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,13 +1,28 @@ import 'package:fluent_ui/fluent_ui.dart'; +import 'package:hive_flutter/adapters.dart'; +import 'package:task_hub/models/task.dart'; import 'package:task_hub/pages/home_page.dart'; import 'package:task_hub/pages/schedule_page.dart'; import 'package:task_hub/pages/settings_page.dart'; import 'package:task_hub/pages/task_page.dart'; +import 'package:task_hub/utils/notice.dart'; +import 'package:timezone/data/latest.dart' as tz; import 'package:window_size/window_size.dart'; -void main() { +void main() async { WidgetsFlutterBinding.ensureInitialized(); setWindowMinSize(const Size(800, 600)); + + tz.initializeTimeZones(); + + await Hive.initFlutter(); + Hive.registerAdapter(TaskAdapter()); + await Hive.openBox('taskBox'); + + await initNotify(); + + checkScheduleTime(); + runApp(const TaskHubApp()); } @@ -21,7 +36,7 @@ class TaskHubApp extends StatelessWidget { themeMode: ThemeMode.system, theme: FluentThemeData( fontFamily: 'CustomFont', - accentColor: Colors.purple + accentColor: Colors.purple, ), debugShowCheckedModeBanner: false, home: const MainLayout(), diff --git a/lib/models/schedule.dart b/lib/models/schedule.dart index bc4d26f..021ae45 100644 --- a/lib/models/schedule.dart +++ b/lib/models/schedule.dart @@ -13,6 +13,6 @@ class Schedule { required this.endTime, this.scheduleTime, DateTime? createTime, - }) : id = id ?? DateTime.timestamp().microsecond, - createTime = DateTime.now(); -} \ No newline at end of file + }) : id = id ?? DateTime.now().millisecondsSinceEpoch ~/ 1000, + createTime = DateTime.now(); +} diff --git a/lib/models/task.dart b/lib/models/task.dart index de46c3f..80b497e 100644 --- a/lib/models/task.dart +++ b/lib/models/task.dart @@ -1,7 +1,7 @@ import 'package:hive/hive.dart'; // flutter packages pub run build_runner build -// part 'todo.g.dart'; +part 'task.g.dart'; @HiveType(typeId: 0) class Task extends HiveObject { @@ -45,10 +45,11 @@ class Task extends HiveObject { this.isCompleted = false, Priority priority = Priority.medium, DateTime? createTime, - }) : id = id ?? DateTime.timestamp().microsecond, + DateTime? updateTime, + }) : id = id ?? DateTime.now().millisecondsSinceEpoch ~/ 1000, this.priorityIndex = priority.index, - createTime = DateTime.now(), - updateTime = DateTime.now(); + createTime = createTime ?? DateTime.now(), + updateTime = updateTime ?? DateTime.now(); } enum Priority { low, medium, high } diff --git a/lib/models/task.g.dart b/lib/models/task.g.dart new file mode 100644 index 0000000..1c1927a --- /dev/null +++ b/lib/models/task.g.dart @@ -0,0 +1,65 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'task.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class TaskAdapter extends TypeAdapter { + @override + final int typeId = 0; + + @override + Task read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return Task( + id: fields[0] as int?, + title: fields[1] as String, + desc: fields[2] as String?, + dueDate: fields[3] as DateTime?, + scheduleTime: fields[4] as DateTime?, + isCompleted: fields[5] as bool, + createTime: fields[7] as DateTime?, + ) + ..priorityIndex = fields[6] as int + ..updateTime = fields[8] as DateTime; + } + + @override + void write(BinaryWriter writer, Task obj) { + writer + ..writeByte(9) + ..writeByte(0) + ..write(obj.id) + ..writeByte(1) + ..write(obj.title) + ..writeByte(2) + ..write(obj.desc) + ..writeByte(3) + ..write(obj.dueDate) + ..writeByte(4) + ..write(obj.scheduleTime) + ..writeByte(5) + ..write(obj.isCompleted) + ..writeByte(6) + ..write(obj.priorityIndex) + ..writeByte(7) + ..write(obj.createTime) + ..writeByte(8) + ..write(obj.updateTime); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is TaskAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/notice.dart b/lib/notice.dart deleted file mode 100644 index 09766d4..0000000 --- a/lib/notice.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter_local_notifications/flutter_local_notifications.dart'; - -// 初始化通知 -Future _initNotify() async { - final notify = FlutterLocalNotificationsPlugin(); - await notify.initialize( - const InitializationSettings( - windows: WindowsInitializationSettings( - appName: 'TaskHub', - appUserModelId: 'com.cxx.task', - guid: 'f47ac10b-58cc-4372-a567-0e02b2c3d479', - iconPath: 'assets/icons/task.png' - ), - ), - ); -} - -// 发送通知 -Future _showNotify(String title, String text) async { - final notify = FlutterLocalNotificationsPlugin(); - final id = DateTime.now().millisecondsSinceEpoch.remainder(10000); - - await notify.show( - id, - title, - text, - const NotificationDetails( - windows: WindowsNotificationDetails(), - ), - ); -} \ No newline at end of file diff --git a/lib/pages/task_page.dart b/lib/pages/task_page.dart index 000f1d2..bc8c4c6 100644 --- a/lib/pages/task_page.dart +++ b/lib/pages/task_page.dart @@ -2,7 +2,9 @@ import 'dart:ui'; import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter/material.dart' show SegmentedButton, ButtonSegment; +import 'package:hive/hive.dart'; import 'package:task_hub/models/task.dart'; +import 'package:task_hub/utils/notice.dart'; import 'package:task_hub/widgets/task_dialog.dart'; import 'package:task_hub/widgets/task_item.dart'; @@ -15,10 +17,24 @@ class TaskPage extends StatefulWidget { } class _TaskPageState extends State { - final List _tasks = []; + late Box _taskBox; + List _tasks = []; String _filter = 'all'; String _sortBy = 'created'; + @override + void initState() { + super.initState(); + _loadTasks(); + } + + void _loadTasks() { + _taskBox = Hive.box('taskBox'); + setState(() { + _tasks = _taskBox.values.toList(); + }); + } + List get _filteredTasks { List tasks = _tasks.where((task) { if (_filter == 'active') return !task.isCompleted; @@ -48,10 +64,12 @@ class _TaskPageState extends State { showDialog( context: context, builder: (context) => TaskDialog( - onSave: (task) { + onSave: (task) async { + await _taskBox.put(task.id, task); setState(() { - _tasks.add(task); + _tasks = _taskBox.values.toList(); }); + checkScheduleTime(); }, ), ); @@ -62,22 +80,26 @@ class _TaskPageState extends State { context: context, builder: (context) => TaskDialog( task: task, - onSave: (editedTask) { + onSave: (editedTask) async { + editedTask.updateTime = DateTime.now(); + await _taskBox.put(editedTask.id, editedTask); setState(() { - final index = _tasks.indexWhere((t) => t.id == task.id); - if (index != -1) { - _tasks[index] = editedTask; - } + _tasks = _taskBox.values.toList(); }); + checkScheduleTime(); }, ), ); } - void _deleteTask(int taskId) { + void _deleteTask(int taskId) async { + final task = _tasks.firstWhere((t) => t.id == taskId); + await task.delete(); + setState(() { - _tasks.removeWhere((task) => task.id == taskId); + _tasks = _taskBox.values.toList(); }); + checkScheduleTime(); } void _toggleTaskComplete(Task task) { @@ -91,7 +113,7 @@ class _TaskPageState extends State { createTime: task.createTime, dueDate: task.dueDate, isCompleted: !task.isCompleted, - priority: task.priority + priority: task.priority, ); } }); diff --git a/lib/utils/common.dart b/lib/utils/common.dart new file mode 100644 index 0000000..818244a --- /dev/null +++ b/lib/utils/common.dart @@ -0,0 +1,3 @@ +bool isSameDate(DateTime a, DateTime b) { + return a.year == b.year && a.month == b.month && a.day == b.day; +} \ No newline at end of file diff --git a/lib/utils/notice.dart b/lib/utils/notice.dart new file mode 100644 index 0000000..ac57226 --- /dev/null +++ b/lib/utils/notice.dart @@ -0,0 +1,82 @@ +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:hive/hive.dart'; +import 'package:timezone/timezone.dart' as tz; + +import '../models/task.dart'; +import 'common.dart'; + +// 初始化通知 +Future initNotify() async { + final notify = FlutterLocalNotificationsPlugin(); + await notify.initialize( + const InitializationSettings( + windows: WindowsInitializationSettings( + appName: 'TaskHub', + appUserModelId: 'com.cxx.task', + guid: 'f47ac10b-58cc-4372-a567-0e02b2c3d479', + iconPath: 'assets/icons/app_icon.ico', + ), + ), + ); +} + +Future showNotify(String title, String body) async { + final notify = FlutterLocalNotificationsPlugin(); + final id = DateTime.now().millisecondsSinceEpoch.remainder(10000); + + await notify.show( + id, + title, + body, + const NotificationDetails(windows: WindowsNotificationDetails()), + ); +} + +// 发送通知 +Future scheduleNotification( + int id, + String title, + String body, + DateTime scheduleTime, +) async { + final notify = FlutterLocalNotificationsPlugin(); + + final local = tz.getLocation('Asia/Shanghai'); + final tzTime = tz.TZDateTime.from(scheduleTime, local); + + await notify.zonedSchedule( + id, + title, + body, + tzTime, + const NotificationDetails(windows: WindowsNotificationDetails()), + androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, + ); +} + +// 取消通知 +Future cancelAllSchedule() async { + final notify = FlutterLocalNotificationsPlugin(); + + await notify.cancelAll(); +} + +void checkScheduleTime() { + final taskBox = Hive.box('taskBox'); + final now = DateTime.now(); + + cancelAllSchedule(); + for (var task in taskBox.values) { + if (task.scheduleTime != null && + !task.isCompleted && + isSameDate(task.scheduleTime!, DateTime.now()) && + task.scheduleTime!.isAfter(now)) { + scheduleNotification( + task.id, + task.title, + task.desc ?? '', + task.scheduleTime!, + ); + } + } +} diff --git a/lib/widgets/common.dart b/lib/widgets/common.dart index 479c345..9020b54 100644 --- a/lib/widgets/common.dart +++ b/lib/widgets/common.dart @@ -79,6 +79,9 @@ Widget buildOmniDateButton({ onPressed: () async { final DateTime? picked = await showOmniDateTimePicker( context: context, + constraints: const BoxConstraints( + maxWidth: 400, + ), initialDate: selectedDate ?? DateTime.now(), firstDate: DateTime( DateTime.now().year, diff --git a/lib/widgets/task_dialog.dart b/lib/widgets/task_dialog.dart index 72405dd..18094ad 100644 --- a/lib/widgets/task_dialog.dart +++ b/lib/widgets/task_dialog.dart @@ -19,6 +19,7 @@ class _TaskDialogState extends State { late Priority _selectedPriority; DateTime? _selectedDueDate; DateTime? _selectedScheduleTime; + DateTime? _createTime; final _formKey = GlobalKey(); @override @@ -29,6 +30,7 @@ class _TaskDialogState extends State { _selectedPriority = widget.task?.priority ?? Priority.medium; _selectedDueDate = widget.task?.dueDate; _selectedScheduleTime = widget.task?.scheduleTime; + _createTime = widget.task?.createTime; } @override @@ -38,8 +40,22 @@ class _TaskDialogState extends State { super.dispose(); } - void onConfirmClick() { + void onConfirmClick() async { if (_formKey.currentState!.validate()) { + if (_selectedScheduleTime != null && + _selectedScheduleTime!.isBefore(DateTime.now())) { + await displayInfoBar( + context, + builder: (context, close) { + return InfoBar( + title: const Text('提醒时间不能早于当前时间'), + severity: InfoBarSeverity.error, + ); + }, + ); + return; + } + final task = Task( id: widget.task?.id, title: _titleController.text, @@ -48,6 +64,7 @@ class _TaskDialogState extends State { scheduleTime: _selectedScheduleTime, priority: _selectedPriority, isCompleted: widget.task?.isCompleted ?? false, + createTime: _createTime, ); widget.onSave(task); Navigator.pop(context); diff --git a/lib/widgets/task_item.dart b/lib/widgets/task_item.dart index df530fb..4ad5dc9 100644 --- a/lib/widgets/task_item.dart +++ b/lib/widgets/task_item.dart @@ -1,4 +1,5 @@ import 'package:fluent_ui/fluent_ui.dart'; +import 'package:intl/intl.dart'; import 'package:task_hub/models/task.dart'; class TaskItem extends StatelessWidget { @@ -39,11 +40,14 @@ class TaskItem extends StatelessWidget { Expanded(child: _buildTitle(context)), ], ), - const SizedBox(height: 4), - if (task.desc != null && task.desc!.isNotEmpty) _buildDesc(), - const SizedBox(height: 4), - if (task.dueDate != null || task.scheduleTime != null) + if (task.desc != null && task.desc!.isNotEmpty) ...[ + const SizedBox(height: 4), + _buildDesc(), + ], + if (task.dueDate != null || task.scheduleTime != null) ...[ + const SizedBox(height: 4), _buildDate(), + ] ], ), ), @@ -92,7 +96,7 @@ class TaskItem extends StatelessWidget { Icon(FluentIcons.calendar, size: 12, color: Colors.grey), const SizedBox(width: 4), Text( - '截止日期: ${_formatDate(task.dueDate!)}', + '截止日期: ${DateFormat('yyyy-MM-dd').format(task.dueDate!)}', style: TextStyle(color: Colors.grey, fontSize: 12), ), const SizedBox(width: 4), @@ -101,7 +105,7 @@ class TaskItem extends StatelessWidget { Icon(FluentIcons.clock, size: 12, color: Colors.grey), const SizedBox(width: 4), Text( - '提醒日期: ${_formatDate(task.scheduleTime!)}', + '提醒日期: ${DateFormat('yyyy-MM-dd HH:mm:ss').format(task.scheduleTime!)}', style: TextStyle(color: Colors.grey, fontSize: 12), ), ], @@ -181,8 +185,4 @@ class TaskItem extends StatelessWidget { ), ); } - - String _formatDate(DateTime date) { - return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}'; - } } diff --git a/pubspec.lock b/pubspec.lock index 7c081bc..2f27014 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "6.4.1" + archive: + dependency: transitive + description: + name: archive + sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.9" args: dependency: transitive description: @@ -129,6 +137,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.0.4" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.4.2" clock: dependency: transitive description: @@ -153,6 +169,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.19.1" + console: + dependency: transitive + description: + name: console + sha256: e04e7824384c5b39389acdd6dc7d33f3efe6b232f6f16d7626f194f6a01ad69a + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.1.0" convert: dependency: transitive description: @@ -312,6 +336,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.0.0" + get_it: + dependency: transitive + description: + name: get_it + sha256: "568d62f0e68666fb5d95519743b3c24a34c7f19d834b0658c46e26d778461f66" + url: "https://pub.flutter-io.cn" + source: hosted + version: "9.2.1" glob: dependency: transitive description: @@ -376,6 +408,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.1.2" + image: + dependency: transitive + description: + name: image + sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.8.0" intl: dependency: "direct main" description: @@ -504,6 +544,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.0.0" + msix: + dependency: "direct dev" + description: + name: msix + sha256: b6b08e7a7b5d1845f2b1d31216d5b1fb558e98251efefe54eb79ed00d27bc2ac + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.16.13" nested: dependency: transitive description: @@ -616,6 +664,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.5.2" + posix: + dependency: transitive + description: + name: posix + sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.5.0" provider: dependency: transitive description: @@ -790,7 +846,7 @@ packages: source: hosted version: "0.7.7" timezone: - dependency: transitive + dependency: "direct main" description: name: timezone sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1 diff --git a/pubspec.yaml b/pubspec.yaml index 1c2afee..197bbd3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1 +1 @@ -name: task_hub description: "待办事项、日程安排" publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: sdk: ^3.10.0 dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 flutter_local_notifications: ^19.5.0 syncfusion_flutter_calendar: ^30.1.37 fluent_ui: ^4.15.0 omni_datetime_picker: ^2.3.1 intl: ^0.20.2 window_size: ^0.1.0 hive: ^2.2.3 hive_flutter: ^1.1.0 dev_dependencies: flutter_test: sdk: flutter hive_generator: ^2.0.1 build_runner: ^2.4.6 flutter_lints: ^5.0.0 flutter: uses-material-design: true assets: - assets/icons/ fonts: - family: CustomFont fonts: - asset: fonts/custom.ttf \ No newline at end of file +name: task_hub description: "待办事项、日程安排" publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: sdk: ^3.10.0 dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 flutter_local_notifications: ^19.5.0 syncfusion_flutter_calendar: ^30.1.37 fluent_ui: ^4.15.0 omni_datetime_picker: ^2.3.1 intl: ^0.20.2 window_size: ^0.1.0 hive: ^2.2.3 hive_flutter: ^1.1.0 timezone: ^0.10.1 dev_dependencies: flutter_test: sdk: flutter hive_generator: ^2.0.1 build_runner: ^2.4.6 flutter_lints: ^5.0.0 msix: ^3.16.13 flutter: uses-material-design: true assets: - assets/icons/ fonts: - family: CustomFont fonts: - asset: fonts/custom.ttf msix_config: display_name: TaskHub publisher_display_name: Cxx identity_name: com.cxx.task msix_version: 1.0.0.0 logo_path: assets/icons/app_icon.ico capabilities: internetClient \ No newline at end of file