├── .flutter-plugins-dependencies ├── .gitignore ├── .gradle ├── 5.1.1 │ ├── fileChanges │ │ └── last-build.bin │ ├── fileHashes │ │ └── fileHashes.lock │ └── gc.properties ├── buildOutputCleanup │ ├── buildOutputCleanup.lock │ └── cache.properties └── vcs-1 │ └── gc.properties ├── .metadata ├── README.md ├── android ├── app │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── odyssey │ │ │ │ └── forget │ │ │ │ └── MainActivity.java │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ └── ic_noti_icon.png │ │ │ ├── drawable-mdpi │ │ │ └── ic_noti_icon.png │ │ │ ├── drawable-xhdpi │ │ │ └── ic_noti_icon.png │ │ │ ├── drawable-xxhdpi │ │ │ └── ic_noti_icon.png │ │ │ ├── drawable-xxxhdpi │ │ │ └── ic_noti_icon.png │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── raw │ │ │ ├── long_sound.mp3 │ │ │ ├── slow_spring_board.mp3 │ │ │ └── very_long_sound.mp3 │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── key.properties └── settings.gradle ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── flutter_export_environment.sh ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── main.m └── slow_spring_board.aiff ├── lib ├── forget_icons.dart ├── forget_theme.dart ├── generated │ └── i18n.dart ├── icons │ └── iconfont.ttf ├── main.dart ├── model │ ├── macro.dart │ ├── model.dart │ ├── onedrive.dart │ └── time_util.dart ├── notifier │ └── notifier.dart ├── pages │ ├── filter_page.dart │ ├── home_page.dart │ ├── label_page.dart │ ├── project_page.dart │ ├── search_page.dart │ ├── task_detail_page.dart │ └── web_page.dart ├── test_page.dart └── widgets │ ├── calendar.dart │ ├── cells.dart │ ├── customize_button.dart │ ├── day_grid_view.dart │ └── time_picker.dart ├── placeholder ├── 2.0x │ ├── intro_img_1.png │ ├── intro_img_2.png │ ├── intro_img_3.png │ ├── motto0.png │ ├── motto1.png │ ├── motto2.png │ ├── motto3.png │ ├── motto4.png │ ├── motto5.png │ ├── motto6.png │ ├── motto7.png │ └── motto8.png ├── 3.0x │ ├── intro_img_1.png │ ├── intro_img_2.png │ ├── intro_img_3.png │ ├── motto0.png │ ├── motto1.png │ ├── motto2.png │ ├── motto3.png │ ├── motto4.png │ ├── motto5.png │ ├── motto6.png │ ├── motto7.png │ └── motto8.png ├── intro_img_1.png ├── intro_img_2.png ├── intro_img_3.png ├── motto0.png ├── motto1.png ├── motto2.png ├── motto3.png ├── motto4.png ├── motto5.png ├── motto6.png ├── motto7.png └── motto8.png ├── preview ├── Screenshot_1.png ├── Screenshot_2.png ├── Screenshot_3.png ├── Screenshot_4.png └── logo.png ├── pubspec.lock ├── pubspec.yaml ├── res └── values │ ├── strings_en.arb │ ├── strings_en_US.arb │ ├── strings_zh.arb │ └── strings_zh_CN.arb └── test └── widget_test.dart /.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_local_notifications","dependencies":[]},{"name":"flutter_webview_plugin","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}]} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # Visual Studio Code related 19 | .vscode/ 20 | 21 | # Flutter/Dart/Pub related 22 | **/doc/api/ 23 | .dart_tool/ 24 | .flutter-plugins 25 | .packages 26 | .pub-cache/ 27 | .pub/ 28 | /build/ 29 | 30 | # Android related 31 | **/android/**/gradle-wrapper.jar 32 | **/android/.gradle 33 | **/android/captures/ 34 | **/android/gradlew 35 | **/android/gradlew.bat 36 | **/android/local.properties 37 | **/android/**/GeneratedPluginRegistrant.java 38 | 39 | # iOS/XCode related 40 | **/ios/**/*.mode1v3 41 | **/ios/**/*.mode2v3 42 | **/ios/**/*.moved-aside 43 | **/ios/**/*.pbxuser 44 | **/ios/**/*.perspectivev3 45 | **/ios/**/*sync/ 46 | **/ios/**/.sconsign.dblite 47 | **/ios/**/.tags* 48 | **/ios/**/.vagrant/ 49 | **/ios/**/DerivedData/ 50 | **/ios/**/Icon? 51 | **/ios/**/Pods/ 52 | **/ios/**/.symlinks/ 53 | **/ios/**/profile 54 | **/ios/**/xcuserdata 55 | **/ios/.generated/ 56 | **/ios/Flutter/App.framework 57 | **/ios/Flutter/Flutter.framework 58 | **/ios/Flutter/Generated.xcconfig 59 | **/ios/Flutter/app.flx 60 | **/ios/Flutter/app.zip 61 | **/ios/Flutter/flutter_assets/ 62 | **/ios/ServiceDefinitions.json 63 | **/ios/Runner/GeneratedPluginRegistrant.* 64 | 65 | # Exceptions to above rules. 66 | !**/ios/**/default.mode1v3 67 | !**/ios/**/default.mode2v3 68 | !**/ios/**/default.pbxuser 69 | !**/ios/**/default.perspectivev3 70 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 71 | -------------------------------------------------------------------------------- /.gradle/5.1.1/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/5.1.1/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/.gradle/5.1.1/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /.gradle/5.1.1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/.gradle/5.1.1/gc.properties -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Wed Jun 26 15:43:37 CST 2019 2 | gradle.version=5.1.1 3 | -------------------------------------------------------------------------------- /.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/.gradle/vcs-1/gc.properties -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8661d8aecd626f7f57ccbcb735553edc05a2e713 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # forget 2 |
3 | 4 | An iOS and android app build by [Flutter](https://flutter.io/docs) 5 | 6 | [app store link](https://apps.apple.com/cn/app/forget-%E6%97%B6%E9%97%B4%E7%AE%A1%E7%90%86/id1448659423) 7 | ## preview 8 | |![](./preview/Screenshot_1.png)|![](./preview/Screenshot_2.png)| 9 | | :---------------------------: | :---------------------------: | 10 | |![](./preview/Screenshot_3.png)|![](./preview/Screenshot_4.png)| 11 | 12 | ## fetures 13 | * State Management: [Provider](https://github.com/rrousselGit/provider) 14 | * Persistence: [sqflite](https://github.com/tekartik/sqflite) + [shared_preferences](https://github.com/flutter/plugins/tree/master/packages/shared_preferences) 15 | * International: [i18n](https://github.com/long1eu/flutter_i18n) 16 | 17 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '2' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | def keystorePropertiesFile = rootProject.file("key.properties") 28 | def keystoreProperties = new Properties() 29 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 30 | 31 | android { 32 | compileSdkVersion 28 33 | 34 | lintOptions { 35 | disable 'InvalidPackage' 36 | } 37 | 38 | defaultConfig { 39 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 40 | applicationId "com.odyssey.forget" 41 | minSdkVersion 21 42 | targetSdkVersion 28 43 | versionCode 2 44 | versionName flutterVersionName 45 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 46 | flavorDimensions "versionCode" 47 | manifestPlaceholders = [ 48 | 'appAuthRedirectScheme': 'msauth://com.odyssey.forget/R2V6kZ6IllvaZdRwOCQTSmdkZXE%3D' 49 | ] 50 | } 51 | 52 | signingConfigs { 53 | release { 54 | keyAlias keystoreProperties['keyAlias'] 55 | keyPassword keystoreProperties['keyPassword'] 56 | storeFile file(keystoreProperties['storeFile']) 57 | storePassword keystoreProperties['storePassword'] 58 | } 59 | } 60 | buildTypes { 61 | release { 62 | // TODO: Add your own signing config for the release build. 63 | // Signing with the debug keys for now, so `flutter run --release` works. 64 | signingConfig signingConfigs.release 65 | 66 | minifyEnabled true 67 | useProguard true 68 | 69 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 70 | } 71 | } 72 | } 73 | 74 | flutter { 75 | source '../..' 76 | } 77 | 78 | dependencies { 79 | testImplementation 'junit:junit:4.12' 80 | androidTestImplementation 'androidx.test:runner:1.2.0' 81 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 82 | } 83 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | #Flutter Wrapper 2 | -keep class io.flutter.app.** { *; } 3 | -keep class io.flutter.plugin.** { *; } 4 | -keep class io.flutter.util.** { *; } 5 | -keep class io.flutter.view.** { *; } 6 | -keep class io.flutter.** { *; } 7 | -keep class io.flutter.plugins.** { *; } -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 18 | 25 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/odyssey/forget/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.odyssey.forget; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_noti_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/drawable-hdpi/ic_noti_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_noti_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/drawable-mdpi/ic_noti_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_noti_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/drawable-xhdpi/ic_noti_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_noti_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/drawable-xxhdpi/ic_noti_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_noti_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/drawable-xxxhdpi/ic_noti_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/raw/long_sound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/raw/long_sound.mp3 -------------------------------------------------------------------------------- /android/app/src/main/res/raw/slow_spring_board.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/raw/slow_spring_board.mp3 -------------------------------------------------------------------------------- /android/app/src/main/res/raw/very_long_sound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/android/app/src/main/res/raw/very_long_sound.mp3 -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.3.2' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | 31 | allprojects { 32 | gradle.projectsEvaluated { 33 | tasks.withType(JavaCompile) { 34 | options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=true 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-Xmx1536M 4 | android.enableR8=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /android/key.properties: -------------------------------------------------------------------------------- 1 | storePassword=123 2 | keyPassword=123 3 | keyAlias=key 4 | storeFile=key.jks -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=C:\src\flutter" 4 | export "FLUTTER_APPLICATION_PATH=C:\Users\hujia\AndroidStudioProjects\forget" 5 | export "FLUTTER_TARGET=lib\main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build\ios" 8 | export "FLUTTER_FRAMEWORK_DIR=C:\src\flutter\bin\cache\artifacts\engine\ios" 9 | export "FLUTTER_BUILD_NAME=1.0.0" 10 | export "FLUTTER_BUILD_NUMBER=1" 11 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | pods_ary = [] 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) { |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | pods_ary.push({:name => podname, :path => podpath}); 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | } 32 | return pods_ary 33 | end 34 | 35 | target 'Runner' do 36 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 37 | # referring to absolute paths on developers' machines. 38 | system('rm -rf .symlinks') 39 | system('mkdir -p .symlinks/plugins') 40 | 41 | # Flutter Pods 42 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 43 | if generated_xcode_build_settings.empty? 44 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 45 | end 46 | generated_xcode_build_settings.map { |p| 47 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 48 | symlink = File.join('.symlinks', 'flutter') 49 | File.symlink(File.dirname(p[:path]), symlink) 50 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 51 | end 52 | } 53 | 54 | # Plugin Pods 55 | plugin_pods = parse_KV_file('../.flutter-plugins') 56 | plugin_pods.map { |p| 57 | symlink = File.join('.symlinks', 'plugins', p[:name]) 58 | File.symlink(p[:path], symlink) 59 | pod p[:name], :path => File.join(symlink, 'ios') 60 | } 61 | end 62 | 63 | post_install do |installer| 64 | installer.pods_project.targets.each do |target| 65 | target.build_configurations.each do |config| 66 | config.build_settings['ENABLE_BITCODE'] = 'NO' 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_local_notifications (0.0.1): 4 | - Flutter 5 | - FMDB (2.7.5): 6 | - FMDB/standard (= 2.7.5) 7 | - FMDB/standard (2.7.5) 8 | - intro_slider (0.0.1): 9 | - Flutter 10 | - shared_preferences (0.0.1): 11 | - Flutter 12 | - sqflite (0.0.1): 13 | - Flutter 14 | - FMDB (~> 2.7.2) 15 | - webview_flutter (0.0.1): 16 | - Flutter 17 | 18 | DEPENDENCIES: 19 | - Flutter (from `.symlinks/flutter/ios`) 20 | - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) 21 | - intro_slider (from `.symlinks/plugins/intro_slider/ios`) 22 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 23 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 24 | - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`) 25 | 26 | SPEC REPOS: 27 | https://github.com/cocoapods/specs.git: 28 | - FMDB 29 | 30 | EXTERNAL SOURCES: 31 | Flutter: 32 | :path: ".symlinks/flutter/ios" 33 | flutter_local_notifications: 34 | :path: ".symlinks/plugins/flutter_local_notifications/ios" 35 | intro_slider: 36 | :path: ".symlinks/plugins/intro_slider/ios" 37 | shared_preferences: 38 | :path: ".symlinks/plugins/shared_preferences/ios" 39 | sqflite: 40 | :path: ".symlinks/plugins/sqflite/ios" 41 | webview_flutter: 42 | :path: ".symlinks/plugins/webview_flutter/ios" 43 | 44 | SPEC CHECKSUMS: 45 | Flutter: 9d0fac939486c9aba2809b7982dfdbb47a7b0296 46 | flutter_local_notifications: 696a74904e0a0ab754e27a5f761da4a7f9153710 47 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 48 | intro_slider: 961a06956afdbaeaafb8488269b52eae4f866dbb 49 | shared_preferences: 5a1d487c427ee18fcd3ea1f2a131569481834b53 50 | sqflite: d1612813fa7db7c667bed9f1d1b508deffc56999 51 | webview_flutter: 9bd234c07e99632356cba112df56fd1d47d6b6e3 52 | 53 | PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09 54 | 55 | COCOAPODS: 1.5.3 56 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | NSAllowsArbitraryLoadsInWebContent 10 | 11 | 12 | CFBundleDevelopmentRegion 13 | en 14 | CFBundleDisplayName 15 | Forget 16 | CFBundleExecutable 17 | $(EXECUTABLE_NAME) 18 | CFBundleIdentifier 19 | $(PRODUCT_BUNDLE_IDENTIFIER) 20 | CFBundleInfoDictionaryVersion 21 | 6.0 22 | CFBundleName 23 | forget 24 | CFBundlePackageType 25 | APPL 26 | CFBundleShortVersionString 27 | 2.0 28 | CFBundleSignature 29 | ???? 30 | CFBundleVersion 31 | 6 32 | LSRequiresIPhoneOS 33 | 34 | UILaunchStoryboardName 35 | LaunchScreen 36 | UIMainStoryboardFile 37 | Main 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UISupportedInterfaceOrientations~ipad 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationPortraitUpsideDown 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | UIViewControllerBasedStatusBarAppearance 52 | 53 | io.flutter.embedded_views_preview 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ios/slow_spring_board.aiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/ios/slow_spring_board.aiff -------------------------------------------------------------------------------- /lib/forget_icons.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class ForgetIcon { 4 | static const IconData dash_circle = const _ForgetIconData(0xe686); 5 | static const IconData select_all = const _ForgetIconData(0xe678); 6 | static const IconData unselect = const _ForgetIconData(0xe679); 7 | static const IconData unassigned = const _ForgetIconData(0xe782); 8 | } 9 | 10 | class _ForgetIconData extends IconData { 11 | const _ForgetIconData(int codePoint) 12 | : super( 13 | codePoint, 14 | fontFamily: 'ForgetIcon', 15 | ); 16 | } -------------------------------------------------------------------------------- /lib/forget_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | ThemeData forgetLightTheme() { 4 | return ThemeData.light().copyWith( 5 | primaryColor: Colors.grey[200], 6 | accentColor: Colors.white, 7 | textSelectionColor: Colors.white, 8 | scaffoldBackgroundColor: Colors.grey[200], 9 | primaryColorBrightness: Brightness.light); 10 | } 11 | 12 | ThemeData forgetDarkTheme() { 13 | return ThemeData.dark().copyWith( 14 | primaryColor: Colors.grey[800], 15 | accentColor: Colors.grey[600], 16 | scaffoldBackgroundColor: Colors.grey[800], 17 | textSelectionColor: Colors.black, 18 | cardColor: Colors.grey[600], 19 | // chipTheme: ThemeData.dark().chipTheme.copyWith(backgroundColor: Colors.grey[850]) 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /lib/generated/i18n.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | // ignore_for_file: non_constant_identifier_names 7 | // ignore_for_file: camel_case_types 8 | // ignore_for_file: prefer_single_quotes 9 | 10 | // This file is automatically generated. DO NOT EDIT, all your changes would be lost. 11 | class S implements WidgetsLocalizations { 12 | const S(); 13 | 14 | static const GeneratedLocalizationsDelegate delegate = 15 | GeneratedLocalizationsDelegate(); 16 | 17 | static S of(BuildContext context) => Localizations.of(context, S); 18 | 19 | @override 20 | TextDirection get textDirection => TextDirection.ltr; 21 | 22 | String get Apr => "Apr"; 23 | String get Aug => "Aug"; 24 | String get Dec => "Dec"; 25 | String get Feb => "Feb"; 26 | String get Jan => "Jan"; 27 | String get Jul => "Jul"; 28 | String get Jun => "Jun"; 29 | String get Mar => "Mar"; 30 | String get May => "May"; 31 | String get Nov => "Nov"; 32 | String get OK => "OK"; 33 | String get Oct => "Oct"; 34 | String get Sep => "Sep"; 35 | String get add_label => "Add Label"; 36 | String get add_project => "Add Project"; 37 | String get additionOrder => "By Addition Order"; 38 | String get assign_task => "Assign Task"; 39 | String get cancel => "Cancel"; 40 | String get choose_labels => "Choose Labels"; 41 | String get choose_project => "Choose Project"; 42 | String get chronological => "By Time"; 43 | String get convert => "Convert to Project"; 44 | String get delete => "Delete"; 45 | String get deleteLabel => "Delete label: "; 46 | String get deleteProj => "Delete Project?"; 47 | String get deleteProjMsg => "This will also delete all the tasks belong to the project."; 48 | String get deleteTasks => "Delete selected tasks?"; 49 | String get delete_label_title => "Delete Label"; 50 | String get done => "Complete"; 51 | String get edit => "Edit"; 52 | String get everyDay => "EveryDay"; 53 | String get everyMonth => "EveryMonth"; 54 | String get everyWeek => "EveryWeek"; 55 | String get input_label_hint => "input your label here."; 56 | String get input_project_hint => "input your project here."; 57 | String get input_task_hint => "input your task here."; 58 | String get label => "label"; 59 | String get language => "Language"; 60 | String get main_title_1 => "Task"; 61 | String get main_title_2 => "Overview"; 62 | String get mottos => "Know yourself+Forth, and fear no darkness+Where there is truth, there is intelligence;\nwhere there is intelligence, there is truth.+Aiya Earendil Elenion Ancalima+I'd pretend I was one of those deaf-mutes\nor should I?+Every flight begins with a fall+to the strongest+The smallest seed of an idea can grow+You're going to have to make a choice"; 63 | String get next_move => "next move"; 64 | String get none => "None"; 65 | String get none_project => "None"; 66 | String get plan => "plan"; 67 | String get priority => "priority"; 68 | String get priorityOrder => "By Priority"; 69 | String get project => "project"; 70 | String get repeat => "Repeat"; 71 | String get search => "Search"; 72 | String get setting => "Setting"; 73 | String get showAll => "Show All"; 74 | String get showCurrent => "Show Current Date"; 75 | String get showIsDone => "Show Completed"; 76 | String get sort => "Sort"; 77 | String get tasks => " tasks"; 78 | String get theme => "Theme"; 79 | String get today => "Today"; 80 | String get tomorrow => "tomorrow"; 81 | String get unassigned => "unassigned"; 82 | String get undo => "Undo"; 83 | String get undone => "Undone..."; 84 | String get wait => "wait"; 85 | String get welcome => "Welcome to"; 86 | } 87 | 88 | class $en_US extends S { 89 | const $en_US(); 90 | 91 | @override 92 | TextDirection get textDirection => TextDirection.ltr; 93 | 94 | @override 95 | String get cancel => "Cancel"; 96 | @override 97 | String get Oct => "Oct"; 98 | @override 99 | String get Apr => "Apr"; 100 | @override 101 | String get deleteProj => "Delete Project?"; 102 | @override 103 | String get deleteLabel => "Delete label: "; 104 | @override 105 | String get project => "project"; 106 | @override 107 | String get language => "Language"; 108 | @override 109 | String get choose_labels => "Choose Labels"; 110 | @override 111 | String get unassigned => "unassigned"; 112 | @override 113 | String get input_label_hint => "input your label here."; 114 | @override 115 | String get none => "None"; 116 | @override 117 | String get convert => "Convert to Project"; 118 | @override 119 | String get setting => "Setting"; 120 | @override 121 | String get Aug => "Aug"; 122 | @override 123 | String get next_move => "next move"; 124 | @override 125 | String get welcome => "Welcome to"; 126 | @override 127 | String get plan => "plan"; 128 | @override 129 | String get tasks => " tasks"; 130 | @override 131 | String get edit => "Edit"; 132 | @override 133 | String get tomorrow => "tomorrow"; 134 | @override 135 | String get sort => "Sort"; 136 | @override 137 | String get priority => "priority"; 138 | @override 139 | String get done => "Complete"; 140 | @override 141 | String get Nov => "Nov"; 142 | @override 143 | String get choose_project => "Choose Project"; 144 | @override 145 | String get none_project => "None"; 146 | @override 147 | String get input_task_hint => "input your task here."; 148 | @override 149 | String get Mar => "Mar"; 150 | @override 151 | String get Sep => "Sep"; 152 | @override 153 | String get wait => "wait"; 154 | @override 155 | String get May => "May"; 156 | @override 157 | String get additionOrder => "By Addition Order"; 158 | @override 159 | String get input_project_hint => "input your project here."; 160 | @override 161 | String get add_project => "Add Project"; 162 | @override 163 | String get everyDay => "EveryDay"; 164 | @override 165 | String get delete => "Delete"; 166 | @override 167 | String get priorityOrder => "By Priority"; 168 | @override 169 | String get showIsDone => "Show Completed"; 170 | @override 171 | String get search => "Search"; 172 | @override 173 | String get undo => "Undo"; 174 | @override 175 | String get assign_task => "Assign Task"; 176 | @override 177 | String get showAll => "Show All"; 178 | @override 179 | String get mottos => "Know yourself+Forth, and fear no darkness+Where there is truth, there is intelligence;\nwhere there is intelligence, there is truth.+Aiya Earendil Elenion Ancalima+I'd pretend I was one of those deaf-mutes\nor should I?+Every flight begins with a fall+to the strongest+The smallest seed of an idea can grow+You're going to have to make a choice"; 180 | @override 181 | String get everyWeek => "EveryWeek"; 182 | @override 183 | String get repeat => "Repeat"; 184 | @override 185 | String get today => "Today"; 186 | @override 187 | String get add_label => "Add Label"; 188 | @override 189 | String get theme => "Theme"; 190 | @override 191 | String get delete_label_title => "Delete Label"; 192 | @override 193 | String get OK => "OK"; 194 | @override 195 | String get deleteTasks => "Delete selected tasks?"; 196 | @override 197 | String get Jul => "Jul"; 198 | @override 199 | String get Feb => "Feb"; 200 | @override 201 | String get Jun => "Jun"; 202 | @override 203 | String get deleteProjMsg => "This will also delete all the tasks belong to the project."; 204 | @override 205 | String get Dec => "Dec"; 206 | @override 207 | String get everyMonth => "EveryMonth"; 208 | @override 209 | String get label => "label"; 210 | @override 211 | String get main_title_1 => "Task"; 212 | @override 213 | String get showCurrent => "Show Current Date"; 214 | @override 215 | String get main_title_2 => "Overview"; 216 | @override 217 | String get Jan => "Jan"; 218 | @override 219 | String get chronological => "By Time"; 220 | @override 221 | String get undone => "Undone..."; 222 | } 223 | 224 | class $en extends S { 225 | const $en(); 226 | } 227 | 228 | class $zh_CN extends S { 229 | const $zh_CN(); 230 | 231 | @override 232 | TextDirection get textDirection => TextDirection.ltr; 233 | 234 | @override 235 | String get cancel => "取消"; 236 | @override 237 | String get Oct => "十月"; 238 | @override 239 | String get Apr => "四月"; 240 | @override 241 | String get deleteProj => "是否删除项目?"; 242 | @override 243 | String get deleteLabel => "是否删除标签:"; 244 | @override 245 | String get project => "项目"; 246 | @override 247 | String get language => "语言"; 248 | @override 249 | String get choose_labels => "选择标签"; 250 | @override 251 | String get unassigned => "未分配"; 252 | @override 253 | String get input_label_hint => "在这里输入标签。"; 254 | @override 255 | String get none => "无"; 256 | @override 257 | String get convert => "转换为项目"; 258 | @override 259 | String get setting => "设置"; 260 | @override 261 | String get Aug => "八月"; 262 | @override 263 | String get next_move => "下一步行动"; 264 | @override 265 | String get welcome => "欢迎使用"; 266 | @override 267 | String get plan => "计划"; 268 | @override 269 | String get tasks => "项任务"; 270 | @override 271 | String get edit => "编辑"; 272 | @override 273 | String get tomorrow => "明天"; 274 | @override 275 | String get sort => "排序"; 276 | @override 277 | String get priority => "重要度"; 278 | @override 279 | String get done => "完成"; 280 | @override 281 | String get Nov => "十一月"; 282 | @override 283 | String get choose_project => "选择项目"; 284 | @override 285 | String get none_project => "无项目"; 286 | @override 287 | String get input_task_hint => "在这里输入任务。"; 288 | @override 289 | String get Mar => "三月"; 290 | @override 291 | String get Sep => "九月"; 292 | @override 293 | String get wait => "等待"; 294 | @override 295 | String get May => "五月"; 296 | @override 297 | String get additionOrder => "按添加顺序"; 298 | @override 299 | String get input_project_hint => "在这里输入项目名称。"; 300 | @override 301 | String get add_project => "新增项目"; 302 | @override 303 | String get everyDay => "每天"; 304 | @override 305 | String get delete => "删除"; 306 | @override 307 | String get priorityOrder => "按重要度"; 308 | @override 309 | String get showIsDone => "显示已完成"; 310 | @override 311 | String get search => "搜索"; 312 | @override 313 | String get undo => "取消完成"; 314 | @override 315 | String get assign_task => "分配任务"; 316 | @override 317 | String get showAll => "显示所有"; 318 | @override 319 | String get mottos => "认识你自己+向前,不要惧怕黑暗+自诚明,自明诚+Aiya Earendil Elenion Ancalima+我可以装聋作哑,我真的可以吗?+飞翔都是从坠落开始+给最强者+再小的意念种子,也会生根成形+你必须做出选择"; 320 | @override 321 | String get everyWeek => "每周"; 322 | @override 323 | String get repeat => "重复"; 324 | @override 325 | String get today => "今天"; 326 | @override 327 | String get add_label => "新增标签"; 328 | @override 329 | String get theme => "主题"; 330 | @override 331 | String get delete_label_title => "删除标签"; 332 | @override 333 | String get OK => "确定"; 334 | @override 335 | String get deleteTasks => "是否删除选中任务?"; 336 | @override 337 | String get Jul => "七月"; 338 | @override 339 | String get Feb => "二月"; 340 | @override 341 | String get Jun => "六月"; 342 | @override 343 | String get deleteProjMsg => "此操作也会删除该项目下的所有任务。"; 344 | @override 345 | String get Dec => "十二月"; 346 | @override 347 | String get everyMonth => "每月"; 348 | @override 349 | String get label => "标签"; 350 | @override 351 | String get main_title_1 => "任务"; 352 | @override 353 | String get showCurrent => "显示当日"; 354 | @override 355 | String get main_title_2 => "总览"; 356 | @override 357 | String get Jan => "一月"; 358 | @override 359 | String get chronological => "按时间"; 360 | @override 361 | String get undone => "未完成..."; 362 | } 363 | 364 | class $zh extends S { 365 | const $zh(); 366 | 367 | @override 368 | TextDirection get textDirection => TextDirection.ltr; 369 | 370 | @override 371 | String get cancel => "取消"; 372 | @override 373 | String get Oct => "十月"; 374 | @override 375 | String get Apr => "四月"; 376 | @override 377 | String get deleteProj => "是否删除项目?"; 378 | @override 379 | String get deleteLabel => "是否删除标签:"; 380 | @override 381 | String get project => "项目"; 382 | @override 383 | String get language => "语言"; 384 | @override 385 | String get choose_labels => "选择标签"; 386 | @override 387 | String get unassigned => "未分配"; 388 | @override 389 | String get input_label_hint => "在这里输入标签。"; 390 | @override 391 | String get none => "无"; 392 | @override 393 | String get convert => "转换为项目"; 394 | @override 395 | String get setting => "设置"; 396 | @override 397 | String get Aug => "八月"; 398 | @override 399 | String get next_move => "下一步行动"; 400 | @override 401 | String get welcome => "欢迎使用"; 402 | @override 403 | String get plan => "计划"; 404 | @override 405 | String get tasks => "项任务"; 406 | @override 407 | String get edit => "编辑"; 408 | @override 409 | String get tomorrow => "明天"; 410 | @override 411 | String get sort => "排序"; 412 | @override 413 | String get priority => "重要度"; 414 | @override 415 | String get done => "完成"; 416 | @override 417 | String get Nov => "十一月"; 418 | @override 419 | String get choose_project => "选择项目"; 420 | @override 421 | String get none_project => "无项目"; 422 | @override 423 | String get input_task_hint => "在这里输入任务。"; 424 | @override 425 | String get Mar => "三月"; 426 | @override 427 | String get Sep => "九月"; 428 | @override 429 | String get wait => "等待"; 430 | @override 431 | String get May => "五月"; 432 | @override 433 | String get additionOrder => "按添加顺序"; 434 | @override 435 | String get input_project_hint => "在这里输入项目名称。"; 436 | @override 437 | String get add_project => "新增项目"; 438 | @override 439 | String get everyDay => "每天"; 440 | @override 441 | String get delete => "删除"; 442 | @override 443 | String get priorityOrder => "按重要度"; 444 | @override 445 | String get showIsDone => "显示已完成"; 446 | @override 447 | String get search => "搜索"; 448 | @override 449 | String get undo => "取消完成"; 450 | @override 451 | String get assign_task => "分配任务"; 452 | @override 453 | String get showAll => "显示所有"; 454 | @override 455 | String get mottos => "认识你自己+向前,不要惧怕黑暗+自诚明,自明诚+Aiya Earendil Elenion Ancalima+我可以装聋作哑,我真的可以吗?+飞翔都是从坠落开始+给最强者+再小的意念种子,也会生根成形+你必须做出选择"; 456 | @override 457 | String get everyWeek => "每周"; 458 | @override 459 | String get repeat => "重复"; 460 | @override 461 | String get today => "今天"; 462 | @override 463 | String get add_label => "新增标签"; 464 | @override 465 | String get theme => "主题"; 466 | @override 467 | String get delete_label_title => "删除标签"; 468 | @override 469 | String get OK => "确定"; 470 | @override 471 | String get deleteTasks => "是否删除选中任务?"; 472 | @override 473 | String get Jul => "七月"; 474 | @override 475 | String get Feb => "二月"; 476 | @override 477 | String get Jun => "六月"; 478 | @override 479 | String get deleteProjMsg => "此操作也会删除该项目下的所有任务。"; 480 | @override 481 | String get Dec => "十二月"; 482 | @override 483 | String get everyMonth => "每月"; 484 | @override 485 | String get label => "标签"; 486 | @override 487 | String get main_title_1 => "任务"; 488 | @override 489 | String get showCurrent => "显示当日"; 490 | @override 491 | String get main_title_2 => "总览"; 492 | @override 493 | String get Jan => "一月"; 494 | @override 495 | String get chronological => "按时间"; 496 | @override 497 | String get undone => "未完成..."; 498 | } 499 | 500 | class GeneratedLocalizationsDelegate extends LocalizationsDelegate { 501 | const GeneratedLocalizationsDelegate(); 502 | 503 | List get supportedLocales { 504 | return const [ 505 | Locale("en", "US"), 506 | Locale("en", ""), 507 | Locale("zh", "CN"), 508 | Locale("zh", ""), 509 | ]; 510 | } 511 | 512 | LocaleListResolutionCallback listResolution({Locale fallback, bool withCountry = true}) { 513 | return (List locales, Iterable supported) { 514 | if (locales == null || locales.isEmpty) { 515 | return fallback ?? supported.first; 516 | } else { 517 | return _resolve(locales.first, fallback, supported, withCountry); 518 | } 519 | }; 520 | } 521 | 522 | LocaleResolutionCallback resolution({Locale fallback, bool withCountry = true}) { 523 | return (Locale locale, Iterable supported) { 524 | return _resolve(locale, fallback, supported, withCountry); 525 | }; 526 | } 527 | 528 | @override 529 | Future load(Locale locale) { 530 | final String lang = getLang(locale); 531 | if (lang != null) { 532 | switch (lang) { 533 | case "en_US": 534 | return SynchronousFuture(const $en_US()); 535 | case "en": 536 | return SynchronousFuture(const $en()); 537 | case "zh_CN": 538 | return SynchronousFuture(const $zh_CN()); 539 | case "zh": 540 | return SynchronousFuture(const $zh()); 541 | default: 542 | // NO-OP. 543 | } 544 | } 545 | return SynchronousFuture(const S()); 546 | } 547 | 548 | @override 549 | bool isSupported(Locale locale) => _isSupported(locale, true); 550 | 551 | @override 552 | bool shouldReload(GeneratedLocalizationsDelegate old) => false; 553 | 554 | /// 555 | /// Internal method to resolve a locale from a list of locales. 556 | /// 557 | Locale _resolve(Locale locale, Locale fallback, Iterable supported, bool withCountry) { 558 | if (locale == null || !_isSupported(locale, withCountry)) { 559 | return fallback ?? supported.first; 560 | } 561 | 562 | final Locale languageLocale = Locale(locale.languageCode, ""); 563 | if (supported.contains(locale)) { 564 | return locale; 565 | } else if (supported.contains(languageLocale)) { 566 | return languageLocale; 567 | } else { 568 | final Locale fallbackLocale = fallback ?? supported.first; 569 | return fallbackLocale; 570 | } 571 | } 572 | 573 | /// 574 | /// Returns true if the specified locale is supported, false otherwise. 575 | /// 576 | bool _isSupported(Locale locale, bool withCountry) { 577 | if (locale != null) { 578 | for (Locale supportedLocale in supportedLocales) { 579 | // Language must always match both locales. 580 | if (supportedLocale.languageCode != locale.languageCode) { 581 | continue; 582 | } 583 | 584 | // If country code matches, return this locale. 585 | if (supportedLocale.countryCode == locale.countryCode) { 586 | return true; 587 | } 588 | 589 | // If no country requirement is requested, check if this locale has no country. 590 | if (true != withCountry && (supportedLocale.countryCode == null || supportedLocale.countryCode.isEmpty)) { 591 | return true; 592 | } 593 | } 594 | } 595 | return false; 596 | } 597 | } 598 | 599 | String getLang(Locale l) => l == null 600 | ? null 601 | : l.countryCode != null && l.countryCode.isEmpty 602 | ? l.languageCode 603 | : l.toString(); 604 | -------------------------------------------------------------------------------- /lib/icons/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hujianhang2996/forget/fe0fefc85fde74cd98c5579d0d81751cae71588f/lib/icons/iconfont.ttf -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_localizations/flutter_localizations.dart'; 3 | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; 4 | 5 | import 'pages/home_page.dart'; 6 | import 'model/model.dart'; 7 | import 'model/macro.dart'; 8 | 9 | import './generated/i18n.dart'; 10 | import 'forget_theme.dart'; 11 | import 'package:provider/provider.dart'; 12 | import './notifier/notifier.dart'; 13 | import 'package:flutter/services.dart'; 14 | import 'package:shared_preferences/shared_preferences.dart'; 15 | import 'package:intro_slider/slide_object.dart'; 16 | import 'package:intro_slider/intro_slider.dart'; 17 | 18 | void main() async { 19 | WidgetsFlutterBinding.ensureInitialized(); 20 | notificationsPlugin = FlutterLocalNotificationsPlugin(); 21 | await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); 22 | prefs = await SharedPreferences.getInstance(); 23 | runApp(MyApp()); 24 | } 25 | 26 | class MyApp extends StatefulWidget { 27 | @override 28 | _MyAppState createState() => _MyAppState(); 29 | } 30 | 31 | class _MyAppState extends State { 32 | @override 33 | Widget build(BuildContext context) { 34 | return FutureBuilder( 35 | future: Future.wait([ 36 | DBOperation.retrieveTasks(), 37 | DBOperation.retrieveProjects(), 38 | DBOperation.retrieveLabels() 39 | ]), 40 | builder: (context, snapshot) { 41 | final String lanCode = prefs.getString('lanCode') ?? 'zh'; 42 | final bool lightTheme = prefs.getBool('theme') ?? true; 43 | final bool showIsDone = prefs.getBool('showIsDone') ?? false; 44 | final bool firstRun = prefs.getBool('firstRun') ?? true; 45 | if (snapshot.connectionState == ConnectionState.done) { 46 | return MultiProvider( 47 | providers: [ 48 | ChangeNotifierProvider.value( 49 | value: TasksNotifier(t: snapshot.data[0], s: showIsDone)), 50 | ChangeNotifierProvider.value( 51 | value: ProjectsNotifier(p: snapshot.data[1])), 52 | ChangeNotifierProvider.value( 53 | value: LabelsNotifier(l: snapshot.data[2])), 54 | ChangeNotifierProvider.value( 55 | value: Setting(lanCode: lanCode, isLightTheme: lightTheme)) 56 | ], 57 | child: Consumer( 58 | builder: (context, setting, _) => MaterialApp( 59 | locale: setting.locale, 60 | debugShowCheckedModeBanner: false, 61 | title: 'forget', 62 | home: Builder(builder: (context) { 63 | if (firstRun) { 64 | return IntroPage(context); 65 | } else { 66 | return HomePage(); 67 | } 68 | }), 69 | theme: setting.lightTheme 70 | ? forgetLightTheme() 71 | : forgetDarkTheme(), //forgetDarkTheme,forgetLightTheme 72 | localizationsDelegates: const [ 73 | S.delegate, 74 | GlobalMaterialLocalizations.delegate, 75 | GlobalWidgetsLocalizations.delegate, 76 | CupertinoLocalizationsDelegate() 77 | ], 78 | supportedLocales: S.delegate.supportedLocales)), 79 | ); 80 | } else { 81 | return MaterialApp( 82 | debugShowCheckedModeBanner: false, 83 | theme: lightTheme ? forgetLightTheme() : forgetDarkTheme(), 84 | home: WelcomePage(lanCode: lanCode)); 85 | } 86 | }, 87 | ); 88 | } 89 | } 90 | 91 | class WelcomePage extends StatelessWidget { 92 | final String lanCode; 93 | const WelcomePage({Key key, @required this.lanCode}) : super(key: key); 94 | 95 | @override 96 | Widget build(BuildContext context) { 97 | return Material( 98 | child: Center( 99 | child: Column( 100 | mainAxisAlignment: MainAxisAlignment.center, 101 | children: [ 102 | Text(lanCode == 'zh' ? '欢迎使用' : 'Welcome to', 103 | style: Theme.of(context).textTheme.display2), 104 | Text('Forget', style: Theme.of(context).textTheme.display1), 105 | Text('Veni Vidi Vici', style: Theme.of(context).textTheme.display1) 106 | ], 107 | ), 108 | ), 109 | ); 110 | } 111 | } 112 | 113 | class IntroPage extends StatelessWidget { 114 | List slides = []; 115 | 116 | final String intro_title_1 = "脑中的事情太多?\nToo many stuffs in your head?"; 117 | final String intro_title_2 = "分配你的任务\nAssign your tasks"; 118 | final String intro_title_3 = "如果某件事情太复杂\nIf something is too complex"; 119 | final String intro_body_1 = "把它们都记在Forget中吧。\nWrite them down in Forget."; 120 | final String intro_body_2 = "下一步行动——可以马上完成或者需要尽快完成的任务。\n" 121 | "Next Move - tasks that can be accomplished immediately or need to be accomplished as soon as possible.\n" 122 | "计划 ——需要某个特定时间进行的任务。\n" 123 | "Plan - Tasks that need to be done at a specific time.\n" 124 | "等待——不是很紧急或者需要待定的任务。\n" 125 | "Waiting - not very urgent or undetermined."; 126 | final String intro_body_3 = "创建一个项目来管理它,在项目下面可以添加多条任务。\nCreate a project to manage it.Multiple tasks can be added under the project."; 127 | 128 | Slide _slide(BuildContext context, String img, String title, String description, Color color){ 129 | return Slide( 130 | title: title, 131 | maxLineTitle: 5, 132 | centerWidget: Image.asset(img), 133 | description: description, 134 | styleTitle: TextStyle(fontSize: 15, color: Colors.white), 135 | styleDescription: TextStyle(fontSize: 13, color: Colors.white), 136 | backgroundColor: color); 137 | } 138 | 139 | IntroPage(BuildContext parentContext) { 140 | slides.add(_slide(parentContext, 'placeholder/intro_img_1.png', intro_title_1, intro_body_1, Colors.grey)); 141 | slides.add(_slide(parentContext, 'placeholder/intro_img_2.png', intro_title_2, intro_body_2, Colors.grey)); 142 | slides.add(_slide(parentContext, 'placeholder/intro_img_3.png', intro_title_3, intro_body_3, Colors.grey)); 143 | } 144 | 145 | VoidCallback onDonePress(BuildContext context) { 146 | return () => Navigator.pushReplacement( 147 | context, 148 | PageRouteBuilder(pageBuilder: (context, animation, secondaryAnimation) { 149 | return HomePage(); 150 | }, transitionsBuilder: ( 151 | context, 152 | animation, 153 | secondaryAnimation, 154 | child, 155 | ) { 156 | return FadeTransition( 157 | //position: Tween(begin: Offset(1, 0), end: Offset(0, 0)).animate(animation), 158 | opacity: Tween(begin: 0, end: 1).animate(animation), 159 | child: child, 160 | ); 161 | })); 162 | } 163 | 164 | @override 165 | Widget build(BuildContext context) { 166 | return IntroSlider(slides: slides, onDonePress: onDonePress(context)); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /lib/model/macro.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/foundation.dart' show SynchronousFuture; 3 | import 'package:flutter/cupertino.dart'; 4 | import 'model.dart'; 5 | import '../generated/i18n.dart'; 6 | import '../pages/task_detail_page.dart'; 7 | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; 8 | import '../notifier/notifier.dart'; 9 | import 'package:provider/provider.dart'; 10 | import '../widgets/cells.dart'; 11 | import 'package:shared_preferences/shared_preferences.dart'; 12 | import 'dart:math'; 13 | import '../forget_icons.dart'; 14 | 15 | FlutterLocalNotificationsPlugin notificationsPlugin; 16 | 17 | double screenWidth(BuildContext context) { 18 | return MediaQuery.of(context).size.width; 19 | } 20 | 21 | double screenHeight(BuildContext context) { 22 | return MediaQuery.of(context).size.height; 23 | } 24 | 25 | IconData iconDataOfFilter(TaskType type) { 26 | switch (type) { 27 | case TaskType.unassigned: 28 | return ForgetIcon.unassigned; 29 | case TaskType.nextMove: 30 | return Icons.play_arrow; 31 | case TaskType.plan: 32 | return Icons.calendar_today; 33 | case TaskType.wait: 34 | return Icons.access_time; 35 | } 36 | } 37 | 38 | BorderRadius cellRadius = BorderRadius.circular(4); 39 | 40 | const double cellHeight = 56; 41 | 42 | final String oneDriveURL = 43 | 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?' 44 | 'client_id=04f758af-9e5b-4b09-b50b-de9b656c8a23' 45 | '&scope=files.readwrite%20offline_access' 46 | '&response_type=code' 47 | '&redirect_uri=msal04f758af-9e5b-4b09-b50b-de9b656c8a23://auth'; 48 | 49 | Random randomWorker = Random.secure(); 50 | 51 | Duration longDuration = Duration(milliseconds: 300); 52 | Duration shortDuration = Duration(milliseconds: 100); 53 | 54 | String stringOfFilter(TaskType type, BuildContext context) { 55 | switch (type) { 56 | case TaskType.unassigned: 57 | return S.of(context).unassigned; 58 | case TaskType.nextMove: 59 | return S.of(context).next_move; 60 | case TaskType.plan: 61 | return S.of(context).plan; 62 | case TaskType.wait: 63 | return S.of(context).wait; 64 | } 65 | } 66 | 67 | add_task_callback( 68 | BuildContext context, Task task, TasksNotifier tasksNotifier) { 69 | Navigator.push( 70 | context, 71 | PageRouteBuilder(pageBuilder: (context, animation, secondaryAnimation) { 72 | return TaskDetailPage(task: task); 73 | }, transitionsBuilder: ( 74 | context, 75 | animation, 76 | secondaryAnimation, 77 | child, 78 | ) { 79 | return SlideTransition( 80 | position: Tween( 81 | begin: const Offset(0.0, 1.0), 82 | end: const Offset(0.0, 0.0), 83 | ).animate(animation), 84 | child: child, 85 | ); 86 | })).then((value) { 87 | if (value == null) { 88 | return; 89 | } 90 | if (value.text == null || value.text.isEmpty) { 91 | return; 92 | } 93 | tasksNotifier.insert(value); 94 | }); 95 | } 96 | 97 | Future showComfirmDialog( 98 | BuildContext context, String title, String descrip) { 99 | return showDialog( 100 | context: context, 101 | builder: (context) => SimpleDialog( 102 | title: Text(title), 103 | shape: 104 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), 105 | children: [ 106 | Container( 107 | margin: EdgeInsets.only(left: 24, right: 24), 108 | child: Text(descrip)), 109 | ButtonBar( 110 | children: [ 111 | FlatButton( 112 | child: Text(S.of(context).cancel), 113 | onPressed: () => Navigator.pop(context, false)), 114 | FlatButton( 115 | child: Text(S.of(context).OK, 116 | style: TextStyle(color: Colors.red)), 117 | onPressed: () => Navigator.pop(context, true)) 118 | ], 119 | ) 120 | ], 121 | )); 122 | } 123 | 124 | SharedPreferences prefs; 125 | 126 | int getSortWay(bool homePage, TaskType taskType) { 127 | int sortWay; 128 | if (homePage) { 129 | sortWay = prefs.getInt('homeSortWay') ?? 0; 130 | } else { 131 | switch (taskType) { 132 | case TaskType.unassigned: 133 | sortWay = prefs.getInt('unassignedSortWay') ?? 0; 134 | break; 135 | case TaskType.nextMove: 136 | sortWay = prefs.getInt('nextMoveSortWay') ?? 0; 137 | break; 138 | case TaskType.plan: 139 | sortWay = prefs.getInt('planSortWay') ?? 0; 140 | break; 141 | case TaskType.wait: 142 | sortWay = prefs.getInt('waitSortWay') ?? 0; 143 | } 144 | } 145 | return sortWay; 146 | } 147 | 148 | add_task_cupertino_callback( 149 | BuildContext context, Task task, TasksNotifier tasksNotifier) { 150 | Navigator.push(context, CupertinoPageRoute(builder: (context) { 151 | return TaskDetailPage(task: task); 152 | })).then((value) { 153 | if (value == null) { 154 | return; 155 | } 156 | if (value.text == null || value.text.isEmpty) { 157 | return; 158 | } 159 | tasksNotifier.insert(value); 160 | }); 161 | } 162 | 163 | GestureTapCallback showTodoSnackBar(BuildContext context) { 164 | return () => Scaffold.of(context).showSnackBar(SnackBar( 165 | content: Text('TODO', textAlign: TextAlign.center), 166 | duration: Duration(milliseconds: 200))); 167 | } 168 | 169 | class CupertinoLocalizationsDelegate 170 | extends LocalizationsDelegate { 171 | const CupertinoLocalizationsDelegate(); 172 | 173 | @override 174 | bool isSupported(Locale locale) => 175 | ['en', 'zh'].contains(locale.languageCode); 176 | 177 | @override 178 | SynchronousFuture<_DefaultCupertinoLocalizations> load(Locale locale) { 179 | return SynchronousFuture<_DefaultCupertinoLocalizations>( 180 | _DefaultCupertinoLocalizations(locale.languageCode)); 181 | } 182 | 183 | @override 184 | bool shouldReload(CupertinoLocalizationsDelegate old) => false; 185 | } 186 | 187 | class _DefaultCupertinoLocalizations extends CupertinoLocalizations { 188 | _DefaultCupertinoLocalizations(this._languageCode) 189 | : assert(_languageCode != null); 190 | 191 | final DefaultCupertinoLocalizations _en = 192 | const DefaultCupertinoLocalizations(); 193 | final String _languageCode; 194 | 195 | final Map> _dict = >{ 196 | 'en': { 197 | 'alert': 'Alert', 198 | 'copy': 'Copy', 199 | 'paste': 'Paste', 200 | 'cut': 'Cut', 201 | 'selectAll': 'Select all' 202 | }, 203 | 'zh': { 204 | 'alert': '提醒', 205 | 'copy': '复制', 206 | 'paste': '粘贴', 207 | 'cut': '剪切', 208 | 'selectAll': '全选' 209 | } 210 | }; 211 | 212 | @override 213 | String get alertDialogLabel => _get('alert'); 214 | 215 | @override 216 | String get anteMeridiemAbbreviation => _en.anteMeridiemAbbreviation; 217 | 218 | @override 219 | String get postMeridiemAbbreviation => _en.postMeridiemAbbreviation; 220 | 221 | @override 222 | String get copyButtonLabel => _get('copy'); 223 | 224 | @override 225 | String get cutButtonLabel => _get('cut'); 226 | 227 | @override 228 | String get pasteButtonLabel => _get('paste'); 229 | 230 | @override 231 | String get selectAllButtonLabel => _get('selectAll'); 232 | 233 | @override 234 | DatePickerDateOrder get datePickerDateOrder => _en.datePickerDateOrder; 235 | 236 | @override 237 | DatePickerDateTimeOrder get datePickerDateTimeOrder => 238 | _en.datePickerDateTimeOrder; 239 | 240 | @override 241 | String datePickerDayOfMonth(int dayIndex) => 242 | _en.datePickerDayOfMonth(dayIndex); 243 | 244 | @override 245 | String datePickerHour(int hour) => _en.datePickerHour(hour); 246 | 247 | @override 248 | String datePickerHourSemanticsLabel(int hour) => 249 | _en.datePickerHourSemanticsLabel(hour); 250 | 251 | @override 252 | String datePickerMediumDate(DateTime date) => _en.datePickerMediumDate(date); 253 | 254 | @override 255 | String datePickerMinute(int minute) => _en.datePickerMinute(minute); 256 | 257 | @override 258 | String datePickerMinuteSemanticsLabel(int minute) => 259 | _en.datePickerMinuteSemanticsLabel(minute); 260 | 261 | @override 262 | String datePickerMonth(int monthIndex) => _en.datePickerMonth(monthIndex); 263 | 264 | @override 265 | String datePickerYear(int yearIndex) => _en.datePickerYear(yearIndex); 266 | 267 | @override 268 | String timerPickerHour(int hour) => _en.timerPickerHour(hour); 269 | 270 | @override 271 | String timerPickerHourLabel(int hour) => _en.timerPickerHourLabel(hour); 272 | 273 | @override 274 | String timerPickerMinute(int minute) => _en.timerPickerMinute(minute); 275 | 276 | @override 277 | String timerPickerMinuteLabel(int minute) => 278 | _en.timerPickerMinuteLabel(minute); 279 | 280 | @override 281 | String timerPickerSecond(int second) => _en.timerPickerSecond(second); 282 | 283 | @override 284 | String timerPickerSecondLabel(int second) => 285 | _en.timerPickerSecondLabel(second); 286 | 287 | String _get(String key) { 288 | return _dict[_languageCode][key]; 289 | } 290 | 291 | @override 292 | // TODO: implement todayLabel 293 | String get todayLabel => null; 294 | } 295 | 296 | class SearchWorker extends SearchDelegate { 297 | final TaskCellShowingIn cellShowingIn; 298 | final int projectID; 299 | final int taskType; 300 | 301 | SearchWorker(this.cellShowingIn, {this.projectID = 0, this.taskType = 0}); 302 | 303 | @override 304 | List buildActions(BuildContext context) { 305 | return [ 306 | IconButton( 307 | icon: Icon( 308 | Icons.clear, 309 | color: Theme.of(context).iconTheme.color, 310 | ), 311 | onPressed: () => query = '', 312 | ) 313 | ]; 314 | } 315 | 316 | @override 317 | Widget buildLeading(BuildContext context) { 318 | // return ActionChip( 319 | // onPressed: () => close(context, ''), 320 | // backgroundColor: Colors.blue, 321 | // elevation: 6, 322 | // avatar: Icon(Icons.arrow_back), 323 | // label: Text( 324 | // 'back', 325 | // style: Theme.of(context) 326 | // .textTheme 327 | // .title 328 | // .copyWith(color: Colors.white), 329 | // )); 330 | 331 | return IconButton( 332 | icon: Icon(Icons.arrow_back, color: Theme.of(context).iconTheme.color), 333 | onPressed: () => close(context, ''), 334 | ); 335 | } 336 | 337 | @override 338 | Widget buildResults(BuildContext context) { 339 | return _build(context); 340 | } 341 | 342 | @override 343 | Widget buildSuggestions(BuildContext context) { 344 | return _build(context); 345 | } 346 | 347 | @override 348 | ThemeData appBarTheme(BuildContext context) { 349 | return Theme.of(context); 350 | } 351 | 352 | Widget _build(BuildContext context) { 353 | if (query.isEmpty) { 354 | return Container(); 355 | } else { 356 | List tasks; 357 | switch (cellShowingIn) { 358 | case TaskCellShowingIn.tasks: 359 | tasks = Provider.of(context).tasks(0); 360 | break; 361 | case TaskCellShowingIn.filter: 362 | tasks = 363 | Provider.of(context).tasksInFilter(taskType, 0); 364 | break; 365 | case TaskCellShowingIn.project: 366 | tasks = 367 | Provider.of(context).tasksInProject(projectID, 0); 368 | break; 369 | case TaskCellShowingIn.label: 370 | tasks = []; 371 | break; 372 | } 373 | 374 | List suggestTasks = tasks 375 | .where((t) => t.text.toLowerCase().contains(query.toLowerCase())) 376 | .toList(); 377 | return ChangeNotifierProvider.value( 378 | value: ListPageStateNotifier(), 379 | child: ListView.builder( 380 | itemBuilder: (context, index) { 381 | return TaskCell( 382 | taskID: suggestTasks[index].id, parentContext: context); 383 | }, 384 | itemCount: suggestTasks.length), 385 | ); 386 | } 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /lib/model/onedrive.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:http/http.dart' as http; 3 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 4 | import 'package:path_provider/path_provider.dart'; 5 | import 'dart:io'; 6 | import 'dart:convert'; 7 | 8 | class OneDrive { 9 | static final String authorizeEndPoint = 'https://login.microsoftonline.com' 10 | '/common/oauth2/v2.0/authorize'; 11 | static final String logOutEndPoint = 'https://login.microsoftonline.com' 12 | '/common/oauth2/v2.0/logout'; 13 | static final String tokenEndPoint = 'https://login.microsoftonline.com' 14 | '/common/oauth2/v2.0/token'; 15 | static final String clientID = 'dbaa2965-9553-4e56-bfe8-c31e3b522519'; 16 | static final String redirectUri = 'msauth%3A%2F%2Fcom.odyssey.forget' 17 | '%2FR2V6kZ6IllvaZdRwOCQTSmdkZXE%253D'; 18 | static final String matchRedirectUri = 'msauth://com.odyssey.forget' 19 | '/R2V6kZ6IllvaZdRwOCQTSmdkZXE%3D?code='; 20 | 21 | static void logIn(VoidCallback callback) async { 22 | final flutterWebviewPlugin = FlutterWebviewPlugin(); 23 | String codeUrl = '$authorizeEndPoint' 24 | '?response_type=code' 25 | '&client_id=$clientID' 26 | '&redirect_uri=$redirectUri' 27 | '&scope=offline_access+files.readwrite.all+user.readwrite+openid'; 28 | 29 | await flutterWebviewPlugin.launch(codeUrl); 30 | flutterWebviewPlugin.onUrlChanged.listen((String url) async { 31 | if (url.contains(matchRedirectUri)) { 32 | await flutterWebviewPlugin.close(); 33 | Directory appDocDir = await getApplicationDocumentsDirectory(); 34 | String appDocPath = appDocDir.path; 35 | var tokenResponse = await http.post(tokenEndPoint, 36 | headers: { 37 | 'Content-Type': 'application/x-www-form-urlencoded', 38 | }, 39 | body: 'client_id=$clientID' 40 | '&code=${url.split('=')[1]}' 41 | '&redirect_uri=$redirectUri' 42 | '&grant_type=authorization_code'); 43 | File file = File(appDocPath + '/token.json'); 44 | file.writeAsString(tokenResponse.body); 45 | callback(); 46 | } 47 | }); 48 | } 49 | 50 | static Future> logInState() async { 51 | Directory appDocDir = await getApplicationDocumentsDirectory(); 52 | String appDocPath = appDocDir.path; 53 | File tokenFile = File(appDocPath + '/token.json'); 54 | bool fileExists = await tokenFile.exists(); 55 | print('fileExists: $fileExists'); 56 | if (fileExists) { 57 | String token = await refreshToken(tokenFile); 58 | var getResponse = await http.get( 59 | 'https://graph.microsoft.com/v1.0/me', 60 | headers: { 61 | 'Authorization': 'Bearer $token', 62 | 'Host': 'graph.microsoft.com' 63 | }); 64 | Map getResponseJson = json.decode(getResponse.body); 65 | // print(getResponse.body); 66 | return {'name': getResponseJson['displayName'], 'login': true}; 67 | // return getResponse.body[0]; 68 | } else { 69 | return {'name': '未登录', 'login': false}; 70 | } 71 | } 72 | 73 | static void logOut() async{ 74 | String codeUrl = '$logOutEndPoint' 75 | '?post_logout_redirect_uri=$redirectUri'; 76 | 77 | Directory appDocDir = await getApplicationDocumentsDirectory(); 78 | String appDocPath = appDocDir.path; 79 | 80 | File tokenFile = File(appDocPath + '/token.json'); 81 | bool fileExists = await tokenFile.exists(); 82 | if(fileExists){ 83 | await tokenFile.delete(); 84 | } 85 | 86 | final flutterWebviewPlugin = FlutterWebviewPlugin(); 87 | await flutterWebviewPlugin.launch(codeUrl); 88 | await flutterWebviewPlugin.close(); 89 | } 90 | 91 | static Future refreshToken(File tokenFile) async { 92 | String token = await tokenFile.readAsString(); 93 | Map tokenJson = json.decode(token); 94 | var refreshRespone = await http.post(tokenEndPoint, 95 | headers: { 96 | 'Content-Type': 'application/x-www-form-urlencoded', 97 | }, 98 | body: 'client_id=$clientID' 99 | '&redirect_uri=$redirectUri' 100 | '&refresh_token=${tokenJson['refresh_token']}' 101 | '&grant_type=refresh_token'); 102 | tokenFile.writeAsString(refreshRespone.body); 103 | Map newTokenJson = json.decode(refreshRespone.body); 104 | return newTokenJson['access_token']; 105 | } 106 | 107 | static void downLoad() async { 108 | Directory appDocDir = await getApplicationDocumentsDirectory(); 109 | String appDocPath = appDocDir.path; 110 | File tokenFile = File(appDocPath + '/token.json'); 111 | bool fileExists = await tokenFile.exists(); 112 | if (fileExists) { 113 | String token = await refreshToken(tokenFile); 114 | 115 | var getResponse = await http.get( 116 | 'https://graph.microsoft.com/v1.0/me/drive/root/children', 117 | headers: { 118 | 'Authorization': 'Bearer $token', 119 | 'Host': 'graph.microsoft.com' 120 | }); 121 | Map filesJson = json.decode(getResponse.body); 122 | for (var file in filesJson['value']) { 123 | print(file['name']); 124 | } 125 | } else { 126 | print('登录失效,请重新登录'); 127 | } 128 | } 129 | 130 | static void upLoad() {} 131 | } 132 | -------------------------------------------------------------------------------- /lib/model/time_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import '../generated/i18n.dart'; 4 | 5 | typedef WeekPickerCallback = VoidCallback Function(int) ; 6 | class TimeUtil { 7 | static const List _daysInMonth = [ 8 | 31, 9 | -1, 10 | 31, 11 | 30, 12 | 31, 13 | 30, 14 | 31, 15 | 31, 16 | 30, 17 | 31, 18 | 30, 19 | 31 20 | ]; 21 | 22 | /// 获取这个月的天数 23 | static int getDaysInMonth(int year, int month) { 24 | if (month == DateTime.february) { 25 | final bool isLeapYear = 26 | (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0); 27 | if (isLeapYear) return 29; 28 | return 28; 29 | } 30 | return _daysInMonth[month - 1]; 31 | } 32 | 33 | /// 获取周Header 34 | static List getDayHeaders( 35 | MaterialLocalizations localizations, double padding) { 36 | List list = []; 37 | for (int i = localizations.firstDayOfWeekIndex; true; i = (i + 1) % 7) { 38 | final String weekday = localizations.narrowWeekdays[i]; 39 | list.add(Flexible( 40 | child: Container( 41 | padding: EdgeInsets.only(top: padding, bottom: padding), 42 | alignment: Alignment.center, 43 | child: Text(weekday), 44 | ))); 45 | if (i == (localizations.firstDayOfWeekIndex - 1) % 7) break; 46 | } 47 | return list; 48 | } 49 | 50 | static List getDayButtons(BuildContext context, 51 | MaterialLocalizations localizations, double padding, List weekList, WeekPickerCallback onPressed) { 52 | List list = []; 53 | for (int i = localizations.firstDayOfWeekIndex; true; i = (i + 1) % 7) { 54 | final String weekday = localizations.narrowWeekdays[i]; 55 | list.add(Flexible( 56 | child: FlatButton( 57 | child: Text(weekday, style: TextStyle(color: weekList.contains(i) ? Colors.deepOrange : null)), 58 | onPressed: onPressed(i), 59 | ))); 60 | if (i == (localizations.firstDayOfWeekIndex - 1) % 7) break; 61 | } 62 | return list; 63 | } 64 | 65 | /// 得到这个月的第一天是星期几(0 是 星期日 1 是 星期一...) 66 | static int computeFirstDayOffset( 67 | int year, int month, MaterialLocalizations localizations) { 68 | // 0-based day of week, with 0 representing Monday. 69 | final int weekdayFromMonday = DateTime(year, month).weekday - 1; 70 | // 0-based day of week, with 0 representing Sunday. 71 | final int firstDayOfWeekFromSunday = localizations.firstDayOfWeekIndex; 72 | // firstDayOfWeekFromSunday recomputed to be Monday-based 73 | final int firstDayOfWeekFromMonday = (firstDayOfWeekFromSunday - 1) % 7; 74 | // Number of days between the first day of week appearing on the calendar, 75 | // and the day corresponding to the 1-st of the month. 76 | return (weekdayFromMonday - firstDayOfWeekFromMonday) % 7; 77 | } 78 | 79 | /// 获取某个月所有的天 80 | static List getDay( 81 | int year, int month, MaterialLocalizations localizations) { 82 | List labels = []; 83 | final int daysInMonth = getDaysInMonth(year, month); 84 | final int firstDayOffset = 85 | computeFirstDayOffset(year, month, localizations); 86 | final int daysInMonthCeil = 6 * DateTime.daysPerWeek; 87 | for (int i = 0; i < daysInMonthCeil; i += 1) { 88 | // 1-based day of month, e.g. 1-31 for January, and 1-29 for February on 89 | // a leap year. 90 | final int day = i - firstDayOffset + 1; 91 | if (day < 1) { 92 | if (month == 1) { 93 | labels 94 | .add(DateTime(year - 1, 12, getDaysInMonth(year - 1, 12) + day)); 95 | } else { 96 | labels.add( 97 | DateTime(year, month - 1, getDaysInMonth(year, month - 1) + day)); 98 | } 99 | } 100 | if (day >= 1 && day <= daysInMonth) { 101 | labels.add(DateTime(year, month, day)); 102 | } 103 | if (day > daysInMonth && day <= daysInMonthCeil - firstDayOffset) { 104 | if (month == 12) { 105 | labels.add(DateTime(year + 1, 1, day - daysInMonth)); 106 | } else { 107 | labels.add(DateTime(year, month + 1, day - daysInMonth)); 108 | } 109 | } 110 | } 111 | return labels; 112 | } 113 | 114 | static DateTime page2date(int page, DateTime startTime) { 115 | int year = startTime.year + ((startTime.month + page - 1) / 12).floor(); 116 | int month = 117 | (startTime.month + page) % 12 == 0 ? 12 : (startTime.month + page) % 12; 118 | return DateTime(year, month); 119 | } 120 | 121 | static int date2page(DateTime date, DateTime startTime) { 122 | return (date.year - startTime.year) * 12 + date.month - startTime.month; 123 | } 124 | 125 | static bool isInSameMonth(DateTime timea, DateTime timeb) { 126 | return (timea.year * 12 + timea.month == timeb.year * 12 + timeb.month); 127 | } 128 | 129 | static bool isInSameDay(DateTime timea, DateTime timeb) { 130 | return (timea.year * 12 + timea.month == timeb.year * 12 + timeb.month && timea.day == timeb.day); 131 | } 132 | 133 | static int compareInDay(DateTime timea, DateTime timeb) { 134 | if(timea.year * 12 + timea.month == timeb.year * 12 + timeb.month && timea.day == timeb.day){ 135 | return 0; 136 | } 137 | return timea.compareTo(timeb); 138 | } 139 | 140 | static String monthName(int month, BuildContext context) { 141 | switch (month) { 142 | case 1: 143 | return S.of(context).Jan; 144 | case 2: 145 | return S.of(context).Feb; 146 | case 3: 147 | return S.of(context).Mar; 148 | case 4: 149 | return S.of(context).Apr; 150 | case 5: 151 | return S.of(context).May; 152 | case 6: 153 | return S.of(context).Jun; 154 | case 7: 155 | return S.of(context).Jul; 156 | case 8: 157 | return S.of(context).Aug; 158 | case 9: 159 | return S.of(context).Sep; 160 | case 10: 161 | return S.of(context).Oct; 162 | case 11: 163 | return S.of(context).Nov; 164 | case 12: 165 | return S.of(context).Dec; 166 | } 167 | } 168 | 169 | static String shortName(DateTime time) { 170 | return '${time.month}-${time.day}'; 171 | } 172 | } 173 | 174 | int ceilDivide(int a, int b) { 175 | int remainder = a % b; 176 | int c = a ~/ b; 177 | return remainder == 0 ? c : (c + 1); 178 | } 179 | -------------------------------------------------------------------------------- /lib/notifier/notifier.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../model/model.dart'; 3 | import 'package:provider/provider.dart'; 4 | import '../model/time_util.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | import '../model/macro.dart'; 7 | 8 | class TasksNotifier extends ChangeNotifier { 9 | bool _showIsDone; 10 | List _tasks; 11 | 12 | bool get showIsDone => _showIsDone; 13 | 14 | TasksNotifier({List t, bool s}) { 15 | _tasks = t ?? []; 16 | _showIsDone = s ?? false; 17 | } 18 | 19 | Task task(int id) { 20 | return _tasks.singleWhere((t) => t.id == id); 21 | } 22 | 23 | Future _refresh() async { 24 | _tasks = await DBOperation.retrieveTasks(); 25 | notifyListeners(); 26 | } 27 | 28 | Future _sortTasks(int sortBy) { 29 | if (sortBy == 0) { 30 | _tasks.sort((a, b) => b.id.compareTo(a.id)); 31 | } 32 | if (sortBy == 1) { 33 | _tasks.sort((a, b) { 34 | if (a.taskType == 2 && b.taskType == 2) { 35 | return a.nextNotifaTime().compareTo(b.nextNotifaTime()); 36 | } else { 37 | return a.taskType.compareTo(b.taskType); 38 | } 39 | }); 40 | } 41 | if (sortBy == 2) { 42 | _tasks.sort((a, b) => b.priority.compareTo(a.priority)); 43 | } 44 | _tasks.sort((a, b){ 45 | if (a.isDone && b.isDone)return 0; 46 | if (a.isDone && !b.isDone)return 1; 47 | if (!a.isDone && b.isDone)return -1; 48 | if (!a.isDone && !b.isDone)return 0; 49 | }); 50 | } 51 | 52 | Future switchShowIsDone({bool newValue}) async { 53 | if (newValue == null){ 54 | _showIsDone = !_showIsDone; 55 | }else{ 56 | _showIsDone = newValue; 57 | } 58 | SharedPreferences prefs = await SharedPreferences.getInstance(); 59 | await prefs.setBool('showIsDone', _showIsDone); 60 | notifyListeners(); 61 | } 62 | 63 | List tasks(int sortBy, {String query}) { 64 | _sortTasks(sortBy); 65 | if (query == null) { 66 | return _tasks.where((t)=>_showIsDone ? true : !t.isDone).toList(); 67 | } else { 68 | return _tasks 69 | .where((t) => query.isEmpty ? false : t.text.toLowerCase().contains(query.toLowerCase())) 70 | .toList(); 71 | } 72 | } 73 | 74 | List tasksInProject(int projectID, int sortBy, {String query}) { 75 | _sortTasks(sortBy); 76 | if (query == null) { 77 | return _tasks.where((t) => t.projectID == projectID).toList().where((t)=>_showIsDone ? true : !t.isDone).toList(); 78 | } else { 79 | return _tasks 80 | .where((t) => t.projectID == projectID) 81 | .toList() 82 | .where((t) => query.isEmpty ? false : t.text.toLowerCase().contains(query.toLowerCase())) 83 | .toList(); 84 | } 85 | } 86 | 87 | List tasksInFilter(int taskType, int sortBy, {String query}) { 88 | _sortTasks(sortBy); 89 | if (query == null) { 90 | return _tasks.where((t) => t.taskType == taskType).toList().where((t)=>_showIsDone ? true : !t.isDone).toList(); 91 | } else { 92 | return _tasks 93 | .where((t) => t.taskType == taskType) 94 | .toList() 95 | .where((t) => query.isEmpty ? false : t.text.toLowerCase().contains(query.toLowerCase())) 96 | .toList(); 97 | } 98 | } 99 | 100 | List tasksInDate(DateTime date, int sortBy, {String query}) { 101 | _sortTasks(sortBy); 102 | if (query == null) { 103 | return _tasks.where((t) { 104 | if (t.taskType != 2) { 105 | return false; 106 | } 107 | switch (t.repeatWay) { 108 | case 0: 109 | return TimeUtil.isInSameDay(t.dateTime, date); 110 | case 1: 111 | return TimeUtil.compareInDay(date, DateTime.now()) >= 0; 112 | case 2: 113 | return t.weekDay.contains(date.weekday == 7 ? 0 : date.weekday) && 114 | TimeUtil.compareInDay(date, DateTime.now()) >= 0; 115 | } 116 | }).toList().where((t)=>_showIsDone ? true : !t.isDone).toList(); 117 | }else{ 118 | return _tasks.where((t) { 119 | if (t.taskType != 2) { 120 | return false; 121 | } 122 | switch (t.repeatWay) { 123 | case 0: 124 | return TimeUtil.isInSameDay(t.dateTime, date); 125 | case 1: 126 | return TimeUtil.compareInDay(date, DateTime.now()) >= 0; 127 | case 2: 128 | return t.weekDay.contains(date.weekday == 7 ? 0 : date.weekday) && 129 | TimeUtil.compareInDay(date, DateTime.now()) >= 0; 130 | } 131 | }).toList().where((t) => query.isEmpty ? false : t.text.toLowerCase().contains(query.toLowerCase())) 132 | .toList(); 133 | } 134 | 135 | } 136 | 137 | List tasksWithLabel(int labelID) { 138 | return _tasks.where((task)=>task.labels.contains(labelID)).toList(); 139 | } 140 | 141 | Future insert(Task task) async { 142 | await DBOperation.insertTask(task); 143 | await _refresh(); 144 | } 145 | 146 | Future delete(int id) async { 147 | await DBOperation.deleteTask(id); 148 | await _refresh(); 149 | } 150 | 151 | Future deleteSeveral(List ids) async { 152 | await DBOperation.deleteTasks(ids); 153 | await _refresh(); 154 | } 155 | 156 | Future update(Task task) async { 157 | await DBOperation.updateTask(task); 158 | await _refresh(); 159 | } 160 | 161 | Future switchDone(int id, {bool isDone}) async { 162 | await DBOperation.switchDoneOfTask(id, isDone: isDone); 163 | await _refresh(); 164 | } 165 | 166 | Future addTasksToProject(List taskIDs, int projectID) async { 167 | await DBOperation.addTasksToProject(taskIDs, projectID); 168 | await _refresh(); 169 | } 170 | 171 | Future addLabelToTasks(List taskIDs, int labelID) async { 172 | await DBOperation.addLabelToTasks(taskIDs, labelID); 173 | await _refresh(); 174 | } 175 | } 176 | 177 | class ProjectsNotifier extends ChangeNotifier { 178 | List _projects; 179 | 180 | List get projects => _projects; 181 | 182 | ProjectsNotifier({List p}) { 183 | _projects = p ?? []; 184 | } 185 | 186 | Future _refresh() async { 187 | _projects = await DBOperation.retrieveProjects(); 188 | notifyListeners(); 189 | } 190 | 191 | Project project(int id) { 192 | return _projects.singleWhere((p) => p.id == id); 193 | } 194 | 195 | Future insert(Project project) async { 196 | await DBOperation.insertProject(project); 197 | await _refresh(); 198 | } 199 | 200 | Future delete(int id, BuildContext context) async { 201 | await DBOperation.deleteProject(id); 202 | await Provider.of(context)._refresh(); 203 | await _refresh(); 204 | } 205 | 206 | Future update(Project project, BuildContext context) async { 207 | await DBOperation.updateProject(project); 208 | await Provider.of(context)._refresh(); 209 | await _refresh(); 210 | } 211 | } 212 | 213 | class LabelsNotifier extends ChangeNotifier { 214 | List