From 2e9ab0181f9ef5da239bc29d57346da091b91e50 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Mon, 16 Oct 2023 09:31:34 +0800 Subject: [PATCH 01/19] =?UTF-8?q?=E5=B0=86=E4=B8=BB=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E7=9A=84AppBar=E7=8B=AC=E7=AB=8B=E4=B8=BA=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=96=87=E4=BB=B6TopBar.dart?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 2 +- lib/screen/about_screen.dart | 7 +++-- .../components/TopBar.dart} | 28 +++++++------------ lib/screen/dashboard/dashboard_screen.dart | 23 +++++++++++++++ 4 files changed, 39 insertions(+), 21 deletions(-) rename lib/screen/{dashboard_screen.dart => dashboard/components/TopBar.dart} (88%) create mode 100644 lib/screen/dashboard/dashboard_screen.dart diff --git a/lib/main.dart b/lib/main.dart index 28ea652..1cf032c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:timemanage/screen/dashboard_screen.dart'; +import 'package:timemanage/screen/dashboard/dashboard_screen.dart'; void main() => runApp(const MyApp()); diff --git a/lib/screen/about_screen.dart b/lib/screen/about_screen.dart index e68dff8..f98d11a 100644 --- a/lib/screen/about_screen.dart +++ b/lib/screen/about_screen.dart @@ -40,8 +40,11 @@ class AboutScreen extends StatelessWidget { textAlign: TextAlign.justify, ), // 应用程序的版权信息 - applicationLegalese: - "Copyright © 2023 中国民航大学 计算机科学与技术学院 \n 计算机科学与技术专业 21034102班 \n 庞浩,葛兴海,蔡玉祥,邹兴云,卫俊钢 小组", + applicationLegalese: ''' + Copyright © 2023 中国民航大学 计算机科学与技术学院 + 计算机科学与技术专业 21034102班 + 庞浩,葛兴海,蔡玉祥,邹兴云,卫俊钢 小组 + ''', /** * 列表的每一项,每一项都是一个ListTile */ diff --git a/lib/screen/dashboard_screen.dart b/lib/screen/dashboard/components/TopBar.dart similarity index 88% rename from lib/screen/dashboard_screen.dart rename to lib/screen/dashboard/components/TopBar.dart index 713a934..6042a40 100644 --- a/lib/screen/dashboard_screen.dart +++ b/lib/screen/dashboard/components/TopBar.dart @@ -6,13 +6,17 @@ import 'package:timemanage/screen/settings_screen.dart'; import 'package:timemanage/screen/about_screen.dart'; import 'package:timemanage/screen/course_screen.dart'; -class DashBoardScreen extends StatelessWidget { - const DashBoardScreen({super.key}); +class TopBar extends StatefulWidget implements PreferredSizeWidget { + const TopBar({Key? key}) : super(key: key); + @override + State createState() => _TopBarState(); + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); +} +class _TopBarState extends State { @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( + Widget build(BuildContext context) => AppBar( backgroundColor: Colors.blueAccent, // 背景色 // 最前面的菜单按钮 leading: MenuBar( @@ -98,17 +102,5 @@ class DashBoardScreen extends StatelessWidget { onPressed: () {}, ) ], - ), - body: const Center( - child: Column( - children: [ - Text('项目列表区'), - Expanded( - child: Text('计时器区'), - ), - ], - ), - ), - ); - } + ); } diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart new file mode 100644 index 0000000..b2edfa6 --- /dev/null +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:timemanage/screen/dashboard/components/TopBar.dart'; + +class DashBoardScreen extends StatelessWidget { + const DashBoardScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: TopBar(), + body: const Center( + child: Column( + children: [ + Text('项目列表区'), + Expanded( + child: Text('计时器区'), + ), + ], + ), + ), + ); + } +} -- 2.34.1 From 33afda967819d8473a3b3110a55395f8386f6996 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Mon, 16 Oct 2023 09:47:03 +0800 Subject: [PATCH 02/19] =?UTF-8?q?=E4=B8=BAmain.dart=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BA=86=E4=B8=80=E4=BA=9B=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 2 ++ lib/screen/dashboard/dashboard_screen.dart | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/main.dart b/lib/main.dart index 1cf032c..a1cfea9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:timemanage/screen/dashboard/dashboard_screen.dart'; +/// 程序入口 +/// 不知道为什么,dart的doc注释是三个斜杠 void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart index b2edfa6..c34851e 100644 --- a/lib/screen/dashboard/dashboard_screen.dart +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -1,13 +1,14 @@ import 'package:flutter/material.dart'; import 'package:timemanage/screen/dashboard/components/TopBar.dart'; +/// 主页 class DashBoardScreen extends StatelessWidget { const DashBoardScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( - appBar: TopBar(), + appBar: TopBar(), // 顶部导航栏,这是一个自定义的组件,看import的路径 body: const Center( child: Column( children: [ -- 2.34.1 From 8a0ed14a884bcd816bae4465b853f09bd8b80adf Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Sun, 29 Oct 2023 16:55:01 +0800 Subject: [PATCH 03/19] temp --- lib/main.dart | 2 +- lib/screen/dashboard/dashboard_screen.dart | 15 ++-------- pubspec.lock | 34 +++++++++++++++++++++- pubspec.yaml | 3 ++ 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index a1cfea9..f8d8b69 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:timemanage/screen/dashboard/dashboard_screen.dart'; /// 程序入口 -/// 不知道为什么,dart的doc注释是三个斜杠 +/// 不知道为什么,dart的doc注释是三个斜杠,而不是/** */ void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart index c34851e..c1714c3 100644 --- a/lib/screen/dashboard/dashboard_screen.dart +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:timemanage/screen/dashboard/components/TopBar.dart'; +import 'package:timemanage/screen/dashboard/components/top_bar.dart'; /// 主页 class DashBoardScreen extends StatelessWidget { @@ -8,17 +8,8 @@ class DashBoardScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: TopBar(), // 顶部导航栏,这是一个自定义的组件,看import的路径 - body: const Center( - child: Column( - children: [ - Text('项目列表区'), - Expanded( - child: Text('计时器区'), - ), - ], - ), - ), + appBar: TopBar(), + body: Text("Hello World!"), ); } } diff --git a/pubspec.lock b/pubspec.lock index a185836..b4d5f41 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + bloc: + dependency: "direct main" + description: + name: bloc + sha256: "6f1b87b6eca9041d5672b6e29273cd1594db48ebb66fd2471066e9f3c3a516bd" + url: "https://pub.dev" + source: hosted + version: "7.2.1" boolean_selector: dependency: transitive description: @@ -126,6 +134,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.0.1" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: cdd1351ced09eeb46cfa7946e095b7679344af927415ca9cd972928fa6d5b23f + url: "https://pub.dev" + source: hosted + version: "7.3.3" flutter_launcher_icons: dependency: "direct dev" description: @@ -256,6 +272,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" package_info_plus: dependency: "direct main" description: @@ -273,7 +297,7 @@ packages: source: hosted version: "2.0.1" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" @@ -312,6 +336,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.7.3" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" simple_mustache: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9c6ae8c..d71991f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,9 @@ dependencies: equatable: ^2.0.3 # 用于对象比较 sqflite: ^2.0.0+3 xdg_directories: ^1.0.0 + flutter_bloc: ^7.0.0 + bloc: ^7.0.0 + path: ^1.8.0 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. -- 2.34.1 From 04a87fced1049dc505a5521d75d4bb8d47c33fe8 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Sun, 29 Oct 2023 17:07:01 +0800 Subject: [PATCH 04/19] temp --- lib/screen/course_screen.dart | 6 -- lib/screen/dashboard/components/TopBar.dart | 3 +- pubspec.lock | 64 +++++++++++++++++++++ 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/lib/screen/course_screen.dart b/lib/screen/course_screen.dart index ad82399..44dcad0 100644 --- a/lib/screen/course_screen.dart +++ b/lib/screen/course_screen.dart @@ -296,12 +296,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -/** - * desc: - * author: xiedong - * date: 4/25/21 - **/ - class SpaceWidget extends StatelessWidget { final double width; final double height; diff --git a/lib/screen/dashboard/components/TopBar.dart b/lib/screen/dashboard/components/TopBar.dart index 6042a40..de04412 100644 --- a/lib/screen/dashboard/components/TopBar.dart +++ b/lib/screen/dashboard/components/TopBar.dart @@ -27,8 +27,7 @@ class _TopBarState extends State { onPressed: () { Navigator.push( context, - MaterialPageRoute( - builder: (context) => const CourseScreen()), + MaterialPageRoute(builder: (context) => SyllabusPage()), ); }, child: const Text('课程表'), diff --git a/pubspec.lock b/pubspec.lock index 197889a..238a458 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -376,6 +376,70 @@ packages: url: "https://pub.dev" source: hosted version: "3.7.3" + provider: + dependency: transitive + description: + name: provider + sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + url: "https://pub.dev" + source: hosted + version: "6.0.5" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + url: "https://pub.dev" + source: hosted + version: "2.3.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + url: "https://pub.dev" + source: hosted + version: "2.3.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" simple_mustache: dependency: transitive description: -- 2.34.1 From aa7250060d1bbaf17ac8e12320a8066ca426ebc4 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Sun, 29 Oct 2023 17:24:38 +0800 Subject: [PATCH 05/19] temp --- lib/screen/dashboard/components/top_bar.dart | 105 +++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 lib/screen/dashboard/components/top_bar.dart diff --git a/lib/screen/dashboard/components/top_bar.dart b/lib/screen/dashboard/components/top_bar.dart new file mode 100644 index 0000000..bbe3934 --- /dev/null +++ b/lib/screen/dashboard/components/top_bar.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:timemanage/screen/projects_screen.dart'; +import 'package:timemanage/screen/reports_screen.dart'; +import 'package:timemanage/screen/export_screen.dart'; +import 'package:timemanage/screen/settings_screen.dart'; +import 'package:timemanage/screen/about_screen.dart'; +import 'package:timemanage/screen/course_screen.dart'; + +class TopBar extends StatefulWidget implements PreferredSizeWidget { + const TopBar({Key? key}) : super(key: key); + @override + State createState() => _TopBarState(); + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); +} + +class _TopBarState extends State { + @override + Widget build(BuildContext context) => AppBar( + backgroundColor: Colors.blueAccent, // 背景色 + // 最前面的菜单按钮 + leading: MenuBar( + children: [ + SubmenuButton( + menuChildren: [ + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => SpaceWidget()), + ); + }, + child: const Text('课程表'), + ), + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ProjectsScreen()), + ); + }, + child: const Text('项目'), + ), + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ReportsScreen()), + ); + }, + child: const Text('统计报告'), + ), + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ExportScreen()), + ); + }, + child: const Text('导入和导出'), + ), + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SettingsScreen()), + ); + }, + child: const Text('设置'), + ), + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AboutScreen()), + ); + }, + child: const Text('关于'), + ), + ], + child: const Icon(Icons.menu), + ), + ], + ), + // 标题 + title: const Text('时间管理'), + actions: [ + // 搜索按钮 + IconButton( + icon: const Icon(Icons.search), + onPressed: () {}, + ), + // 筛选按钮 + IconButton( + icon: const Icon(Icons.filter_alt), + onPressed: () {}, + ) + ], + ); +} -- 2.34.1 From 210af5c9b063894b38cd1b914d89d4ccd49e6adc Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Thu, 2 Nov 2023 16:34:47 +0800 Subject: [PATCH 06/19] =?UTF-8?q?=E5=8D=AB=E4=BF=8A=E9=92=A2=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=20=E8=AF=BE=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/local.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/local.properties b/android/local.properties index 50b5543..b97bc0e 100644 --- a/android/local.properties +++ b/android/local.properties @@ -1,2 +1,2 @@ -sdk.dir=C:\\Users\\26538\\AppData\\Local\\Android\\sdk -flutter.sdk=F:\\study\\school\\ruanjian\\flutter\\flutter \ No newline at end of file +sdk.dir=C:\\Users\\28749\\AppData\\Local\\Android\\sdk +flutter.sdk=C:\\flutter \ No newline at end of file -- 2.34.1 From e7d9b8298cd241a583e4878c73fe8d256e3361d0 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Thu, 2 Nov 2023 16:43:57 +0800 Subject: [PATCH 07/19] init --- android/local.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/local.properties b/android/local.properties index 50b5543..b97bc0e 100644 --- a/android/local.properties +++ b/android/local.properties @@ -1,2 +1,2 @@ -sdk.dir=C:\\Users\\26538\\AppData\\Local\\Android\\sdk -flutter.sdk=F:\\study\\school\\ruanjian\\flutter\\flutter \ No newline at end of file +sdk.dir=C:\\Users\\28749\\AppData\\Local\\Android\\sdk +flutter.sdk=C:\\flutter \ No newline at end of file -- 2.34.1 From fe9774dda6dac6aef263d7c5ed44f96b6020622e Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Fri, 3 Nov 2023 15:57:31 +0800 Subject: [PATCH 08/19] =?UTF-8?q?=E9=87=8D=E5=86=99=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=EF=BC=8C=E5=AE=9E=E7=8E=B0=E9=83=A8=E5=88=86=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .metadata | 12 - .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .../buildOutputCleanup/cache.properties | 4 +- .../com/example/timemaneger/MainActivity.kt | 6 + android/local.properties | 5 +- build/flutter_assets/NOTICES | 50 ++ lib/db/data_provider.dart | 44 -- lib/db/database_provider.dart | 259 --------- lib/db/mock_data_provider.dart | 166 ------ lib/db/timer_entry_database.dart | 53 ++ lib/main.dart | 11 +- lib/model/timer_entry.dart | 89 ++- lib/screen/course_screen.dart | 525 +++--------------- lib/screen/dashboard/components/TopBar.dart | 105 ---- .../dashboard/components/home_menu_bar.dart | 86 +++ lib/screen/dashboard/components/top_bar.dart | 105 ---- lib/screen/dashboard/dashboard_screen.dart | 179 +++++- lib/screen/dashboard_screen.dart | 244 -------- pubspec.yaml | 2 + 19 files changed, 505 insertions(+), 1440 deletions(-) create mode 100644 android/app/src/main/kotlin/com/example/timemaneger/MainActivity.kt delete mode 100644 lib/db/data_provider.dart delete mode 100644 lib/db/database_provider.dart delete mode 100644 lib/db/mock_data_provider.dart create mode 100644 lib/db/timer_entry_database.dart delete mode 100644 lib/screen/dashboard/components/TopBar.dart create mode 100644 lib/screen/dashboard/components/home_menu_bar.dart delete mode 100644 lib/screen/dashboard/components/top_bar.dart delete mode 100644 lib/screen/dashboard_screen.dart diff --git a/.metadata b/.metadata index 53163cb..e033a7e 100644 --- a/.metadata +++ b/.metadata @@ -18,18 +18,6 @@ migration: - platform: android create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - - platform: ios - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - - platform: linux - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - - platform: macos - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - - platform: web - create_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a - base_revision: ff5b5b5fa6f35b717667719ddfdb1521d8bdd05a # User provided section diff --git a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 611539fa24a0b52295d35a1522a98fa6b6196834..79f20e4eaca354c0f3bf8a0aef53005e6647b134 100644 GIT binary patch literal 17 UcmZP;Z8@D@z!2fb00t~=041RVZvX%Q literal 17 TcmZP;Z8@D@z!2fb00E2uB@_bY diff --git a/android/.gradle/buildOutputCleanup/cache.properties b/android/.gradle/buildOutputCleanup/cache.properties index 97040f3..a19bd4f 100644 --- a/android/.gradle/buildOutputCleanup/cache.properties +++ b/android/.gradle/buildOutputCleanup/cache.properties @@ -1,2 +1,2 @@ -#Tue Oct 24 13:39:52 CST 2023 -gradle.version=7.4.2 +#Thu Nov 02 19:05:51 CST 2023 +gradle.version=7.5 diff --git a/android/app/src/main/kotlin/com/example/timemaneger/MainActivity.kt b/android/app/src/main/kotlin/com/example/timemaneger/MainActivity.kt new file mode 100644 index 0000000..df6663b --- /dev/null +++ b/android/app/src/main/kotlin/com/example/timemaneger/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.timemaneger + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/android/local.properties b/android/local.properties index b97bc0e..68205dc 100644 --- a/android/local.properties +++ b/android/local.properties @@ -1,2 +1,5 @@ sdk.dir=C:\\Users\\28749\\AppData\\Local\\Android\\sdk -flutter.sdk=C:\\flutter \ No newline at end of file +flutter.sdk=C:\\flutter +flutter.buildMode=debug +flutter.versionName=1.0.0 +flutter.versionCode=1 \ No newline at end of file diff --git a/build/flutter_assets/NOTICES b/build/flutter_assets/NOTICES index e9eeb5c..87d5c05 100644 --- a/build/flutter_assets/NOTICES +++ b/build/flutter_assets/NOTICES @@ -1986,6 +1986,31 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +bloc +flutter_bloc + +The MIT License (MIT) +Copyright (c) 2018 Felix Angelov + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- boolean_selector meta @@ -30781,6 +30806,31 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------- +nested +provider + +MIT License + +Copyright (c) 2019 Remi Rousselet + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -------------------------------------------------------------------------------- package_info_plus diff --git a/lib/db/data_provider.dart b/lib/db/data_provider.dart deleted file mode 100644 index 7a30880..0000000 --- a/lib/db/data_provider.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:timemanage/model/project.dart'; -import 'package:timemanage/model/timer_entry.dart'; - -abstract class DataProvider { - Future createProject({required String name, Color? colour}); - Future> listProjects(); - Future editProject(Project project); - Future deleteProject(Project project); - Future createTimer({ - String? description, - int? projectID, - DateTime? startTime, - DateTime? endTime, - }); - Future> listTimers(); - Future editTimer(TimerEntry timer); - Future deleteTimer(TimerEntry timer); - - Future import(DataProvider other) async { - List otherEntries = await other.listTimers(); - List otherProjects = await other.listProjects(); - - List newOtherProjects = await Stream.fromIterable(otherProjects) - .asyncMap((event) => createProject(name: event.name)) - .toList(); - - for (TimerEntry otherEntry in otherEntries) { - int projectOffset = otherProjects - .indexWhere((element) => element.id == otherEntry.projectID); - int? projectID; - if (projectOffset >= 0) { - projectID = newOtherProjects[projectOffset].id; - } - - await createTimer( - description: otherEntry.description, - projectID: projectID, - startTime: otherEntry.startTime, - endTime: otherEntry.endTime, - ); - } - } -} diff --git a/lib/db/database_provider.dart b/lib/db/database_provider.dart deleted file mode 100644 index 238c774..0000000 --- a/lib/db/database_provider.dart +++ /dev/null @@ -1,259 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:sqflite/sqflite.dart'; -import 'package:timemanage/db/data_provider.dart'; -import 'package:timemanage/model/timer_entry.dart'; -import 'package:timemanage/model/project.dart'; -import 'package:path/path.dart' as p; -import 'package:xdg_directories/xdg_directories.dart'; - -class DatabaseProvider extends DataProvider { - final Database _db; - static const int _dbVersion = 4; - - DatabaseProvider(this._db); - - Future close() async { - await _db.close(); - } - - static void _onConfigure(Database db) async { - await db.execute("PRAGMA foreign_keys = OFF"); - } - - static void _onCreate(Database db, int version) async { - await db.execute(''' - create table if not exists projects( - id integer not null primary key autoincrement, - name text not null, - colour int not null, - archived boolean not null default 0 - ) - '''); - await db.execute(''' - create table if not exists timers( - id integer not null primary key autoincrement, - project_id integer default null, - description text not null, - start_time int not null, - end_time int default null, - notes text default null, - foreign key(project_id) references projects(id) on delete set null - ) - '''); - await db.execute(''' - create index if not exists timers_start_time on timers(start_time) - '''); - } - - static void _onUpgrade(Database db, int version, int newVersion) async { - if (version < 2) { - await db.execute(''' - alter table projects add column archived boolean not null default false - '''); - } - if (version < 3) { - await db.execute(''' - alter table timers add column notes text default null - '''); - } - if (version < 4) { - // fix the bug of the default value being `false` for project archives instead of `0`. - // `false` works fine on sqlite >= 3.23.0. Unfortunately, some Android phones still have - // ancient sqlite versions, so to them `false` is a string rather than an integer with - // value `0` - Batch b = db.batch(); - b.execute(''' - create table projects_tmp( - id integer not null primary key autoincrement, - name text not null, - colour int not null, - archived boolean not null default 0 - ) - '''); - b.execute("insert into projects_tmp select * from projects"); - b.execute("drop table projects"); - b.execute(''' - create table projects( - id integer not null primary key autoincrement, - name text not null, - colour int not null, - archived boolean not null default 0 - ) - '''); - b.execute(''' - insert into projects select id, name, colour, - case archived - when 'false' then 0 - when 'true' then 1 - when '0' then 0 - when '1' then 1 - when 0 then 0 - when 1 then 1 - else 0 - end as archived - from projects_tmp - '''); - b.execute("drop table projects_tmp"); - await b.commit(noResult: true); - } - } - - static Future open(String path) async { - // open the database - Database db = await openDatabase(path, - onConfigure: _onConfigure, - onCreate: _onCreate, - onUpgrade: _onUpgrade, - version: _dbVersion); - await db.execute("PRAGMA foreign_keys = ON"); - DatabaseProvider repo = DatabaseProvider(db); - - return repo; - } - - /// the c in crud - @override - Future createProject( - {required String name, Color? colour, bool? archived}) async { - colour ??= Color.fromARGB(255, 60, 108, 186); - archived ??= false; - - int id = await _db.rawInsert( - "insert into projects(name, colour, archived) values(?, ?, ?)", - [name, colour.value, archived ? 1 : 0]); - return Project(id: id, name: name, colour: colour, archived: archived); - } - - /// the r in crud - @override - Future> listProjects() async { - List> rawProjects = await _db.rawQuery(''' - select id, name, colour, - case archived - when 'false' then 0 - when 'true' then 1 - when '0' then 0 - when '1' then 1 - when 0 then 0 - when 1 then 1 - else 0 - end as archived - from projects order by name asc - '''); - return rawProjects - .map((Map row) => Project( - id: row["id"] as int, - name: row["name"] as String, - colour: Color(row["colour"] as int), - archived: (row["archived"] as int?) == 1)) - .toList(); - } - - /// the u in crud - @override - Future editProject(Project project) async { - int rows = await _db.rawUpdate( - "update projects set name=?, colour=?, archived=? where id=?", - [ - project.name, - project.colour, - project.archived ? 1 : 0, - project.id - ]); - assert(rows == 1); - } - - /// the d in crud - @override - Future deleteProject(Project project) async { - await _db - .rawDelete("delete from projects where id=?", [project.id]); - } - - /// the c in crud - @override - Future createTimer( - {String? description, - int? projectID, - DateTime? startTime, - DateTime? endTime, - String? notes}) async { - int st = startTime?.millisecondsSinceEpoch ?? - DateTime.now().millisecondsSinceEpoch; - int? et = endTime?.millisecondsSinceEpoch; - int id = await _db.rawInsert( - "insert into timers(project_id, description, start_time, end_time, notes) values(?, ?, ?, ?, ?)", - [projectID, description, st, et, notes]); - return TimerEntry( - id: id, - description: description, - projectID: projectID, - startTime: DateTime.fromMillisecondsSinceEpoch(st), - endTime: endTime, - notes: notes); - } - - /// the r in crud - @override - Future> listTimers() async { - List> rawTimers = await _db.rawQuery( - "select id, project_id, description, start_time, end_time, notes from timers order by start_time asc"); - return rawTimers - .map((Map row) => TimerEntry( - id: row["id"] as int, - projectID: row["project_id"] as int?, - description: row["description"] as String?, - startTime: - DateTime.fromMillisecondsSinceEpoch(row["start_time"] as int), - endTime: row["end_time"] != null - ? DateTime.fromMillisecondsSinceEpoch(row["end_time"] as int) - : null, - notes: row["notes"] as String?)) - .toList(); - } - - /// the u in crud - @override - Future editTimer(TimerEntry timer) async { - int st = timer.startTime.millisecondsSinceEpoch; - int? et = timer.endTime?.millisecondsSinceEpoch; - await _db.rawUpdate( - "update timers set project_id=?, description=?, start_time=?, end_time=?, notes=? where id=?", - [ - timer.projectID, - timer.description, - st, - et, - timer.notes, - timer.id - ]); - } - - /// the d in crud - @override - Future deleteTimer(TimerEntry timer) async { - await _db.rawDelete("delete from timers where id=?", [timer.id]); - } - - static Future getDatabaseFile() async { - final dbPath = - (Platform.isLinux) ? dataHome.path : await getDatabasesPath(); - return File(p.join(dbPath, 'timecop.db')); - } - - static Future isValidDatabaseFile(String path) async { - try { - Database db = await openDatabase(path, readOnly: true); - await db.rawQuery( - "select id, name, colour, archived from projects order by name asc limit 1"); - await db.rawQuery( - "select id, project_id, description, start_time, end_time, notes from timers order by start_time asc limit 1"); - await db.close(); - return true; - } on Exception catch (_) { - return false; - } - } -} diff --git a/lib/db/mock_data_provider.dart b/lib/db/mock_data_provider.dart deleted file mode 100644 index 9e3efcd..0000000 --- a/lib/db/mock_data_provider.dart +++ /dev/null @@ -1,166 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:timemanage/db/data_provider.dart'; -import 'package:timemanage/model/project.dart'; -import 'package:timemanage/model/timer_entry.dart'; -import 'dart:math'; - -class MockDataProvider extends DataProvider { - String localeKey; - static final Map> l10n = { - "en": { - "administration": "Administration", - "mockups": "Mockups", - "ui-layout": "UI Layout", - "coffee": "Coffee", - "app-development": "App development" - }, - "zh-CN": { - "ui-layout": "UI布局", - "administration": "管理", - "coffee": "咖啡", - "mockups": "样机", - "app-development": "应用程式开发", - }, - }; - - MockDataProvider(Locale locale) : localeKey = locale.languageCode { - if (locale.languageCode == "zh") { - localeKey += "-${locale.countryCode!}"; - } - } - - @override - Future> listProjects() async { - return [ - Project( - id: 1, - name: "Time Manager", - colour: Colors.cyan[600]!, - archived: false), - Project( - id: 2, - name: l10n[localeKey]!["administration"]!, - colour: Colors.pink[600]!, - archived: false, - ), - ]; - } - - @override - Future> listTimers() async { - int tid = 1; - Random rand = Random(42); - - // start with running timers - List entries = [ - TimerEntry( - id: tid++, - description: l10n[localeKey]!["ui-layout"], - projectID: 1, - startTime: DateTime.now() - .subtract(const Duration(hours: 2, minutes: 10, seconds: 1)), - endTime: null, - ), - TimerEntry( - id: tid++, - description: l10n[localeKey]!["coffee"], - projectID: 2, - startTime: - DateTime.now().subtract(const Duration(minutes: 3, seconds: 14)), - endTime: null, - ), - ]; - - // add some fake March stuff - for (int w = 0; w < 4; w++) { - for (int d = 0; d < 5; d++) { - String descriptionKey; - double r = rand.nextDouble(); - if (r <= 0.2) { - descriptionKey = 'mockups'; - } else if (r <= 0.5) { - descriptionKey = 'ui-layout'; - } else { - descriptionKey = 'app-development'; - } - - entries.add(TimerEntry( - id: tid++, - description: l10n[localeKey]![descriptionKey], - projectID: 1, - startTime: DateTime( - 2020, - 3, - (w * 7) + d + 2, - rand.nextInt(3) + 8, - rand.nextInt(60), - rand.nextInt(60), - ), - endTime: DateTime( - 2020, - 3, - (w * 7) + d + 2, - rand.nextInt(3) + 13, - rand.nextInt(60), - rand.nextInt(60), - ), - )); - - entries.add(TimerEntry( - id: tid++, - description: l10n[localeKey]!['administration'], - projectID: 2, - startTime: DateTime( - 2020, - 3, - (w * 7) + d + 2, - 14, - rand.nextInt(30), - rand.nextInt(60), - ), - endTime: DateTime( - 2020, - 3, - (w * 7) + d + 2, - 15, - rand.nextInt(30), - rand.nextInt(60), - ), - )); - } - } - return entries; - } - - @override - Future createProject( - {required String name, Color? colour, bool? archived}) async { - return Project( - id: -1, name: name, colour: colour!, archived: archived ?? false); - } - - @override - Future editProject(Project project) async {} - @override - Future deleteProject(Project project) async {} - @override - Future createTimer( - {String? description, - int? projectID, - DateTime? startTime, - DateTime? endTime}) async { - DateTime st = startTime ?? DateTime.now(); - return TimerEntry( - id: -1, - description: description, - projectID: projectID, - startTime: st, - endTime: endTime, - ); - } - - @override - Future editTimer(TimerEntry timer) async {} - @override - Future deleteTimer(TimerEntry timer) async {} -} diff --git a/lib/db/timer_entry_database.dart b/lib/db/timer_entry_database.dart new file mode 100644 index 0000000..155bf10 --- /dev/null +++ b/lib/db/timer_entry_database.dart @@ -0,0 +1,53 @@ +import 'package:sqflite/sqflite.dart'; +import 'package:timemanage/model/timer_entry.dart'; + +class TimerEntryDatabase { + static final TimerEntryDatabase instance = TimerEntryDatabase._init(); + + static Database? _database; + + TimerEntryDatabase._init(); + + /// 以上暂时理解为固定格式 + + // 获取数据库 + Future get database async { + if (_database != null) return _database!; + + _database = await _initDB('timer_entry.db'); + return _database!; + } + + // 初始化数据库 + Future _initDB(String filePath) async { + final dbPath = await getDatabasesPath(); + final path = dbPath + filePath; + + // 打开数据库 + return await openDatabase(path, version: 1, onCreate: _createDB); + } + + // 创建数据库 + Future _createDB(Database db, int version) async { + // 创建表 + const idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; + const textType = 'TEXT NOT NULL'; + const integerType = 'INTEGER NOT NULL'; + + // 创建计时器表 + await db.execute(''' + CREATE TABLE $tableTimerEntry ( + ${TimerEntryFields.id} $idType, + ${TimerEntryFields.name} $textType, + ${TimerEntryFields.duration} $integerType, + ${TimerEntryFields.createdAt} $textType + ) + '''); + } + + // 关闭数据库 + Future close() async { + final db = await instance.database; + db.close(); + } +} diff --git a/lib/main.dart b/lib/main.dart index f8d8b69..4a1b0be 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,15 +13,18 @@ class MyApp extends StatelessWidget { return MaterialApp( // 此处放所有组件的主题颜色设置 theme: ThemeData( + // 默认菜单栏主题颜色 menuBarTheme: MenuBarThemeData( style: MenuStyle( backgroundColor: MaterialStatePropertyAll(Colors.blueAccent), ), ), - menuTheme: MenuThemeData( - style: MenuStyle( - backgroundColor: MaterialStatePropertyAll(Colors.white)), - ), + // // 菜单的每一项的主题颜色 + // menuTheme: MenuThemeData( + // style: MenuStyle( + // backgroundColor: + // MaterialStatePropertyAll(Colors.lightBlueAccent)), + // ), ), // 主页 home: DashBoardScreen(), diff --git a/lib/model/timer_entry.dart b/lib/model/timer_entry.dart index e842a75..0267b67 100644 --- a/lib/model/timer_entry.dart +++ b/lib/model/timer_entry.dart @@ -1,58 +1,45 @@ -import 'package:equatable/equatable.dart'; +const String tableTimerEntry = 'timer_entry'; // 表名 -// 计时器实体类 -class TimerEntry extends Equatable { - final int id; - final String? description; - final int? projectID; - final DateTime startTime; - final DateTime? endTime; - final String? notes; +/// 用于数据库建表 +class TimerEntryFields { + static const String id = '_id'; + static const String name = 'name'; + static const String duration = 'duration'; + static const String createdAt = 'createdAt'; - // 构造函数 - const TimerEntry( - {required this.id, - required this.description, - required this.projectID, - required this.startTime, - required this.endTime, - this.notes = ""}); + static final List values = [ + /// Add all fields + id, name, duration, createdAt + ]; +} - // 返回属性列表,用于比较两个对象是否相等 - @override - List get props => - [id, description, projectID, startTime, endTime, notes]; - @override - bool get stringify => true; +// 计时器实体类,继承可比较类 +class TimerEntry { + int? id; // 计时器ID + String? name; // 计时器名称 + int? duration; // 计时器时长 + DateTime? createdAt; // 计时器创建时间 - // 克隆方法 - TimerEntry.clone(TimerEntry timer, - {String? description, - int? projectID, - DateTime? startTime, - DateTime? endTime, - String? notes}) - : this( - id: timer.id, - description: description ?? timer.description, - projectID: projectID ?? timer.projectID, - startTime: startTime ?? timer.startTime, - endTime: endTime ?? timer.endTime, - notes: notes ?? timer.notes, - ); + // 构造函数 + TimerEntry({ + this.id, + this.name, + this.duration, + this.createdAt, + }); - // 格式化时间 - static String formatDuration(Duration d) { - if (d.inHours > 0) { - return "${d.inHours}:${(d.inMinutes - (d.inHours * 60)).toString().padLeft(2, "0")}:${(d.inSeconds - (d.inMinutes * 60)).toString().padLeft(2, "0")}"; - } else { - return "${d.inMinutes.toString().padLeft(2, "0")}:${(d.inSeconds - (d.inMinutes * 60)).toString().padLeft(2, "0")}"; - } - } + // 重写等号运算符 + @override + bool operator ==(Object other) => + identical(this, other) || + other is TimerEntry && + runtimeType == other.runtimeType && + id == other.id && + name == other.name && + duration == other.duration && + createdAt == other.createdAt; - // 格式化时间 - String formatTime() { - Duration d = (endTime ?? DateTime.now()).difference(startTime); - return formatDuration(d); - } + @override + int get hashCode => + id.hashCode ^ name.hashCode ^ duration.hashCode ^ createdAt.hashCode; } diff --git a/lib/screen/course_screen.dart b/lib/screen/course_screen.dart index c1594d0..196af3f 100644 --- a/lib/screen/course_screen.dart +++ b/lib/screen/course_screen.dart @@ -1,316 +1,13 @@ -// import 'package:flutter/cupertino.dart'; -// import 'package:flutter/material.dart'; - -// class SpaceWidget extends StatelessWidget { -// final double width; -// final double height; - -// SpaceWidget({this.width = 0.0, this.height = 0.0}); - -// @override -// Widget build(BuildContext context) { -// return SizedBox( -// width: width, -// height: height, -// ); -// } -// } - -// class SyllabusPage extends StatefulWidget { -// @override -// State createState() => PageState(); -// } - -// class PageState extends State { -// var colorList = [ -// Colors.red, -// Colors.lightBlueAccent, -// Colors.grey, -// Colors.cyan, -// Colors.amber, -// Colors.deepPurpleAccent, -// Colors.purpleAccent -// ]; -// var infoList = ["高等数学-周某某教授@综合楼201", "大学英语-王某某讲师@行政楼501"]; -// var weekList = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; - -// var dateList = []; -// var currentWeekIndex = 0; - -// @override -// void initState() { -// super.initState(); - -// var monday = 1; -// var mondayTime = DateTime.now(); - -// //获取本周星期一是几号 -// while (mondayTime.weekday != monday) { -// mondayTime = mondayTime.subtract(new Duration(days: 1)); -// } - -// mondayTime.year; //2020 年 -// mondayTime.month; //6(这里和js中的月份有区别,js中是从0开始,dart则从1开始,我们无需再进行加一处理) 月 -// mondayTime.day; //6 日 -// // nowTime.hour ;//6 时 -// // nowTime.minute ;//6 分 -// // nowTime.second ;//6 秒 -// for (int i = 0; i < 7; i++) { -// dateList.add( -// mondayTime.month.toString() + "/" + (mondayTime.day + i).toString()); -// if ((mondayTime.day + i) == DateTime.now().day) { -// setState(() { -// currentWeekIndex = i + 1; -// }); -// } -// } -// // print('Recent monday '+DateTime.now().day.toString()); -// } - -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// body: Column( -// mainAxisAlignment: MainAxisAlignment.start, -// children: [ -// SizedBox( -// child: GridView.builder( -// shrinkWrap: true, -// physics: NeverScrollableScrollPhysics(), -// itemCount: 8, -// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( -// crossAxisCount: 8, childAspectRatio: 1 / 1), -// itemBuilder: (BuildContext context, int index) { -// return Container( -// color: index == this.currentWeekIndex -// ? Color(0xf7f7f7) -// : Colors.white, -// child: Center( -// child: index == 0 -// ? Column( -// mainAxisAlignment: MainAxisAlignment.center, -// children: [ -// Text("星期", -// style: TextStyle( -// fontSize: 14, color: Colors.black87)), -// SpaceWidget(height: 5), -// Text("日期", style: TextStyle(fontSize: 12)), -// ], -// ) -// : Column( -// mainAxisAlignment: MainAxisAlignment.center, -// children: [ -// Text(weekList[index - 1], -// style: TextStyle( -// fontSize: 14, -// color: index == currentWeekIndex -// ? Colors.lightBlue -// : Colors.black87)), -// SpaceWidget(height: 5), -// Text(dateList[index - 1], -// style: TextStyle( -// fontSize: 12, -// color: index == currentWeekIndex -// ? Colors.lightBlue -// : Colors.black87)), -// ], -// ), -// ), -// ); -// }), -// ), -// Expanded( -// child: SingleChildScrollView( -// child: Row( -// children: [ -// Expanded( -// flex: 1, -// child: GridView.builder( -// shrinkWrap: true, -// // physics:ClampingScrollPhysics(), -// itemCount: 10, -// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( -// crossAxisCount: 1, childAspectRatio: 1 / 2), -// itemBuilder: (BuildContext context, int index) { -// return Container( -// // width: 25, -// // height:s 80, -// child: Center( -// child: Text( -// (index + 1).toInt().toString(), -// style: TextStyle(fontSize: 15), -// ), -// ), -// decoration: BoxDecoration( -// color: Color(0xff5ff5), -// // border: Border.all(color: Colors.black12, width: 0.5), -// border: Border( -// bottom: BorderSide( -// color: Colors.black12, width: 0.5), -// right: BorderSide( -// color: Colors.black12, width: 0.5), -// ), -// )); -// }), -// ), -// Expanded( -// flex: 7, -// child: GridView.builder( -// shrinkWrap: true, -// physics: NeverScrollableScrollPhysics(), -// itemCount: 35, -// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( -// crossAxisCount: 7, childAspectRatio: 1 / 4), -// itemBuilder: (BuildContext context, int index) { -// return Container( -// child: Stack( -// children: [ -// Column( -// mainAxisAlignment: -// MainAxisAlignment.spaceBetween, -// children: [ -// Flexible( -// flex: 1, -// child: Container( -// width: double.infinity, -// height: double.infinity, -// decoration: BoxDecoration( -// color: Colors.white, -// // border: Border.all(color: Colors.black12, width: 0.5), -// border: Border( -// bottom: BorderSide( -// color: Colors.black12, -// width: 0.5), -// right: BorderSide( -// color: Colors.black12, -// width: 0.5), -// ), -// )), -// ), -// Flexible( -// flex: 1, -// child: Container( -// width: double.infinity, -// height: double.infinity, -// decoration: BoxDecoration( -// color: Colors.white, -// // border: Border.all(color: Colors.black12, width: 0.5), -// border: Border( -// bottom: BorderSide( -// color: Colors.black12, -// width: 0.5), -// right: BorderSide( -// color: Colors.black12, -// width: 0.5), -// ), -// )), -// ), -// ], -// ), -// if (index % 5 == 0 || index % 5 == 1) -// Container( -// margin: EdgeInsets.all(0.5), -// decoration: BoxDecoration( -// borderRadius: BorderRadius.circular(2), -// color: colorList[index % 7], -// ), -// child: Center( -// child: Text( -// infoList[index % 2], -// textAlign: TextAlign.center, -// style: TextStyle( -// color: Colors.white, -// fontSize: 11, -// letterSpacing: 1), -// ), -// ), -// ) -// ], -// ), -// ); -// }), -// ) -// ], -// ), -// ), -// ), -// _bottomView -// ], -// ), -// ); -// } - -// @override -// String pageTitle() => "我的课表"; - -// Widget _topView = SizedBox( -// height: 30, -// child: Expanded( -// child: ListView.builder( -// scrollDirection: Axis.horizontal, -// itemCount: 7, -// itemBuilder: (BuildContext context, int index) { -// return Text("dd"); -// }), -// ), -// ); -// Widget _centerView = Expanded( -// child: GridView.builder( -// itemCount: 63, -// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( -// crossAxisCount: 7, -// ), -// itemBuilder: (BuildContext context, int index) { -// return Container( -// // width: 25, -// // height: 80, -// child: Center( -// child: Text( -// (index + 1).toString(), -// style: TextStyle(fontSize: 15), -// ), -// ), -// decoration: BoxDecoration( -// color: Color(0xff5ff5), -// border: Border.all(color: Colors.black12, width: 0.5), -// )); -// }), -// ); - -// Widget _bottomView = SizedBox( -// height: 30, -// child: Row( -// children: [ -// //底部view可自行扩充 -// ], -// ), -// ); -// } - -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -class SpaceWidget extends StatelessWidget { - final double width; - final double height; - - SpaceWidget({this.width = 0.0, this.height = 0.0}); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: width, - height: height, - ); - } -} +class CourseScreen extends StatefulWidget { + const CourseScreen({super.key}); -class SyllabusPage extends StatefulWidget { @override - State createState() => PageState(); + State createState() => CourseScreenState(); } -class PageState extends State { +class CourseScreenState extends State { var colorList = [ Colors.red, Colors.lightBlueAccent, @@ -335,7 +32,7 @@ class PageState extends State { //获取本周星期一是几号 while (mondayTime.weekday != monday) { - mondayTime = mondayTime.subtract(new Duration(days: 1)); + mondayTime = mondayTime.subtract(Duration(days: 1)); } mondayTime.year; //2020 年 @@ -345,8 +42,7 @@ class PageState extends State { // nowTime.minute ;//6 分 // nowTime.second ;//6 秒 for (int i = 0; i < 7; i++) { - dateList.add( - mondayTime.month.toString() + "/" + (mondayTime.day + i).toString()); + dateList.add("${mondayTime.month}/${mondayTime.day + i}"); if ((mondayTime.day + i) == DateTime.now().day) { setState(() { currentWeekIndex = i + 1; @@ -371,18 +67,18 @@ class PageState extends State { crossAxisCount: 8, childAspectRatio: 1 / 1), itemBuilder: (BuildContext context, int index) { return Container( - color: index == this.currentWeekIndex - ? Color(0xf7f7f7) + color: index == currentWeekIndex + ? Color(0x00f7f7f7) : Colors.white, child: Center( child: index == 0 ? Column( mainAxisAlignment: MainAxisAlignment.center, - children: [ + children: const [ Text("星期", style: TextStyle( fontSize: 14, color: Colors.black87)), - SpaceWidget(height: 5), + SizedBox(height: 5), Text("日期", style: TextStyle(fontSize: 12)), ], ) @@ -395,7 +91,7 @@ class PageState extends State { color: index == currentWeekIndex ? Colors.lightBlue : Colors.black87)), - SpaceWidget(height: 5), + SizedBox(height: 5), Text(dateList[index - 1], style: TextStyle( fontSize: 12, @@ -422,16 +118,8 @@ class PageState extends State { crossAxisCount: 1, childAspectRatio: 1 / 2), itemBuilder: (BuildContext context, int index) { return Container( - // width: 25, - // height:s 80, - child: Center( - child: Text( - (index + 1).toInt().toString(), - style: TextStyle(fontSize: 15), - ), - ), decoration: BoxDecoration( - color: Color(0xff5ff5), + color: Color(0x00ff5ff5), // border: Border.all(color: Colors.black12, width: 0.5), border: Border( bottom: BorderSide( @@ -439,6 +127,14 @@ class PageState extends State { right: BorderSide( color: Colors.black12, width: 0.5), ), + ), + // width: 25, + // height:s 80, + child: Center( + child: Text( + (index + 1).toInt().toString(), + style: TextStyle(fontSize: 15), + ), )); }), ), @@ -483,69 +179,67 @@ class PageState extends State { ); }, - child: Container( - child: Stack( - children: [ - Column( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Flexible( - flex: 1, - child: Container( - width: double.infinity, - height: double.infinity, - decoration: BoxDecoration( - color: Colors.white, - border: Border( - bottom: BorderSide( - color: Colors.black12, - width: 0.5), - right: BorderSide( - color: Colors.black12, - width: 0.5), - ), - )), - ), - Flexible( - flex: 1, - child: Container( - width: double.infinity, - height: double.infinity, - decoration: BoxDecoration( - color: Colors.white, - border: Border( - bottom: BorderSide( - color: Colors.black12, - width: 0.5), - right: BorderSide( - color: Colors.black12, - width: 0.5), - ), - )), - ), - ], - ), - if (index % 5 == 0 || index % 5 == 1) - Container( - margin: EdgeInsets.all(0.5), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(2), - color: colorList[index % 7], - ), - child: Center( - child: Text( - infoList[index % 2], - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white, - fontSize: 11, - letterSpacing: 1), - ), + child: Stack( + children: [ + Column( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Flexible( + flex: 1, + child: Container( + width: double.infinity, + height: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + border: Border( + bottom: BorderSide( + color: Colors.black12, + width: 0.5), + right: BorderSide( + color: Colors.black12, + width: 0.5), + ), + )), + ), + Flexible( + flex: 1, + child: Container( + width: double.infinity, + height: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + border: Border( + bottom: BorderSide( + color: Colors.black12, + width: 0.5), + right: BorderSide( + color: Colors.black12, + width: 0.5), + ), + )), + ), + ], + ), + if (index % 5 == 0 || index % 5 == 1) + Container( + margin: EdgeInsets.all(0.5), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2), + color: colorList[index % 7], + ), + child: Center( + child: Text( + infoList[index % 2], + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 11, + letterSpacing: 1), ), - ) - ], - ), + ), + ) + ], ), ); }), @@ -560,77 +254,28 @@ class PageState extends State { ); } - @override String pageTitle() => "我的课表"; - Widget _topView = SizedBox( - height: 30, - child: Expanded( - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: 7, - itemBuilder: (BuildContext context, int index) { - return Text("dd"); - }), - ), - ); - Widget _centerView = Expanded( - child: GridView.builder( - itemCount: 63, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 7, - ), - itemBuilder: (BuildContext context, int index) { - return Container( - // width: 25, - // height: 80, - child: Center( - child: Text( - (index + 1).toString(), - style: TextStyle(fontSize: 15), - ), - ), - decoration: BoxDecoration( - color: Color(0xff5ff5), - border: Border.all(color: Colors.black12, width: 0.5), - )); - }), - ); - - Widget _bottomView = SizedBox( + final Widget _bottomView = SizedBox( height: 30, child: Row( - children: [ + children: const [ //底部view可自行扩充 ], ), ); } -// class EditCoursePage extends StatelessWidget { -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// appBar: AppBar( -// title: Text('编辑课程'), -// ), -// body: Center( -// child: Text('在这里编辑课程信息'), -// ), -// ); -// } -// } - class EditCoursePage extends StatefulWidget { final Function(String) onSave; - EditCoursePage({required this.onSave}); + const EditCoursePage({super.key, required this.onSave}); @override - _EditCoursePageState createState() => _EditCoursePageState(); + EditCoursePageState createState() => EditCoursePageState(); } -class _EditCoursePageState extends State { +class EditCoursePageState extends State { final _formKey = GlobalKey(); final _courseController = TextEditingController(); diff --git a/lib/screen/dashboard/components/TopBar.dart b/lib/screen/dashboard/components/TopBar.dart deleted file mode 100644 index de04412..0000000 --- a/lib/screen/dashboard/components/TopBar.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:timemanage/screen/projects_screen.dart'; -import 'package:timemanage/screen/reports_screen.dart'; -import 'package:timemanage/screen/export_screen.dart'; -import 'package:timemanage/screen/settings_screen.dart'; -import 'package:timemanage/screen/about_screen.dart'; -import 'package:timemanage/screen/course_screen.dart'; - -class TopBar extends StatefulWidget implements PreferredSizeWidget { - const TopBar({Key? key}) : super(key: key); - @override - State createState() => _TopBarState(); - @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); -} - -class _TopBarState extends State { - @override - Widget build(BuildContext context) => AppBar( - backgroundColor: Colors.blueAccent, // 背景色 - // 最前面的菜单按钮 - leading: MenuBar( - children: [ - SubmenuButton( - menuChildren: [ - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => SyllabusPage()), - ); - }, - child: const Text('课程表'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ProjectsScreen()), - ); - }, - child: const Text('项目'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ReportsScreen()), - ); - }, - child: const Text('统计报告'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ExportScreen()), - ); - }, - child: const Text('导入和导出'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SettingsScreen()), - ); - }, - child: const Text('设置'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const AboutScreen()), - ); - }, - child: const Text('关于'), - ), - ], - child: const Icon(Icons.menu), - ), - ], - ), - // 标题 - title: const Text('时间管理'), - actions: [ - // 搜索按钮 - IconButton( - icon: const Icon(Icons.search), - onPressed: () {}, - ), - // 筛选按钮 - IconButton( - icon: const Icon(Icons.filter_alt), - onPressed: () {}, - ) - ], - ); -} diff --git a/lib/screen/dashboard/components/home_menu_bar.dart b/lib/screen/dashboard/components/home_menu_bar.dart new file mode 100644 index 0000000..6ea42c4 --- /dev/null +++ b/lib/screen/dashboard/components/home_menu_bar.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:timemanage/screen/projects_screen.dart'; +import 'package:timemanage/screen/reports_screen.dart'; +import 'package:timemanage/screen/export_screen.dart'; +import 'package:timemanage/screen/settings_screen.dart'; +import 'package:timemanage/screen/about_screen.dart'; +import 'package:timemanage/screen/course_screen.dart'; + +class HomeMenuBar extends StatefulWidget { + const HomeMenuBar({Key? key}) : super(key: key); + + @override + HomeMenuBarState createState() => HomeMenuBarState(); +} + +class HomeMenuBarState extends State { + @override + Widget build(BuildContext context) { + return MenuBar( + children: [ + SubmenuButton( + menuChildren: [ + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => CourseScreen()), + ); + }, + child: const Text('课程表'), + ), + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ProjectsScreen()), + ); + }, + child: const Text('项目'), + ), + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ReportsScreen()), + ); + }, + child: const Text('统计报告'), + ), + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const ExportScreen()), + ); + }, + child: const Text('导入和导出'), + ), + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SettingsScreen()), + ); + }, + child: const Text('设置'), + ), + MenuItemButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const AboutScreen()), + ); + }, + child: const Text('关于'), + ), + ], + child: const Icon(Icons.menu), + ), + ], + ); + } +} diff --git a/lib/screen/dashboard/components/top_bar.dart b/lib/screen/dashboard/components/top_bar.dart deleted file mode 100644 index bbe3934..0000000 --- a/lib/screen/dashboard/components/top_bar.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:timemanage/screen/projects_screen.dart'; -import 'package:timemanage/screen/reports_screen.dart'; -import 'package:timemanage/screen/export_screen.dart'; -import 'package:timemanage/screen/settings_screen.dart'; -import 'package:timemanage/screen/about_screen.dart'; -import 'package:timemanage/screen/course_screen.dart'; - -class TopBar extends StatefulWidget implements PreferredSizeWidget { - const TopBar({Key? key}) : super(key: key); - @override - State createState() => _TopBarState(); - @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); -} - -class _TopBarState extends State { - @override - Widget build(BuildContext context) => AppBar( - backgroundColor: Colors.blueAccent, // 背景色 - // 最前面的菜单按钮 - leading: MenuBar( - children: [ - SubmenuButton( - menuChildren: [ - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => SpaceWidget()), - ); - }, - child: const Text('课程表'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ProjectsScreen()), - ); - }, - child: const Text('项目'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ReportsScreen()), - ); - }, - child: const Text('统计报告'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ExportScreen()), - ); - }, - child: const Text('导入和导出'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SettingsScreen()), - ); - }, - child: const Text('设置'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const AboutScreen()), - ); - }, - child: const Text('关于'), - ), - ], - child: const Icon(Icons.menu), - ), - ], - ), - // 标题 - title: const Text('时间管理'), - actions: [ - // 搜索按钮 - IconButton( - icon: const Icon(Icons.search), - onPressed: () {}, - ), - // 筛选按钮 - IconButton( - icon: const Icon(Icons.filter_alt), - onPressed: () {}, - ) - ], - ); -} diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart index c1714c3..36787a0 100644 --- a/lib/screen/dashboard/dashboard_screen.dart +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -1,15 +1,180 @@ import 'package:flutter/material.dart'; -import 'package:timemanage/screen/dashboard/components/top_bar.dart'; +import 'package:timemanage/screen/dashboard/components/home_menu_bar.dart'; -/// 主页 -class DashBoardScreen extends StatelessWidget { - const DashBoardScreen({super.key}); +// 主页 +class DashBoardScreen extends StatefulWidget { + const DashBoardScreen({Key? key}) : super(key: key); + + @override + DashBoardScreenState createState() => DashBoardScreenState(); +} + +class DashBoardScreenState extends State { + // 计时器列表 + List timers = []; + + // 添加计时器 + // TODO: 对这个函数添加注释 + void _addTimer() { + final controller = TextEditingController(); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('新计时器'), + content: TextField( + controller: controller, + decoration: const InputDecoration(hintText: '计时器名称'), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, 'Cancel'), + child: const Text('取消'), + ), + TextButton( + onPressed: () { + final name = controller.text; + if (name.isNotEmpty) { + setState(() { + timers.add(TimerModel(name: name)); + }); + } + Navigator.pop(context, 'OK'); + }, + child: const Text('OK'), + ), + ], + ); + }, + ); + } @override Widget build(BuildContext context) { return Scaffold( - appBar: TopBar(), - body: Text("Hello World!"), - ); + // 应用栏 + appBar: AppBar( + backgroundColor: Colors.blueAccent, // 背景色 + // 最前面的菜单按钮 + leading: HomeMenuBar(), + // 标题 + title: const Text('时间管理'), + actions: [ + // 搜索按钮 + // TODO: 实现搜索功能 + IconButton( + icon: const Icon(Icons.search), + onPressed: () {}, + ), + // 筛选按钮 + // TODO: 实现筛选功能 + IconButton( + icon: const Icon(Icons.filter_alt), + onPressed: () {}, + ) + ], + ), + // 显示计时器列表 + body: ListView.builder( + itemCount: timers.length, + itemBuilder: (context, index) { + return Column( + children: [ + ListTile( + leading: CircleAvatar( + child: Text((index + 1).toString()), + ), + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + timers[index].name, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + StreamBuilder( + stream: Stream.periodic( + const Duration(milliseconds: 100), + (_) => timers[index].stopwatch.elapsed.inSeconds), + builder: (context, snapshot) { + final seconds = snapshot.data ?? 0; + final hours = seconds ~/ 3600; + final minutes = (seconds % 3600) ~/ 60; + final remainingSeconds = seconds % 60; + return Text( + '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}', + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ); + }, + ), + ], + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: timers[index].isActive + ? const Icon(Icons.pause) + : const Icon(Icons.play_arrow), + onPressed: () { + setState(() { + if (timers[index].isActive) { + timers[index].stop(); + } else { + timers[index].start(); + } + }); + }, + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + setState(() { + timers[index].stopwatch.stop(); // 确保计时器停止以防止内存泄漏 + timers.removeAt(index); + }); + }, + ), + ], + ), + ), + const SizedBox(height: 10), // 添加间隔 + ], + ); + }, + ), + floatingActionButton: FloatingActionButton( + onPressed: _addTimer, + child: const Icon(Icons.add), + )); + } +} + +class TimerModel { + String name; + Stopwatch stopwatch; + bool isActive; + + TimerModel({required this.name}) + : stopwatch = Stopwatch(), + isActive = false; + + void start() { + stopwatch.start(); + isActive = true; + } + + void stop() { + stopwatch.stop(); + isActive = false; + } + + void reset() { + stopwatch.reset(); } } diff --git a/lib/screen/dashboard_screen.dart b/lib/screen/dashboard_screen.dart deleted file mode 100644 index 2942a8a..0000000 --- a/lib/screen/dashboard_screen.dart +++ /dev/null @@ -1,244 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:timemanage/screen/projects_screen.dart'; -import 'package:timemanage/screen/reports_screen.dart'; -import 'package:timemanage/screen/export_screen.dart'; -import 'package:timemanage/screen/settings_screen.dart'; -import 'package:timemanage/screen/about_screen.dart'; -import 'package:timemanage/screen/course_screen.dart'; - -class DashBoardScreen extends StatefulWidget { - const DashBoardScreen({Key? key}) : super(key: key); - - @override - _DashBoardScreenState createState() => _DashBoardScreenState(); -} - -class _DashBoardScreenState extends State { - List timers = []; - - void _addTimer() { - final controller = TextEditingController(); - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('新计时器'), - content: TextField( - controller: controller, - decoration: const InputDecoration(hintText: '计时器名称'), - ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context, 'Cancel'), - child: const Text('取消'), - ), - TextButton( - onPressed: () { - final name = controller.text; - if (name.isNotEmpty) { - setState(() { - timers.add(TimerModel(name: name)); - }); - } - Navigator.pop(context, 'OK'); - }, - child: const Text('OK'), - ), - ], - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - backgroundColor: Colors.blueAccent, // 背景色 - // 最前面的菜单按钮 - leading: MenuBar( - children: [ - SubmenuButton( - menuChildren: [ - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => SyllabusPage()), - ); - }, - child: const Text('课程表'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ProjectsScreen()), - ); - }, - child: const Text('项目'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ReportsScreen()), - ); - }, - child: const Text('统计报告'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ExportScreen()), - ); - }, - child: const Text('导入和导出'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SettingsScreen()), - ); - }, - child: const Text('设置'), - ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const AboutScreen()), - ); - }, - child: const Text('关于'), - ), - ], - child: const Icon(Icons.menu), - ), - ], - ), - // 标题 - title: const Text('时间管理'), - actions: [ - // 搜索按钮 - IconButton( - icon: const Icon(Icons.search), - onPressed: () {}, - ), - // 筛选按钮 - IconButton( - icon: const Icon(Icons.add), - onPressed: _addTimer, - ), - IconButton( - icon: const Icon(Icons.filter_alt), - onPressed: () {}, - ) - ], - ), - body: ListView.builder( - itemCount: timers.length, - itemBuilder: (context, index) { - return Column( - children: [ - ListTile( - leading: CircleAvatar( - child: Text((index + 1).toString()), - ), - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - timers[index].name, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - StreamBuilder( - stream: Stream.periodic(const Duration(milliseconds: 100), - (_) => timers[index].stopwatch.elapsed.inSeconds), - builder: (context, snapshot) { - final seconds = snapshot.data ?? 0; - final hours = seconds ~/ 3600; - final minutes = (seconds % 3600) ~/ 60; - final remainingSeconds = seconds % 60; - return Text( - '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}', - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ); - }, - ), - ], - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: timers[index].isActive - ? const Icon(Icons.pause) - : const Icon(Icons.play_arrow), - onPressed: () { - setState(() { - if (timers[index].isActive) { - timers[index].stop(); - } else { - timers[index].start(); - } - }); - }, - ), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () { - setState(() { - timers[index].stopwatch.stop(); // 确保计时器停止以防止内存泄漏 - timers.removeAt(index); - }); - }, - ), - ], - ), - ), - const SizedBox(height: 10), // 添加间隔 - ], - ); - }, - ), - ); - } -} - -class TimerModel { - String name; - Stopwatch stopwatch; - bool isActive; - - TimerModel({required this.name}) - : stopwatch = Stopwatch(), - isActive = false; - - void start() { - stopwatch.start(); - isActive = true; - } - - void stop() { - stopwatch.stop(); - isActive = false; - } - - void reset() { - stopwatch.reset(); - } -} diff --git a/pubspec.yaml b/pubspec.yaml index 13d2c5a..4f797b5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,6 +43,8 @@ dependencies: bloc: ^7.0.0 path: ^1.8.0 shared_preferences: ^2.0.8 + sqflige: ^2.0.0+3 # 用于数据库 + intl: ^0.17.0 # 用于时间格式化 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. -- 2.34.1 From e52f3aaa49e4f7faa6e82ef052af169e8220d902 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Fri, 3 Nov 2023 16:24:43 +0800 Subject: [PATCH 09/19] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0CRUD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/db/timer_entry_database.dart | 53 ++++++++++++++++++++++++++++++++ lib/model/timer_entry.dart | 30 ++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/lib/db/timer_entry_database.dart b/lib/db/timer_entry_database.dart index 155bf10..8341806 100644 --- a/lib/db/timer_entry_database.dart +++ b/lib/db/timer_entry_database.dart @@ -45,6 +45,59 @@ class TimerEntryDatabase { '''); } + /// CRUD + + // 创建计时器 + Future create(TimerEntry timerEntry) async { + final db = await instance.database; + + // 获取计时器ID + final id = await db.insert(tableTimerEntry, timerEntry.toJson()); + return timerEntry.copy(id: id); + } + + // 获取计时器 + Future read(int id) async { + final db = await instance.database; + + final maps = await db.query(tableTimerEntry, + columns: TimerEntryFields.values, + where: '${TimerEntryFields.id} = ?', + whereArgs: [id]); + + if (maps.isNotEmpty) { + return TimerEntry.fromJson(maps.first); + } else { + throw Exception('ID $id not found'); + } + } + + // 获取所有计时器 + Future> readAll() async { + final db = await instance.database; + + const orderBy = '${TimerEntryFields.createdAt} ASC'; + final result = await db.query(tableTimerEntry, orderBy: orderBy); + + return result.map((json) => TimerEntry.fromJson(json)).toList(); + } + + // 更新计时器 + Future update(TimerEntry timerEntry) async { + final db = await instance.database; + + return db.update(tableTimerEntry, timerEntry.toJson(), + where: '${TimerEntryFields.id} = ?', whereArgs: [timerEntry.id]); + } + + // 删除计时器 + Future delete(int id) async { + final db = await instance.database; + + return await db.delete(tableTimerEntry, + where: '${TimerEntryFields.id} = ?', whereArgs: [id]); + } + // 关闭数据库 Future close() async { final db = await instance.database; diff --git a/lib/model/timer_entry.dart b/lib/model/timer_entry.dart index 0267b67..2276f59 100644 --- a/lib/model/timer_entry.dart +++ b/lib/model/timer_entry.dart @@ -42,4 +42,34 @@ class TimerEntry { @override int get hashCode => id.hashCode ^ name.hashCode ^ duration.hashCode ^ createdAt.hashCode; + + // 将计时器实体类转换为json,用于数据库存储 + Map toJson() => { + TimerEntryFields.id: id, + TimerEntryFields.name: name, + TimerEntryFields.duration: duration, + TimerEntryFields.createdAt: createdAt?.toIso8601String(), + }; + + // 将json转换为计时器实体类,用于数据库读取 + static TimerEntry fromJson(Map json) => TimerEntry( + id: json[TimerEntryFields.id] as int?, + name: json[TimerEntryFields.name] as String?, + duration: json[TimerEntryFields.duration] as int?, + createdAt: DateTime.parse(json[TimerEntryFields.createdAt] as String), + ); + + // copy函数,用于复制计时器实体类 + TimerEntry copy({ + int? id, + String? name, + int? duration, + DateTime? createdAt, + }) => + TimerEntry( + id: id ?? this.id, + name: name ?? this.name, + duration: duration ?? this.duration, + createdAt: createdAt ?? this.createdAt, + ); } -- 2.34.1 From dfbda2dc4bde79799a2ae659755fe064acd394a6 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Fri, 3 Nov 2023 18:55:36 +0800 Subject: [PATCH 10/19] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E8=AE=A1=E6=97=B6=E5=99=A8=E7=9A=84=E6=98=BE=E7=A4=BA=E7=95=8C?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../buildOutputCleanup.lock | Bin 17 -> 17 bytes lib/screen/dashboard/dashboard_screen.dart | 265 +++++++++--------- pubspec.lock | 8 + pubspec.yaml | 3 +- 4 files changed, 146 insertions(+), 130 deletions(-) diff --git a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 79f20e4eaca354c0f3bf8a0aef53005e6647b134..a19fd81f63a8f0c7eedd5bca0587310e37439f2e 100644 GIT binary patch literal 17 UcmZP;Z8@D@z!2fb00u0J042QxivR!s literal 17 UcmZP;Z8@D@z!2fb00t~=041RVZvX%Q diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart index 36787a0..3fe78e7 100644 --- a/lib/screen/dashboard/dashboard_screen.dart +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -1,4 +1,8 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; +import 'package:timemanage/db/timer_entry_database.dart'; +import 'package:timemanage/model/timer_entry.dart'; import 'package:timemanage/screen/dashboard/components/home_menu_bar.dart'; // 主页 @@ -10,149 +14,154 @@ class DashBoardScreen extends StatefulWidget { } class DashBoardScreenState extends State { - // 计时器列表 - List timers = []; + late List timers; // 计时器列表 + bool isLoading = false; // 是否正在加载 + + // 初始化状态 + @override + void initState() { + super.initState(); + refreshTimers(); + } + + // 销毁状态,关闭数据库 + @override + void dispose() { + TimerEntryDatabase.instance.close(); + super.dispose(); + } + + // 刷新计时器列表 + Future refreshTimers() async { + setState(() { + isLoading = true; + }); + + timers = await TimerEntryDatabase.instance.readAll(); + + setState(() { + isLoading = false; + }); + } // 添加计时器 - // TODO: 对这个函数添加注释 void _addTimer() { - final controller = TextEditingController(); + final controller1 = TextEditingController(); + final controller2 = TextEditingController(); showDialog( context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('新计时器'), - content: TextField( - controller: controller, - decoration: const InputDecoration(hintText: '计时器名称'), + builder: (context) => AlertDialog( + title: const Text('新建计时器'), + // 输入计时器名称和计时器时长 + content: SizedBox( + height: 100, + child: Column( + children: [ + TextField( + decoration: InputDecoration(hintText: '计时器名称'), + controller: controller1, + ), + TextField( + decoration: InputDecoration(hintText: '计时器时长'), + controller: controller2, + ), + ], ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context, 'Cancel'), - child: const Text('取消'), - ), - TextButton( - onPressed: () { - final name = controller.text; - if (name.isNotEmpty) { - setState(() { - timers.add(TimerModel(name: name)); - }); - } - Navigator.pop(context, 'OK'); - }, - child: const Text('OK'), - ), - ], - ); - }, + ), + // 确定or取消 + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, 'Cancel'), + child: const Text('取消'), + ), + TextButton( + onPressed: () { + final name = controller1.text; + final duration = controller2.text; + // TODO: 检查持续时间输入的是否合法,如果合法则添加计时器,否则提示错误 + if (name.isNotEmpty) { + setState(() { + timers.add( + TimerEntry(name: name, duration: int.parse(duration))); + }); + } + Navigator.pop(context, 'OK'); + }, + child: const Text('确定'), + ), + ], + ), ); } @override Widget build(BuildContext context) { return Scaffold( - // 应用栏 - appBar: AppBar( - backgroundColor: Colors.blueAccent, // 背景色 - // 最前面的菜单按钮 - leading: HomeMenuBar(), - // 标题 - title: const Text('时间管理'), - actions: [ - // 搜索按钮 - // TODO: 实现搜索功能 - IconButton( - icon: const Icon(Icons.search), - onPressed: () {}, - ), - // 筛选按钮 - // TODO: 实现筛选功能 - IconButton( - icon: const Icon(Icons.filter_alt), - onPressed: () {}, - ) - ], - ), - // 显示计时器列表 - body: ListView.builder( - itemCount: timers.length, - itemBuilder: (context, index) { - return Column( + // 应用栏 + appBar: AppBar( + backgroundColor: Colors.blueAccent, // 背景色 + // 最前面的菜单按钮 + leading: HomeMenuBar(), + // 标题 + title: const Text('时间管理'), + actions: [ + // 搜索按钮 + // TODO: 实现搜索功能 + IconButton( + icon: const Icon(Icons.search), + onPressed: () {}, + ), + // // 筛选按钮 + // IconButton( + // icon: const Icon(Icons.filter_alt), + // onPressed: () {}, + // ) + ], + ), + // 显示计时器列表 + body: isLoading + ? const Center(child: CircularProgressIndicator()) + : timers.isEmpty + ? const Center(child: Text('还没有计时器,先添加一个吧!')) + : buildTimers(), + // 添加计时器的悬浮按钮 + floatingActionButton: FloatingActionButton( + onPressed: _addTimer, + child: const Icon(Icons.add), + ), + ); + } + + // 计时器显示列表 + Widget buildTimers() => ListView.builder( + itemCount: timers.length, + itemBuilder: (BuildContext context, int index) { + final timer = timers[index]; + // 输出timer + // log("message: ${timers.length}"); + return ListTile( + title: Text(timer.name.toString()), + subtitle: Text(timer.duration.toString()), + trailing: Row( + mainAxisSize: MainAxisSize.min, children: [ - ListTile( - leading: CircleAvatar( - child: Text((index + 1).toString()), - ), - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - timers[index].name, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - StreamBuilder( - stream: Stream.periodic( - const Duration(milliseconds: 100), - (_) => timers[index].stopwatch.elapsed.inSeconds), - builder: (context, snapshot) { - final seconds = snapshot.data ?? 0; - final hours = seconds ~/ 3600; - final minutes = (seconds % 3600) ~/ 60; - final remainingSeconds = seconds % 60; - return Text( - '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}', - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ); - }, - ), - ], - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: timers[index].isActive - ? const Icon(Icons.pause) - : const Icon(Icons.play_arrow), - onPressed: () { - setState(() { - if (timers[index].isActive) { - timers[index].stop(); - } else { - timers[index].start(); - } - }); - }, - ), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () { - setState(() { - timers[index].stopwatch.stop(); // 确保计时器停止以防止内存泄漏 - timers.removeAt(index); - }); - }, - ), - ], - ), + IconButton( + icon: const Icon(Icons.play_arrow), + onPressed: () {}, + ), + IconButton( + icon: const Icon(Icons.pause), + onPressed: () {}, + ), + IconButton( + icon: const Icon(Icons.stop), + onPressed: () {}, ), - const SizedBox(height: 10), // 添加间隔 ], - ); - }, - ), - floatingActionButton: FloatingActionButton( - onPressed: _addTimer, - child: const Icon(Icons.add), - )); - } + ), + ); + }, + ); } class TimerModel { diff --git a/pubspec.lock b/pubspec.lock index 238a458..8710183 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -224,6 +224,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + url: "https://pub.dev" + source: hosted + version: "0.17.0" js: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4f797b5..e891a9c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,13 +37,12 @@ dependencies: font_awesome_flutter: ^9.2.0 url_launcher: ^6.1.14 equatable: ^2.0.3 # 用于对象比较 - sqflite: ^2.0.0+3 xdg_directories: ^1.0.0 flutter_bloc: ^7.0.0 bloc: ^7.0.0 path: ^1.8.0 shared_preferences: ^2.0.8 - sqflige: ^2.0.0+3 # 用于数据库 + sqflite: ^2.0.0+3 # 用于数据库 intl: ^0.17.0 # 用于时间格式化 # The following adds the Cupertino Icons font to your application. -- 2.34.1 From 88b35080c09214b5e5e880f1910684b268db77a0 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Fri, 3 Nov 2023 21:57:47 +0800 Subject: [PATCH 11/19] commit --- CONTRIBUTORS.MD | 14 +++++-------- build/flutter_assets/NOTICES | 30 ++++++++++++++++++++++++++++ git-diagnostics-2023-11-03-1901.zip | Bin 0 -> 1405 bytes 3 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 git-diagnostics-2023-11-03-1901.zip diff --git a/CONTRIBUTORS.MD b/CONTRIBUTORS.MD index aa910b0..a906172 100644 --- a/CONTRIBUTORS.MD +++ b/CONTRIBUTORS.MD @@ -1,11 +1,7 @@ -<<<<<<< HEAD -======= -| 姓名 | 知士荟 | 头歌 | Github | -| ------ | -------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------- | +| 姓名 | 知士荟 | 头歌 | Github | +| ------ | ----------------------------------------------------- | ------------------------------------------------ | ---------------------------------------- | | 葛兴海 | [@葛兴海](https://www.learnerhub.net/#/users/12147/docs) | [@葛兴海](https://code.educoder.net/user/ps9up4ig6) | [@Sheeet](https://github.com/icesheeet) | -| 庞浩 | [@庞浩](https://www.learnerhub.net/#/users/12027/docs) | [@庞浩](https://code.educoder.net/user/mbhvfy6mx) | | +| 庞浩 | [@庞浩](https://www.learnerhub.net/#/users/12027/docs) | [@庞浩](https://code.educoder.net/user/mbhvfy6mx) | | | 卫俊钢 | [@卫俊钢](https://www.learnerhub.net/#/users/12144/docs) | [@卫俊钢](https://www.educoder.net/users/p2jf6ytqz) | [@JungangWei](https://github.com/githubwjg) | -| 邹兴云 | [@邹兴云](https://www.learnerhub.net/#/users/12026/docs) | [@邹兴云](https://www.educoder.net/users/p8fjyvg3u) | | -| 蔡玉祥 | [@蔡玉祥](https://www.learnerhub.net/#/users/12015/docs) | [@蔡玉祥](https://www.educoder.net/users/mszfy297n) | | ->>>>>>> gxh_branch - +| 邹兴云 | [@邹兴云](https://www.learnerhub.net/#/users/12026/docs) | [@邹兴云](https://www.educoder.net/users/p8fjyvg3u) | | +| 蔡玉祥 | [@蔡玉祥](https://www.learnerhub.net/#/users/12015/docs) | [@蔡玉祥](https://www.educoder.net/users/mszfy297n) | | diff --git a/build/flutter_assets/NOTICES b/build/flutter_assets/NOTICES index 87d5c05..1ec5a6e 100644 --- a/build/flutter_assets/NOTICES +++ b/build/flutter_assets/NOTICES @@ -28947,6 +28947,36 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- +intl + +Copyright 2013, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + -------------------------------------------------------------------------------- js diff --git a/git-diagnostics-2023-11-03-1901.zip b/git-diagnostics-2023-11-03-1901.zip new file mode 100644 index 0000000000000000000000000000000000000000..96c9dd3d5b14e16964aac6b46a93034a7f30aa1a GIT binary patch literal 1405 zcmWIWW@h1HU|`^2Fqn}XzGGSKq!&P*1|tIlKLaO2N@ikuUVd>&W^%D!PJVi52rC04 z&t;cX)Bc@#hYbW=s_TxHtt$x>KdD#P@P#o*ddik5VheXPdEE5eXIC^ai!VXWApD!$ zahJrl&Nng^YEq3&$%<)S1;=fL*e6sfe>SQQnDIlfSo7FhgAZ$WMPKiDs4)M)+dYR) zy!JZTP_pV$@YSzNi~VkUg`O(?df{u(qHAmOPHlT9dQIt){-)D5?Kd4h%g-;eHuO5e zy7#=y5Y*LcTJfI&Et7mDWW}3=F3Q7#IYB5tpBom6}|F&2gzS zyz}k=qwc$1tDit;PF?!!H+wz#4)|Z5ks`x7$!uS3&yh*u`DV-_9(=2Jt(raK`LE#H zhF^bw`?obO|8m`wzG?S=ExP^Z>C?J1 zulFl|yM1|+&-R!9kNZ6Scj0fQVaprVo5GX0CkYpci};-=PJVFgNc@>Q5j$21WwltH zzr_7X-9+z1twY-*iA!dE6VyctIE4#2C-0c(C83qLq9xO&+e_laPawu!o5HZ?t7 z``FFD|Z`?8cf{)YQBhDE=ue|n|<&u}O zrX@{imgfqva7z{E*(WQe8wXt7_}t=QWZ!D0@QXfuk1S@L6wOPyBy+UTXI66aNA8Sj ztK*l|=54V}ek!RgUn!PZed*hhw=)&jx3Hb#GI2225uUJ>Ig`C_vO{0vT4e*S#z2lG zvQp+{Q_Mi@jyVPw6OvO__vUP!SgdhLc;d^BE80e~hM$GzSIzy_E23`1HPK6@N-1)k z(pwjQm3>x1nd+DPws8F3C(0X5u#*l zFw zm()xk*O(*p+Qv&4w+aXSNs8XEYU5HS zu2UKs)(u9oUj7=H+Pn*xFDzhWyKH#l2b=YQ1-@*@? Date: Sat, 4 Nov 2023 09:51:25 +0800 Subject: [PATCH 12/19] =?UTF-8?q?=E8=AE=A1=E6=97=B6=E5=99=A8=E7=95=8C?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../buildOutputCleanup.lock | Bin 17 -> 17 bytes build/flutter_assets/CONTRIBUTORS.md | 14 +-- lib/db/timer_entry_database.dart | 23 ++-- lib/model/timer_entry.dart | 62 ++++++---- lib/screen/dashboard/dashboard_screen.dart | 106 ++++++++---------- 5 files changed, 109 insertions(+), 96 deletions(-) diff --git a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index a19fd81f63a8f0c7eedd5bca0587310e37439f2e..4b7d84949b032253994e405a8cb9470f4fe69dc6 100644 GIT binary patch literal 17 UcmZP;Z8@D@z!2fb00yji040k9SO5S3 literal 17 UcmZP;Z8@D@z!2fb00u0J042QxivR!s diff --git a/build/flutter_assets/CONTRIBUTORS.md b/build/flutter_assets/CONTRIBUTORS.md index aa910b0..a906172 100644 --- a/build/flutter_assets/CONTRIBUTORS.md +++ b/build/flutter_assets/CONTRIBUTORS.md @@ -1,11 +1,7 @@ -<<<<<<< HEAD -======= -| 姓名 | 知士荟 | 头歌 | Github | -| ------ | -------------------------------------------------------- | --------------------------------------------------- | ------------------------------------------- | +| 姓名 | 知士荟 | 头歌 | Github | +| ------ | ----------------------------------------------------- | ------------------------------------------------ | ---------------------------------------- | | 葛兴海 | [@葛兴海](https://www.learnerhub.net/#/users/12147/docs) | [@葛兴海](https://code.educoder.net/user/ps9up4ig6) | [@Sheeet](https://github.com/icesheeet) | -| 庞浩 | [@庞浩](https://www.learnerhub.net/#/users/12027/docs) | [@庞浩](https://code.educoder.net/user/mbhvfy6mx) | | +| 庞浩 | [@庞浩](https://www.learnerhub.net/#/users/12027/docs) | [@庞浩](https://code.educoder.net/user/mbhvfy6mx) | | | 卫俊钢 | [@卫俊钢](https://www.learnerhub.net/#/users/12144/docs) | [@卫俊钢](https://www.educoder.net/users/p2jf6ytqz) | [@JungangWei](https://github.com/githubwjg) | -| 邹兴云 | [@邹兴云](https://www.learnerhub.net/#/users/12026/docs) | [@邹兴云](https://www.educoder.net/users/p8fjyvg3u) | | -| 蔡玉祥 | [@蔡玉祥](https://www.learnerhub.net/#/users/12015/docs) | [@蔡玉祥](https://www.educoder.net/users/mszfy297n) | | ->>>>>>> gxh_branch - +| 邹兴云 | [@邹兴云](https://www.learnerhub.net/#/users/12026/docs) | [@邹兴云](https://www.educoder.net/users/p8fjyvg3u) | | +| 蔡玉祥 | [@蔡玉祥](https://www.learnerhub.net/#/users/12015/docs) | [@蔡玉祥](https://www.educoder.net/users/mszfy297n) | | diff --git a/lib/db/timer_entry_database.dart b/lib/db/timer_entry_database.dart index 8341806..80c2603 100644 --- a/lib/db/timer_entry_database.dart +++ b/lib/db/timer_entry_database.dart @@ -12,7 +12,14 @@ class TimerEntryDatabase { // 获取数据库 Future get database async { - if (_database != null) return _database!; + // TODO: 临时测试,每次都删除重新创建数据库 + if (_database != null) { + await deleteDatabase('timer_entry.db'); + } + + if (_database != null) { + return _database!; + } _database = await _initDB('timer_entry.db'); return _database!; @@ -32,15 +39,12 @@ class TimerEntryDatabase { // 创建表 const idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; const textType = 'TEXT NOT NULL'; - const integerType = 'INTEGER NOT NULL'; // 创建计时器表 await db.execute(''' CREATE TABLE $tableTimerEntry ( ${TimerEntryFields.id} $idType, ${TimerEntryFields.name} $textType, - ${TimerEntryFields.duration} $integerType, - ${TimerEntryFields.createdAt} $textType ) '''); } @@ -59,7 +63,6 @@ class TimerEntryDatabase { // 获取计时器 Future read(int id) async { final db = await instance.database; - final maps = await db.query(tableTimerEntry, columns: TimerEntryFields.values, where: '${TimerEntryFields.id} = ?', @@ -76,8 +79,14 @@ class TimerEntryDatabase { Future> readAll() async { final db = await instance.database; - const orderBy = '${TimerEntryFields.createdAt} ASC'; - final result = await db.query(tableTimerEntry, orderBy: orderBy); + // // 插入一条测试数据 + // await db.insert( + // tableTimerEntry, + // TimerEntry( + // name: '测试计时器', + // ).toJson()); + + final result = await db.query(tableTimerEntry); return result.map((json) => TimerEntry.fromJson(json)).toList(); } diff --git a/lib/model/timer_entry.dart b/lib/model/timer_entry.dart index 2276f59..2894407 100644 --- a/lib/model/timer_entry.dart +++ b/lib/model/timer_entry.dart @@ -4,12 +4,14 @@ const String tableTimerEntry = 'timer_entry'; // 表名 class TimerEntryFields { static const String id = '_id'; static const String name = 'name'; - static const String duration = 'duration'; static const String createdAt = 'createdAt'; + static const String endAt = 'endAt'; + static const String isActive = 'isActive'; + static const String stopWatch = 'stopWatch'; static final List values = [ /// Add all fields - id, name, duration, createdAt + id, name, createdAt, endAt, isActive, stopWatch ]; } @@ -17,16 +19,32 @@ class TimerEntryFields { class TimerEntry { int? id; // 计时器ID String? name; // 计时器名称 - int? duration; // 计时器时长 DateTime? createdAt; // 计时器创建时间 + DateTime? endAt; // 计时器结束时间 + bool? isActive; // 计时器是否在计时 + Stopwatch? stopWatch; // 计时器 // 构造函数 - TimerEntry({ - this.id, - this.name, - this.duration, - this.createdAt, - }); + TimerEntry({this.name}) + : createdAt = DateTime.now(), + isActive = false, + stopWatch = Stopwatch(); + + void start() { + stopWatch!.start(); + isActive = true; + } + + void pause() { + stopWatch!.stop(); + isActive = false; + } + + void stop() { + stopWatch!.stop(); + isActive = false; + endAt = DateTime.now(); + } // 重写等号运算符 @override @@ -36,40 +54,40 @@ class TimerEntry { runtimeType == other.runtimeType && id == other.id && name == other.name && - duration == other.duration && - createdAt == other.createdAt; + createdAt == other.createdAt && + endAt == other.endAt && + isActive == other.isActive && + stopWatch == other.stopWatch; @override int get hashCode => - id.hashCode ^ name.hashCode ^ duration.hashCode ^ createdAt.hashCode; + id.hashCode ^ + name.hashCode ^ + createdAt.hashCode ^ + endAt.hashCode ^ + isActive.hashCode ^ + stopWatch.hashCode; // 将计时器实体类转换为json,用于数据库存储 Map toJson() => { - TimerEntryFields.id: id, TimerEntryFields.name: name, - TimerEntryFields.duration: duration, - TimerEntryFields.createdAt: createdAt?.toIso8601String(), }; // 将json转换为计时器实体类,用于数据库读取 static TimerEntry fromJson(Map json) => TimerEntry( - id: json[TimerEntryFields.id] as int?, name: json[TimerEntryFields.name] as String?, - duration: json[TimerEntryFields.duration] as int?, - createdAt: DateTime.parse(json[TimerEntryFields.createdAt] as String), ); // copy函数,用于复制计时器实体类 TimerEntry copy({ int? id, String? name, - int? duration, DateTime? createdAt, + DateTime? endAt, + bool? isActive, + Stopwatch? stopWatch, }) => TimerEntry( - id: id ?? this.id, name: name ?? this.name, - duration: duration ?? this.duration, - createdAt: createdAt ?? this.createdAt, ); } diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart index 3fe78e7..a83644a 100644 --- a/lib/screen/dashboard/dashboard_screen.dart +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -40,14 +40,14 @@ class DashBoardScreenState extends State { timers = await TimerEntryDatabase.instance.readAll(); setState(() { + timers = timers; isLoading = false; }); } // 添加计时器 void _addTimer() { - final controller1 = TextEditingController(); - final controller2 = TextEditingController(); + final controller = TextEditingController(); showDialog( context: context, builder: (context) => AlertDialog( @@ -59,11 +59,7 @@ class DashBoardScreenState extends State { children: [ TextField( decoration: InputDecoration(hintText: '计时器名称'), - controller: controller1, - ), - TextField( - decoration: InputDecoration(hintText: '计时器时长'), - controller: controller2, + controller: controller, ), ], ), @@ -76,13 +72,13 @@ class DashBoardScreenState extends State { ), TextButton( onPressed: () { - final name = controller1.text; - final duration = controller2.text; + final name = controller.text; // TODO: 检查持续时间输入的是否合法,如果合法则添加计时器,否则提示错误 if (name.isNotEmpty) { setState(() { - timers.add( - TimerEntry(name: name, duration: int.parse(duration))); + timers.add(TimerEntry( + name: name, + )); }); } Navigator.pop(context, 'OK'); @@ -132,58 +128,52 @@ class DashBoardScreenState extends State { ); } - // 计时器显示列表 + // 计时器显示列表,显示创建时间和计时器名称,计时时间,以及开始、暂停、停止按钮 Widget buildTimers() => ListView.builder( itemCount: timers.length, - itemBuilder: (BuildContext context, int index) { + itemBuilder: (context, index) { final timer = timers[index]; - // 输出timer - // log("message: ${timers.length}"); - return ListTile( - title: Text(timer.name.toString()), - subtitle: Text(timer.duration.toString()), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: const Icon(Icons.play_arrow), - onPressed: () {}, - ), - IconButton( - icon: const Icon(Icons.pause), - onPressed: () {}, - ), - IconButton( - icon: const Icon(Icons.stop), - onPressed: () {}, - ), - ], + return Card( + color: Colors.white, + child: ListTile( + // 计时器名称 + title: Text( + timer.name!, + style: const TextStyle(fontSize: 24), + ), + // 计时器创建时间 + subtitle: Text( + '创建时间:${timer.createdAt!.toString().substring(0, 19)}', + style: const TextStyle(fontSize: 16), + ), + // 计时器计时时间 + trailing: Text( + timer.stopWatch!.elapsed.toString().substring(0, 10), + style: const TextStyle(fontSize: 24), + ), + // 计时器开始按钮 + leading: IconButton( + icon: const Icon(Icons.play_arrow), + onPressed: () { + setState(() { + timer.start(); + }); + }, + ), + // 计时器暂停按钮 + onTap: () { + setState(() { + timer.pause(); + }); + }, + // 计时器停止按钮 + onLongPress: () { + setState(() { + timer.stop(); + }); + }, ), ); }, ); } - -class TimerModel { - String name; - Stopwatch stopwatch; - bool isActive; - - TimerModel({required this.name}) - : stopwatch = Stopwatch(), - isActive = false; - - void start() { - stopwatch.start(); - isActive = true; - } - - void stop() { - stopwatch.stop(); - isActive = false; - } - - void reset() { - stopwatch.reset(); - } -} -- 2.34.1 From 323220c87c925038539ae98a7d810d5a0f74b511 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Sat, 4 Nov 2023 10:13:12 +0800 Subject: [PATCH 13/19] bug --- lib/model/timer_entry.dart | 2 + lib/screen/dashboard/dashboard_screen.dart | 112 +++++++++++++-------- 2 files changed, 74 insertions(+), 40 deletions(-) diff --git a/lib/model/timer_entry.dart b/lib/model/timer_entry.dart index 2894407..9848982 100644 --- a/lib/model/timer_entry.dart +++ b/lib/model/timer_entry.dart @@ -30,6 +30,8 @@ class TimerEntry { isActive = false, stopWatch = Stopwatch(); + get stopwatch => null; + void start() { stopWatch!.start(); isActive = true; diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart index a83644a..57da8ed 100644 --- a/lib/screen/dashboard/dashboard_screen.dart +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -133,46 +133,78 @@ class DashBoardScreenState extends State { itemCount: timers.length, itemBuilder: (context, index) { final timer = timers[index]; - return Card( - color: Colors.white, - child: ListTile( - // 计时器名称 - title: Text( - timer.name!, - style: const TextStyle(fontSize: 24), - ), - // 计时器创建时间 - subtitle: Text( - '创建时间:${timer.createdAt!.toString().substring(0, 19)}', - style: const TextStyle(fontSize: 16), - ), - // 计时器计时时间 - trailing: Text( - timer.stopWatch!.elapsed.toString().substring(0, 10), - style: const TextStyle(fontSize: 24), - ), - // 计时器开始按钮 - leading: IconButton( - icon: const Icon(Icons.play_arrow), - onPressed: () { - setState(() { - timer.start(); - }); - }, - ), - // 计时器暂停按钮 - onTap: () { - setState(() { - timer.pause(); - }); - }, - // 计时器停止按钮 - onLongPress: () { - setState(() { - timer.stop(); - }); - }, - ), + return ListView.builder( + itemCount: timers.length, + itemBuilder: (context, index) { + return Column( + children: [ + ListTile( + leading: CircleAvatar( + child: Text((index + 1).toString()), + ), + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + timer.name!, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + StreamBuilder( + stream: Stream.periodic( + const Duration(milliseconds: 100), + (_) => timer.stopwatch.elapsed.inSeconds), + builder: (context, snapshot) { + final seconds = snapshot.data ?? 0; + final hours = seconds ~/ 3600; + final minutes = (seconds % 3600) ~/ 60; + final remainingSeconds = seconds % 60; + return Text( + '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}', + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ); + }, + ), + ], + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: timers[index].isActive! + ? const Icon(Icons.pause) + : const Icon(Icons.play_arrow), + onPressed: () { + setState(() { + if (timers[index].isActive!) { + timers[index].stop(); + } else { + timers[index].start(); + } + }); + }, + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + setState(() { + timers[index].stopwatch.stop(); // 确保计时器停止以防止内存泄漏 + timers.removeAt(index); + }); + }, + ), + ], + ), + ), + const SizedBox(height: 10), // 添加间隔 + ], + ); + }, ); }, ); -- 2.34.1 From 89337cbd722209e4538004cd304058dd6e6b036b Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Sat, 4 Nov 2023 10:39:50 +0800 Subject: [PATCH 14/19] bug --- lib/model/timer_entry.dart | 9 ++ lib/screen/dashboard/dashboard_screen.dart | 154 +++++++++++---------- 2 files changed, 90 insertions(+), 73 deletions(-) diff --git a/lib/model/timer_entry.dart b/lib/model/timer_entry.dart index 9848982..268ea83 100644 --- a/lib/model/timer_entry.dart +++ b/lib/model/timer_entry.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + const String tableTimerEntry = 'timer_entry'; // 表名 /// 用于数据库建表 @@ -35,6 +37,7 @@ class TimerEntry { void start() { stopWatch!.start(); isActive = true; + log("message: timer start"); } void pause() { @@ -48,6 +51,12 @@ class TimerEntry { endAt = DateTime.now(); } + void reset() { + stopWatch!.reset(); + isActive = false; + endAt = null; + } + // 重写等号运算符 @override bool operator ==(Object other) => diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart index 57da8ed..c0ca06c 100644 --- a/lib/screen/dashboard/dashboard_screen.dart +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -129,83 +129,91 @@ class DashBoardScreenState extends State { } // 计时器显示列表,显示创建时间和计时器名称,计时时间,以及开始、暂停、停止按钮 - Widget buildTimers() => ListView.builder( - itemCount: timers.length, - itemBuilder: (context, index) { - final timer = timers[index]; - return ListView.builder( - itemCount: timers.length, - itemBuilder: (context, index) { - return Column( + Widget buildTimers() { + return ListView.builder( + itemCount: timers.length, + itemBuilder: (context, index) { + var localTime = timers[index].createdAt!.toLocal(); + return Column( + children: [ + ListTile( + leading: CircleAvatar( + child: Text((index + 1).toString()), + ), + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - ListTile( - leading: CircleAvatar( - child: Text((index + 1).toString()), + Text( + '${localTime.year}-${localTime.month}-${localTime.day} ${localTime.hour}:${localTime.minute}:${localTime.second}', + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + fontStyle: FontStyle.italic, ), - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - timer.name!, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - StreamBuilder( - stream: Stream.periodic( - const Duration(milliseconds: 100), - (_) => timer.stopwatch.elapsed.inSeconds), - builder: (context, snapshot) { - final seconds = snapshot.data ?? 0; - final hours = seconds ~/ 3600; - final minutes = (seconds % 3600) ~/ 60; - final remainingSeconds = seconds % 60; - return Text( - '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}', - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ); - }, - ), - ], + ), + Text( + timers[index].name!, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - icon: timers[index].isActive! - ? const Icon(Icons.pause) - : const Icon(Icons.play_arrow), - onPressed: () { - setState(() { - if (timers[index].isActive!) { - timers[index].stop(); - } else { - timers[index].start(); - } - }); - }, - ), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () { - setState(() { - timers[index].stopwatch.stop(); // 确保计时器停止以防止内存泄漏 - timers.removeAt(index); - }); - }, + ), + StreamBuilder( + stream: + Stream.periodic(const Duration(milliseconds: 100), (_) { + log("message: refresh timer"); + return timers[index].stopwatch.elapsed.inSeconds; + }), + builder: (context, snapshot) { + final seconds = snapshot.data ?? 0; + final hours = seconds ~/ 3600; + final minutes = (seconds % 3600) ~/ 60; + final remainingSeconds = seconds % 60; + log("message: build"); + return Text( + '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}', + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, ), - ], - ), + ); + }, ), - const SizedBox(height: 10), // 添加间隔 ], - ); - }, - ); - }, - ); + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: timers[index].isActive! + ? const Icon(Icons.pause) + : const Icon(Icons.play_arrow), + onPressed: () { + setState(() { + if (timers[index].isActive!) { + timers[index].stop(); + } else { + timers[index].start(); + } + }); + }, + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + setState(() { + timers[index].stopwatch.stop(); // 确保计时器停止以防止内存泄漏 + timers.removeAt(index); + }); + }, + ), + ], + ), + ), + const SizedBox(height: 10), // 添加间隔 + ], + ); + }, + ); + } } -- 2.34.1 From 45d2ab3178088dcf486c2f7393e7b9f165d69572 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Sat, 4 Nov 2023 11:57:08 +0800 Subject: [PATCH 15/19] =?UTF-8?q?=E7=94=A8=E4=BE=8B=E4=B8=80=20=E8=AE=A1?= =?UTF-8?q?=E6=97=B6=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.gradle/7.4.2/checksums/checksums.lock | Bin 17 -> 0 bytes .../dependencies-accessors.lock | Bin 17 -> 0 bytes .../dependencies-accessors/gc.properties | 0 .../.gradle/7.4.2/fileChanges/last-build.bin | Bin 1 -> 0 bytes .../.gradle/7.4.2/fileHashes/fileHashes.bin | Bin 23497 -> 0 bytes .../.gradle/7.4.2/fileHashes/fileHashes.lock | Bin 17 -> 0 bytes android/.gradle/7.4.2/gc.properties | 0 .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .../buildOutputCleanup/cache.properties | 2 +- android/app/build.gradle | 4 +- android/app/src/main/AndroidManifest.xml | 2 +- .../com/example/timemanage/MainActivity.kt | 6 - lib/db/timer_entry_database.dart | 4 +- lib/model/timer_entry.dart | 111 +++++++++--------- .../dashboard/components/home_menu_bar.dart | 11 -- lib/screen/dashboard/dashboard_screen.dart | 20 ++-- lib/screen/projects_screen.dart | 16 --- 17 files changed, 70 insertions(+), 106 deletions(-) delete mode 100644 android/.gradle/7.4.2/checksums/checksums.lock delete mode 100644 android/.gradle/7.4.2/dependencies-accessors/dependencies-accessors.lock delete mode 100644 android/.gradle/7.4.2/dependencies-accessors/gc.properties delete mode 100644 android/.gradle/7.4.2/fileChanges/last-build.bin delete mode 100644 android/.gradle/7.4.2/fileHashes/fileHashes.bin delete mode 100644 android/.gradle/7.4.2/fileHashes/fileHashes.lock delete mode 100644 android/.gradle/7.4.2/gc.properties delete mode 100644 android/app/src/main/kotlin/com/example/timemanage/MainActivity.kt delete mode 100644 lib/screen/projects_screen.dart diff --git a/android/.gradle/7.4.2/checksums/checksums.lock b/android/.gradle/7.4.2/checksums/checksums.lock deleted file mode 100644 index 061a0e4cb6021a02aeae304caf123af77e1d74ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 TcmZQx5xl?V>-tE41}FdkIiUoG diff --git a/android/.gradle/7.4.2/dependencies-accessors/dependencies-accessors.lock b/android/.gradle/7.4.2/dependencies-accessors/dependencies-accessors.lock deleted file mode 100644 index f9fae7c1d99c28b300b503cffef36b65c5bddd46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 ScmZRM;g!u!u&&5vfC2y=NCI;J diff --git a/android/.gradle/7.4.2/dependencies-accessors/gc.properties b/android/.gradle/7.4.2/dependencies-accessors/gc.properties deleted file mode 100644 index e69de29..0000000 diff --git a/android/.gradle/7.4.2/fileChanges/last-build.bin b/android/.gradle/7.4.2/fileChanges/last-build.bin deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/android/.gradle/7.4.2/fileHashes/fileHashes.bin b/android/.gradle/7.4.2/fileHashes/fileHashes.bin deleted file mode 100644 index ee330c91cbcd442a41f07512f936eb8bba722c71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23497 zcmeI3c{o*F|A!AD9EXF@K$D>)WJt(dWXKT5oP>HJ$@CN%Qqe#IB}#@SrJfW~p_C*E zN#-KSkdjhl?kT@@?6tg|z5V`s|9Y>zuFInPtb2dg`s}m!+2?BSwG_&1`UGs0|1^>R z{7L>rCO{@YCO{@YCO{@YCO{@YCO{@YCO{@YCO{@YCO{@YCO{@YCO{@YCO{_ezmkA0 zSO_n+nAywOnSTfbQ79|;z#m)oY4&=Yj4fbbFI%>u|38SlrZCo*HJE^LD?!AMalNC* z)ci}uxPvg_Cz^_kY!^%0VBAIt@wh!{oNJ8ljAGoG4e^AtcWz(3V(E%;$IFN(8A{4V zTsFFn@eOrTJfeJPcS~Ue#%-ggczfUYX!+0qjIS@6;zNxm`_nZt;dp7pQ(c|p z2@m2q-M1PW?Qf4`+?o^dI}csre&zBM!|hiio|j$1?`pX|2jg~;h~Mv>nWN5`4f8ZB zMZEB&QLO({Z*^?E%^2d34!?J(DbO~+xP##ok82Y>S$-PB6uS+GSE_QUh3$CdiM_Bv z8}S#henWEA_tP-$G!yaK7vgok4ivcF_5Fy~T^%+rOg$TdjW@SNypeISN<7_77~{?= zh`({q*vtr1yM%GmB*dH382P#f4?o2CdS}F2oYw|c&DlQ)w{vESH}A9CdrwCJCdj$<(9{|ncEc4bSt@Wm`xGmCN+qE zjC%2{dSD0K4#NfU-siHXzL{(6$HrSDJn=O%F3hjJs1M^7x#)#&Z`nhKIIS%)&X7a= zd;J?tGtWTyKD0?de1yYd){b9+Fdtic#7C<`PTmvlnSpJ`ya4gB)E`0>dG7G|vQ9>P zV)VVBWlCBDHs0%L;#;sfHVUY~xsE-QK`#+_0T*LWt? zkmzPR595|5h-v$*p!<+9UaG*mjHq5jRcF8`#lW z437&FAH>Zar1Em#9%SajGOl&Zq@qFKW(+rRTy_@L7d@PwnfkV zG2CBPyAXF!j-ydigl}Nu9UBne9IK?AZOPFMxj*6_sqP)&jPSb{H{FK#Hc7FUk?K`u z7`Iw8#g7Y_JaC}ZVcha2;@*389yBR$evffRG2%Po{6+08O@G1dyqV%pxFdac&Oe87 z<12{!yzcr@CKwKLcHWA(zpaNs*!L}2*mw(xDSpkN`*Gv-3CQ;&9yq6_GE6SSVLf95Z+Z zh=#RKJ;yh9(=VL<+Zq~AjexrgCk+c|OjF}lch{>A$_-X)5^o7Bo-v7IH6-+iHH_CB z8W39jlQ*MI=2K&d+uy9SlBppYCR&9y6ZR$6T6GEWUWMu49>d95t%tK>-rI>=c}(=4 zzC-0M{3%Ip@;D=mXk3~t?P+YhF~_>3#V1^q;?8RDvJ;I68#abroW^3o(axH` zyA)zs*TKt0G-&NQPsjEb9;|$;CN|Ql4DNMi*&io}hK^@sRLYkHvkt|xDSbO#RfwC_ zMxs%FrYk7QFlC8NYU=3?$G(8)6((y)fsvq5r%6}bN`B|2c>AbVYkpIf{G8zf4Q1M3iIy>cf5%01?1t;7`DKXGkhZ(d$qM;SZyTz+m@5yMt)31$N?H)nH7#jPC zh912}{-u1}i6Y@NE}A=60INamB^pb*`}~}Weao$QmAf2{d|b{tD=Dx?@#{D+mUn92 z*WG6hq=Xb`lzX~D!vu^3jphV8+{*a|e+@shP_gwoO793){4mOD@Oco8_6w@Ti!a8G zS9{%i&|>!WAnQ8#na>a4YVgnHYqe8W`#wCgbH>8DOJ7(G0Y9Q4GbHw8WYKxQc!5^+ z?HgLMS!c!TMKtD_Wzv_Y52YD6lxBhLw%a2VhG@R9-ITH=# z<#T#!^#>1p_{i63*fReot3hWz>4BToT&HHXxuWUZ8YcSr){!@7K?AN_^Q z*}<6xhG&cqIWibqSq%Z^-PyPrOK;|d$!@>XaKSn-?}JTO39BJkN;HfvRK32oCn=AS zpcCI#I&u~o24Ey;yv!#@a5cij|Lr^9#J(Y_2AiDeK}Kxu4OWB3Pc%aR^2x3nTAiKNvCZXtnuG*2n8yWZ zRAtcNvJ%Z!Ib!(z^xyB|9M|%PsDrcr)`eBIxDN8Etth#+C(2Y3Z4=Ab5_)8j#}3%tw!RG&3kKYwZslx{s^8*_|xD8 zpC|YlD@50ZG(YyZTwE3t9ps&>jx#cdM%TS_5506pEB1NM2wgRNYAfr?shUKiFjnfy z#~ZuVMD-SVUUA-?z-sUvB^nC{Z~uv@1H@A>Y z$K|BZ=Q(_r&`gLmX8I(XxNa3Y{dw(qOQDJn<*)`az}X0&)y$Yf?tIOKMOp_=ozu#D z(*uoFU?ga?VbJ0BpqCp@S^HLrRw<775s!KulrHo5295R@bU4F9b(7NaVArKJ9L`Im zy2Nk78qlC>6O9U?xt({ZZ7Uvymo&Uu5!??AP>b29E}+BJ$bY!~S4yqGI>|!cs<5t- zXx4R5r+JsSHE1bR2RIj{(`$S~UulA;N+;h}aOG6yQ$V;HZ39UwkGNI&L_00(an2k< z$DjstPT(xM33NE)lNdes&EVylUzE=d^`$hFE?fuCd!lh-frihHQ1!som(rVPZ3=R% zE9ZGmG(;p`7>~&5JT=*ss1_nW0j{ei*8yk6-AXhpkF4S}>gaJazW!mHe$1nf)!?1x zs8w9|wQzWTm$rZ9lJUeEUvRB6Sz|TWHfYqnpu??vtRP7@&$01VfaXW_*4L_1&|n@( zpi$dEhcmLo-M%F&___qP92>5Ztg2@<_*#g@U*og;c{kt6l~CUur1j;?Nt^+$Z16RP z&d|B8_MVD)oO5UM_Mcwt(9i-SL8CGE3vN~}L;b4qrVL5i=7lTInT&w1V}Guk2L6pV zI*X0cvlh#lb8#;e=%?ACcL~gj#zQn#KjoUkvE+JNs2dm0yf{b7-&}V>Cchd{b3kUL`E6 zb@fZhv=)&+CnoL=&V_%joC?;3H#7}ywYfi6@c*&vvskwN^)P7YFe^|f-0wk$tI<}m z=l#5l;4kfdgK~E~mC*ix2H$I<5tZ)0-T%E#_c_jsDiIWQYt~uOZHR_Zi?00vgPZp< zL)T@A2-VcF&Ps5UXi%MZ_9pXF+^_39`84<*L3iGI!Xq0s|FUE@;4OQEDa8$hD64jrLY+?pMl`(L8&14{Hso z0MS^PD$q#v_>pNT5lNTJ_pf0!c&0fs)R}V$jdnavunX`Rv5|uUHLQF44#c5EV*#S)tDU@WNnma2`K2!1W2U@dknpSEDa6 zxZ?c%?p-QxYInc0-g1<69b)&0#=>QFDk{t~8?(&;Ke!st-H-ha;Bla6S^s#nY2e>p QIIHx@@$B`?D`kcM12lYQI{*Lx diff --git a/android/.gradle/7.4.2/fileHashes/fileHashes.lock b/android/.gradle/7.4.2/fileHashes/fileHashes.lock deleted file mode 100644 index f38361e005ee9f6cacf5ed1d76fd25fb31826e1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 UcmZSfQkI*3(Dy0strh1Iqva literal 17 UcmZP;Z8@D@z!2fb00yji040k9SO5S3 diff --git a/android/.gradle/buildOutputCleanup/cache.properties b/android/.gradle/buildOutputCleanup/cache.properties index a19bd4f..05c9ac1 100644 --- a/android/.gradle/buildOutputCleanup/cache.properties +++ b/android/.gradle/buildOutputCleanup/cache.properties @@ -1,2 +1,2 @@ -#Thu Nov 02 19:05:51 CST 2023 +#Sat Nov 04 11:50:30 CST 2023 gradle.version=7.5 diff --git a/android/app/build.gradle b/android/app/build.gradle index 9e37a04..53eea4a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -23,7 +23,7 @@ if (flutterVersionName == null) { } android { - namespace "com.example.timemanage" + namespace "com.example.timemaneger" compileSdkVersion flutter.compileSdkVersion ndkVersion flutter.ndkVersion @@ -42,7 +42,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.timemanage" + applicationId "com.example.timemaneger" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion flutter.minSdkVersion diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d83fd0b..433791a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ > readAll() async { final db = await instance.database; - // // 插入一条测试数据 + // 插入一条测试数据 // await db.insert( // tableTimerEntry, // TimerEntry( diff --git a/lib/model/timer_entry.dart b/lib/model/timer_entry.dart index 268ea83..5c117cf 100644 --- a/lib/model/timer_entry.dart +++ b/lib/model/timer_entry.dart @@ -2,7 +2,7 @@ import 'dart:developer'; const String tableTimerEntry = 'timer_entry'; // 表名 -/// 用于数据库建表 +// 数据库表结构 class TimerEntryFields { static const String id = '_id'; static const String name = 'name'; @@ -17,88 +17,89 @@ class TimerEntryFields { ]; } -// 计时器实体类,继承可比较类 class TimerEntry { int? id; // 计时器ID - String? name; // 计时器名称 - DateTime? createdAt; // 计时器创建时间 + String name; // 计时器名称 + Stopwatch? stopwatch; // 计时器 + bool isActice; // 计时器是否在计时 + DateTime createdAt; // 计时器创建时间 DateTime? endAt; // 计时器结束时间 - bool? isActive; // 计时器是否在计时 - Stopwatch? stopWatch; // 计时器 // 构造函数 - TimerEntry({this.name}) - : createdAt = DateTime.now(), - isActive = false, - stopWatch = Stopwatch(); + TimerEntry({required this.name}) + : stopwatch = Stopwatch(), + isActice = false, + createdAt = DateTime.now(); - get stopwatch => null; + bool get isActive => isActice; + // 开始计时 void start() { - stopWatch!.start(); - isActive = true; - log("message: timer start"); + stopwatch!.start(); + isActice = true; } + // 暂停计时 void pause() { - stopWatch!.stop(); - isActive = false; + stopwatch!.stop(); + isActice = false; } + // 停止计时 void stop() { - stopWatch!.stop(); - isActive = false; + stopwatch!.stop(); + isActice = false; endAt = DateTime.now(); } + // 重置计时 void reset() { - stopWatch!.reset(); - isActive = false; + stopwatch!.reset(); + isActice = false; endAt = null; } - // 重写等号运算符 - @override - bool operator ==(Object other) => - identical(this, other) || - other is TimerEntry && - runtimeType == other.runtimeType && - id == other.id && - name == other.name && - createdAt == other.createdAt && - endAt == other.endAt && - isActive == other.isActive && - stopWatch == other.stopWatch; - - @override - int get hashCode => - id.hashCode ^ - name.hashCode ^ - createdAt.hashCode ^ - endAt.hashCode ^ - isActive.hashCode ^ - stopWatch.hashCode; - - // 将计时器实体类转换为json,用于数据库存储 - Map toJson() => { - TimerEntryFields.name: name, - }; - - // 将json转换为计时器实体类,用于数据库读取 - static TimerEntry fromJson(Map json) => TimerEntry( - name: json[TimerEntryFields.name] as String?, - ); - - // copy函数,用于复制计时器实体类 + // 复制计时器 TimerEntry copy({ int? id, String? name, DateTime? createdAt, DateTime? endAt, bool? isActive, - Stopwatch? stopWatch, + Stopwatch? stopwatch, }) => TimerEntry( name: name ?? this.name, - ); + ) + ..id = id ?? this.id + ..createdAt = createdAt ?? this.createdAt + ..endAt = endAt ?? this.endAt + ..isActice = isActive ?? this.isActice + ..stopwatch = stopwatch ?? this.stopwatch; + + // fromJson + static TimerEntry fromJson(Map json) { + return TimerEntry( + name: json[TimerEntryFields.name] as String, + ) + ..id = json[TimerEntryFields.id] as int? + ..createdAt = DateTime.parse(json[TimerEntryFields.createdAt] as String) + ..endAt = json[TimerEntryFields.endAt] == null + ? null + : DateTime.parse(json[TimerEntryFields.endAt] as String) + ..isActice = json[TimerEntryFields.isActive] == 1 ? true : false + ..stopwatch = Stopwatch(); + } + + // toJson + Map toJson() { + return { + TimerEntryFields.id: id, + TimerEntryFields.name: name, + TimerEntryFields.createdAt: createdAt.toIso8601String(), + TimerEntryFields.endAt: endAt?.toIso8601String(), + TimerEntryFields.isActive: isActice ? 1 : 0, + TimerEntryFields.stopWatch: stopwatch!.elapsed.inMilliseconds, + }; + } } diff --git a/lib/screen/dashboard/components/home_menu_bar.dart b/lib/screen/dashboard/components/home_menu_bar.dart index 6ea42c4..88f7f28 100644 --- a/lib/screen/dashboard/components/home_menu_bar.dart +++ b/lib/screen/dashboard/components/home_menu_bar.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:timemanage/screen/projects_screen.dart'; import 'package:timemanage/screen/reports_screen.dart'; import 'package:timemanage/screen/export_screen.dart'; import 'package:timemanage/screen/settings_screen.dart'; @@ -29,16 +28,6 @@ class HomeMenuBarState extends State { }, child: const Text('课程表'), ), - MenuItemButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const ProjectsScreen()), - ); - }, - child: const Text('项目'), - ), MenuItemButton( onPressed: () { Navigator.push( diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart index c0ca06c..85f14e2 100644 --- a/lib/screen/dashboard/dashboard_screen.dart +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -133,7 +133,6 @@ class DashBoardScreenState extends State { return ListView.builder( itemCount: timers.length, itemBuilder: (context, index) { - var localTime = timers[index].createdAt!.toLocal(); return Column( children: [ ListTile( @@ -144,32 +143,27 @@ class DashBoardScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '${localTime.year}-${localTime.month}-${localTime.day} ${localTime.hour}:${localTime.minute}:${localTime.second}', + timers[index].createdAt.toString().substring(0, 16), style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, - fontStyle: FontStyle.italic, ), ), Text( - timers[index].name!, + timers[index].name, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), StreamBuilder( - stream: - Stream.periodic(const Duration(milliseconds: 100), (_) { - log("message: refresh timer"); - return timers[index].stopwatch.elapsed.inSeconds; - }), + stream: Stream.periodic(const Duration(milliseconds: 100), + (_) => timers[index].stopwatch!.elapsed.inSeconds), builder: (context, snapshot) { final seconds = snapshot.data ?? 0; final hours = seconds ~/ 3600; final minutes = (seconds % 3600) ~/ 60; final remainingSeconds = seconds % 60; - log("message: build"); return Text( '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}', style: const TextStyle( @@ -185,12 +179,12 @@ class DashBoardScreenState extends State { mainAxisSize: MainAxisSize.min, children: [ IconButton( - icon: timers[index].isActive! + icon: timers[index].isActive ? const Icon(Icons.pause) : const Icon(Icons.play_arrow), onPressed: () { setState(() { - if (timers[index].isActive!) { + if (timers[index].isActive) { timers[index].stop(); } else { timers[index].start(); @@ -202,7 +196,7 @@ class DashBoardScreenState extends State { icon: const Icon(Icons.delete), onPressed: () { setState(() { - timers[index].stopwatch.stop(); // 确保计时器停止以防止内存泄漏 + timers[index].stopwatch!.stop(); // 确保计时器停止以防止内存泄漏 timers.removeAt(index); }); }, diff --git a/lib/screen/projects_screen.dart b/lib/screen/projects_screen.dart deleted file mode 100644 index 15e1650..0000000 --- a/lib/screen/projects_screen.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:flutter/material.dart'; - -class ProjectsScreen extends StatelessWidget { - const ProjectsScreen({super.key}); - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('项目'), - ), - body: const Center( - child: Text('项目界面'), - ), - ); - } -} -- 2.34.1 From f8938fb12a19a5004094d6b4f41a1a5dee405eb1 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Sat, 4 Nov 2023 11:58:57 +0800 Subject: [PATCH 16/19] bug fixed --- .vscode/settings.json | 3 +++ lib/db/timer_entry_database.dart | 2 -- lib/model/timer_entry.dart | 2 -- lib/screen/dashboard/dashboard_screen.dart | 2 -- 4 files changed, 3 insertions(+), 6 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c5f3f6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/lib/db/timer_entry_database.dart b/lib/db/timer_entry_database.dart index 6ed5174..cabef67 100644 --- a/lib/db/timer_entry_database.dart +++ b/lib/db/timer_entry_database.dart @@ -39,8 +39,6 @@ class TimerEntryDatabase { // 创建表 const idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; const textType = 'TEXT NOT NULL'; - const boolType = 'BOOLEAN NOT NULL'; - const intType = 'INTEGER NOT NULL'; // 创建计时器表 await db.execute(''' diff --git a/lib/model/timer_entry.dart b/lib/model/timer_entry.dart index 5c117cf..b8ba0a5 100644 --- a/lib/model/timer_entry.dart +++ b/lib/model/timer_entry.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - const String tableTimerEntry = 'timer_entry'; // 表名 // 数据库表结构 diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart index 85f14e2..d24051a 100644 --- a/lib/screen/dashboard/dashboard_screen.dart +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:timemanage/db/timer_entry_database.dart'; import 'package:timemanage/model/timer_entry.dart'; -- 2.34.1 From bec6671e50ec1bfe608a71f8a0d848a64d149704 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Sat, 4 Nov 2023 14:42:52 +0800 Subject: [PATCH 17/19] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=BE=E7=A8=8B?= =?UTF-8?q?=E8=A1=A8=E6=97=A5=E6=9C=9F=E6=98=BE=E7=A4=BA=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../buildOutputCleanup/buildOutputCleanup.lock | Bin 17 -> 17 bytes lib/screen/course_screen.dart | 15 ++------------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index fc5460da27f36d52cabf3b072c53cfe933e808fc..3e60c5b6c7175097ef50ba9fdc2bfed0426579d9 100644 GIT binary patch literal 17 VcmZQJ3aRAUHzj-)0~jz(1pp`=1Lpt$ literal 17 VcmZQJ3aRAUHzj-)0~jz>0strh1Iqva diff --git a/lib/screen/course_screen.dart b/lib/screen/course_screen.dart index 196af3f..30a30d2 100644 --- a/lib/screen/course_screen.dart +++ b/lib/screen/course_screen.dart @@ -35,21 +35,10 @@ class CourseScreenState extends State { mondayTime = mondayTime.subtract(Duration(days: 1)); } - mondayTime.year; //2020 年 - mondayTime.month; //6(这里和js中的月份有区别,js中是从0开始,dart则从1开始,我们无需再进行加一处理) 月 - mondayTime.day; //6 日 - // nowTime.hour ;//6 时 - // nowTime.minute ;//6 分 - // nowTime.second ;//6 秒 for (int i = 0; i < 7; i++) { - dateList.add("${mondayTime.month}/${mondayTime.day + i}"); - if ((mondayTime.day + i) == DateTime.now().day) { - setState(() { - currentWeekIndex = i + 1; - }); - } + dateList.add("${mondayTime.month}-${mondayTime.day}"); + mondayTime = mondayTime.add(Duration(days: 1)); } - // print('Recent monday '+DateTime.now().day.toString()); } @override -- 2.34.1 From fed3ea946ec8a997a26bb3380c9058efa712b398 Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Sat, 4 Nov 2023 15:17:43 +0800 Subject: [PATCH 18/19] ? --- .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .../plugins/GeneratedPluginRegistrant.java | 5 ++++ ios/Runner/GeneratedPluginRegistrant.m | 7 +++++ lib/db/timer_entry_database.dart | 15 ++++++---- lib/screen/reports_screen.dart | 15 +++++----- macos/Flutter/GeneratedPluginRegistrant.swift | 2 ++ pubspec.lock | 24 ++++++++++++++++ pubspec.yaml | 1 + test/widget_test.dart | 26 +++++++----------- 9 files changed, 67 insertions(+), 28 deletions(-) diff --git a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 3e60c5b6c7175097ef50ba9fdc2bfed0426579d9..9a072076395bcd0c470848171eccddbeead83488 100644 GIT binary patch literal 17 VcmZQJ3aRAUHzj-)0~j!!1pp{j1RnqZ literal 17 VcmZQJ3aRAUHzj-)0~jz(1pp`=1Lpt$ diff --git a/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java index 0fadd5e..e27461e 100644 --- a/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java +++ b/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -20,6 +20,11 @@ public final class GeneratedPluginRegistrant { } catch (Exception e) { Log.e(TAG, "Error registering plugin package_info_plus, dev.fluttercommunity.plus.packageinfo.PackageInfoPlugin", e); } + try { + flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin()); + } catch (Exception e) { + Log.e(TAG, "Error registering plugin path_provider_android, io.flutter.plugins.pathprovider.PathProviderPlugin", e); + } try { flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin()); } catch (Exception e) { diff --git a/ios/Runner/GeneratedPluginRegistrant.m b/ios/Runner/GeneratedPluginRegistrant.m index b37c01c..ae90871 100644 --- a/ios/Runner/GeneratedPluginRegistrant.m +++ b/ios/Runner/GeneratedPluginRegistrant.m @@ -12,6 +12,12 @@ @import package_info_plus; #endif +#if __has_include() +#import +#else +@import path_provider_foundation; +#endif + #if __has_include() #import #else @@ -34,6 +40,7 @@ + (void)registerWithRegistry:(NSObject*)registry { [FLTPackageInfoPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPackageInfoPlusPlugin"]]; + [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]]; [SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]]; [SqflitePlugin registerWithRegistrar:[registry registrarForPlugin:@"SqflitePlugin"]]; [FLTURLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTURLLauncherPlugin"]]; diff --git a/lib/db/timer_entry_database.dart b/lib/db/timer_entry_database.dart index cabef67..2fcb0bf 100644 --- a/lib/db/timer_entry_database.dart +++ b/lib/db/timer_entry_database.dart @@ -39,12 +39,17 @@ class TimerEntryDatabase { // 创建表 const idType = 'INTEGER PRIMARY KEY AUTOINCREMENT'; const textType = 'TEXT NOT NULL'; + const nullTextType = 'TEXT'; // 创建计时器表 await db.execute(''' CREATE TABLE $tableTimerEntry ( ${TimerEntryFields.id} $idType, ${TimerEntryFields.name} $textType, + ${TimerEntryFields.createdAt} $textType, + ${TimerEntryFields.endAt} $nullTextType, + ${TimerEntryFields.isActive} $textType, + ${TimerEntryFields.stopWatch} $textType ) '''); } @@ -80,11 +85,11 @@ class TimerEntryDatabase { final db = await instance.database; // 插入一条测试数据 - // await db.insert( - // tableTimerEntry, - // TimerEntry( - // name: '测试计时器', - // ).toJson()); + await db.insert( + tableTimerEntry, + TimerEntry( + name: '测试计时器', + ).toJson()); final result = await db.query(tableTimerEntry); diff --git a/lib/screen/reports_screen.dart b/lib/screen/reports_screen.dart index e245c6a..1f30bb6 100644 --- a/lib/screen/reports_screen.dart +++ b/lib/screen/reports_screen.dart @@ -1,16 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; class ReportsScreen extends StatelessWidget { const ReportsScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('统计报告'), - ), - body: const Center( - child: Text('统计报告界面'), - ), - ); + appBar: AppBar( + title: const Text('统计报告'), + ), + // 调用android屏幕使用时间API,显示圆环图统计报告 + body: Center( + child: Text('统计报告'), + )); } } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index aefca13..fb60284 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,12 +6,14 @@ import FlutterMacOS import Foundation import package_info_plus +import path_provider_foundation import shared_preferences_foundation import sqflite import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 8710183..1b4edc9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -328,6 +328,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + url: "https://pub.dev" + source: hosted + version: "2.3.1" path_provider_linux: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e891a9c..568aeea 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,6 +48,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + path_provider: ^2.1.1 dev_dependencies: flutter_test: diff --git a/test/widget_test.dart b/test/widget_test.dart index e2ae07f..3e52687 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -5,26 +5,20 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. +import 'dart:io'; + import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.dart'; import 'package:timemanage/main.dart'; +import 'package:path_provider/path_provider.dart'; void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); + getDatabasePath(); +} - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); +void getDatabasePath() async { + Directory documentsDirectory = await getApplicationDocumentsDirectory(); + String path = join(documentsDirectory.path, 'timer_entry.db'); + print('Database Path: $path'); } -- 2.34.1 From 763588b0465fd5755a6e4b5d199566409acf0d3b Mon Sep 17 00:00:00 2001 From: gexinghai <2874903098@qq.com> Date: Mon, 6 Nov 2023 09:48:02 +0800 Subject: [PATCH 19/19] =?UTF-8?q?=E8=AE=A1=E6=97=B6=E5=99=A8=E5=88=86?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../buildOutputCleanup.lock | Bin 17 -> 17 bytes build/flutter_assets/NOTICES | 3 + lib/db/timer_entry_database.dart | 15 +-- lib/model/timer_entry.dart | 5 +- lib/screen/dashboard/dashboard_screen.dart | 9 +- lib/screen/reports_screen.dart | 109 ++++++++++++++++-- pubspec.lock | 6 +- pubspec.yaml | 1 + test/widget_test.dart | 16 +-- 9 files changed, 124 insertions(+), 40 deletions(-) diff --git a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 9a072076395bcd0c470848171eccddbeead83488..df7dfed0abee00c98c6ce492e343cb647f6916ad 100644 GIT binary patch literal 17 VcmZQJ3aRAUHzj-)0~oNf0{|zL16%+A literal 17 VcmZQJ3aRAUHzj-)0~j!!1pp{j1RnqZ diff --git a/build/flutter_assets/NOTICES b/build/flutter_assets/NOTICES index 1ec5a6e..fbea4de 100644 --- a/build/flutter_assets/NOTICES +++ b/build/flutter_assets/NOTICES @@ -6861,6 +6861,9 @@ SOFTWARE. -------------------------------------------------------------------------------- flutter_lints flutter_markdown +path_provider +path_provider_android +path_provider_foundation path_provider_linux path_provider_platform_interface path_provider_windows diff --git a/lib/db/timer_entry_database.dart b/lib/db/timer_entry_database.dart index 2fcb0bf..e26c6a9 100644 --- a/lib/db/timer_entry_database.dart +++ b/lib/db/timer_entry_database.dart @@ -12,11 +12,6 @@ class TimerEntryDatabase { // 获取数据库 Future get database async { - // TODO: 临时测试,每次都删除重新创建数据库 - if (_database != null) { - await deleteDatabase('timer_entry.db'); - } - if (_database != null) { return _database!; } @@ -85,11 +80,11 @@ class TimerEntryDatabase { final db = await instance.database; // 插入一条测试数据 - await db.insert( - tableTimerEntry, - TimerEntry( - name: '测试计时器', - ).toJson()); + // await db.insert( + // tableTimerEntry, + // TimerEntry( + // name: '测试计时器', + // ).toJson()); final result = await db.query(tableTimerEntry); diff --git a/lib/model/timer_entry.dart b/lib/model/timer_entry.dart index b8ba0a5..e0d5ae0 100644 --- a/lib/model/timer_entry.dart +++ b/lib/model/timer_entry.dart @@ -27,10 +27,13 @@ class TimerEntry { TimerEntry({required this.name}) : stopwatch = Stopwatch(), isActice = false, - createdAt = DateTime.now(); + createdAt = DateTime.now(), + endAt = DateTime.now(); bool get isActive => isActice; + num get stopWatch => stopWatch; + // 开始计时 void start() { stopwatch!.start(); diff --git a/lib/screen/dashboard/dashboard_screen.dart b/lib/screen/dashboard/dashboard_screen.dart index d24051a..eca6180 100644 --- a/lib/screen/dashboard/dashboard_screen.dart +++ b/lib/screen/dashboard/dashboard_screen.dart @@ -73,11 +73,14 @@ class DashBoardScreenState extends State { final name = controller.text; // TODO: 检查持续时间输入的是否合法,如果合法则添加计时器,否则提示错误 if (name.isNotEmpty) { + TimerEntry timerEntry = TimerEntry( + name: name, + ); setState(() { - timers.add(TimerEntry( - name: name, - )); + timers.add(timerEntry); }); + // 写入数据库 + TimerEntryDatabase.instance.create(timerEntry); } Navigator.pop(context, 'OK'); }, diff --git a/lib/screen/reports_screen.dart b/lib/screen/reports_screen.dart index 1f30bb6..205acec 100644 --- a/lib/screen/reports_screen.dart +++ b/lib/screen/reports_screen.dart @@ -1,17 +1,110 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:timemanage/db/timer_entry_database.dart'; -class ReportsScreen extends StatelessWidget { +class ReportsScreen extends StatefulWidget { const ReportsScreen({super.key}); + + @override + State createState() => _ReportsScreenState(); +} + +// 计时器分类统计 +class _ReportsScreenState extends State { + // 读取数据库中的计时器 + var timers = TimerEntryDatabase.instance.readAll(); + Map persentMap = {}; + @override + void initState() { + super.initState(); + classifyTimers().then((value) { + setState(() { + persentMap = value; + }); + }); + for (var key in persentMap.keys) { + log(key); + } + } + + Future> classifyTimers() async { + var timers = await TimerEntryDatabase.instance.readAll(); + Map persentMap = {}; + for (var timer in timers) { + var response = await postData(timer.name); + var classification = + jsonDecode(response.body)['item']['lv1_tag_list'][0]['tag']; + // 解决百度API返回的分类中文乱码问题,转换为ISO-8859-1编码 + classification = utf8.decode(classification.runes.toList()); + persentMap[classification] = timer.stopWatch; + log('classification: $classification'); + } + return persentMap; + } + + // 调用百度API进行分类 + Future postData(String name) async { + // ignore: non_constant_identifier_names + var API_KEY = "YGxSMNdWKO5Gpt12dNVY2nGq"; + // ignore: non_constant_identifier_names + var SECRET_KEY = "G5NBDTiRPhsBqMi1Od0XZw4RSMlFgeek"; + var accessToken = await getAccessToken(API_KEY, SECRET_KEY); + var url = Uri.parse( + 'https://aip.baidubce.com/rpc/2.0/nlp/v1/topic?charset=UTF-8&access_token=$accessToken'); + var payload = jsonEncode({ + "title": name, + "content": name, + }); + var headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }; + var response = await http.post( + url, + headers: headers, + body: payload, + ); + return response; + } + + // ignore: non_constant_identifier_names + Future getAccessToken(String API_KEY, String SECRET_KEY) async { + var url = 'https://aip.baidubce.com/oauth/2.0/token'; + var params = { + 'grant_type': 'client_credentials', + 'client_id': API_KEY, + 'client_secret': SECRET_KEY, + }; + + var response = await http.post( + Uri.parse(url), + body: params, + ); + + if (response.statusCode == 200) { + var data = jsonDecode(response.body); + return data['access_token']; + } else { + throw Exception('Failed to get access token'); + } + } + @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('统计报告'), + appBar: AppBar( + title: const Text('统计报告'), + ), + body: ListView.builder( + itemCount: persentMap.length, + itemBuilder: (context, index) => ListTile( + title: Text(persentMap.keys.toList()[index]), + subtitle: Text(persentMap.values.toList()[index].toString()), ), - // 调用android屏幕使用时间API,显示圆环图统计报告 - body: Center( - child: Text('统计报告'), - )); + ), + ); } } diff --git a/pubspec.lock b/pubspec.lock index 1b4edc9..a20babd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -201,13 +201,13 @@ packages: source: hosted version: "9.2.0" http: - dependency: transitive + dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "0.13.6" http_parser: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 568aeea..c7728bd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: shared_preferences: ^2.0.8 sqflite: ^2.0.0+3 # 用于数据库 intl: ^0.17.0 # 用于时间格式化 + http: ^0.13.3 # 用于网络请求 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. diff --git a/test/widget_test.dart b/test/widget_test.dart index 3e52687..629cab6 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -5,20 +5,6 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:path/path.dart'; - -import 'package:timemanage/main.dart'; -import 'package:path_provider/path_provider.dart'; - void main() { - getDatabasePath(); -} - -void getDatabasePath() async { - Directory documentsDirectory = await getApplicationDocumentsDirectory(); - String path = join(documentsDirectory.path, 'timer_entry.db'); - print('Database Path: $path'); + print('Hello World!'); } -- 2.34.1