├── .gitignore ├── .metadata ├── .vscode └── settings.json ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── simple_app │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ └── splash.png │ │ │ ├── drawable-mdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-hdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-mdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-night-xhdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-xxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-xxxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-night │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ └── splash.png │ │ │ ├── drawable-xxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-xxxhdpi │ │ │ └── splash.png │ │ │ ├── drawable │ │ │ ├── background.png │ │ │ └── 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 │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ ├── values │ │ │ └── styles.xml │ │ │ └── xml │ │ │ └── file_paths.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── 15199.mp3 ├── logo_lockup_flutter_vertical.png └── logo_lockup_flutter_vertical_wht.png ├── build.sh ├── devtools_options.yaml ├── images └── env │ ├── 0}5OX~((%WGFCIN19`EL@DA.png │ ├── 4~BQ3HG0O7TK11RFP@]$~_W.png │ ├── 9J@{U3NEC~7%P)$KG}[ZOHY.png │ └── JGY{NH0VV[F]XT07XQ$BJO3.png ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── 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 │ └── Runner-Bridging-Header.h ├── lib ├── common │ ├── color.dart │ └── global.dart ├── components │ ├── base │ │ ├── base_animated_opacity.dart │ │ ├── base_icon.dart │ │ ├── base_text.dart │ │ ├── build_base_app_bar.dart │ │ ├── draggable_floating_action_button.dart │ │ ├── hide_key_bord.dart │ │ ├── loading.dart │ │ └── tabs.dart │ ├── search_bar.dart │ └── todoList │ │ ├── build_todo_list_title.dart │ │ └── todo_list.dart ├── data │ └── index.dart ├── generated │ ├── intl │ │ ├── messages_all.dart │ │ ├── messages_en.dart │ │ └── messages_zh_CN.dart │ └── l10n.dart ├── l10n │ ├── intl_en.arb │ └── intl_zh_CN.arb ├── main.dart ├── model │ └── note.dart ├── page │ ├── calculator.dart │ ├── count_down.dart │ ├── note_editor_page.dart │ ├── note_page.dart │ ├── setting_page.dart │ ├── todo_list_page.dart │ └── tool_kit_page.dart ├── provider │ ├── current_locale.dart │ └── current_theme.dart ├── router.dart └── utils │ ├── common_utils_import.dart │ ├── index.dart │ ├── notification.dart │ ├── request_notice_permission.dart │ ├── show_dialog.dart │ └── show_toast.dart ├── my_webApp ├── assets │ ├── AssetManifest.bin │ ├── AssetManifest.bin.json │ ├── AssetManifest.json │ ├── FontManifest.json │ ├── NOTICES │ ├── assets │ │ └── 15199.mp3 │ ├── fonts │ │ └── MaterialIcons-Regular.otf │ ├── packages │ │ ├── cupertino_icons │ │ │ └── assets │ │ │ │ └── CupertinoIcons.ttf │ │ ├── flutter_sound_web │ │ │ ├── howler │ │ │ │ └── howler.js │ │ │ └── src │ │ │ │ ├── flutter_sound.js │ │ │ │ ├── flutter_sound_player.js │ │ │ │ └── flutter_sound_recorder.js │ │ ├── fluttertoast │ │ │ └── assets │ │ │ │ ├── toastify.css │ │ │ │ └── toastify.js │ │ └── quill_native_bridge_linux │ │ │ └── assets │ │ │ └── xclip │ └── shaders │ │ └── ink_sparkle.frag ├── canvaskit │ ├── canvaskit.js │ ├── canvaskit.js.symbols │ ├── canvaskit.wasm │ ├── chromium │ │ ├── canvaskit.js │ │ ├── canvaskit.js.symbols │ │ └── canvaskit.wasm │ ├── skwasm.js │ ├── skwasm.js.symbols │ ├── skwasm.wasm │ └── skwasm.worker.js ├── favicon.png ├── flutter.js ├── flutter_bootstrap.js ├── flutter_service_worker.js ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html ├── main.dart.js ├── manifest.json └── version.json ├── pubspec.lock ├── pubspec.yaml ├── release └── app.apk ├── test └── widget_test.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.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 | .vscode/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Web related 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /.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: 5f105a6ca7a5ac7b8bc9b241f4c2d86f4188cf5c 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "appbar", 4 | "autofocus", 5 | "AUTOINCREMENT", 6 | "bord", 7 | "cupertino", 8 | "Draggable", 9 | "fluttertoast", 10 | "initialise", 11 | "listdata", 12 | "listkey", 13 | "localizely", 14 | "LTRB", 15 | "mipmap", 16 | "navigatorkey", 17 | "Neumorphic", 18 | "nullsafety", 19 | "prefs", 20 | "prefsinstance", 21 | "RGBO", 22 | "screenutil", 23 | "sqflite", 24 | "todolist", 25 | "unfocus", 26 | "zefyr" 27 | ] 28 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple_app 2 | 3 | 这是一个好玩的flutter项目, 仅供学习,喜欢的话请点个star。 4 | 5 | ## 主要有这四大核心应用 6 | 1. 代办事项 7 | 2. 便签 8 | 3. 计算器 9 | 4. 计时器 10 | 11 | ## 功能点如下 12 | 13 | 1. 国际化(支持中英文语言环境切换) 14 | 2. 夜间模式 15 | 3. 下拉加载数据 16 | 4. 权限申请和本地通知 17 | 5. sqlite持久化储存 18 | 6. 启动页 19 | 7. 富文本编辑 20 | 8. 防误触退出App 21 | 9. 便签长按多选删除 22 | 10. todo置顶or取消置顶 23 | 11. todoList删除新增展开动画 24 | 12. todo滑动列表右滑菜单 25 | 13. 计算器表达式字体放大缩小动画,字体自动缩放大小 26 | 14. 便签列表瀑布流展示 27 | 15. 微光背景 28 | 16. 新拟物风格ui 29 | 17. 音乐播放振动 30 | 18. ... 31 | 32 | ## 环境配置如下 33 | 34 | ``` 35 | Flutter (Channel stable, 2.10.0, on Microsoft Windows [Version 10.0.19043.1526], locale zh-CN) 36 | • Flutter version 2.10.0 at D:\software\flutter 37 | • Upstream repository https://github.com/flutter/flutter.git 38 | • Framework revision 5f105a6ca7 (3 weeks ago), 2022-02-01 14:15:42 -0800 39 | • Engine revision 776efd2034 40 | • Dart version 2.16.0 41 | • DevTools version 2.9.2 42 | • Pub download mirror https://pub.flutter-io.cn 43 | • Flutter download mirror https://storage.flutter-io.cn 44 | 45 | [√] Android toolchain - develop for Android devices (Android SDK version 32.0.0) 46 | • Android SDK at C:\Users\Admin\AppData\Local\Android\sdk 47 | • Platform android-32, build-tools 32.0.0 48 | • Java binary at: D:\software\AndroidStudio\jre\bin\java 49 | • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189) 50 | • All Android licenses accepted. 51 | ``` 52 | 推荐安装 2.10.0 版本 [flutter](https://storage.flutter-io.cn/flutter_infra_release/releases/stable/windows/flutter_windows_2.10.0-stable.zip) 53 | ### Flutter 设定镜像配置环境变量 54 | ```shell 55 | set PUB_HOSTED_URL=https://pub.flutter-io.cn 56 | set FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn 57 | ``` 58 | 59 | ## 打包部署命令 60 | ```shell 61 | flutter build apk --obfuscate --split-debug-info=splitMap --target-platform android-arm,android-arm64,android-x64 --split-per-abi 62 | ``` 63 | ## 打包arm64架构app 64 | ``` 65 | flutter build apk --obfuscate --split-debug-info=splitMap --target-platform android-arm64 66 | ``` 67 | 68 | ### app相关截图如下 69 | ![calculator.png](https://upload-images.jianshu.io/upload_images/20032554-c2a1648a1a6f9a13.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 70 | 71 | ![count_down_home.png](https://upload-images.jianshu.io/upload_images/20032554-f4e79de1d8b68b92.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 72 | 73 | ![count_down_ing.png](https://upload-images.jianshu.io/upload_images/20032554-9aaea95e175e65a8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 74 | 75 | ![edit_creae_note_page.png](https://upload-images.jianshu.io/upload_images/20032554-23bf59b5385d5852.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 76 | 77 | ![home.png](https://upload-images.jianshu.io/upload_images/20032554-7382a36eab4379f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 78 | 79 | ![note_page.png](https://upload-images.jianshu.io/upload_images/20032554-f71aa2c730780b19.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 80 | 81 | ![setting.png](https://upload-images.jianshu.io/upload_images/20032554-34701e3d1497d6c8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 82 | 83 | ![startPage.png](https://upload-images.jianshu.io/upload_images/20032554-7c4be9756b500435.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 84 | 85 | ![stick_todoList.png](https://upload-images.jianshu.io/upload_images/20032554-ce7d45c828dbfb45.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 86 | 87 | ![todoList.png](https://upload-images.jianshu.io/upload_images/20032554-f1430f7df3650799.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 88 | 89 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /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 = '1' 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 plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion flutter.compileSdkVersion 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.example.simple_app" 47 | // minSdkVersion flutter.minSdkVersion 48 | minSdkVersion 18 49 | targetSdkVersion flutter.targetSdkVersion 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | } 62 | 63 | flutter { 64 | source '../..' 65 | } 66 | 67 | dependencies { 68 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 69 | } 70 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 11 | 21 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 39 | 44 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/simple_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.simple_app 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-night-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-night-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-night-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-night-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-night-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-night-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-night/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/drawable/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.0.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /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-7.3-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/15199.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/assets/15199.mp3 -------------------------------------------------------------------------------- /assets/logo_lockup_flutter_vertical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/assets/logo_lockup_flutter_vertical.png -------------------------------------------------------------------------------- /assets/logo_lockup_flutter_vertical_wht.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/assets/logo_lockup_flutter_vertical_wht.png -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | 2 | flutter build web --release --web-renderer auto 3 | 4 | # 创建目标目录 5 | TARGET_DIR="my_webApp" 6 | mkdir -p $TARGET_DIR # 移动生成的文件到目标目录 7 | mv build/web/* $TARGET_DIR -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | description: This file stores settings for Dart & Flutter DevTools. 2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states 3 | extensions: 4 | -------------------------------------------------------------------------------- /images/env/0}5OX~((%WGFCIN19`EL@DA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/images/env/0}5OX~((%WGFCIN19`EL@DA.png -------------------------------------------------------------------------------- /images/env/4~BQ3HG0O7TK11RFP@]$~_W.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/images/env/4~BQ3HG0O7TK11RFP@]$~_W.png -------------------------------------------------------------------------------- /images/env/9J@{U3NEC~7%P)$KG}[ZOHY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/images/env/9J@{U3NEC~7%P)$KG}[ZOHY.png -------------------------------------------------------------------------------- /images/env/JGY{NH0VV[F]XT07XQ$BJO3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/images/env/JGY{NH0VV[F]XT07XQ$BJO3.png -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /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 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /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/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | if #available(iOS 10.0, *) { 12 | UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate 13 | } 14 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/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 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Simple App 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | simple_app 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | NSMicrophoneUsageDescription 47 | This sample uses the microphone to record your speech and convert it to text. 48 | UIBackgroundModes 49 | 50 | audio 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/common/color.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | // 主题色 121,188,154 4 | // const Color themeColor = Color.fromRGBO(121, 188, 157, 1); 5 | // 副主题色 6 | const Color secondThemeColor = Colors.blue; 7 | //0xff79BD98 8 | // app 背景颜色 9 | const Color appBackgroundColor = Color(0xfff7f7f7); 10 | // 搜索框灰色背景颜色 11 | const Color searchBarFillColor = Color(0xffEDEDED); 12 | // 暗黑色 13 | const easyDarkColor = Color.fromRGBO(48, 48, 48, 0.3); 14 | //暗黑色 15 | const darkColor = Color(0xFF3E3E3E); 16 | -------------------------------------------------------------------------------- /lib/common/global.dart: -------------------------------------------------------------------------------- 1 | class ConstantKey { 2 | static String get localeKey => 'localeKey'; 3 | static String get isNightMode => 'isNightMode'; 4 | static String get todoListKey => 'todoListKey'; 5 | } 6 | -------------------------------------------------------------------------------- /lib/components/base/base_animated_opacity.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | baseAnimatedOpacity({required bool value, required Widget child}) { 4 | return AnimatedOpacity( 5 | opacity: value ? 1 : 0, 6 | duration: const Duration(milliseconds: 500), 7 | child: child); 8 | } 9 | -------------------------------------------------------------------------------- /lib/components/base/base_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | import 'package:simple_app/main.dart'; 4 | import 'package:simple_app/provider/current_theme.dart'; 5 | 6 | Widget baseIcon(IconData icon, {Color? color}) { 7 | return Icon( 8 | icon, 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/base/base_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | 4 | // 16号字体 5 | Text baseText(String data, {Color? color, num fontSize = 16}) { 6 | return Text( 7 | data, 8 | style: TextStyle( 9 | fontSize: ScreenUtil().setSp(fontSize), color: color, height: 1.5), 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /lib/components/base/build_base_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import "package:provider/provider.dart"; 5 | import 'package:simple_app/main.dart'; 6 | import 'package:simple_app/provider/current_theme.dart'; 7 | 8 | // 基础Appbar 9 | AppBar buildBaseAppBar( 10 | {String? title, 11 | Widget? leading, 12 | List? action, 13 | Widget? titleWidget, 14 | Color? backgroundColor}) { 15 | return AppBar( 16 | toolbarHeight: ScreenUtil().setSp(55), 17 | leading: leading, 18 | actions: action, 19 | systemOverlayStyle: SystemUiOverlayStyle( 20 | // Status bar color 21 | statusBarColor: Colors.transparent, 22 | ), 23 | title: titleWidget ?? 24 | Text( 25 | title!, 26 | style: TextStyle(fontSize: ScreenUtil().setSp(18)), 27 | ), 28 | centerTitle: true, 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /lib/components/base/draggable_floating_action_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | // ignore: must_be_immutable 5 | class DragAbleFloatingActionButton extends StatefulWidget { 6 | //子控件 7 | Widget child; 8 | GlobalKey parentKey; 9 | DragAbleFloatingActionButton( 10 | {Key? key, required this.child, required this.parentKey}) 11 | : super(key: key); 12 | 13 | @override 14 | State createState() => 15 | _DragAbleFloatingActionButtonState(); 16 | } 17 | 18 | class _DragAbleFloatingActionButtonState 19 | extends State { 20 | Offset position = const Offset(0, 0); 21 | double? maxHeight; 22 | double? maxWidth; 23 | final GlobalKey _key = GlobalKey(); 24 | 25 | Offset? _minOffset; 26 | Offset? _maxOffset; 27 | void _updatePosition(PointerMoveEvent pointerMoveEvent) { 28 | double newOffsetX = position.dx + pointerMoveEvent.delta.dx; 29 | double newOffsetY = position.dy + pointerMoveEvent.delta.dy; 30 | // 边界限定 31 | if (newOffsetX < _minOffset!.dx) { 32 | newOffsetX = _minOffset!.dx; 33 | } else if (newOffsetX > _maxOffset!.dx) { 34 | newOffsetX = _maxOffset!.dx; 35 | } 36 | 37 | if (newOffsetY < _minOffset!.dy) { 38 | newOffsetY = _minOffset!.dy; 39 | } else if (newOffsetY > _maxOffset!.dy) { 40 | newOffsetY = _maxOffset!.dy; 41 | } 42 | setState(() { 43 | position = Offset(newOffsetX, newOffsetY); 44 | }); 45 | } 46 | 47 | @override 48 | void initState() { 49 | super.initState(); 50 | WidgetsBinding.instance?.addPostFrameCallback(_setBoundary); 51 | } 52 | 53 | void _setBoundary(_) { 54 | final RenderBox renderBox = 55 | _key.currentContext?.findRenderObject() as RenderBox; 56 | try { 57 | final Size size = renderBox.size; 58 | setState(() { 59 | _minOffset = const Offset(30, 30); 60 | _maxOffset = Offset(maxWidth! - size.width, maxHeight! - size.height); 61 | }); 62 | } catch (e) { 63 | if (kDebugMode) { 64 | print('catch: $e'); 65 | } 66 | } 67 | } 68 | 69 | @override 70 | Widget build(BuildContext context) { 71 | if (position.dx == 0 && position.dy == 0) { 72 | maxHeight = MediaQuery.of(context).size.height; 73 | maxWidth = MediaQuery.of(context).size.width; 74 | position = Offset(maxWidth! - 56, maxHeight! - 56); 75 | } 76 | 77 | return Stack( 78 | children: [ 79 | Positioned( 80 | top: position.dy, 81 | left: position.dx, 82 | child: Listener( 83 | onPointerMove: (PointerMoveEvent pointerMoveEvent) { 84 | _updatePosition(pointerMoveEvent); 85 | }, 86 | child: Container( 87 | child: widget.child, 88 | key: _key, 89 | ), 90 | ), 91 | ) 92 | ], 93 | ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/components/base/hide_key_bord.dart: -------------------------------------------------------------------------------- 1 | // 因为不需要保持状态,所以这里继承的是StatelessWidget 2 | import 'package:flutter/material.dart'; 3 | 4 | class HideKeyboard extends StatelessWidget { 5 | final Widget child; 6 | 7 | const HideKeyboard({Key? key, required this.child}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return GestureDetector( 12 | child: child, 13 | onTap: () { 14 | FocusScopeNode currentFocus = FocusScope.of(context); 15 | if (!currentFocus.hasPrimaryFocus && 16 | currentFocus.focusedChild != null) { 17 | /// 取消焦点,相当于关闭键盘 18 | FocusManager.instance.primaryFocus?.unfocus(); 19 | } 20 | }, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/components/base/loading.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import "package:flutter/material.dart"; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | 5 | class Loading extends StatelessWidget { 6 | const Loading({Key? key}) : super(key: key); 7 | @override 8 | Widget build(BuildContext context) { 9 | return Center( 10 | child: SizedBox( 11 | width: ScreenUtil().setWidth(45), 12 | height: ScreenUtil().setHeight(45), 13 | child: // 模糊进度条(会执行一个旋转动画) 14 | const CupertinoActivityIndicator( 15 | color: Colors.blue, 16 | ), 17 | ), 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/base/tabs.dart: -------------------------------------------------------------------------------- 1 | import 'package:back_button_interceptor/back_button_interceptor.dart'; 2 | import "package:flutter/cupertino.dart"; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | import 'package:simple_app/common/color.dart'; 6 | import 'package:simple_app/common/global.dart'; 7 | import 'package:simple_app/generated/l10n.dart'; 8 | import 'package:simple_app/page/setting_page.dart'; 9 | import 'package:simple_app/page/tool_kit_page.dart'; 10 | import 'package:simple_app/utils/Notification.dart'; 11 | import 'package:simple_app/utils/index.dart'; 12 | import 'package:simple_app/utils/show_toast.dart'; 13 | 14 | class Tabs extends StatefulWidget { 15 | const Tabs({Key? key}) : super(key: key); 16 | 17 | @override 18 | _TabsState createState() => _TabsState(); 19 | } 20 | 21 | class _TabsState extends State { 22 | final tabsList = [ 23 | const ToolKitPage(), 24 | const SettingPage(), 25 | ]; 26 | // 27 | int currentIndex = 0; 28 | late Widget currentPage; 29 | DateTime? _lastQuitTime; 30 | @override 31 | void initState() { 32 | currentPage = tabsList[currentIndex]; 33 | super.initState(); 34 | BackButtonInterceptor.add(myInterceptor, name: '/', context: context); 35 | // 获取正在进行中的任务列表 36 | getLocalStorageData(ConstantKey.todoListKey).then((res) { 37 | List underwayList = filterListData(res, false); 38 | if (underwayList.isNotEmpty) { 39 | showNotification( 40 | message: S.of(context).todoNotCompleteMessage, 41 | payload: '/todo_list_page'); 42 | } 43 | }); 44 | } 45 | 46 | bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) { 47 | if (info.ifRouteChanged(context)) { 48 | return false; 49 | } 50 | if (_lastQuitTime == null || 51 | DateTime.now().difference(_lastQuitTime!).inSeconds > 1) { 52 | _lastQuitTime = DateTime.now(); 53 | showToast(S.of(context).tryAgainExitApp); 54 | return true; 55 | } else { 56 | return false; 57 | } 58 | } 59 | 60 | @override 61 | void dispose() { 62 | super.dispose(); 63 | BackButtonInterceptor.remove(myInterceptor); 64 | } 65 | 66 | @override 67 | Widget build(BuildContext context) { 68 | final List bottomTabsList = [ 69 | BottomNavigationBarItem( 70 | icon: const Icon( 71 | Icons.toc_outlined, 72 | ), 73 | label: S.of(context).toolKit), 74 | BottomNavigationBarItem( 75 | icon: const Icon( 76 | CupertinoIcons.settings, 77 | ), 78 | label: S.of(context).setting), 79 | ]; 80 | 81 | return Scaffold( 82 | backgroundColor: const Color.fromRGBO(244, 245, 245, 0.5), 83 | bottomNavigationBar: BottomNavigationBar( 84 | type: BottomNavigationBarType.fixed, 85 | currentIndex: currentIndex, 86 | items: bottomTabsList, 87 | onTap: (index) { 88 | setState(() { 89 | currentIndex = index; 90 | currentPage = tabsList[currentIndex]; 91 | }); 92 | }, 93 | ), 94 | body: currentPage, 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/components/search_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:simple_app/common/color.dart'; 4 | 5 | class MySearchBar extends StatefulWidget { 6 | final TextEditingController _todoController; 7 | final void Function(String) addConfirm; 8 | final TextInputAction textInputAction; 9 | final String placeHolder; 10 | final Color fillColor; 11 | final Icon prefixIcon; 12 | final int inputWidth; 13 | const MySearchBar(this._todoController, this.addConfirm, this.textInputAction, 14 | this.placeHolder, 15 | {Key? key, 16 | required this.prefixIcon, 17 | this.fillColor = Colors.white, 18 | this.inputWidth = 250}) 19 | : super(key: key); 20 | 21 | @override 22 | MySearchBarState createState() => MySearchBarState(); 23 | } 24 | 25 | class MySearchBarState extends State { 26 | final FocusNode textFieldFocusNode = FocusNode(); 27 | @override 28 | void initState() { 29 | super.initState(); 30 | } 31 | 32 | @override 33 | void didChangeDependencies() { 34 | super.didChangeDependencies(); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return ConstrainedBox( 40 | constraints: BoxConstraints( 41 | maxHeight: ScreenUtil().setSp(40), 42 | maxWidth: ScreenUtil().setSp(widget.inputWidth)), 43 | child: TextField( 44 | controller: widget._todoController, 45 | textInputAction: widget.textInputAction, 46 | onSubmitted: widget.addConfirm, 47 | focusNode: textFieldFocusNode, 48 | style: 49 | TextStyle(color: Colors.black87, fontSize: ScreenUtil().setSp(15)), 50 | decoration: InputDecoration( 51 | contentPadding: const EdgeInsets.symmetric(vertical: 4.0), 52 | hintText: widget.placeHolder, 53 | 54 | hintStyle: TextStyle( 55 | fontSize: ScreenUtil().setSp(15), color: Colors.black26), 56 | prefixIcon: widget.prefixIcon, 57 | // 设置右边图标 58 | suffixIcon: IconButton( 59 | enableFeedback: false, 60 | icon: const Icon(Icons.clear), 61 | iconSize: ScreenUtil().setSp(20), 62 | splashColor: Colors.transparent, 63 | color: Theme.of(context).primaryColor, 64 | onPressed: () { 65 | widget._todoController.text = ""; 66 | textFieldFocusNode.unfocus(); 67 | }), 68 | border: const OutlineInputBorder(borderSide: BorderSide.none), 69 | filled: true, 70 | fillColor: widget.fillColor, 71 | ), 72 | ), 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/components/todoList/build_todo_list_title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:simple_app/components/base/base_icon.dart'; 4 | 5 | Widget buildTodoListTitle(String title, int count, 6 | {void Function()? onTap, 7 | required bool isSpread, 8 | required Color backgroundColor}) { 9 | return Container( 10 | color: backgroundColor, 11 | padding: EdgeInsets.all(ScreenUtil().setHeight(10)), 12 | child: InkWell( 13 | onTap: onTap, 14 | child: Row( 15 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 16 | children: [ 17 | Row( 18 | crossAxisAlignment: CrossAxisAlignment.center, 19 | children: [ 20 | Text( 21 | title, 22 | style: TextStyle( 23 | fontWeight: FontWeight.bold, 24 | color: Colors.white, 25 | fontSize: ScreenUtil().setSp(20)), 26 | ), 27 | AnimatedRotation( 28 | turns: isSpread ? 0.5 : 0, 29 | curve: Curves.easeInExpo, 30 | duration: const Duration(milliseconds: 250), 31 | child: baseIcon(Icons.keyboard_arrow_up_sharp,color: Colors.white), 32 | ) 33 | ], 34 | ), 35 | ClipOval( 36 | child: Container( 37 | width: ScreenUtil().setHeight(20), 38 | height: ScreenUtil().setHeight(20), 39 | color: const Color(0xffE6E6FA), 40 | child: Center( 41 | child: Text( 42 | count.toString(), 43 | textAlign: TextAlign.center, 44 | style: TextStyle( 45 | fontSize: ScreenUtil().setSp(10), 46 | color: const Color(0xFF666666)), 47 | ), 48 | ))), 49 | ], 50 | ), 51 | ), 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /lib/data/index.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io' if (dart.library.html) 'dart:html'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:path/path.dart' as p; 4 | import 'package:path_provider/path_provider.dart'; 5 | import 'package:sembast_web/sembast_web.dart'; 6 | import 'package:simple_app/model/note.dart'; 7 | import 'package:sembast/sembast.dart'; 8 | // import 'package:sembast/sembast_io.dart' 9 | // if (dart.library.html) 'package:sembast_web/sembast_web.dart'; 10 | import 'package:sembast/sembast_io.dart'; 11 | 12 | // if (dart.library.html) 'package:sembast_web/sembast_web.dart'; 13 | class DBProvider { 14 | static final DBProvider _singleton = DBProvider._internal(); 15 | 16 | factory DBProvider() { 17 | return _singleton; 18 | } 19 | 20 | DBProvider._internal(); 21 | 22 | Database? _dbInstance; 23 | final _store = intMapStoreFactory.store('NoteList'); 24 | 25 | // 获取db 实例对象 操作数据库 26 | Future get db async { 27 | if (_dbInstance != null) { 28 | return _dbInstance!; 29 | } 30 | _dbInstance = await _initDB(); 31 | return _dbInstance!; 32 | } 33 | 34 | // 初始化数据库 35 | Future _initDB() async { 36 | DatabaseFactory dbFactory; 37 | String dbPath; 38 | if (!kIsWeb) { 39 | var dir = await getApplicationDocumentsDirectory(); 40 | await dir.create(recursive: true); 41 | dbPath = p.join(dir.path, 'NoteList.db'); 42 | dbFactory = databaseFactoryIo; 43 | } else { 44 | dbPath = 'NoteList.db'; 45 | dbFactory = databaseFactoryWeb; 46 | } 47 | return await dbFactory.openDatabase(dbPath); 48 | } 49 | 50 | // 保存储存数据 51 | Future saveData(Note note) async { 52 | final db = await this.db; 53 | return await _store.add(db, note.toJson()); 54 | } 55 | 56 | // 查询所有数据 57 | Future> findAll() async { 58 | final db = await this.db; 59 | final records = await _store.find(db); 60 | return records.map((record) { 61 | return Note.fromJson(record.value, record.key); 62 | }).toList(); 63 | } 64 | 65 | // 根据id 查询数据 66 | Future findNoteById(int id) async { 67 | final db = await this.db; 68 | final record = await _store.record(id).get(db); 69 | if (record != null) { 70 | return Note.fromJson(record, id); 71 | } 72 | return null; 73 | } 74 | 75 | // 根据 note id 更新数据 76 | Future update(Note note) async { 77 | final db = await this.db; 78 | final finder = Finder(filter: Filter.byKey(note.id)); 79 | return await _store.update(db, note.toJson(), finder: finder); 80 | } 81 | 82 | // 根据 note id 删除数据 83 | Future deleteData(int id) async { 84 | final db = await this.db; 85 | final finder = Finder(filter: Filter.byKey(id)); 86 | return await _store.delete(db, finder: finder); 87 | } 88 | 89 | // 根据 note id 批量删除数据 90 | Future deleteByIds(List ids) async { 91 | final db = await this.db; 92 | final finder = Finder(filter: Filter.inList('id', ids)); 93 | return await _store.delete(db, finder: finder); 94 | } 95 | 96 | // 根据标题 模糊查询 97 | Future> findTitleNoteList(String title) async { 98 | final db = await this.db; 99 | final finder = Finder( 100 | filter: Filter.or([ 101 | Filter.matches('title', title), 102 | Filter.matches('content', title), 103 | ]), 104 | ); 105 | final records = await _store.find(db, finder: finder); 106 | return records.map((record) { 107 | return Note.fromJson(record.value, record.key); 108 | }).toList(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_all.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that looks up messages for specific locales by 3 | // delegating to the appropriate library. 4 | 5 | // Ignore issues from commonly used lints in this file. 6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new 7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering 8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment 9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases 10 | // ignore_for_file:comment_references 11 | 12 | import 'dart:async'; 13 | 14 | import 'package:flutter/foundation.dart'; 15 | import 'package:intl/intl.dart'; 16 | import 'package:intl/message_lookup_by_library.dart'; 17 | import 'package:intl/src/intl_helpers.dart'; 18 | 19 | import 'messages_en.dart' as messages_en; 20 | import 'messages_zh_CN.dart' as messages_zh_cn; 21 | 22 | typedef Future LibraryLoader(); 23 | Map _deferredLibraries = { 24 | 'en': () => new SynchronousFuture(null), 25 | 'zh_CN': () => new SynchronousFuture(null), 26 | }; 27 | 28 | MessageLookupByLibrary? _findExact(String localeName) { 29 | switch (localeName) { 30 | case 'en': 31 | return messages_en.messages; 32 | case 'zh_CN': 33 | return messages_zh_cn.messages; 34 | default: 35 | return null; 36 | } 37 | } 38 | 39 | /// User programs should call this before using [localeName] for messages. 40 | Future initializeMessages(String localeName) { 41 | var availableLocale = Intl.verifiedLocale( 42 | localeName, (locale) => _deferredLibraries[locale] != null, 43 | onFailure: (_) => null); 44 | if (availableLocale == null) { 45 | return new SynchronousFuture(false); 46 | } 47 | var lib = _deferredLibraries[availableLocale]; 48 | lib == null ? new SynchronousFuture(false) : lib(); 49 | initializeInternalMessageLookup(() => new CompositeMessageLookup()); 50 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); 51 | return new SynchronousFuture(true); 52 | } 53 | 54 | bool _messagesExistFor(String locale) { 55 | try { 56 | return _findExact(locale) != null; 57 | } catch (e) { 58 | return false; 59 | } 60 | } 61 | 62 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { 63 | var actualLocale = 64 | Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); 65 | if (actualLocale == null) return null; 66 | return _findExact(actualLocale); 67 | } 68 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_en.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a en locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'en'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "NoteNotNullMessage": 26 | MessageLookupByLibrary.simpleMessage("content cannot be empty!"), 27 | "addTodo": MessageLookupByLibrary.simpleMessage("Add todo"), 28 | "addTodoMessage": 29 | MessageLookupByLibrary.simpleMessage("You added a new todo!"), 30 | "afternoon": MessageLookupByLibrary.simpleMessage("afternoon"), 31 | "beforeDawn": MessageLookupByLibrary.simpleMessage("before dawn"), 32 | "calculator": MessageLookupByLibrary.simpleMessage("calculator"), 33 | "cancel": MessageLookupByLibrary.simpleMessage("cancel"), 34 | "cancelCountDownMessage": MessageLookupByLibrary.simpleMessage( 35 | "The countdown is not over yet. Are you sure you want to exit?"), 36 | "cancelTopping": MessageLookupByLibrary.simpleMessage("Cancel topping"), 37 | "complete": MessageLookupByLibrary.simpleMessage("Have finished"), 38 | "confirm": MessageLookupByLibrary.simpleMessage("confirm"), 39 | "countDown": MessageLookupByLibrary.simpleMessage("Count down"), 40 | "createNote": MessageLookupByLibrary.simpleMessage("Create note"), 41 | "delete": MessageLookupByLibrary.simpleMessage("delete"), 42 | "deleteFail": MessageLookupByLibrary.simpleMessage("Delete failed!"), 43 | "deleteMessage": MessageLookupByLibrary.simpleMessage( 44 | "Are you sure you want to delete?"), 45 | "deleteSuccess": 46 | MessageLookupByLibrary.simpleMessage("Deleted successfully!"), 47 | "deleteTodoMessage": MessageLookupByLibrary.simpleMessage( 48 | "Are you sure to delete the current todo?"), 49 | "dialogDeleteMessage": MessageLookupByLibrary.simpleMessage( 50 | "Are you sure you want to delete?"), 51 | "editorNote": MessageLookupByLibrary.simpleMessage("Editor note"), 52 | "enableAudioMessage": 53 | MessageLookupByLibrary.simpleMessage("Turn on sound?"), 54 | "enableShimmerMessage": 55 | MessageLookupByLibrary.simpleMessage("Enable shimmer background?"), 56 | "evening": MessageLookupByLibrary.simpleMessage("evening"), 57 | "forenoon": MessageLookupByLibrary.simpleMessage("forenoon"), 58 | "hint": MessageLookupByLibrary.simpleMessage("hint"), 59 | "item": MessageLookupByLibrary.simpleMessage("item"), 60 | "lastUpdateTime": 61 | MessageLookupByLibrary.simpleMessage("Last update time"), 62 | "lateNight": MessageLookupByLibrary.simpleMessage("late at night"), 63 | "morning": MessageLookupByLibrary.simpleMessage("morning"), 64 | "night": MessageLookupByLibrary.simpleMessage("night"), 65 | "nightMode": MessageLookupByLibrary.simpleMessage("Night mode"), 66 | "noDevelopment": MessageLookupByLibrary.simpleMessage("No development"), 67 | "noLongerTips": MessageLookupByLibrary.simpleMessage("No longer tips?"), 68 | "noNotificationPermission": MessageLookupByLibrary.simpleMessage( 69 | "No notification permission, go to the settings interface to set the permission?"), 70 | "noSelectNoteMessage": 71 | MessageLookupByLibrary.simpleMessage("Please select data!"), 72 | "noon": MessageLookupByLibrary.simpleMessage("noon"), 73 | "notEmpty": MessageLookupByLibrary.simpleMessage("Can not be empty"), 74 | "notNoteMessage": MessageLookupByLibrary.simpleMessage( 75 | "You haven\'t added a note yet. Please click the button to add a note!"), 76 | "notSearchNoteMessage": MessageLookupByLibrary.simpleMessage( 77 | "Unfortunately, no data was found!"), 78 | "note": MessageLookupByLibrary.simpleMessage("note"), 79 | "notificationDescription": 80 | MessageLookupByLibrary.simpleMessage("For a better day!"), 81 | "notificationName": 82 | MessageLookupByLibrary.simpleMessage("important notice"), 83 | "outCalculationRange": 84 | MessageLookupByLibrary.simpleMessage("Out of calculation range!"), 85 | "placeSearchContent": MessageLookupByLibrary.simpleMessage( 86 | "Please enter the search content!"), 87 | "saveFail": MessageLookupByLibrary.simpleMessage("Saved failed"), 88 | "saveSuccess": 89 | MessageLookupByLibrary.simpleMessage("Saved successfully"), 90 | "searchNote": MessageLookupByLibrary.simpleMessage("Search note"), 91 | "selected": MessageLookupByLibrary.simpleMessage("Selected"), 92 | "setting": MessageLookupByLibrary.simpleMessage("setting"), 93 | "startTiming": MessageLookupByLibrary.simpleMessage("Start timing"), 94 | "switchLanguage": 95 | MessageLookupByLibrary.simpleMessage("Switch language to english"), 96 | "timeOut": MessageLookupByLibrary.simpleMessage("Time out!"), 97 | "tipSaveMessage": MessageLookupByLibrary.simpleMessage( 98 | "You haven\'t saved it yet? Save?"), 99 | "title": MessageLookupByLibrary.simpleMessage("title"), 100 | "todoCompleteMessage": MessageLookupByLibrary.simpleMessage( 101 | "Today\'s todo has been completed, please make persistent efforts."), 102 | "todoList": MessageLookupByLibrary.simpleMessage("Todo list"), 103 | "todoNotCompleteMessage": MessageLookupByLibrary.simpleMessage( 104 | "Today\'s agency has not been completed yet. Please remember to complete it on time"), 105 | "todoNoticeTitle": 106 | MessageLookupByLibrary.simpleMessage("This is an announcement."), 107 | "toolKit": MessageLookupByLibrary.simpleMessage("Tool kit"), 108 | "topping": MessageLookupByLibrary.simpleMessage("topping"), 109 | "translate": MessageLookupByLibrary.simpleMessage("translate"), 110 | "tryAgainExitApp": 111 | MessageLookupByLibrary.simpleMessage("Press again to exit the App"), 112 | "underway": MessageLookupByLibrary.simpleMessage("underway"), 113 | "updateFail": MessageLookupByLibrary.simpleMessage("Update failed"), 114 | "updateSuccess": 115 | MessageLookupByLibrary.simpleMessage("Update successful"), 116 | "weatherQuery": MessageLookupByLibrary.simpleMessage("Weather query") 117 | }; 118 | } 119 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_zh_CN.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a zh_CN locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'zh_CN'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "NoteNotNullMessage": MessageLookupByLibrary.simpleMessage("内容不能为空!"), 26 | "addTodo": MessageLookupByLibrary.simpleMessage("添加代办事项"), 27 | "addTodoMessage": MessageLookupByLibrary.simpleMessage("您添加了一条新代办事项!"), 28 | "afternoon": MessageLookupByLibrary.simpleMessage("下午"), 29 | "beforeDawn": MessageLookupByLibrary.simpleMessage("凌晨"), 30 | "calculator": MessageLookupByLibrary.simpleMessage("计算器"), 31 | "cancel": MessageLookupByLibrary.simpleMessage("取消"), 32 | "cancelCountDownMessage": 33 | MessageLookupByLibrary.simpleMessage("倒计时还未结束,确定要退出吗?"), 34 | "cancelTopping": MessageLookupByLibrary.simpleMessage("取消置顶"), 35 | "complete": MessageLookupByLibrary.simpleMessage("已经完成"), 36 | "confirm": MessageLookupByLibrary.simpleMessage("确认"), 37 | "countDown": MessageLookupByLibrary.simpleMessage("倒计时"), 38 | "createNote": MessageLookupByLibrary.simpleMessage("新建便签"), 39 | "delete": MessageLookupByLibrary.simpleMessage("删除"), 40 | "deleteFail": MessageLookupByLibrary.simpleMessage("删除失败"), 41 | "deleteMessage": MessageLookupByLibrary.simpleMessage("你确定要删除吗?"), 42 | "deleteSuccess": MessageLookupByLibrary.simpleMessage("删除成功!"), 43 | "deleteTodoMessage": 44 | MessageLookupByLibrary.simpleMessage("确定删除当前代办事项吗?"), 45 | "dialogDeleteMessage": MessageLookupByLibrary.simpleMessage("你确定要删除吗?"), 46 | "editorNote": MessageLookupByLibrary.simpleMessage("编辑便签"), 47 | "enableAudioMessage": MessageLookupByLibrary.simpleMessage("是否开启声音?"), 48 | "enableShimmerMessage": 49 | MessageLookupByLibrary.simpleMessage("是否开启微光背景?"), 50 | "evening": MessageLookupByLibrary.simpleMessage("傍晚"), 51 | "forenoon": MessageLookupByLibrary.simpleMessage("上午"), 52 | "hint": MessageLookupByLibrary.simpleMessage("提示"), 53 | "item": MessageLookupByLibrary.simpleMessage("项"), 54 | "lastUpdateTime": MessageLookupByLibrary.simpleMessage("最后更新时间"), 55 | "lateNight": MessageLookupByLibrary.simpleMessage("深夜"), 56 | "morning": MessageLookupByLibrary.simpleMessage("早晨"), 57 | "night": MessageLookupByLibrary.simpleMessage("晚上"), 58 | "nightMode": MessageLookupByLibrary.simpleMessage("夜间模式"), 59 | "noDevelopment": MessageLookupByLibrary.simpleMessage("暂未开发"), 60 | "noLongerTips": MessageLookupByLibrary.simpleMessage("不再提示?"), 61 | "noNotificationPermission": 62 | MessageLookupByLibrary.simpleMessage("没有通知权限, 前往设置界面设置权限?"), 63 | "noSelectNoteMessage": MessageLookupByLibrary.simpleMessage("请选择数据"), 64 | "noon": MessageLookupByLibrary.simpleMessage("中午"), 65 | "notEmpty": MessageLookupByLibrary.simpleMessage("不能为空!"), 66 | "notNoteMessage": 67 | MessageLookupByLibrary.simpleMessage("你还未添加添加便签,请点击按钮添加便签吧!"), 68 | "notSearchNoteMessage": 69 | MessageLookupByLibrary.simpleMessage("很遗憾,没有搜索到数据!"), 70 | "note": MessageLookupByLibrary.simpleMessage("便签"), 71 | "notificationDescription": 72 | MessageLookupByLibrary.simpleMessage("为了美好的每一天!"), 73 | "notificationName": MessageLookupByLibrary.simpleMessage("重要通知"), 74 | "outCalculationRange": MessageLookupByLibrary.simpleMessage("超出计算范围!"), 75 | "placeSearchContent": MessageLookupByLibrary.simpleMessage("请输入搜索内容!"), 76 | "saveFail": MessageLookupByLibrary.simpleMessage("保存失败"), 77 | "saveSuccess": MessageLookupByLibrary.simpleMessage("保存成功"), 78 | "searchNote": MessageLookupByLibrary.simpleMessage("搜索便签"), 79 | "selected": MessageLookupByLibrary.simpleMessage("已选择"), 80 | "setting": MessageLookupByLibrary.simpleMessage("设置"), 81 | "startTiming": MessageLookupByLibrary.simpleMessage("开始计时"), 82 | "switchLanguage": MessageLookupByLibrary.simpleMessage("切换语言为英语"), 83 | "timeOut": MessageLookupByLibrary.simpleMessage("时间到!"), 84 | "tipSaveMessage": MessageLookupByLibrary.simpleMessage("你还未保存?是否保存?"), 85 | "title": MessageLookupByLibrary.simpleMessage("标题"), 86 | "todoCompleteMessage": 87 | MessageLookupByLibrary.simpleMessage("今日的代办事项已经完成, 请再接再厉!"), 88 | "todoList": MessageLookupByLibrary.simpleMessage("待办事项列表"), 89 | "todoNotCompleteMessage": 90 | MessageLookupByLibrary.simpleMessage("今天的代办事项还未完成哦,请记得按时完成哦"), 91 | "todoNoticeTitle": MessageLookupByLibrary.simpleMessage("这是一条通知"), 92 | "toolKit": MessageLookupByLibrary.simpleMessage("工具箱"), 93 | "topping": MessageLookupByLibrary.simpleMessage("置顶"), 94 | "translate": MessageLookupByLibrary.simpleMessage("翻译"), 95 | "tryAgainExitApp": MessageLookupByLibrary.simpleMessage("再按一次退出App"), 96 | "underway": MessageLookupByLibrary.simpleMessage("正在进行"), 97 | "updateFail": MessageLookupByLibrary.simpleMessage("更新失败"), 98 | "updateSuccess": MessageLookupByLibrary.simpleMessage("更新成功"), 99 | "weatherQuery": MessageLookupByLibrary.simpleMessage("天气查询") 100 | }; 101 | } 102 | -------------------------------------------------------------------------------- /lib/l10n/intl_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "toolKit": "Tool kit", 3 | "setting": "setting", 4 | "translate": "translate", 5 | "weatherQuery": "Weather query", 6 | "note": "note", 7 | "calculator": "calculator", 8 | "todoList": "Todo list", 9 | "nightMode": "Night mode", 10 | "switchLanguage": "Switch language to english", 11 | "hint": "hint", 12 | "dialogDeleteMessage": "Are you sure you want to delete?", 13 | "cancel": "cancel", 14 | "delete": "delete", 15 | "confirm": "confirm", 16 | "addTodo": "Add todo", 17 | "addTodoMessage": "You added a new todo!", 18 | "todoNoticeTitle": "This is an announcement.", 19 | "todoCompleteMessage": "Today's todo has been completed, please make persistent efforts.", 20 | "todoNotCompleteMessage": "Today's agency has not been completed yet. Please remember to complete it on time", 21 | "noNotificationPermission": "No notification permission, go to the settings interface to set the permission?", 22 | "notificationName": "important notice", 23 | "notificationDescription": "For a better day!", 24 | "underway": "underway", 25 | "deleteTodoMessage": "Are you sure to delete the current todo?", 26 | "complete": "Have finished", 27 | "notEmpty": "Can not be empty", 28 | "tryAgainExitApp": "Press again to exit the App", 29 | "noDevelopment": "No development", 30 | "searchNote": "Search note", 31 | "editorNote": "Editor note", 32 | "createNote": "Create note", 33 | "title": "title", 34 | "notNoteMessage": "You haven't added a note yet. Please click the button to add a note!", 35 | "notSearchNoteMessage": "Unfortunately, no data was found!", 36 | "NoteNotNullMessage": "content cannot be empty!", 37 | "updateSuccess": "Update successful", 38 | "updateFail": "Update failed", 39 | "saveSuccess": "Saved successfully", 40 | "saveFail": "Saved failed", 41 | "placeSearchContent": "Please enter the search content!", 42 | "selected": "Selected", 43 | "item": "item", 44 | "deleteSuccess": "Deleted successfully!", 45 | "deleteFail": "Delete failed!", 46 | "deleteMessage": "Are you sure you want to delete?", 47 | "topping": "topping", 48 | "cancelTopping": "Cancel topping", 49 | "noSelectNoteMessage": "Please select data!", 50 | "outCalculationRange": "Out of calculation range!", 51 | "countDown": "Count down", 52 | "startTiming": "Start timing", 53 | "timeOut": "Time out!", 54 | "cancelCountDownMessage": "The countdown is not over yet. Are you sure you want to exit?", 55 | "enableShimmerMessage": "Enable shimmer background?", 56 | "enableAudioMessage": "Turn on sound?", 57 | "lastUpdateTime": "Last update time", 58 | "beforeDawn": "before dawn", 59 | "morning": "morning", 60 | "forenoon": "forenoon", 61 | "noon": "noon", 62 | "afternoon": "afternoon", 63 | "evening": "evening", 64 | "night": "night", 65 | "lateNight": "late at night", 66 | "tipSaveMessage": "You haven't saved it yet? Save?", 67 | "noLongerTips": "No longer tips?" 68 | } -------------------------------------------------------------------------------- /lib/l10n/intl_zh_CN.arb: -------------------------------------------------------------------------------- 1 | { 2 | "toolKit": "工具箱", 3 | "setting": "设置", 4 | "translate": "翻译", 5 | "weatherQuery": "天气查询", 6 | "note": "便签", 7 | "calculator": "计算器", 8 | "todoList": "待办事项列表", 9 | "nightMode": "夜间模式", 10 | "switchLanguage": "切换语言为英语", 11 | "hint": "提示", 12 | "dialogDeleteMessage": "你确定要删除吗?", 13 | "cancel": "取消", 14 | "delete": "删除", 15 | "confirm": "确认", 16 | "addTodo": "添加代办事项", 17 | "addTodoMessage": "您添加了一条新代办事项!", 18 | "todoNoticeTitle": "这是一条通知", 19 | "todoCompleteMessage": "今日的代办事项已经完成, 请再接再厉!", 20 | "todoNotCompleteMessage": "今天的代办事项还未完成哦,请记得按时完成哦", 21 | "noNotificationPermission": "没有通知权限, 前往设置界面设置权限?", 22 | "notificationName": "重要通知", 23 | "notificationDescription": "为了美好的每一天!", 24 | "underway": "正在进行", 25 | "deleteTodoMessage": "确定删除当前代办事项吗?", 26 | "complete": "已经完成", 27 | "notEmpty": "不能为空!", 28 | "tryAgainExitApp": "再按一次退出App", 29 | "noDevelopment": "暂未开发", 30 | "searchNote": "搜索便签", 31 | "editorNote": "编辑便签", 32 | "createNote": "新建便签", 33 | "title": "标题", 34 | "notNoteMessage": "你还未添加添加便签,请点击按钮添加便签吧!", 35 | "notSearchNoteMessage": "很遗憾,没有搜索到数据!", 36 | "NoteNotNullMessage": "内容不能为空!", 37 | "updateSuccess": "更新成功", 38 | "updateFail": "更新失败", 39 | "saveSuccess": "保存成功", 40 | "saveFail": "保存失败", 41 | "placeSearchContent": "请输入搜索内容!", 42 | "selected": "已选择", 43 | "item": "项", 44 | "deleteSuccess": "删除成功!", 45 | "deleteFail": "删除失败", 46 | "deleteMessage": "你确定要删除吗?", 47 | "topping": "置顶", 48 | "cancelTopping": "取消置顶", 49 | "noSelectNoteMessage": "请选择数据", 50 | "outCalculationRange": "超出计算范围!", 51 | "countDown": "倒计时", 52 | "startTiming": "开始计时", 53 | "timeOut": "时间到!", 54 | "cancelCountDownMessage": "倒计时还未结束,确定要退出吗?", 55 | "enableShimmerMessage": "是否开启微光背景?", 56 | "enableAudioMessage": "是否开启声音?", 57 | "lastUpdateTime": "最后更新时间", 58 | "beforeDawn": "凌晨", 59 | "morning": "早晨", 60 | "forenoon": "上午", 61 | "noon": "中午", 62 | "afternoon": "下午", 63 | "evening": "傍晚", 64 | "night": "晚上", 65 | "lateNight": "深夜", 66 | "tipSaveMessage": "你还未保存?是否保存?", 67 | "noLongerTips": "不再提示?" 68 | } 69 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_localizations/flutter_localizations.dart'; 4 | import 'package:flutter_quill/translations.dart'; 5 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 6 | import 'package:provider/provider.dart'; 7 | import 'package:shared_preferences/shared_preferences.dart'; 8 | import 'package:simple_app/common/color.dart'; 9 | import 'package:simple_app/common/global.dart'; 10 | import 'package:simple_app/generated/l10n.dart'; 11 | import 'package:simple_app/provider/current_locale.dart'; 12 | import 'package:simple_app/provider/current_theme.dart'; 13 | import 'package:simple_app/router.dart'; 14 | import 'package:simple_app/utils/Notification.dart'; 15 | 16 | CurrentTheme? nightMode; 17 | CurrentLocale? locale; 18 | 19 | late String? strLocale; 20 | final Future _prefs = SharedPreferences.getInstance(); 21 | 22 | void main(List args) { 23 | WidgetsFlutterBinding.ensureInitialized(); 24 | // 读取持久化数据 25 | _prefs.then((prefs) async { 26 | strLocale = prefs.getString(ConstantKey.localeKey); 27 | bool? isNightMode = prefs.getBool(ConstantKey.isNightMode); 28 | // 优先读取持久化数据 29 | if (strLocale != null) { 30 | locale = CurrentLocale(locale: strLocaleToLocale(strLocale!)); 31 | } else { 32 | locale = CurrentLocale(); 33 | } 34 | if (isNightMode != null) { 35 | nightMode = CurrentTheme( 36 | themeMode: isNightMode ? ThemeMode.dark : ThemeMode.light); 37 | } else { 38 | nightMode = CurrentTheme(themeMode: ThemeMode.light); 39 | } 40 | 41 | runApp(MultiProvider( 42 | providers: [ 43 | ChangeNotifierProvider(create: (_) => locale), 44 | ChangeNotifierProvider(create: (_) => nightMode), 45 | ], 46 | child: const MyApp(), 47 | )); 48 | // 初始化本地通知插件 49 | // 是否为web环境 50 | if (!kIsWeb) { 51 | await flutterLocalNotificationsPluginInit(); 52 | } 53 | }); 54 | } 55 | 56 | class MyApp extends StatefulWidget { 57 | const MyApp({Key? key}) : super(key: key); 58 | 59 | @override 60 | State createState() => _MyAppState(); 61 | } 62 | 63 | // 通过navigatorKey的方式 保存全局的context 64 | final GlobalKey navigatorKey = GlobalKey(); 65 | 66 | class _MyAppState extends State { 67 | @override 68 | Widget build(BuildContext context) { 69 | return ScreenUtilInit( 70 | designSize: const Size(375, 750), 71 | minTextAdapt: true, 72 | splitScreenMode: true, 73 | child: MaterialApp( 74 | debugShowCheckedModeBanner: false, 75 | initialRoute: '/', 76 | routes: routes, 77 | title: 'simple_app', 78 | // 国际化 79 | localizationsDelegates: const [ 80 | GlobalMaterialLocalizations.delegate, 81 | GlobalWidgetsLocalizations.delegate, 82 | GlobalCupertinoLocalizations.delegate, 83 | FlutterQuillLocalizations.delegate, 84 | S.delegate 85 | ], 86 | themeMode: context.watch().value, 87 | theme: ThemeData( 88 | brightness: Brightness.light, 89 | ), 90 | darkTheme: 91 | ThemeData(brightness: Brightness.dark, primaryColor: darkColor), 92 | //应用支持的语言列表 93 | supportedLocales: const [ 94 | Locale('en', 'US'), // English 95 | Locale('zh', 'CN'), // 中文 96 | ], 97 | // 保存全局navigatorkey 98 | navigatorKey: navigatorKey, 99 | // 当前语言 100 | locale: context.watch().value, 101 | localeResolutionCallback: (deviceLocale, supportedLocales) { 102 | _prefs.then((prefs) { 103 | strLocale = prefs.getString(ConstantKey.localeKey); 104 | // 如果本地环境不为中文,而且没有设置过语言,则设置为英文 105 | if (locale == null && 106 | localeToStrLocale(deviceLocale!) != 'zh' && 107 | strLocale == null) { 108 | locale?.setLocale(const Locale('en', 'US')); 109 | } 110 | }); 111 | return null; 112 | }, 113 | ), 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/model/note.dart: -------------------------------------------------------------------------------- 1 | //记录便列表数据类 2 | class Note { 3 | int? id; 4 | // 便签标题 5 | String? title; 6 | // 便签内 7 | late String content; 8 | // 创建时间 9 | late int time; 10 | // 更新时间 11 | late int updateTime; 12 | 13 | Note( 14 | {this.id, 15 | this.title, 16 | required this.content, 17 | required this.time, 18 | required this.updateTime}); 19 | 20 | //将json 序列化为model对象 21 | Note.fromJson(Map json, int value) { 22 | title = json['title']; 23 | content = json['content']; 24 | time = json['time']; 25 | updateTime = json['updateTime']; 26 | id = value; 27 | } 28 | 29 | Map toJson() { 30 | final Map data = {}; 31 | data['id'] = id; 32 | data['title'] = title; 33 | data['content'] = content; 34 | data['time'] = time; 35 | data['updateTime'] = updateTime; 36 | return data; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/page/setting_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:provider/provider.dart'; 4 | import 'package:simple_app/common/color.dart'; 5 | import 'package:simple_app/components/base/base_icon.dart'; 6 | import 'package:simple_app/components/base/base_text.dart'; 7 | import 'package:simple_app/components/base/build_base_app_bar.dart'; 8 | import 'package:simple_app/generated/l10n.dart'; 9 | import 'package:simple_app/provider/current_locale.dart'; 10 | import 'package:simple_app/provider/current_theme.dart'; 11 | 12 | class SettingPage extends StatefulWidget { 13 | const SettingPage({Key? key}) : super(key: key); 14 | 15 | @override 16 | State createState() => _SettingPageState(); 17 | } 18 | 19 | class _SettingPageState extends State { 20 | // 切换当前语言环境 21 | void changeLanguage(bool value) { 22 | if (value) { 23 | context.read().setLocale(const Locale('en', 'US')); 24 | } else { 25 | context.read().setLocale(const Locale('zh', 'CN')); 26 | } 27 | } 28 | 29 | // 切换夜间模式 30 | void changeNightMode(value) { 31 | if (value) { 32 | context.read().changeMode(ThemeMode.dark); 33 | } else { 34 | context.read().changeMode(ThemeMode.light); 35 | } 36 | } 37 | 38 | // final NeumorphicStyle _neumorphicStyle = NeumorphicStyle( 39 | // depth: -4, 40 | // boxShape: NeumorphicBoxShape.roundRect(BorderRadius.circular(20))); 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | Color primaryColor = Theme.of(context).primaryColor; 45 | 46 | return Scaffold( 47 | appBar: buildBaseAppBar( 48 | title: S.of(context).setting, 49 | ), 50 | body: Container( 51 | padding: EdgeInsets.all(ScreenUtil().setWidth(10)), 52 | child: LayoutBuilder( 53 | builder: (BuildContext context, BoxConstraints constraints) { 54 | return SizedBox( 55 | height: constraints.maxHeight, 56 | child: SingleChildScrollView( 57 | physics: const BouncingScrollPhysics( 58 | parent: AlwaysScrollableScrollPhysics()), 59 | child: Column( 60 | children: [ 61 | Padding( 62 | padding: const EdgeInsets.all(10), 63 | child: Row( 64 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 65 | children: [ 66 | Row( 67 | children: [ 68 | baseIcon(Icons.language_rounded, 69 | color: primaryColor), 70 | SizedBox( 71 | width: ScreenUtil().setWidth(10), 72 | ), 73 | baseText( 74 | S.of(context).switchLanguage, 75 | ) 76 | ], 77 | ), 78 | Switch( 79 | // height: ScreenUtil().setHeight(30), 80 | // style: const NeumorphicSwitchStyle( 81 | // activeTrackColor: themeColor), 82 | value: context 83 | .watch() 84 | .languageIsEnglishMode, 85 | onChanged: changeLanguage, 86 | ) 87 | ], 88 | ), 89 | ), 90 | const SizedBox( 91 | height: 10, 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(10), 95 | child: Row( 96 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 97 | children: [ 98 | Row( 99 | children: [ 100 | baseIcon(Icons.mode_night_rounded, 101 | color: primaryColor), 102 | SizedBox( 103 | width: ScreenUtil().setWidth(10), 104 | ), 105 | baseText( 106 | S.of(context).nightMode, 107 | ) 108 | ], 109 | ), 110 | Switch( 111 | // style: const NeumorphicSwitchStyle( 112 | // activeTrackColor: themeColor), 113 | value: context.watch().isNightMode, 114 | onChanged: changeNightMode, 115 | ) 116 | ], 117 | ), 118 | ) 119 | ], 120 | ), 121 | ), 122 | ); 123 | }, 124 | ), 125 | ), 126 | ); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib/page/tool_kit_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:provider/provider.dart'; 5 | import 'package:simple_app/components/base/build_base_app_bar.dart'; 6 | import 'package:simple_app/generated/l10n.dart'; 7 | import 'package:simple_app/page/calculator.dart'; 8 | import 'package:simple_app/page/count_down.dart'; 9 | import 'package:simple_app/page/note_page.dart'; 10 | import 'package:simple_app/page/todo_list_page.dart'; 11 | import 'package:simple_app/provider/current_theme.dart'; 12 | import 'package:simple_app/utils/show_toast.dart'; 13 | 14 | class ToolKitPage extends StatefulWidget { 15 | const ToolKitPage({Key? key}) : super(key: key); 16 | 17 | @override 18 | _ToolKitPageState createState() => _ToolKitPageState(); 19 | } 20 | 21 | class _ToolKitPageState extends State { 22 | void toRouterPage(Map map) { 23 | if (map['RouterPath'].isEmpty) { 24 | showToast(S.of(context).noDevelopment); 25 | } else { 26 | Navigator.push( 27 | context, CupertinoPageRoute(builder: (context) => map['comp'])); 28 | } 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | final List toolList = [ 34 | { 35 | "RouterPath": '/count_down', 36 | "label": S.of(context).countDown, 37 | "comp": const CountDownPage() 38 | }, 39 | { 40 | "RouterPath": '/note_list_page', 41 | "label": S.of(context).note, 42 | "comp": const NotePage() 43 | }, 44 | { 45 | "RouterPath": 'calculator', 46 | "label": S.of(context).calculator, 47 | "comp": const CalculatorPage() 48 | }, 49 | { 50 | "RouterPath": '/todo_list_page', 51 | "label": S.of(context).todoList, 52 | "comp": const TodoListPage() 53 | }, 54 | ]; 55 | 56 | return Scaffold( 57 | appBar: buildBaseAppBar(title: S.of(context).toolKit), 58 | body: LayoutBuilder( 59 | builder: (BuildContext context, BoxConstraints constraints) { 60 | return SizedBox( 61 | height: constraints.maxHeight, 62 | child: SingleChildScrollView( 63 | physics: const BouncingScrollPhysics( 64 | parent: AlwaysScrollableScrollPhysics()), 65 | child: Column( 66 | children: toolList.map((e) { 67 | Color buttonTextColor; 68 | String routerPath = e['RouterPath']; 69 | if (routerPath.isEmpty) { 70 | buttonTextColor = Colors.grey; 71 | } else { 72 | buttonTextColor = 73 | context.watch().isNightMode 74 | ? Colors.white 75 | : Colors.black; 76 | } 77 | return Container( 78 | height: ScreenUtil().setHeight(50), 79 | margin: EdgeInsets.only(top: ScreenUtil().setHeight(35)), 80 | child: Row( 81 | mainAxisAlignment: MainAxisAlignment.center, 82 | children: [ 83 | SizedBox( 84 | width: ScreenUtil().setWidth(250), 85 | child: ElevatedButton( 86 | // style: const NeumorphicStyle( 87 | // shape: NeumorphicShape.concave, 88 | // depth: 4, 89 | // lightSource: LightSource.bottomRight, 90 | // boxShape: NeumorphicBoxShape.stadium()), 91 | onPressed: () => toRouterPage(e), 92 | child: Center( 93 | child: Text( 94 | "${e['label']}", 95 | style: TextStyle(color: buttonTextColor), 96 | ), 97 | )), 98 | ), 99 | ], 100 | ), 101 | ); 102 | }).toList(), 103 | ), 104 | ), 105 | ); 106 | }, 107 | )); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/provider/current_locale.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | import 'package:simple_app/common/global.dart'; 4 | import 'package:simple_app/utils/notification.dart'; 5 | 6 | 7 | 8 | const _defaultLocale = Locale('en', 'US'); 9 | void main(List args) {} 10 | 11 | class CurrentLocale with ChangeNotifier { 12 | // 当前语言环境 13 | Locale locale; 14 | CurrentLocale({this.locale = const Locale('zh', 'CN')}); 15 | Locale get value => locale; 16 | // 语言是否为英语环境 17 | bool get languageIsEnglishMode => locale.languageCode == 'en' ? true : false; 18 | 19 | String get localeType => localeToStrLocale(locale); 20 | 21 | // 初始化加载当前语言 22 | void initLocale({Locale? nativeLocale = _defaultLocale}) async { 23 | if (nativeLocale != null) { 24 | locale = nativeLocale; 25 | // 开启通知 26 | notifyListeners(); 27 | // 异步发起通知 28 | Future.delayed(const Duration(seconds: 2), () { 29 | showNotification(message: 'todo', payload: '/todo_list'); 30 | }); 31 | } 32 | } 33 | 34 | //切换当前语言 并持久化保存 35 | void setLocale(Locale value) async { 36 | locale = value; 37 | final SharedPreferences prefs = await SharedPreferences.getInstance(); 38 | prefs.setString(ConstantKey.localeKey, localeToStrLocale(locale)); 39 | notifyListeners(); 40 | } 41 | } 42 | 43 | // 将持久化存储的locale数据转为locale; 44 | Locale strLocaleToLocale(String strLocale) { 45 | Locale locale; 46 | if (strLocale == 'zh') { 47 | locale = const Locale('zh', 'CN'); 48 | } else { 49 | locale = const Locale('en', 'US'); 50 | } 51 | return locale; 52 | } 53 | 54 | /// 将locale类 转化为 zh或cn字符串方便进行保存 55 | String localeToStrLocale(Locale locale) { 56 | String strLocale; 57 | if (locale.languageCode == 'zh') { 58 | strLocale = 'zh'; 59 | } else { 60 | strLocale = 'en'; 61 | } 62 | return strLocale; 63 | } 64 | -------------------------------------------------------------------------------- /lib/provider/current_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | import 'package:simple_app/common/color.dart'; 5 | import 'package:simple_app/common/global.dart'; 6 | import 'package:simple_app/main.dart'; 7 | 8 | class CurrentTheme with ChangeNotifier { 9 | // 当前语言环境 10 | ThemeMode themeMode; 11 | 12 | ThemeMode get value => themeMode; 13 | 14 | CurrentTheme({this.themeMode = ThemeMode.light}); 15 | // 是否为夜间模式 16 | bool get isNightMode => themeMode == ThemeMode.dark ? true : false; 17 | // Color primaryColor = 18 | // Theme.of(navigatorKey.currentState!.context).primaryColor; 19 | // // 暗色或白色背景色 20 | // Color get themeBackgroundColor => 21 | // isNightMode ? easyDarkColor : const Color.fromRGBO(246, 246, 246, 1.0); 22 | 23 | // // 暗色或主题色 24 | // Color get themeOrDarkColor => isNightMode ? darkColor : primaryColor; 25 | // // 渐变色 26 | // List get gradientColors => isNightMode 27 | // ? [Colors.black12, Colors.black] 28 | // : [const Color.fromRGBO(152, 203, 179, 0.5), primaryColor]; 29 | // //暗色 或 白色 30 | // Color get darkOrWhiteColor => isNightMode ? Colors.white : Colors.black; 31 | 32 | // // 主题色 or 副主题色 33 | // Color get mainThemeOrSecondThemeColor => 34 | // isNightMode ? Colors.blue : primaryColor; 35 | 36 | changeMode(ThemeMode mode) async { 37 | themeMode = mode; 38 | final SharedPreferences prefs = await SharedPreferences.getInstance(); 39 | prefs.setBool(ConstantKey.isNightMode, isNightMode); 40 | SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle( 41 | statusBarColor: Colors.transparent, 42 | // systemNavigationBarColor: themeOrDarkColor, 43 | ); 44 | SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); 45 | notifyListeners(); 46 | } 47 | 48 | // 初始化 49 | void initNightMode({bool? isNightMode}) async { 50 | if (isNightMode != null) { 51 | if (isNightMode) { 52 | themeMode = ThemeMode.dark; 53 | notifyListeners(); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:simple_app/components/base/tabs.dart'; 3 | import 'package:simple_app/page/calculator.dart'; 4 | import 'package:simple_app/page/note_page.dart'; 5 | import 'package:simple_app/page/todo_list_page.dart'; 6 | 7 | final routes = { 8 | '/': (BuildContext context) => const Tabs(), 9 | '/todo_list_page': (BuildContext context) => const TodoListPage(), 10 | '/calculator': (BuildContext context) => const CalculatorPage(), 11 | '/note': (BuildContext context) => const NotePage(), 12 | }; 13 | -------------------------------------------------------------------------------- /lib/utils/common_utils_import.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/lib/utils/common_utils_import.dart -------------------------------------------------------------------------------- /lib/utils/index.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 6 | import 'package:provider/provider.dart'; 7 | import 'package:shared_preferences/shared_preferences.dart'; 8 | import 'package:simple_app/generated/l10n.dart'; 9 | import 'package:simple_app/provider/current_theme.dart'; 10 | 11 | final Future _prefs = SharedPreferences.getInstance(); 12 | 13 | SharedPreferences? prefsinstance; 14 | // 获取数据持久化存储对象的数据 15 | Future getLocalStorageData(key) async { 16 | // 初始化数据持久化存储对象 17 | prefsinstance ??= await _prefs.then((value) => value); 18 | String? value = prefsinstance?.getString(key); 19 | value ??= "[]"; 20 | //将string 转为json 21 | return json.decode(value); 22 | } 23 | 24 | //筛选listdata数据 返回正在进行中的todolist 或者已经完成的todoList 25 | List filterListData(List arr, bool done) { 26 | return arr.where((element) => element['done'] == done).toList(); 27 | } 28 | 29 | // showCupertinoModalPopup 展示ios的风格弹出框,通常情况下和CupertinoActionSheet配合使用, 30 | showBaseCupertinoModalPopup(BuildContext context, Function onConfirm) { 31 | showCupertinoModalPopup( 32 | context: context, 33 | builder: (BuildContext context) { 34 | return CupertinoActionSheet( 35 | title: Text(S.of(context).hint), 36 | message: Text(S.of(context).deleteMessage), 37 | actions: [ 38 | CupertinoActionSheetAction( 39 | onPressed: () => Navigator.pop(context), 40 | isDefaultAction: true, 41 | child: Text( 42 | S.of(context).cancel, 43 | style: TextStyle( 44 | fontSize: ScreenUtil().setSp(25), 45 | ), 46 | ), 47 | ), 48 | CupertinoActionSheetAction( 49 | onPressed: () { 50 | onConfirm(); 51 | Navigator.pop(context); 52 | }, 53 | isDestructiveAction: true, 54 | child: Text( 55 | S.of(context).delete, 56 | style: TextStyle(fontSize: ScreenUtil().setSp(25)), 57 | ), 58 | ), 59 | ], 60 | ); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /lib/utils/notification.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; 4 | import 'package:simple_app/generated/l10n.dart'; 5 | import 'package:simple_app/main.dart'; 6 | 7 | import 'request_notice_permission.dart'; 8 | 9 | FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = 10 | FlutterLocalNotificationsPlugin(); 11 | const AndroidInitializationSettings initializationSettingsAndroid = 12 | AndroidInitializationSettings('@mipmap/ic_launcher'); 13 | // const IOSInitializationSettings initializationSettingsIOS = 14 | // IOSInitializationSettings(); 15 | // const MacOSInitializationSettings initializationSettingsMacOS = 16 | // MacOSInitializationSettings(); 17 | const InitializationSettings initializationSettings = InitializationSettings( 18 | android: initializationSettingsAndroid, 19 | ); 20 | // 显示通知栏 21 | Future showNotification( 22 | {required String message, String? payload, String? title}) async { 23 | title ??= S.of(navigatorKey.currentState!.context).todoNoticeTitle; 24 | // 判断是否有通知权限 25 | if (await requestNoticePermission()) { 26 | AndroidNotificationDetails androidPlatformChannelSpecifics = 27 | AndroidNotificationDetails('your channel id', 28 | S.of(navigatorKey.currentState!.context).notificationName, 29 | channelDescription: S 30 | .of(navigatorKey.currentState!.context) 31 | .notificationDescription, 32 | importance: Importance.max, 33 | priority: Priority.high, 34 | ticker: 'ticker'); 35 | NotificationDetails platformChannelSpecifics = 36 | NotificationDetails(android: androidPlatformChannelSpecifics); 37 | 38 | await flutterLocalNotificationsPlugin 39 | .show(0, title, message, platformChannelSpecifics, payload: payload); 40 | } 41 | } 42 | 43 | // 点击通知栏触发的事件 跳转到todoListPage 页面 44 | void selectNotification(NotificationResponse? response) async { 45 | debugPrint('notification payload: $response.payload'); 46 | if (response?.payload != null) { 47 | navigatorKey.currentState?.pushNamed(response?.payload as String); 48 | } 49 | } 50 | 51 | // 初始化本地通知插件 52 | Future flutterLocalNotificationsPluginInit() { 53 | // initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project 54 | return flutterLocalNotificationsPlugin.initialize(initializationSettings, 55 | onDidReceiveBackgroundNotificationResponse: selectNotification); 56 | } 57 | -------------------------------------------------------------------------------- /lib/utils/request_notice_permission.dart: -------------------------------------------------------------------------------- 1 | import 'package:permission_handler/permission_handler.dart'; 2 | import 'package:simple_app/generated/l10n.dart'; 3 | import 'package:simple_app/main.dart'; 4 | import 'package:simple_app/utils/show_dialog.dart'; 5 | 6 | // 获取app 是否具有通知权限 如果没有则弹出对话框 跳转到设置界面 7 | Future requestNoticePermission() async { 8 | // 判断是否没有通知权限 如果没有则弹出对话框 打开设置界面提示用户添加权限 9 | if (await Permission.notification.isDenied) { 10 | // 打开对话框 提示用户是否前往设置界面设置权限 11 | bool? result = await showConfirmDialog(navigatorKey.currentState!.context, 12 | message: 13 | S.of(navigatorKey.currentState!.context).noNotificationPermission); 14 | //判断result是否为空, 不为空说明点击了确定按钮 15 | if (result != null) { 16 | openAppSettings(); 17 | } 18 | return false; 19 | } else { 20 | return true; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/utils/show_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import "package:flutter/material.dart"; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:provider/provider.dart'; 5 | import 'package:simple_app/generated/l10n.dart'; 6 | import 'package:simple_app/provider/current_theme.dart'; 7 | 8 | // 弹出对话框 9 | Future showConfirmDialog(BuildContext context, 10 | {String? tips, 11 | String? message, 12 | String? cancelText, 13 | String? confirmText, 14 | // 是否显示不在提示框 15 | bool showTips = true, 16 | void Function(bool?)? onChange}) { 17 | tips ??= S.of(context).hint; 18 | message ??= S.of(context).dialogDeleteMessage; 19 | cancelText ??= S.of(context).cancel; 20 | confirmText ??= S.of(context).confirm; 21 | TextStyle textStyle = TextStyle(); 22 | return showDialog( 23 | context: context, 24 | builder: (context) { 25 | return CupertinoAlertDialog( 26 | title: Text(tips!), 27 | content: WrapMaterialCheckBox( 28 | message: message!, 29 | label: S.of(context).noLongerTips, 30 | onChange: onChange, 31 | showTips: showTips, 32 | ), 33 | actions: [ 34 | TextButton( 35 | child: Text(cancelText!, style: textStyle), 36 | onPressed: () => Navigator.of(context).pop(), // 关闭对话框 37 | ), 38 | TextButton( 39 | child: Text( 40 | confirmText!, 41 | style: textStyle, 42 | ), 43 | onPressed: () { 44 | Navigator.of(context).pop(true); 45 | }, 46 | ), 47 | ], 48 | ); 49 | }, 50 | ); 51 | } 52 | 53 | // 封装checkbox 54 | class WrapMaterialCheckBox extends StatefulWidget { 55 | // 提示信息 56 | final String message; 57 | // checkBox 左边文字 58 | final String label; 59 | final void Function(bool?)? onChange; 60 | final bool showTips; 61 | const WrapMaterialCheckBox( 62 | {Key? key, 63 | required this.message, 64 | required this.label, 65 | this.onChange, 66 | required this.showTips}) 67 | : super(key: key); 68 | 69 | @override 70 | State createState() => WrapMaterialCheckBoxState(); 71 | } 72 | 73 | class WrapMaterialCheckBoxState extends State { 74 | bool checkBoxValue = false; 75 | 76 | @override 77 | Widget build(BuildContext context) { 78 | return Material( 79 | type: MaterialType.transparency, 80 | child: Padding( 81 | padding: EdgeInsets.only(top: ScreenUtil().setHeight(10)), 82 | child: Column( 83 | children: [ 84 | Text(widget.message), 85 | widget.showTips 86 | ? Row( 87 | mainAxisAlignment: MainAxisAlignment.center, 88 | children: [ 89 | SizedBox( 90 | width: 30, 91 | height: 30, 92 | child: Checkbox( 93 | shape: const CircleBorder(), 94 | value: checkBoxValue, 95 | onChanged: (bool? value) { 96 | setState(() => checkBoxValue = value!); 97 | if (widget.onChange != null) { 98 | widget.onChange!(value); 99 | } 100 | }, 101 | ), 102 | ), 103 | Text(widget.label) 104 | ], 105 | ) 106 | : Container() 107 | ], 108 | ), 109 | )); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /lib/utils/show_toast.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:fluttertoast/fluttertoast.dart'; 4 | 5 | showToast(String message) { 6 | Fluttertoast.showToast( 7 | msg: message, 8 | gravity: ToastGravity.CENTER, 9 | timeInSecForIosWeb: 1, 10 | backgroundColor: const Color.fromRGBO(0, 0, 0, 0.6), 11 | fontSize: ScreenUtil().setSp(15)); 12 | } 13 | -------------------------------------------------------------------------------- /my_webApp/assets/AssetManifest.bin: -------------------------------------------------------------------------------- 1 | assets/15199.mp3  assetassets/15199.mp32packages/cupertino_icons/assets/CupertinoIcons.ttf  asset2packages/cupertino_icons/assets/CupertinoIcons.ttf+packages/flutter_sound_web/howler/howler.js  asset+packages/flutter_sound_web/howler/howler.js/packages/flutter_sound_web/src/flutter_sound.js  asset/packages/flutter_sound_web/src/flutter_sound.js6packages/flutter_sound_web/src/flutter_sound_player.js  asset6packages/flutter_sound_web/src/flutter_sound_player.js8packages/flutter_sound_web/src/flutter_sound_recorder.js  asset8packages/flutter_sound_web/src/flutter_sound_recorder.js)packages/fluttertoast/assets/toastify.css  asset)packages/fluttertoast/assets/toastify.css(packages/fluttertoast/assets/toastify.js  asset(packages/fluttertoast/assets/toastify.js/packages/quill_native_bridge_linux/assets/xclip  asset/packages/quill_native_bridge_linux/assets/xclip -------------------------------------------------------------------------------- /my_webApp/assets/AssetManifest.bin.json: -------------------------------------------------------------------------------- 1 | "DQkHEGFzc2V0cy8xNTE5OS5tcDMMAQ0BBwVhc3NldAcQYXNzZXRzLzE1MTk5Lm1wMwcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGYMAQ0BBwVhc3NldAcycGFja2FnZXMvY3VwZXJ0aW5vX2ljb25zL2Fzc2V0cy9DdXBlcnRpbm9JY29ucy50dGYHK3BhY2thZ2VzL2ZsdXR0ZXJfc291bmRfd2ViL2hvd2xlci9ob3dsZXIuanMMAQ0BBwVhc3NldAcrcGFja2FnZXMvZmx1dHRlcl9zb3VuZF93ZWIvaG93bGVyL2hvd2xlci5qcwcvcGFja2FnZXMvZmx1dHRlcl9zb3VuZF93ZWIvc3JjL2ZsdXR0ZXJfc291bmQuanMMAQ0BBwVhc3NldAcvcGFja2FnZXMvZmx1dHRlcl9zb3VuZF93ZWIvc3JjL2ZsdXR0ZXJfc291bmQuanMHNnBhY2thZ2VzL2ZsdXR0ZXJfc291bmRfd2ViL3NyYy9mbHV0dGVyX3NvdW5kX3BsYXllci5qcwwBDQEHBWFzc2V0BzZwYWNrYWdlcy9mbHV0dGVyX3NvdW5kX3dlYi9zcmMvZmx1dHRlcl9zb3VuZF9wbGF5ZXIuanMHOHBhY2thZ2VzL2ZsdXR0ZXJfc291bmRfd2ViL3NyYy9mbHV0dGVyX3NvdW5kX3JlY29yZGVyLmpzDAENAQcFYXNzZXQHOHBhY2thZ2VzL2ZsdXR0ZXJfc291bmRfd2ViL3NyYy9mbHV0dGVyX3NvdW5kX3JlY29yZGVyLmpzBylwYWNrYWdlcy9mbHV0dGVydG9hc3QvYXNzZXRzL3RvYXN0aWZ5LmNzcwwBDQEHBWFzc2V0BylwYWNrYWdlcy9mbHV0dGVydG9hc3QvYXNzZXRzL3RvYXN0aWZ5LmNzcwcocGFja2FnZXMvZmx1dHRlcnRvYXN0L2Fzc2V0cy90b2FzdGlmeS5qcwwBDQEHBWFzc2V0ByhwYWNrYWdlcy9mbHV0dGVydG9hc3QvYXNzZXRzL3RvYXN0aWZ5LmpzBy9wYWNrYWdlcy9xdWlsbF9uYXRpdmVfYnJpZGdlX2xpbnV4L2Fzc2V0cy94Y2xpcAwBDQEHBWFzc2V0By9wYWNrYWdlcy9xdWlsbF9uYXRpdmVfYnJpZGdlX2xpbnV4L2Fzc2V0cy94Y2xpcA==" -------------------------------------------------------------------------------- /my_webApp/assets/AssetManifest.json: -------------------------------------------------------------------------------- 1 | {"assets/15199.mp3":["assets/15199.mp3"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"],"packages/flutter_sound_web/howler/howler.js":["packages/flutter_sound_web/howler/howler.js"],"packages/flutter_sound_web/src/flutter_sound.js":["packages/flutter_sound_web/src/flutter_sound.js"],"packages/flutter_sound_web/src/flutter_sound_player.js":["packages/flutter_sound_web/src/flutter_sound_player.js"],"packages/flutter_sound_web/src/flutter_sound_recorder.js":["packages/flutter_sound_web/src/flutter_sound_recorder.js"],"packages/fluttertoast/assets/toastify.css":["packages/fluttertoast/assets/toastify.css"],"packages/fluttertoast/assets/toastify.js":["packages/fluttertoast/assets/toastify.js"],"packages/quill_native_bridge_linux/assets/xclip":["packages/quill_native_bridge_linux/assets/xclip"]} -------------------------------------------------------------------------------- /my_webApp/assets/FontManifest.json: -------------------------------------------------------------------------------- 1 | [{"family":"MaterialIcons","fonts":[{"asset":"fonts/MaterialIcons-Regular.otf"}]},{"family":"packages/cupertino_icons/CupertinoIcons","fonts":[{"asset":"packages/cupertino_icons/assets/CupertinoIcons.ttf"}]}] -------------------------------------------------------------------------------- /my_webApp/assets/assets/15199.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/my_webApp/assets/assets/15199.mp3 -------------------------------------------------------------------------------- /my_webApp/assets/fonts/MaterialIcons-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/my_webApp/assets/fonts/MaterialIcons-Regular.otf -------------------------------------------------------------------------------- /my_webApp/assets/packages/cupertino_icons/assets/CupertinoIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/my_webApp/assets/packages/cupertino_icons/assets/CupertinoIcons.ttf -------------------------------------------------------------------------------- /my_webApp/assets/packages/flutter_sound_web/src/flutter_sound.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018, 2019, 2020 Dooboolab. 3 | * 4 | * This file is part of Flutter-Sound. 5 | * 6 | * Flutter-Sound is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License version 3 (LGPL-V3), as published by 8 | * the Free Software Foundation. 9 | * 10 | * Flutter-Sound is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public License 16 | * along with Flutter-Sound. If not, see . 17 | */ 18 | 19 | const VERSION = '9.17.0' 20 | 21 | 22 | const VERBOSE = 0; 23 | const DBG = 1; 24 | const INFO = 2; 25 | const WARNING = 3; 26 | const ERROR = 4; 27 | const WTF = 5; 28 | const NOTHING = 6; 29 | 30 | 31 | 32 | 33 | const codec = 34 | { 35 | defaultCodec: 'defaultCodec', 36 | aacADTS: 'aacADTS', 37 | opusOGG: 'opusOGG', 38 | opusCAF: 'opusCAF', 39 | mp3: 'mp3', 40 | vorbisOGG: 'vorbisOGG', 41 | pcm16: 'pcm16', 42 | pcm16WAV: 'pcm16WAV', 43 | pcm16AIFF: 'pcm16AIFF', 44 | pcm16CAF: 'pcm16CAF', 45 | flac: 'flac', 46 | aacMP4: 'aacMP4', 47 | amrNB: 'amrNB', 48 | amrWB: 'amrWB', 49 | pcm8: 'pcm8', 50 | pcmFloat32: 'pcmFloat32', 51 | pcmWebM: 'pcmWebM', 52 | opusWebM: 'opusWebM', 53 | vorbisWebM: 'vorbisWebM', 54 | }; 55 | 56 | 57 | const tabCodec = 58 | [ 59 | codec.opusWebM, // codec.defaultCodec, 60 | codec.aacADTS, 61 | codec.opusOGG, 62 | codec.opusCAF, 63 | codec.mp3, 64 | codec.vorbisOGG, 65 | codec.pcm16, 66 | codec.pcm16WAV, 67 | codec.pcm16AIFF, 68 | codec.pcm16CAF, 69 | codec.flac, 70 | codec.aacMP4, 71 | codec.amrNB, 72 | codec.amrWB, 73 | codec.pcm8, 74 | codec.pcmFloat32, 75 | codec.pcmWebM, 76 | codec.opusWebM, 77 | codec.vorbisWebM, 78 | ]; 79 | 80 | const mime_types = 81 | [ 82 | 'audio/webm\;codecs=opus', // defaultCodec, 83 | 'audio/aac', // aacADTS, //* 84 | 'audio/opus\;codecs=opus', // opusOGG, // 'audio/ogg' 'audio/opus' 85 | 'audio/x-caf', // opusCAF, 86 | 'audio/mpeg', // mp3, //* 87 | 'audio/ogg\;codecs=vorbis', // vorbisOGG,// 'audio/ogg' // 'audio/vorbis' 88 | 'audio/pcm', // pcm16, 89 | 'audio/wav\;codecs=1', // pcm16WAV, 90 | 'audio/aiff', // pcm16AIFF, 91 | 'audio/x-caf', // pcm16CAF, 92 | 'audio/x-flac', // flac, // 'audio/flac' 93 | 'audio/mp4', // aacMP4, //* 94 | 'audio/AMR', // amrNB, //* 95 | 'audio/AMR-WB', // amrWB, //* 96 | 'audio/pcm', // pcm8, 97 | 'audio/pcm', // pcmFloat32, 98 | 'audio/webm\;codecs=pcm', // pcmWebM, 99 | 'audio/webm\;codecs=opus', // opusWebM, 100 | 'audio/webm\;codecs=vorbis', // vorbisWebM 101 | ]; 102 | 103 | const tabFormat = 104 | [ 105 | 'opus', // defaultCodec, 106 | 'aac', // aacADTS, 107 | 'opus', // opusOGG, 108 | 'caf', // opusCAF, 109 | 'mp3', // mp3, 110 | 'vorbis', // vorbisOGG, 111 | '', // pcm16, 112 | 'wav', // pcm16WAV, 113 | 'aiff', // pcm16AIFF, 114 | 'caf', // pcm16CAF, 115 | 'flac', // flac, 116 | 'mp4', // aacMP4, 117 | 'AMR', // amrNB, 118 | 'AMR-WB', // amrWB, 119 | '', // pcm8, 120 | '', // pcmFloat32, 121 | 'pcm', // pcmWebM, 122 | 'opus', // opusWebM, 123 | 'webm', // vorbisWebM 124 | 125 | ]; 126 | 127 | 128 | 129 | var instanceNumber = 0; 130 | var lastUrl = ''; 131 | 132 | 133 | 134 | function getRecordURL( aPath,) 135 | { 136 | var path ; 137 | var myStorage; 138 | if ((aPath == null) || (aPath == '')) 139 | { 140 | path = lastUrl; 141 | } else 142 | { 143 | path = aPath; 144 | 145 | } 146 | if (path.substring(0,1) == '/') 147 | { 148 | myStorage = window.localStorage; 149 | } else 150 | { 151 | myStorage = window.sessionStorage; 152 | } 153 | 154 | var url = myStorage.getItem(path); 155 | return url; 156 | } 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /my_webApp/assets/packages/fluttertoast/assets/toastify.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Minified by jsDelivr using clean-css v4.2.3. 3 | * Original file: /npm/toastify-js@1.9.3/src/toastify.css 4 | * 5 | * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files 6 | */ 7 | /*! 8 | * Toastify js 1.9.3 9 | * https://github.com/apvarun/toastify-js 10 | * @license MIT licensed 11 | * 12 | * Copyright (C) 2018 Varun A P 13 | */ 14 | .toastify{padding:12px 20px;color:#fff;display:inline-block;box-shadow:0 3px 6px -1px rgba(0,0,0,.12),0 10px 36px -4px rgba(77,96,232,.3);background:-webkit-linear-gradient(315deg,#73a5ff,#5477f5);background:linear-gradient(135deg,#73a5ff,#5477f5);position:fixed;opacity:0;transition:all .4s cubic-bezier(.215,.61,.355,1);border-radius:2px;cursor:pointer;text-decoration:none;max-width:calc(50% - 20px);z-index:2147483647}.toastify.on{opacity:1}.toast-close{opacity:.4;padding:0 5px}.toastify-right{right:15px}.toastify-left{left:15px}.toastify-top{top:-150px}.toastify-bottom{bottom:-150px}.toastify-rounded{border-radius:25px}.toastify-avatar{width:1.5em;height:1.5em;margin:-7px 5px;border-radius:2px}.toastify-center{margin-left:auto;margin-right:auto;left:0;right:0;max-width:fit-content;max-width:-moz-fit-content}@media only screen and (max-width:360px){.toastify-left,.toastify-right{margin-left:auto;margin-right:auto;left:0;right:0;max-width:fit-content}} -------------------------------------------------------------------------------- /my_webApp/assets/packages/fluttertoast/assets/toastify.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Minified by jsDelivr using Terser v5.3.0. 3 | * Original file: /npm/toastify-js@1.9.3/src/toastify.js 4 | * 5 | * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files 6 | */ 7 | /*! 8 | * Toastify js 1.9.3 9 | * https://github.com/apvarun/toastify-js 10 | * @license MIT licensed 11 | * 12 | * Copyright (C) 2018 Varun A P 13 | */ 14 | !function(t,o){"object"==typeof module && module && module.exports?module.exports=o():t.Toastify=o()}(this,(function(t){var o=function(t){return new o.lib.init(t)};function i(t,o){return o.offset[t]?isNaN(o.offset[t])?o.offset[t]:o.offset[t]+"px":"0px"}function s(t,o){return!(!t||"string"!=typeof o)&&!!(t.className&&t.className.trim().split(/\s+/gi).indexOf(o)>-1)}return o.lib=o.prototype={toastify:"1.9.3",constructor:o,init:function(t){return t||(t={}),this.options={},this.toastElement=null,this.options.text=t.text||"Hi there!",this.options.node=t.node,this.options.duration=0===t.duration?0:t.duration||3e3,this.options.selector=t.selector,this.options.callback=t.callback||function(){},this.options.destination=t.destination,this.options.newWindow=t.newWindow||!1,this.options.close=t.close||!1,this.options.gravity="bottom"===t.gravity?"toastify-bottom":"toastify-top",this.options.positionLeft=t.positionLeft||!1,this.options.position=t.position||"",this.options.backgroundColor=t.backgroundColor,this.options.avatar=t.avatar||"",this.options.className=t.className||"",this.options.stopOnFocus=void 0===t.stopOnFocus||t.stopOnFocus,this.options.onClick=t.onClick,this.options.offset=t.offset||{x:0,y:0},this},buildToast:function(){if(!this.options)throw"Toastify is not initialized";var t=document.createElement("div");if(t.className="toastify on "+this.options.className,this.options.position?t.className+=" toastify-"+this.options.position:!0===this.options.positionLeft?(t.className+=" toastify-left",console.warn("Property `positionLeft` will be depreciated in further versions. Please use `position` instead.")):t.className+=" toastify-right",t.className+=" "+this.options.gravity,this.options.backgroundColor&&(t.style.background=this.options.backgroundColor),this.options.node&&this.options.node.nodeType===Node.ELEMENT_NODE)t.appendChild(this.options.node);else if(t.innerHTML=this.options.text,""!==this.options.avatar){var o=document.createElement("img");o.src=this.options.avatar,o.className="toastify-avatar","left"==this.options.position||!0===this.options.positionLeft?t.appendChild(o):t.insertAdjacentElement("afterbegin",o)}if(!0===this.options.close){var s=document.createElement("span");s.innerHTML="✖",s.className="toast-close",s.addEventListener("click",function(t){t.stopPropagation(),this.removeElement(this.toastElement),window.clearTimeout(this.toastElement.timeOutValue)}.bind(this));var n=window.innerWidth>0?window.innerWidth:screen.width;("left"==this.options.position||!0===this.options.positionLeft)&&n>360?t.insertAdjacentElement("afterbegin",s):t.appendChild(s)}if(this.options.stopOnFocus&&this.options.duration>0){var e=this;t.addEventListener("mouseover",(function(o){window.clearTimeout(t.timeOutValue)})),t.addEventListener("mouseleave",(function(){t.timeOutValue=window.setTimeout((function(){e.removeElement(t)}),e.options.duration)}))}if(void 0!==this.options.destination&&t.addEventListener("click",function(t){t.stopPropagation(),!0===this.options.newWindow?window.open(this.options.destination,"_blank"):window.location=this.options.destination}.bind(this)),"function"==typeof this.options.onClick&&void 0===this.options.destination&&t.addEventListener("click",function(t){t.stopPropagation(),this.options.onClick()}.bind(this)),"object"==typeof this.options.offset){var a=i("x",this.options),p=i("y",this.options),r="left"==this.options.position?a:"-"+a,l="toastify-top"==this.options.gravity?p:"-"+p;t.style.transform="translate("+r+","+l+")"}return t},showToast:function(){var t;if(this.toastElement=this.buildToast(),!(t=void 0===this.options.selector?document.body:document.getElementById(this.options.selector)))throw"Root element is not defined";return t.insertBefore(this.toastElement,t.firstChild),o.reposition(),this.options.duration>0&&(this.toastElement.timeOutValue=window.setTimeout(function(){this.removeElement(this.toastElement)}.bind(this),this.options.duration)),this},hideToast:function(){this.toastElement.timeOutValue&&clearTimeout(this.toastElement.timeOutValue),this.removeElement(this.toastElement)},removeElement:function(t){t.className=t.className.replace(" on",""),window.setTimeout(function(){this.options.node&&this.options.node.parentNode&&this.options.node.parentNode.removeChild(this.options.node),t.parentNode&&t.parentNode.removeChild(t),this.options.callback.call(t),o.reposition()}.bind(this),400)}},o.reposition=function(){for(var t,o={top:15,bottom:15},i={top:15,bottom:15},n={top:15,bottom:15},e=document.getElementsByClassName("toastify"),a=0;a0?window.innerWidth:screen.width)<=360?(e[a].style[t]=n[t]+"px",n[t]+=p+15):!0===s(e[a],"toastify-left")?(e[a].style[t]=o[t]+"px",o[t]+=p+15):(e[a].style[t]=i[t]+"px",i[t]+=p+15)}return this},o.lib.init.prototype=o.lib,o})); 15 | -------------------------------------------------------------------------------- /my_webApp/assets/packages/quill_native_bridge_linux/assets/xclip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/my_webApp/assets/packages/quill_native_bridge_linux/assets/xclip -------------------------------------------------------------------------------- /my_webApp/canvaskit/canvaskit.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/my_webApp/canvaskit/canvaskit.wasm -------------------------------------------------------------------------------- /my_webApp/canvaskit/chromium/canvaskit.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/my_webApp/canvaskit/chromium/canvaskit.wasm -------------------------------------------------------------------------------- /my_webApp/canvaskit/skwasm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/my_webApp/canvaskit/skwasm.wasm -------------------------------------------------------------------------------- /my_webApp/canvaskit/skwasm.worker.js: -------------------------------------------------------------------------------- 1 | "use strict";var Module={};var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){var nodeWorkerThreads=require("worker_threads");var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",data=>onmessage({data:data}));var fs=require("fs");Object.assign(global,{self:global,require:require,Module:Module,location:{href:__filename},Worker:nodeWorkerThreads.Worker,importScripts:f=>(0,eval)(fs.readFileSync(f,"utf8")+"//# sourceURL="+f),postMessage:msg=>parentPort.postMessage(msg),performance:global.performance||{now:Date.now}})}var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");if(ENVIRONMENT_IS_NODE){fs.writeSync(2,text+"\n");return}console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=(info,receiveInstance)=>{var module=Module["wasmModule"];Module["wasmModule"]=null;var instance=new WebAssembly.Instance(module,info);return receiveInstance(instance)};self.onunhandledrejection=e=>{throw e.reason??e};function handleMessage(e){try{if(e.data.cmd==="load"){let messageQueue=[];self.onmessage=e=>messageQueue.push(e);self.startWorker=instance=>{Module=instance;postMessage({"cmd":"loaded"});for(let msg of messageQueue){handleMessage(msg)}self.onmessage=handleMessage};Module["wasmModule"]=e.data.wasmModule;for(const handler of e.data.handlers){Module[handler]=(...args)=>{postMessage({cmd:"callHandler",handler:handler,args:args})}}Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;(e.data.urlOrBlob?import(e.data.urlOrBlob):import("./skwasm.js")).then(exports=>exports.default(Module))}else if(e.data.cmd==="run"){Module["__emscripten_thread_init"](e.data.pthread_ptr,/*isMainBrowserThread=*/0,/*isMainRuntimeThread=*/0,/*canBlock=*/1);Module["__emscripten_thread_mailbox_await"](e.data.pthread_ptr);Module["establishStackSpace"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInitTLS();if(!initializedJS){initializedJS=true}try{Module["invokeEntryPoint"](e.data.start_routine,e.data.arg)}catch(ex){if(ex!="unwind"){throw ex}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["__emscripten_thread_exit"](-1)}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="checkMailbox"){if(initializedJS){Module["checkMailbox"]()}}else if(e.data.cmd){err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){if(Module["__emscripten_thread_crashed"]){Module["__emscripten_thread_crashed"]()}throw ex}}self.onmessage=handleMessage; 2 | -------------------------------------------------------------------------------- /my_webApp/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/my_webApp/favicon.png -------------------------------------------------------------------------------- /my_webApp/flutter.js: -------------------------------------------------------------------------------- 1 | (()=>{var L=()=>navigator.vendor==="Google Inc."||navigator.agent==="Edg/",W=()=>typeof ImageDecoder>"u"?!1:L(),E=()=>typeof Intl.v8BreakIterator<"u"&&typeof Intl.Segmenter<"u",P=()=>{let s=[0,97,115,109,1,0,0,0,1,5,1,95,1,120,0];return WebAssembly.validate(new Uint8Array(s))},p={hasImageCodecs:W(),hasChromiumBreakIterators:E(),supportsWasmGC:P(),crossOriginIsolated:window.crossOriginIsolated};function u(...s){return new URL(S(...s),document.baseURI).toString()}function S(...s){return s.filter(t=>!!t).map((t,i)=>i===0?_(t):j(_(t))).filter(t=>t.length).join("/")}function j(s){let t=0;for(;t0&&s.charAt(t-1)==="/";)t--;return s.substring(0,t)}function b(s,t){return s.canvasKitBaseUrl?s.canvasKitBaseUrl:t.engineRevision&&!t.useLocalCanvasKit?S("https://www.gstatic.com/flutter-canvaskit",t.engineRevision):"canvaskit"}var h=class{constructor(){this._scriptLoaded=!1}setTrustedTypesPolicy(t){this._ttPolicy=t}async loadEntrypoint(t){let{entrypointUrl:i=u("main.dart.js"),onEntrypointLoaded:r,nonce:e}=t||{};return this._loadJSEntrypoint(i,r,e)}async load(t,i,r,e,n){n??=o=>{o.initializeEngine(r).then(l=>l.runApp())};let{entryPointBaseUrl:a}=r;if(t.compileTarget==="dart2wasm")return this._loadWasmEntrypoint(t,i,a,n);{let o=t.mainJsPath??"main.dart.js",l=u(a,o);return this._loadJSEntrypoint(l,n,e)}}didCreateEngineInitializer(t){typeof this._didCreateEngineInitializerResolve=="function"&&(this._didCreateEngineInitializerResolve(t),this._didCreateEngineInitializerResolve=null,delete _flutter.loader.didCreateEngineInitializer),typeof this._onEntrypointLoaded=="function"&&this._onEntrypointLoaded(t)}_loadJSEntrypoint(t,i,r){let e=typeof i=="function";if(!this._scriptLoaded){this._scriptLoaded=!0;let n=this._createScriptTag(t,r);if(e)console.debug("Injecting 103 | 104 | 105 | -------------------------------------------------------------------------------- /my_webApp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple_app", 3 | "short_name": "simple_app", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /my_webApp/version.json: -------------------------------------------------------------------------------- 1 | {"app_name":"simple_app","version":"1.0.0","build_number":"1","package_name":"simple_app"} -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: simple_app 2 | description: A new Flutter project. 3 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.16.0 <3.0.0" 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | flutter_screenutil: ^5.9.3 12 | fluttertoast: ^8.2.8 13 | provider: ^6.1.2 14 | shared_preferences: ^2.3.3 15 | permission_handler: ^11.3.1 16 | flutter_local_notifications: ^18.0.1 17 | date_format: ^2.0.9 18 | flutter_native_splash: ^2.4.3 19 | cupertino_icons: ^1.0.8 20 | back_button_interceptor: ^8.0.0 21 | device_info_plus: ^11.1.1 22 | sqflite: ^2.4.1 23 | path_provider: ^2.1.5 24 | flutter_staggered_grid_view: ^0.7.0 25 | flutter_slidable: ^3.1.1 26 | flutter_quill: ^10.8.5 27 | decimal: ^3.0.2 28 | auto_size_text: ^3.0.0 29 | flutter_sticky_header: ^0.7.0 30 | animated_flip_counter: ^0.3.4 31 | percent_indicator: ^4.2.3 32 | vibration: ^2.0.1 33 | flutter_sound: ^9.17.0 34 | shimmer: ^3.0.0 35 | sembast: ^3.7.5+2 36 | sembast_web: ^2.4.0+4 37 | flutter_localizations: 38 | sdk: flutter 39 | 40 | dev_dependencies: 41 | flutter_test: 42 | sdk: flutter 43 | flutter_lints: ^5.0.0 44 | 45 | flutter: 46 | uses-material-design: true 47 | assets: 48 | - assets/15199.mp3 49 | flutter_intl: 50 | enabled: true 51 | localizely: 52 | project_id: "66" 53 | # 配置启动页 54 | flutter_native_splash: 55 | color: "#e1f5fe" 56 | image: assets/logo_lockup_flutter_vertical.png 57 | color_dark: "#042a49" 58 | image_dark: assets/logo_lockup_flutter_vertical_wht.png 59 | android: true 60 | ios: true 61 | web: false 62 | android_gravity: center 63 | fullerene: true 64 | -------------------------------------------------------------------------------- /release/app.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/release/app.apk -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:simple_app/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | simple_app 33 | 34 | 35 | 36 | 39 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple_app", 3 | "short_name": "simple_app", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(simple_app LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "simple_app") 5 | 6 | cmake_policy(SET CMP0063 NEW) 7 | 8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 9 | 10 | # Configure build options. 11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 12 | if(IS_MULTICONFIG) 13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 14 | CACHE STRING "" FORCE) 15 | else() 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | set(CMAKE_BUILD_TYPE "Debug" CACHE 18 | STRING "Flutter build mode" FORCE) 19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 20 | "Debug" "Profile" "Release") 21 | endif() 22 | endif() 23 | 24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 28 | 29 | # Use Unicode for all projects. 30 | add_definitions(-DUNICODE -D_UNICODE) 31 | 32 | # Compilation settings that should be applied to most targets. 33 | function(APPLY_STANDARD_SETTINGS TARGET) 34 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 36 | target_compile_options(${TARGET} PRIVATE /EHsc) 37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 39 | endfunction() 40 | 41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 42 | 43 | # Flutter library and tool build rules. 44 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 45 | 46 | # Application build 47 | add_subdirectory("runner") 48 | 49 | # Generated plugin build rules, which manage building the plugins and adding 50 | # them to the application. 51 | include(flutter/generated_plugins.cmake) 52 | 53 | 54 | # === Installation === 55 | # Support files are copied into place next to the executable, so that it can 56 | # run in place. This is done instead of making a separate bundle (as on Linux) 57 | # so that building and running from within Visual Studio will work. 58 | set(BUILD_BUNDLE_DIR "$") 59 | # Make the "install" step default, as it's required to run. 60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 63 | endif() 64 | 65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 67 | 68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 69 | COMPONENT Runtime) 70 | 71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 72 | COMPONENT Runtime) 73 | 74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 75 | COMPONENT Runtime) 76 | 77 | if(PLUGIN_BUNDLED_LIBRARIES) 78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 80 | COMPONENT Runtime) 81 | endif() 82 | 83 | # Fully re-copy the assets directory on each build to avoid having stale files 84 | # from a previous install. 85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 86 | install(CODE " 87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 88 | " COMPONENT Runtime) 89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 91 | 92 | # Install the AOT library on non-Debug builds only. 93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 94 | CONFIGURATIONS Profile;Release 95 | COMPONENT Runtime) 96 | -------------------------------------------------------------------------------- /windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 11 | 12 | # === Flutter Library === 13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 14 | 15 | # Published to parent scope for install step. 16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 20 | 21 | list(APPEND FLUTTER_LIBRARY_HEADERS 22 | "flutter_export.h" 23 | "flutter_windows.h" 24 | "flutter_messenger.h" 25 | "flutter_plugin_registrar.h" 26 | "flutter_texture_registrar.h" 27 | ) 28 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 29 | add_library(flutter INTERFACE) 30 | target_include_directories(flutter INTERFACE 31 | "${EPHEMERAL_DIR}" 32 | ) 33 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 34 | add_dependencies(flutter flutter_assemble) 35 | 36 | # === Wrapper === 37 | list(APPEND CPP_WRAPPER_SOURCES_CORE 38 | "core_implementations.cc" 39 | "standard_codec.cc" 40 | ) 41 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 42 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 43 | "plugin_registrar.cc" 44 | ) 45 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 46 | list(APPEND CPP_WRAPPER_SOURCES_APP 47 | "flutter_engine.cc" 48 | "flutter_view_controller.cc" 49 | ) 50 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 51 | 52 | # Wrapper sources needed for a plugin. 53 | add_library(flutter_wrapper_plugin STATIC 54 | ${CPP_WRAPPER_SOURCES_CORE} 55 | ${CPP_WRAPPER_SOURCES_PLUGIN} 56 | ) 57 | apply_standard_settings(flutter_wrapper_plugin) 58 | set_target_properties(flutter_wrapper_plugin PROPERTIES 59 | POSITION_INDEPENDENT_CODE ON) 60 | set_target_properties(flutter_wrapper_plugin PROPERTIES 61 | CXX_VISIBILITY_PRESET hidden) 62 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 63 | target_include_directories(flutter_wrapper_plugin PUBLIC 64 | "${WRAPPER_ROOT}/include" 65 | ) 66 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 67 | 68 | # Wrapper sources needed for the runner. 69 | add_library(flutter_wrapper_app STATIC 70 | ${CPP_WRAPPER_SOURCES_CORE} 71 | ${CPP_WRAPPER_SOURCES_APP} 72 | ) 73 | apply_standard_settings(flutter_wrapper_app) 74 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 75 | target_include_directories(flutter_wrapper_app PUBLIC 76 | "${WRAPPER_ROOT}/include" 77 | ) 78 | add_dependencies(flutter_wrapper_app flutter_assemble) 79 | 80 | # === Flutter tool backend === 81 | # _phony_ is a non-existent file to force this command to run every time, 82 | # since currently there's no way to get a full input/output list from the 83 | # flutter tool. 84 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 85 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 86 | add_custom_command( 87 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 88 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 89 | ${CPP_WRAPPER_SOURCES_APP} 90 | ${PHONY_OUTPUT} 91 | COMMAND ${CMAKE_COMMAND} -E env 92 | ${FLUTTER_TOOL_ENVIRONMENT} 93 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 94 | windows-x64 $ 95 | VERBATIM 96 | ) 97 | add_custom_target(flutter_assemble DEPENDS 98 | "${FLUTTER_LIBRARY}" 99 | ${FLUTTER_LIBRARY_HEADERS} 100 | ${CPP_WRAPPER_SOURCES_CORE} 101 | ${CPP_WRAPPER_SOURCES_PLUGIN} 102 | ${CPP_WRAPPER_SOURCES_APP} 103 | ) 104 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void RegisterPlugins(flutter::PluginRegistry* registry) { 13 | PermissionHandlerWindowsPluginRegisterWithRegistrar( 14 | registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); 15 | UrlLauncherWindowsRegisterWithRegistrar( 16 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 17 | } 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | permission_handler_windows 7 | url_launcher_windows 8 | ) 9 | 10 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 11 | ) 12 | 13 | set(PLUGIN_BUNDLED_LIBRARIES) 14 | 15 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 16 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 17 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 20 | endforeach(plugin) 21 | 22 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 23 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 25 | endforeach(ffi_plugin) 26 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "utils.cpp" 8 | "win32_window.cpp" 9 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 10 | "Runner.rc" 11 | "runner.exe.manifest" 12 | ) 13 | apply_standard_settings(${BINARY_NAME}) 14 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 15 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 16 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 17 | add_dependencies(${BINARY_NAME} flutter_assemble) 18 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #ifdef FLUTTER_BUILD_NUMBER 64 | #define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0 67 | #endif 68 | 69 | #ifdef FLUTTER_BUILD_NAME 70 | #define VERSION_AS_STRING #FLUTTER_BUILD_NAME 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "simple_app" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "simple_app" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "simple_app.exe" "\0" 98 | VALUE "ProductName", "simple_app" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | return true; 30 | } 31 | 32 | void FlutterWindow::OnDestroy() { 33 | if (flutter_controller_) { 34 | flutter_controller_ = nullptr; 35 | } 36 | 37 | Win32Window::OnDestroy(); 38 | } 39 | 40 | LRESULT 41 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 42 | WPARAM const wparam, 43 | LPARAM const lparam) noexcept { 44 | // Give Flutter, including plugins, an opportunity to handle window messages. 45 | if (flutter_controller_) { 46 | std::optional result = 47 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 48 | lparam); 49 | if (result) { 50 | return *result; 51 | } 52 | } 53 | 54 | switch (message) { 55 | case WM_FONTCHANGE: 56 | flutter_controller_->engine()->ReloadSystemFonts(); 57 | break; 58 | } 59 | 60 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 61 | } 62 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.CreateAndShow(L"simple_app", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/path-yu/simple_app/cba8bf127bf6020dbb8b914b8bac7a5d79b6ed21/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | if (target_length == 0) { 52 | return std::string(); 53 | } 54 | std::string utf8_string; 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /windows/runner/win32_window.cpp: -------------------------------------------------------------------------------- 1 | #include "win32_window.h" 2 | 3 | #include 4 | 5 | #include "resource.h" 6 | 7 | namespace { 8 | 9 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 10 | 11 | // The number of Win32Window objects that currently exist. 12 | static int g_active_window_count = 0; 13 | 14 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 15 | 16 | // Scale helper to convert logical scaler values to physical using passed in 17 | // scale factor 18 | int Scale(int source, double scale_factor) { 19 | return static_cast(source * scale_factor); 20 | } 21 | 22 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 23 | // This API is only needed for PerMonitor V1 awareness mode. 24 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 25 | HMODULE user32_module = LoadLibraryA("User32.dll"); 26 | if (!user32_module) { 27 | return; 28 | } 29 | auto enable_non_client_dpi_scaling = 30 | reinterpret_cast( 31 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 32 | if (enable_non_client_dpi_scaling != nullptr) { 33 | enable_non_client_dpi_scaling(hwnd); 34 | FreeLibrary(user32_module); 35 | } 36 | } 37 | 38 | } // namespace 39 | 40 | // Manages the Win32Window's window class registration. 41 | class WindowClassRegistrar { 42 | public: 43 | ~WindowClassRegistrar() = default; 44 | 45 | // Returns the singleton registar instance. 46 | static WindowClassRegistrar* GetInstance() { 47 | if (!instance_) { 48 | instance_ = new WindowClassRegistrar(); 49 | } 50 | return instance_; 51 | } 52 | 53 | // Returns the name of the window class, registering the class if it hasn't 54 | // previously been registered. 55 | const wchar_t* GetWindowClass(); 56 | 57 | // Unregisters the window class. Should only be called if there are no 58 | // instances of the window. 59 | void UnregisterWindowClass(); 60 | 61 | private: 62 | WindowClassRegistrar() = default; 63 | 64 | static WindowClassRegistrar* instance_; 65 | 66 | bool class_registered_ = false; 67 | }; 68 | 69 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 70 | 71 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 72 | if (!class_registered_) { 73 | WNDCLASS window_class{}; 74 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 75 | window_class.lpszClassName = kWindowClassName; 76 | window_class.style = CS_HREDRAW | CS_VREDRAW; 77 | window_class.cbClsExtra = 0; 78 | window_class.cbWndExtra = 0; 79 | window_class.hInstance = GetModuleHandle(nullptr); 80 | window_class.hIcon = 81 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 82 | window_class.hbrBackground = 0; 83 | window_class.lpszMenuName = nullptr; 84 | window_class.lpfnWndProc = Win32Window::WndProc; 85 | RegisterClass(&window_class); 86 | class_registered_ = true; 87 | } 88 | return kWindowClassName; 89 | } 90 | 91 | void WindowClassRegistrar::UnregisterWindowClass() { 92 | UnregisterClass(kWindowClassName, nullptr); 93 | class_registered_ = false; 94 | } 95 | 96 | Win32Window::Win32Window() { 97 | ++g_active_window_count; 98 | } 99 | 100 | Win32Window::~Win32Window() { 101 | --g_active_window_count; 102 | Destroy(); 103 | } 104 | 105 | bool Win32Window::CreateAndShow(const std::wstring& title, 106 | const Point& origin, 107 | const Size& size) { 108 | Destroy(); 109 | 110 | const wchar_t* window_class = 111 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 112 | 113 | const POINT target_point = {static_cast(origin.x), 114 | static_cast(origin.y)}; 115 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 116 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 117 | double scale_factor = dpi / 96.0; 118 | 119 | HWND window = CreateWindow( 120 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 121 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 122 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 123 | nullptr, nullptr, GetModuleHandle(nullptr), this); 124 | 125 | if (!window) { 126 | return false; 127 | } 128 | 129 | return OnCreate(); 130 | } 131 | 132 | // static 133 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 134 | UINT const message, 135 | WPARAM const wparam, 136 | LPARAM const lparam) noexcept { 137 | if (message == WM_NCCREATE) { 138 | auto window_struct = reinterpret_cast(lparam); 139 | SetWindowLongPtr(window, GWLP_USERDATA, 140 | reinterpret_cast(window_struct->lpCreateParams)); 141 | 142 | auto that = static_cast(window_struct->lpCreateParams); 143 | EnableFullDpiSupportIfAvailable(window); 144 | that->window_handle_ = window; 145 | } else if (Win32Window* that = GetThisFromHandle(window)) { 146 | return that->MessageHandler(window, message, wparam, lparam); 147 | } 148 | 149 | return DefWindowProc(window, message, wparam, lparam); 150 | } 151 | 152 | LRESULT 153 | Win32Window::MessageHandler(HWND hwnd, 154 | UINT const message, 155 | WPARAM const wparam, 156 | LPARAM const lparam) noexcept { 157 | switch (message) { 158 | case WM_DESTROY: 159 | window_handle_ = nullptr; 160 | Destroy(); 161 | if (quit_on_close_) { 162 | PostQuitMessage(0); 163 | } 164 | return 0; 165 | 166 | case WM_DPICHANGED: { 167 | auto newRectSize = reinterpret_cast(lparam); 168 | LONG newWidth = newRectSize->right - newRectSize->left; 169 | LONG newHeight = newRectSize->bottom - newRectSize->top; 170 | 171 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 172 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 173 | 174 | return 0; 175 | } 176 | case WM_SIZE: { 177 | RECT rect = GetClientArea(); 178 | if (child_content_ != nullptr) { 179 | // Size and position the child window. 180 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 181 | rect.bottom - rect.top, TRUE); 182 | } 183 | return 0; 184 | } 185 | 186 | case WM_ACTIVATE: 187 | if (child_content_ != nullptr) { 188 | SetFocus(child_content_); 189 | } 190 | return 0; 191 | } 192 | 193 | return DefWindowProc(window_handle_, message, wparam, lparam); 194 | } 195 | 196 | void Win32Window::Destroy() { 197 | OnDestroy(); 198 | 199 | if (window_handle_) { 200 | DestroyWindow(window_handle_); 201 | window_handle_ = nullptr; 202 | } 203 | if (g_active_window_count == 0) { 204 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 205 | } 206 | } 207 | 208 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 209 | return reinterpret_cast( 210 | GetWindowLongPtr(window, GWLP_USERDATA)); 211 | } 212 | 213 | void Win32Window::SetChildContent(HWND content) { 214 | child_content_ = content; 215 | SetParent(content, window_handle_); 216 | RECT frame = GetClientArea(); 217 | 218 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 219 | frame.bottom - frame.top, true); 220 | 221 | SetFocus(child_content_); 222 | } 223 | 224 | RECT Win32Window::GetClientArea() { 225 | RECT frame; 226 | GetClientRect(window_handle_, &frame); 227 | return frame; 228 | } 229 | 230 | HWND Win32Window::GetHandle() { 231 | return window_handle_; 232 | } 233 | 234 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 235 | quit_on_close_ = quit_on_close; 236 | } 237 | 238 | bool Win32Window::OnCreate() { 239 | // No-op; provided for subclasses. 240 | return true; 241 | } 242 | 243 | void Win32Window::OnDestroy() { 244 | // No-op; provided for subclasses. 245 | } 246 | -------------------------------------------------------------------------------- /windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates and shows a win32 window with |title| and position and size using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size to will treat the width height passed in to this function 35 | // as logical pixels and scale to appropriate for the default monitor. Returns 36 | // true if the window was created successfully. 37 | bool CreateAndShow(const std::wstring& title, 38 | const Point& origin, 39 | const Size& size); 40 | 41 | // Release OS resources associated with window. 42 | void Destroy(); 43 | 44 | // Inserts |content| into the window tree. 45 | void SetChildContent(HWND content); 46 | 47 | // Returns the backing Window handle to enable clients to set icon and other 48 | // window properties. Returns nullptr if the window has been destroyed. 49 | HWND GetHandle(); 50 | 51 | // If true, closing this window will quit the application. 52 | void SetQuitOnClose(bool quit_on_close); 53 | 54 | // Return a RECT representing the bounds of the current client area. 55 | RECT GetClientArea(); 56 | 57 | protected: 58 | // Processes and route salient window messages for mouse handling, 59 | // size change and DPI. Delegates handling of these to member overloads that 60 | // inheriting classes can handle. 61 | virtual LRESULT MessageHandler(HWND window, 62 | UINT const message, 63 | WPARAM const wparam, 64 | LPARAM const lparam) noexcept; 65 | 66 | // Called when CreateAndShow is called, allowing subclass window-related 67 | // setup. Subclasses should return false if setup fails. 68 | virtual bool OnCreate(); 69 | 70 | // Called when Destroy is called. 71 | virtual void OnDestroy(); 72 | 73 | private: 74 | friend class WindowClassRegistrar; 75 | 76 | // OS callback called by message pump. Handles the WM_NCCREATE message which 77 | // is passed when the non-client area is being created and enables automatic 78 | // non-client DPI scaling so that the non-client area automatically 79 | // responsponds to changes in DPI. All other messages are handled by 80 | // MessageHandler. 81 | static LRESULT CALLBACK WndProc(HWND const window, 82 | UINT const message, 83 | WPARAM const wparam, 84 | LPARAM const lparam) noexcept; 85 | 86 | // Retrieves a class instance pointer for |window| 87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 88 | 89 | bool quit_on_close_ = false; 90 | 91 | // window handle for top level window. 92 | HWND window_handle_ = nullptr; 93 | 94 | // window handle for hosted content. 95 | HWND child_content_ = nullptr; 96 | }; 97 | 98 | #endif // RUNNER_WIN32_WINDOW_H_ 99 | --------------------------------------------------------------------------------