├── .github
└── workflows
│ └── split_apk.yml
├── .gitignore
├── .metadata
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── viewus
│ │ │ │ ├── hive_note_app
│ │ │ │ └── MainActivity.kt
│ │ │ │ └── v_notes
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-hdpi
│ │ │ └── ic_stat_onesignal_default.png
│ │ │ ├── drawable-mdpi
│ │ │ └── ic_stat_onesignal_default.png
│ │ │ ├── drawable-v21
│ │ │ └── launch_background.xml
│ │ │ ├── drawable-xhdpi
│ │ │ └── ic_stat_onesignal_default.png
│ │ │ ├── drawable-xxhdpi
│ │ │ └── ic_stat_onesignal_default.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ └── ic_stat_onesignal_default.png
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── values-night
│ │ │ └── styles.xml
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── settings_aar.gradle
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Podfile.lock
├── 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
│ ├── GoogleService-Info.plist
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
├── RunnerTests
│ └── RunnerTests.swift
└── build
│ └── Pods.build
│ └── Release-iphonesimulator
│ └── abseil.build
│ └── dgph
├── lib
├── app
│ └── src
│ │ └── app.dart
├── config
│ └── router
│ │ ├── navigates_to.dart
│ │ ├── route_guard.dart
│ │ ├── routes.dart
│ │ └── routes_name.dart
├── data
│ ├── exceptions
│ │ ├── app_exceptions.dart
│ │ └── user_suspended_exception.dart
│ ├── models
│ │ ├── cloud_note_models
│ │ │ ├── cloud_note_model.dart
│ │ │ ├── cloud_note_model.g.dart
│ │ │ ├── user_model.dart
│ │ │ └── user_model.g.dart
│ │ └── local_note_model
│ │ │ ├── note_model.dart
│ │ │ └── note_model.g.dart
│ ├── network
│ │ ├── base_api_service.dart
│ │ └── network_services_api.dart
│ └── repositories
│ │ ├── auth_repository.dart
│ │ └── cloud_note_repository.dart
├── helpers
│ └── hive_manager.dart
├── main.dart
├── presentation
│ ├── pages
│ │ ├── cloud_notes
│ │ │ ├── auth
│ │ │ │ ├── forgot_password.dart
│ │ │ │ ├── login_screen.dart
│ │ │ │ ├── register_screen.dart
│ │ │ │ ├── reset_password.dart
│ │ │ │ └── verify_code.dart
│ │ │ ├── cloud_create_note.dart
│ │ │ ├── cloud_edit_note.dart
│ │ │ ├── cloud_notes.dart
│ │ │ └── cloud_read_note_screen.dart
│ │ ├── home
│ │ │ └── home.dart
│ │ ├── local_notes
│ │ │ ├── create_note_screen.dart
│ │ │ ├── edit_note_screen.dart
│ │ │ ├── local_notes.dart
│ │ │ └── read_notes_screens.dart
│ │ ├── notifications
│ │ │ └── notifications_view.dart
│ │ ├── settings
│ │ │ └── settings_screen.dart
│ │ └── trash
│ │ │ └── trashed_notes.dart
│ ├── views.dart
│ └── widget
│ │ ├── mNew_text_widget.dart
│ │ └── mbutton.dart
├── services
│ └── service_locator.dart
├── state
│ └── cubits
│ │ ├── auth_cubit
│ │ ├── auth_cubit.dart
│ │ └── auth_state.dart
│ │ ├── cloud_note_cubit
│ │ ├── cloud_note_cubit.dart
│ │ └── cloud_note_state.dart
│ │ ├── note_style_cubit
│ │ ├── note_style_cubit.dart
│ │ └── note_style_state.dart
│ │ ├── play_button_cubit
│ │ ├── play_button_cubit.dart
│ │ └── play_button_state.dart
│ │ └── theme_cubit
│ │ ├── theme_cubit.dart
│ │ └── theme_state.dart
└── utils
│ ├── colors
│ └── m_colors.dart
│ ├── const_values.dart
│ ├── constant
│ ├── api_constant.dart
│ └── asset_dir.dart
│ ├── greetings.dart
│ ├── text_style
│ └── m_text_style.dart
│ ├── themes
│ └── custom_theme.dart
│ └── tools
│ ├── hex_to_color.dart
│ ├── message_dialog.dart
│ ├── money_formatter.dart
│ ├── sized_box_ex.dart
│ └── slide_transition.dart
├── pubspec.lock
├── pubspec.yaml
├── shorebird.yaml
├── ss
├── 145535058_221013679682765_5476232847126577393_n.png
└── 145944556_3545455032189582_7968282205009447548_n.png
└── test
├── unit_test.dart
└── widget_test.dart
/.github/workflows/split_apk.yml:
--------------------------------------------------------------------------------
1 | #name: Build V Notes Android APK
2 | #on:
3 | # push:
4 | # branches:
5 | # - master
6 | # pull_request:
7 | # branches:
8 | # - master
9 | #jobs:
10 | # build:
11 | # name: Build APK's
12 | # runs-on: ubuntu-latest
13 | # steps:
14 | # - uses: actions/checkout@v2
15 | # - uses: actions/setup-java@v1
16 | # with:
17 | # java-version: '12.x'
18 | # - uses: subosito/flutter-action@v1.5.3
19 | # with:
20 | # channel: "stable"
21 | # - run: flutter pub get
22 | # - run: flutter pub outdated
23 | # - run: flutter pub upgrade
24 | # - run: flutter build apk --release
25 | # - uses: ncipollo/release-action@v1
26 | # with:
27 | # artifacts: 'build/app/outputs/flutter-apk/app-release.apk'
28 | # token: ${{ secrets.GITHUB_TOKEN }}
29 | # commit: master
30 | # tag: v1.0.${{ github.run_number }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 |
33 | # Web related
34 | lib/generated_plugin_registrant.dart
35 |
36 | # Symbolication related
37 | app.*.symbols
38 |
39 | # Obfuscation related
40 | app.*.map.json
41 |
42 | # Exceptions to above rules.
43 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
44 |
45 | # env
46 | .env
47 |
48 | api_credentials.yaml
49 |
--------------------------------------------------------------------------------
/.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: "dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668"
8 | channel: "stable"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
17 | base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
18 | - platform: android
19 | create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
20 | base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
21 | - platform: ios
22 | create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
23 | base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
24 | - platform: linux
25 | create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
26 | base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
27 | - platform: macos
28 | create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
29 | base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
30 | - platform: web
31 | create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
32 | base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
33 | - platform: windows
34 | create_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
35 | base_revision: dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668
36 |
37 | # User provided section
38 |
39 | # List of Local paths (relative to this file) that should be
40 | # ignored by the migrate tool.
41 | #
42 | # Files that are not part of the templates will be ignored by default.
43 | unmanaged_files:
44 | - 'lib/main.dart'
45 | - 'ios/Runner.xcodeproj/project.pbxproj'
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## VNotes
2 |
3 |
This is an old project I was working on when learning to use Hive, will start working on it full time and will be adding new features as soon as possible.
4 | [Quick Update: I am currently working on the Project Full Time to make sure users get the best out of the app.]
5 |
6 | You can download the app from Play Store Here
7 |
8 | Any contribution will be accepted if it meets what is needed, Thanks.
9 |
10 | [//]: # ( If you see this error "Google Service Json Not Found" )
11 | Please delete pubspec.lock if you are having some issues and if you want to make a pull request, please ignore pubspec.lock Thanks
12 |
13 | ## Plugin's Used
14 | 1. Hive (To locally store Data)
15 | 2. Toast (To display useful message)
16 | 3. Path Provider
17 | 4. Upgrader (To alert users when app has a new update)
18 | 5. Bloc (Cubit) (For State management)
19 | 6. Shared Preferences (To Persist some values)
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | ## Getting Started
33 | For help getting started with Flutter, view our
34 | [online documentation](https://flutter.dev/docs), which offers tutorials,
35 | samples, guidance on mobile development, and a full API reference.
36 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 |
2 |
3 | # This file configures the analyzer, which statically analyzes Dart code to
4 | # check for errors, warnings, and lints.
5 | #
6 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
7 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
8 | # invoked from the command line by running `flutter analyze`.
9 |
10 | # The following line activates a set of recommended lints for Flutter apps,
11 | # packages, and plugins designed to encourage good coding practices.
12 | include: package:flutter_lints/flutter.yaml
13 |
14 | linter:
15 | # The lint rules applied to this project can be customized in the
16 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
17 | # included above or to enable additional rules. A list of all available lints
18 | # and their documentation is published at
19 | # https://dart-lang.github.io/linter/lints/index.html.
20 | #
21 | # Instead of disabling a lint rule for the entire project in the
22 | # section below, it can also be suppressed for a single line of code
23 | # or a specific dart file by using the `// ignore: name_of_lint` and
24 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
25 | # producing the lint.
26 | rules:
27 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
28 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
29 |
30 | # Additional information about this file can be found at
31 | # https://dart.dev/guides/language/analysis-options
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 | /key.properties
9 |
10 | #google json file
11 | app/google-services.json
12 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
5 | id "dev.flutter.flutter-gradle-plugin"
6 | }
7 |
8 | def keystoreProperties = new Properties()
9 | def keystorePropertiesFile = rootProject.file('key.properties')
10 | if (keystorePropertiesFile.exists()) {
11 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
12 | }
13 |
14 | android {
15 | namespace = "com.viewus.v_notes"
16 | compileSdkVersion 35
17 |
18 | compileOptions {
19 | sourceCompatibility JavaVersion.VERSION_1_8
20 | targetCompatibility JavaVersion.VERSION_1_8
21 | }
22 |
23 | kotlinOptions {
24 | jvmTarget = '1.8'
25 | }
26 |
27 | sourceSets {
28 | main.java.srcDirs += 'src/main/kotlin'
29 | }
30 |
31 | defaultConfig {
32 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
33 | applicationId "com.viewus.v_notes"
34 | minSdkVersion 22
35 | targetSdkVersion 35
36 | multiDexEnabled true
37 | versionCode flutter.versionCode
38 | versionName flutter.versionName
39 | }
40 |
41 | signingConfigs {
42 | release {
43 | keyAlias keystoreProperties['keyAlias']
44 | keyPassword keystoreProperties['keyPassword']
45 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
46 | storePassword keystoreProperties['storePassword']
47 | }
48 | }
49 |
50 | buildTypes {
51 | release {
52 | // TODO: Add your own signing config for the release build.
53 | // Signing with the debug keys for now, so `flutter run --release` works.
54 | signingConfig signingConfigs.release
55 |
56 | minifyEnabled true
57 | shrinkResources true
58 | debuggable false
59 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
60 | }
61 | }
62 | }
63 |
64 | flutter {
65 | source '../..'
66 | }
67 |
68 | dependencies {
69 | def multidex_version = "2.0.1"
70 | implementation "androidx.multidex:multidex:$multidex_version"
71 | }
72 |
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | #Flutter Wrapper
2 | -keep class io.flutter.app.** { *; }
3 | -keep class io.flutter.plugin.** { *; }
4 | -keep class io.flutter.util.** { *; }
5 | -keep class io.flutter.view.** { *; }
6 | -keep class io.flutter.** { *; }
7 | -keep class io.flutter.plugins.** { *; }
8 | # -keep class com.google.firebase.** { *; } // uncomment this if you are using firebase in the project
9 | -dontwarn io.flutter.embedding.**
10 | -ignorewarnings
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
9 |
13 |
21 |
25 |
29 |
32 |
37 |
41 |
42 |
43 |
44 |
45 |
46 |
48 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/viewus/hive_note_app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.viewus.hive_note_app
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity()
6 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/viewus/v_notes/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.viewus.v_notes
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_stat_onesignal_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/android/app/src/main/res/drawable-hdpi/ic_stat_onesignal_default.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/ic_stat_onesignal_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/android/app/src/main/res/drawable-mdpi/ic_stat_onesignal_default.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/ic_stat_onesignal_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/android/app/src/main/res/drawable-xhdpi/ic_stat_onesignal_default.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/ic_stat_onesignal_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/android/app/src/main/res/drawable-xxhdpi/ic_stat_onesignal_default.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/ic_stat_onesignal_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/android/app/src/main/res/drawable-xxxhdpi/ic_stat_onesignal_default.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = "../build"
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | }
12 | subprojects {
13 | project.evaluationDependsOn(":app")
14 | }
15 |
16 | tasks.register("clean", Delete) {
17 | delete rootProject.buildDir
18 | }
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
2 | android.useAndroidX=true
3 | android.enableJetifier=true
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Mar 07 11:00:26 WAT 2022
2 | distributionBase=GRADLE_USER_HOME
3 | #distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
5 | distributionPath=wrapper/dists
6 | zipStorePath=wrapper/dists
7 | zipStoreBase=GRADLE_USER_HOME
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }()
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | plugins {
20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
21 | id "com.android.application" version "8.3.2" apply false
22 | id "org.jetbrains.kotlin.android" version "1.9.10" apply false
23 | }
24 |
25 | include ":app"
--------------------------------------------------------------------------------
/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | platform :ios, '13.0'
3 | inhibit_all_warnings!
4 |
5 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
6 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
7 |
8 | project 'Runner', {
9 | 'Debug' => :debug,
10 | 'Profile' => :release,
11 | 'Release' => :release,
12 | }
13 |
14 | def flutter_root
15 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
16 | unless File.exist?(generated_xcode_build_settings_path)
17 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
18 | end
19 |
20 | File.foreach(generated_xcode_build_settings_path) do |line|
21 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
22 | return matches[1].strip if matches
23 | end
24 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
25 | end
26 |
27 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
28 |
29 | flutter_ios_podfile_setup
30 |
31 | target 'Runner' do
32 | use_frameworks!
33 | use_modular_headers!
34 |
35 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
36 | end
37 |
38 | post_install do |installer|
39 | installer.pods_project.targets.each do |target|
40 | flutter_additional_ios_build_settings(target)
41 | target.build_configurations.each do |config|
42 | if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f < 13.0
43 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
44 | config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64 i386"
45 | config.build_settings['ENABLE_BITCODE'] = 'NO'
46 | config.build_settings['SWIFT_VERSION'] = '5.0'
47 | end
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - flutter_tts (0.0.1):
4 | - Flutter
5 | - fluttertoast (0.0.2):
6 | - Flutter
7 | - Toast
8 | - package_info_plus (0.4.5):
9 | - Flutter
10 | - path_provider_foundation (0.0.1):
11 | - Flutter
12 | - FlutterMacOS
13 | - shared_preferences_foundation (0.0.1):
14 | - Flutter
15 | - FlutterMacOS
16 | - Toast (4.1.1)
17 |
18 | DEPENDENCIES:
19 | - Flutter (from `Flutter`)
20 | - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`)
21 | - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
22 | - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
23 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
24 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
25 |
26 | SPEC REPOS:
27 | trunk:
28 | - Toast
29 |
30 | EXTERNAL SOURCES:
31 | Flutter:
32 | :path: Flutter
33 | flutter_tts:
34 | :path: ".symlinks/plugins/flutter_tts/ios"
35 | fluttertoast:
36 | :path: ".symlinks/plugins/fluttertoast/ios"
37 | package_info_plus:
38 | :path: ".symlinks/plugins/package_info_plus/ios"
39 | path_provider_foundation:
40 | :path: ".symlinks/plugins/path_provider_foundation/darwin"
41 | shared_preferences_foundation:
42 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
43 |
44 | SPEC CHECKSUMS:
45 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
46 | flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d
47 | fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
48 | package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
49 | path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
50 | shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
51 | Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
52 |
53 | PODFILE CHECKSUM: 68b745aaca7f4f7b981818ae727abd881eccad0d
54 |
55 | COCOAPODS: 1.16.2
56 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/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 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @main
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 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/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/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CLIENT_ID
6 | 120784368845-a7n0rij9jepli3uvdbde6innv01h419d.apps.googleusercontent.com
7 | REVERSED_CLIENT_ID
8 | com.googleusercontent.apps.120784368845-a7n0rij9jepli3uvdbde6innv01h419d
9 | ANDROID_CLIENT_ID
10 | 120784368845-p5ids6tgl3tmkk94fkfvukoj2bgt32nj.apps.googleusercontent.com
11 | API_KEY
12 | AIzaSyDkVKVJCO26sawYxDeKUiN4uZWsyAemZkA
13 | GCM_SENDER_ID
14 | 120784368845
15 | PLIST_VERSION
16 | 1
17 | BUNDLE_ID
18 | com.example.noteApp
19 | PROJECT_ID
20 | vnotes-8af7a
21 | STORAGE_BUCKET
22 | vnotes-8af7a.appspot.com
23 | IS_ADS_ENABLED
24 |
25 | IS_ANALYTICS_ENABLED
26 |
27 | IS_APPINVITE_ENABLED
28 |
29 | IS_GCM_ENABLED
30 |
31 | IS_SIGNIN_ENABLED
32 |
33 | GOOGLE_APP_ID
34 | 1:120784368845:ios:ff9262eb2890cd431ba584
35 |
36 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | VNotes
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | CADisableMinimumFrameDurationOnPhone
45 |
46 | UIApplicationSupportsIndirectInputEvents
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/ios/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/ios/build/Pods.build/Release-iphonesimulator/abseil.build/dgph:
--------------------------------------------------------------------------------
1 | DGPH1.04Jun 8 202114:00:05 / Users
godsendjoseph AndroidStudioProjects
hive_note_app ios Pods
--------------------------------------------------------------------------------
/lib/app/src/app.dart:
--------------------------------------------------------------------------------
1 | import 'package:flashy_flushbar/flashy_flushbar_provider.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_quill/flutter_quill.dart';
4 | import 'package:flutter_screenutil/flutter_screenutil.dart';
5 | import 'package:note_app/config/router/routes.dart';
6 | import 'package:note_app/config/router/routes_name.dart';
7 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
8 | import 'package:note_app/utils/themes/custom_theme.dart';
9 | import 'package:flutter_bloc/flutter_bloc.dart';
10 |
11 | class App extends StatefulWidget {
12 | const App({Key? key}) : super(key: key);
13 |
14 | @override
15 | _AppState createState() => _AppState();
16 | }
17 |
18 | class _AppState extends State {
19 | @override
20 | Widget build(BuildContext context) {
21 | return ScreenUtilInit(
22 | designSize: const Size(375, 812),
23 | builder: (BuildContext context, Widget? child) {
24 | return MaterialApp(
25 | debugShowCheckedModeBanner: false,
26 | theme: context.watch().state.isDarkTheme == false
27 | ? buildLightTheme()
28 | : buildDarkTheme(),
29 | title: 'VNotes',
30 | builder: FlashyFlushbarProvider.init(),
31 | initialRoute: RoutesName.home_screen,
32 | onGenerateRoute: Routes.generateRoute,
33 | localizationsDelegates: [
34 | FlutterQuillLocalizations.delegate,
35 | ],
36 | );
37 | },
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/config/router/navigates_to.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | Future navigateTo(
4 | BuildContext context, {
5 | required String destination,
6 | Map? arguments,
7 | }) {
8 | return Navigator.pushNamed(
9 | context,
10 | destination,
11 | arguments: arguments,
12 | );
13 | }
14 |
15 | Future navigateReplaceTo(
16 | BuildContext context, {
17 | required String destination,
18 | Map? arguments,
19 | }) {
20 | return Navigator.pushReplacementNamed(context, destination,
21 | arguments: arguments);
22 | }
23 |
24 | Future navigateEndTo(
25 | BuildContext context, {
26 | required String destination,
27 | Map? arguments,
28 | }) {
29 | return Navigator.pushNamedAndRemoveUntil(
30 | context,
31 | destination,
32 | arguments: arguments,
33 | (route) => false,
34 | );
35 | }
--------------------------------------------------------------------------------
/lib/config/router/route_guard.dart:
--------------------------------------------------------------------------------
1 | import 'package:note_app/config/router/routes_name.dart';
2 |
3 | class RoutesGuard {
4 | static final protectedRoutes = [
5 | RoutesName.cloud_notes,
6 | RoutesName.cloud_create_notes_screen,
7 | RoutesName.cloud_edit_notes_screen,
8 | RoutesName.cloud_read_notes_screen,
9 | ];
10 |
11 | static final requiresVerification = [
12 | ...protectedRoutes,
13 | ];
14 |
15 | static bool requiresAuth(String? routeName) {
16 | return protectedRoutes.contains(routeName);
17 | }
18 |
19 | static bool requiresEmailVerification(String? routeName) {
20 | return requiresVerification.contains(routeName);
21 | }
22 | }
--------------------------------------------------------------------------------
/lib/config/router/routes.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:note_app/config/router/route_guard.dart';
3 | import 'package:note_app/config/router/routes_name.dart';
4 | import 'package:note_app/presentation/views.dart';
5 | import 'package:note_app/services/service_locator.dart';
6 | import 'package:note_app/state/cubits/auth_cubit/auth_cubit.dart';
7 |
8 | class Routes {
9 | static Route generateRoute(RouteSettings settings) {
10 | final authCubit = getIt();
11 | final state = authCubit.state;
12 | final args = settings.arguments as Map?;
13 |
14 | if (RoutesGuard.requiresAuth(settings.name)) {
15 | if (state is AuthEmailUnverified) {
16 | return MaterialPageRoute(
17 | builder: (_) => const VerifyCode(from: 'protected_route'),
18 | maintainState: true, // Preserve route state
19 | );
20 | }
21 |
22 | if (state is! AuthAuthenticated) {
23 | authCubit.saveAttemptedRoute(settings.name!);
24 | return MaterialPageRoute(
25 | builder: (_) => const LoginScreen(),
26 | maintainState: true,
27 | );
28 | }
29 | }
30 |
31 | switch (settings.name) {
32 | //
33 | case RoutesName.home_screen:
34 | return MaterialPageRoute(builder: (_) => const HomeScreen());
35 |
36 | case RoutesName.local_notes:
37 | return MaterialPageRoute(builder: (_) => const LocalNotesScreen());
38 |
39 | case RoutesName.cloud_notes:
40 | return MaterialPageRoute(builder: (_) => const CloudNotesScreen());
41 |
42 | case RoutesName.settings_screen:
43 | return MaterialPageRoute(builder: (_) => const SettingsScreen());
44 |
45 | case RoutesName.create_notes_screen:
46 | return MaterialPageRoute(builder: (_) => const CreateNoteScreen());
47 |
48 | case RoutesName.trash_notes:
49 | return MaterialPageRoute(builder: (_) => const TrashedNotes());
50 | //
51 |
52 | // Local Notes
53 | case RoutesName.read_notes_screen:
54 | return MaterialPageRoute(
55 | builder: (_) => ReadNotesScreen(
56 | note: args!['note'],
57 | noteKey: args['noteKey'],
58 | ));
59 |
60 | case RoutesName.edit_notes_screen:
61 | return MaterialPageRoute(
62 | builder: (_) => EditNoteScreen(
63 | notes: args!['notes'],
64 | noteKey: args['noteKey'],
65 | ));
66 | // ends here
67 |
68 | // Cloud Notes
69 | case RoutesName.cloud_create_notes_screen:
70 | return MaterialPageRoute(builder: (_) => const CloudCreateNote());
71 |
72 | case RoutesName.cloud_read_notes_screen:
73 | return MaterialPageRoute(
74 | builder: (_) => CloudReadNote(
75 | note: args!['note'],
76 | noteKey: args['noteKey'],
77 | ));
78 |
79 | case RoutesName.cloud_edit_notes_screen:
80 | return MaterialPageRoute(
81 | builder: (_) => CloudEditNote(
82 | notes: args!['notes'],
83 | noteKey: args['noteKey'],
84 | ));
85 | // ends here
86 |
87 | //Auth
88 | case RoutesName.login_screen:
89 | return MaterialPageRoute(builder: (_) => const LoginScreen());
90 |
91 | case RoutesName.register_screen:
92 | return MaterialPageRoute(builder: (_) => const RegisterScreen());
93 |
94 | case RoutesName.verify_code_screen:
95 | return MaterialPageRoute(
96 | builder: (_) => VerifyCode(
97 | from: args!['from'],
98 | ));
99 | // ends here
100 |
101 | default:
102 | return MaterialPageRoute(
103 | builder: (_) {
104 | return const Scaffold(
105 | body: Center(
106 | child: Text(
107 | 'No Routes Generated',
108 | ),
109 | ),
110 | );
111 | },
112 | );
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/lib/config/router/routes_name.dart:
--------------------------------------------------------------------------------
1 | class RoutesName {
2 | // initiate routes
3 | static const String home_screen = 'home_screen';
4 | static const String local_notes = 'local_notes';
5 | static const String cloud_notes = 'cloud_notes';
6 | static const String settings_screen = 'settings_screen';
7 | static const String trash_notes = 'trash_notes';
8 | static const String wrapper = 'wrapper';
9 |
10 | // ends here
11 |
12 | // Local Notes Route
13 | static const String create_notes_screen = 'create_notes_screen';
14 | static const String read_notes_screen = 'read_notes_screen';
15 | static const String edit_notes_screen = 'edit_notes_screen';
16 |
17 | // ends here
18 |
19 | // Cloud Notes Route
20 | static const String cloud_create_notes_screen = 'cloud_create_notes_screen';
21 | static const String cloud_edit_notes_screen = 'cloud_edit_notes_screen';
22 | static const String cloud_read_notes_screen = 'cloud_read_notes_screen';
23 |
24 | // ends here
25 |
26 | // Auth Route
27 | static const String login_screen = 'login_screen';
28 | static const String register_screen = 'register_screen';
29 | static const String verify_code_screen = 'verify_code_screen';
30 | // ends here
31 | }
32 |
--------------------------------------------------------------------------------
/lib/data/exceptions/app_exceptions.dart:
--------------------------------------------------------------------------------
1 | /// Base class for custom application exceptions.
2 | class AppExceptions implements Exception {
3 | final _message; // Message associated with the exception
4 | final _prefix; // Prefix for the exception
5 |
6 | /// Constructor for creating a [NoInternAppExceptionsetException] instance.
7 | ///
8 | /// the [message] parameter represents the message associated with the exception.
9 | /// the [prefix] parameter represents the prefix for the exception.
10 | AppExceptions([this._message, this._prefix]);
11 |
12 | @override
13 | String toString() {
14 | return _message?.toString() ?? _prefix;
15 | }
16 | }
17 |
18 | /// Exception class representing a no internet connection error.
19 | class NoInternetException extends AppExceptions {
20 | /// Constructor for creating a [NoInternetException] instance.
21 | ///
22 | /// the [message] parameter represents the error message.
23 | NoInternetException([String? message])
24 | : super(message, 'No Internet Connection.');
25 | }
26 |
27 | /// Exception class representing a n unauthorized access error.
28 | class UnauthorisedException extends AppExceptions {
29 | /// Constructor for creating a [UnauthorisedException] instance.
30 | ///
31 | /// the [message] parameter represents the error message.
32 | UnauthorisedException([String? message])
33 | : super(message, 'You do not have access to this.');
34 | }
35 |
36 | /// Exception class representing network request timing out error.
37 | class RequestTimeOutException extends AppExceptions {
38 | /// Constructor for creating a [RequestTimeOutException] instance.
39 | ///
40 | /// the [message] parameter represents the error message.
41 | RequestTimeOutException([String? message])
42 | : super(message, 'Request Time Out.');
43 | }
44 |
45 | /// Exception class representing a data fetching error during communication.
46 | class FetchDataException extends AppExceptions {
47 | /// Constructor for creating a [FetchDataException] instance.
48 | ///
49 | /// the [message] parameter represents the error message.
50 | FetchDataException([String? message]) : super(message, '');
51 | }
52 |
53 | /// Exception class representing an Invalid input error.
54 | class InvalidInputException extends AppExceptions {
55 | /// Constructor for creating a [InvalidInputException] instance.
56 | ///
57 | /// the [message] parameter represents the error message.
58 | InvalidInputException([String? message]) : super(message, 'Invalid Input');
59 | }
60 |
61 | /// Exception class representing a bad request error.
62 | class BadRequestException extends AppExceptions {
63 | /// Constructor for creating a [BadRequestException] instance.
64 | ///
65 | /// the [message] parameter represents the error message.
66 | BadRequestException([String? message]) : super(message, 'Invalid Request');
67 | }
--------------------------------------------------------------------------------
/lib/data/exceptions/user_suspended_exception.dart:
--------------------------------------------------------------------------------
1 | class UserSuspendedException implements Exception {
2 | final String message;
3 | UserSuspendedException(this.message);
4 | @override
5 | String toString() => message;
6 | }
--------------------------------------------------------------------------------
/lib/data/models/cloud_note_models/cloud_note_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 |
3 | part 'cloud_note_model.g.dart';
4 |
5 | @HiveType(typeId: 1)
6 | class CloudNoteModel {
7 | @HiveField(1)
8 | final int? id;
9 |
10 | @HiveField(2)
11 | final String? uuid;
12 |
13 | @HiveField(3)
14 | final String? title;
15 |
16 | @HiveField(4)
17 | final String? notes;
18 |
19 | CloudNoteModel({
20 | this.id,
21 | this.uuid,
22 | this.title,
23 | this.notes,
24 | });
25 |
26 | factory CloudNoteModel.fromJson(Map response) {
27 | return CloudNoteModel(
28 | id: response['id'],
29 | uuid: response['uuid'],
30 | title: response['note_title'],
31 | notes: response['note_content'],
32 | );
33 | }
34 |
35 | static List parseResponse(Map responseData) {
36 | if (responseData['success'] == true && responseData['data'] != null) {
37 | List noteData = responseData['data'];
38 | return noteData.map((json) => CloudNoteModel.fromJson(json)).toList();
39 | }
40 | return [];
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/lib/data/models/cloud_note_models/cloud_note_model.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'cloud_note_model.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class CloudNoteModelAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 1;
12 |
13 | @override
14 | CloudNoteModel read(BinaryReader reader) {
15 | final numOfFields = reader.readByte();
16 | final fields = {
17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
18 | };
19 | return CloudNoteModel(
20 | id: fields[1] as int?,
21 | uuid: fields[2] as String?,
22 | title: fields[3] as String?,
23 | notes: fields[4] as String?,
24 | );
25 | }
26 |
27 | @override
28 | void write(BinaryWriter writer, CloudNoteModel obj) {
29 | writer
30 | ..writeByte(4)
31 | ..writeByte(1)
32 | ..write(obj.id)
33 | ..writeByte(2)
34 | ..write(obj.uuid)
35 | ..writeByte(3)
36 | ..write(obj.title)
37 | ..writeByte(4)
38 | ..write(obj.notes);
39 | }
40 |
41 | @override
42 | int get hashCode => typeId.hashCode;
43 |
44 | @override
45 | bool operator ==(Object other) =>
46 | identical(this, other) ||
47 | other is CloudNoteModelAdapter &&
48 | runtimeType == other.runtimeType &&
49 | typeId == other.typeId;
50 | }
51 |
--------------------------------------------------------------------------------
/lib/data/models/cloud_note_models/user_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 |
3 | part 'user_model.g.dart';
4 |
5 | @HiveType(typeId: 2)
6 | class UserModel {
7 | @HiveField(1)
8 | final int? id;
9 |
10 | @HiveField(2)
11 | final String? uuid;
12 |
13 | @HiveField(3)
14 | final String? firstName;
15 |
16 | @HiveField(4)
17 | final String? lastName;
18 |
19 | @HiveField(5)
20 | final String? userName;
21 |
22 | @HiveField(6)
23 | final String? email;
24 |
25 | @HiveField(7)
26 | final String? emailVerified;
27 |
28 | @HiveField(8)
29 | final int? hasSubscription;
30 |
31 | @HiveField(9)
32 | final int? hasExceedTier;
33 |
34 | @HiveField(10)
35 | final String? planDuration;
36 |
37 | @HiveField(11)
38 | final String? planSubDate;
39 |
40 | @HiveField(12)
41 | final String? accessToken;
42 |
43 | UserModel({
44 | this.id,
45 | this.uuid,
46 | this.firstName,
47 | this.lastName,
48 | this.userName,
49 | this.email,
50 | this.emailVerified,
51 | this.hasSubscription,
52 | this.hasExceedTier,
53 | this.planDuration,
54 | this.planSubDate,
55 | this.accessToken,
56 | });
57 |
58 |
59 | factory UserModel.fromJsonLocalToken(responseData) {
60 | return UserModel(
61 | accessToken: responseData['data']['token'],
62 | );
63 | }
64 |
65 | factory UserModel.fromJsonUserDetails(responseData) {
66 | return UserModel(
67 | id: responseData['data']['user']['id'],
68 | uuid: responseData['data']['user']['uuid'],
69 | firstName: responseData['data']['user']['first_name'],
70 | lastName: responseData['data']['user']['last_name'],
71 | userName: responseData['data']['user']['username'],
72 | email: responseData['data']['user']['email'],
73 | emailVerified: responseData['data']['user']['email_verified_at'],
74 | hasSubscription: responseData['data']['user']['has_subscription'],
75 | hasExceedTier: responseData['data']['user']['has_exceed_tier'],
76 | planDuration: responseData['data']['user']['plan_duration'],
77 | planSubDate: responseData['data']['user']['plan_sub_date'],
78 | );
79 | }
80 | }
--------------------------------------------------------------------------------
/lib/data/models/cloud_note_models/user_model.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'user_model.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class UserModelAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 2;
12 |
13 | @override
14 | UserModel read(BinaryReader reader) {
15 | final numOfFields = reader.readByte();
16 | final fields = {
17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
18 | };
19 | return UserModel(
20 | id: fields[1] as int?,
21 | uuid: fields[2] as String?,
22 | firstName: fields[3] as String?,
23 | lastName: fields[4] as String?,
24 | userName: fields[5] as String?,
25 | email: fields[6] as String?,
26 | emailVerified: fields[7] as String?,
27 | hasSubscription: fields[8] as int?,
28 | hasExceedTier: fields[9] as int?,
29 | planDuration: fields[10] as String?,
30 | planSubDate: fields[11] as String?,
31 | accessToken: fields[12] as String?,
32 | );
33 | }
34 |
35 | @override
36 | void write(BinaryWriter writer, UserModel obj) {
37 | writer
38 | ..writeByte(12)
39 | ..writeByte(1)
40 | ..write(obj.id)
41 | ..writeByte(2)
42 | ..write(obj.uuid)
43 | ..writeByte(3)
44 | ..write(obj.firstName)
45 | ..writeByte(4)
46 | ..write(obj.lastName)
47 | ..writeByte(5)
48 | ..write(obj.userName)
49 | ..writeByte(6)
50 | ..write(obj.email)
51 | ..writeByte(7)
52 | ..write(obj.emailVerified)
53 | ..writeByte(8)
54 | ..write(obj.hasSubscription)
55 | ..writeByte(9)
56 | ..write(obj.hasExceedTier)
57 | ..writeByte(10)
58 | ..write(obj.planDuration)
59 | ..writeByte(11)
60 | ..write(obj.planSubDate)
61 | ..writeByte(12)
62 | ..write(obj.accessToken);
63 | }
64 |
65 | @override
66 | int get hashCode => typeId.hashCode;
67 |
68 | @override
69 | bool operator ==(Object other) =>
70 | identical(this, other) ||
71 | other is UserModelAdapter &&
72 | runtimeType == other.runtimeType &&
73 | typeId == other.typeId;
74 | }
75 |
--------------------------------------------------------------------------------
/lib/data/models/local_note_model/note_model.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_quill/flutter_quill.dart';
5 | import 'package:flutter_quill/quill_delta.dart';
6 | import 'package:hive/hive.dart';
7 |
8 | part 'note_model.g.dart';
9 |
10 | @HiveType(typeId: 0)
11 | class NoteModel {
12 | @HiveField(0)
13 | final String? title;
14 |
15 | @HiveField(1)
16 | final String? notes;
17 |
18 | @HiveField(2)
19 | final dynamic dateTime;
20 |
21 | NoteModel({
22 | this.title,
23 | this.notes,
24 | this.dateTime,
25 | });
26 |
27 | QuillController get quillController {
28 | try {
29 | // Try to parse as Quill JSON
30 | final List jsonData = jsonDecode(notes ?? "");
31 | final delta = Delta.fromJson(jsonData);
32 | final document = Document.fromDelta(delta);
33 |
34 | return QuillController(
35 | document: document,
36 | selection: const TextSelection.collapsed(offset: 0),
37 | );
38 | } catch (e) {
39 | // If parsing fails, it's likely a legacy note with plain text
40 | // Create a Document with the plain text content
41 | final delta = Delta()
42 | ..insert(notes ?? "")
43 | ..insert('\n'); // Ensure it ends with a newline for proper formatting
44 |
45 | final document = Document.fromDelta(delta);
46 |
47 | return QuillController(
48 | document: document,
49 | selection: const TextSelection.collapsed(offset: 0),
50 | );
51 | }
52 | }
53 |
54 | // Extract plain text from notes (whether it's Quill JSON or plain text)
55 | String getPlainText() {
56 | try {
57 | // Try to parse as Quill JSON first
58 | final List jsonData = jsonDecode(notes ?? "");
59 | final delta = Delta.fromJson(jsonData);
60 | final document = Document.fromDelta(delta);
61 | return document.toPlainText().trim();
62 | } catch (e) {
63 | // If parsing fails, return the raw text
64 | return notes ?? "";
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/data/models/local_note_model/note_model.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'note_model.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class NoteModelAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 0;
12 |
13 | @override
14 | NoteModel read(BinaryReader reader) {
15 | final numOfFields = reader.readByte();
16 | final fields = {
17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
18 | };
19 | return NoteModel(
20 | title: fields[0] as String?,
21 | notes: fields[1] as String?,
22 | dateTime: fields[2] as dynamic,
23 | );
24 | }
25 |
26 | @override
27 | void write(BinaryWriter writer, NoteModel obj) {
28 | writer
29 | ..writeByte(3)
30 | ..writeByte(0)
31 | ..write(obj.title)
32 | ..writeByte(1)
33 | ..write(obj.notes)
34 | ..writeByte(2)
35 | ..write(obj.dateTime);
36 | }
37 |
38 | @override
39 | int get hashCode => typeId.hashCode;
40 |
41 | @override
42 | bool operator ==(Object other) =>
43 | identical(this, other) ||
44 | other is NoteModelAdapter &&
45 | runtimeType == other.runtimeType &&
46 | typeId == other.typeId;
47 | }
48 |
--------------------------------------------------------------------------------
/lib/data/network/base_api_service.dart:
--------------------------------------------------------------------------------
1 | abstract class BaseApiService {
2 | Future getApi({
3 | String? requestEnd,
4 | Map? queryParams,
5 | String? bearer,
6 | });
7 |
8 | Future postApi({
9 | String requestEnd,
10 | Map? params,
11 | String? bearer,
12 | });
13 |
14 | Future deleteApi(
15 | String url,
16 | String? bearer,
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/lib/data/network/network_services_api.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:convert';
3 | import 'dart:io';
4 |
5 | import 'package:http/http.dart' as http;
6 | import 'package:note_app/data/exceptions/app_exceptions.dart';
7 | import 'package:note_app/data/network/base_api_service.dart';
8 | import 'package:note_app/utils/const_values.dart';
9 | import 'package:note_app/utils/constant/api_constant.dart';
10 |
11 | class NetworkServicesApi implements BaseApiService {
12 | @override
13 | Future getApi({
14 | String? requestEnd,
15 | Map? queryParams,
16 | String? bearer,
17 | }) async {
18 | dynamic jsonResponse;
19 | try {
20 | //
21 | final Uri url = Uri.parse('${ApiConstants.apiUrl}/$requestEnd');
22 | final Uri finalUri = url.replace(queryParameters: queryParams);
23 |
24 | final response = await http.get(
25 | finalUri,
26 | headers: {
27 | 'Accept': 'application/json',
28 | 'Authorization': 'Bearer $bearer',
29 | },
30 | ).timeout(
31 | const Duration(seconds: 30),
32 | );
33 | jsonResponse = returnResponse(response);
34 |
35 | if (response.statusCode == 200) {}
36 | } on SocketException {
37 | throw NoInternetException();
38 | } on TimeoutException {
39 | throw FetchDataException('Time out try again');
40 | }
41 |
42 | return jsonResponse;
43 | }
44 |
45 | @override
46 | Future postApi({
47 | String? requestEnd,
48 | Map? params,
49 | String? bearer,
50 | }) async {
51 | dynamic jsonResponse;
52 | try {
53 | //
54 | var url = Uri.decodeFull('${ApiConstants.apiUrl}/$requestEnd');
55 | final response = await http.post(
56 | Uri.parse(url),
57 | body: params,
58 | headers: {
59 | 'Accept': 'application/json',
60 | 'Authorization': 'Bearer $bearer',
61 | },
62 | ).timeout(
63 | const Duration(seconds: 30),
64 | );
65 | jsonResponse = returnResponse(response);
66 |
67 | if (response.statusCode == 200) {}
68 | } on SocketException {
69 | throw NoInternetException();
70 | } on TimeoutException {
71 | throw FetchDataException('Time out try again');
72 | }
73 |
74 | return jsonResponse;
75 | }
76 |
77 | @override
78 | Future deleteApi(
79 | String url,
80 | String? bearer,
81 | ) async {
82 | dynamic jsonResponse;
83 | try {
84 | //
85 | final response = await http.delete(
86 | Uri.parse(url),
87 | headers: {
88 | 'Accept': 'application/json',
89 | 'Authorization': 'Bearer $bearer',
90 | },
91 | ).timeout(
92 | const Duration(seconds: 30),
93 | );
94 | jsonResponse = returnResponse(response);
95 |
96 | if (response.statusCode == 200) {}
97 | } on SocketException {
98 | throw NoInternetException();
99 | } on TimeoutException {
100 | throw FetchDataException('Time out try again');
101 | }
102 |
103 | return jsonResponse;
104 | }
105 |
106 | dynamic returnResponse(http.Response response) {
107 | logger.i(response.statusCode);
108 | logger.i(response.body);
109 |
110 | dynamic jsonResponse = json.decode(response.body);
111 | logger.i('Parsed Response: $jsonResponse');
112 |
113 | switch (response.statusCode) {
114 | case 200:
115 | return jsonResponse;
116 | case 201:
117 | return jsonResponse;
118 | case 400:
119 | return jsonResponse;
120 | case 401:
121 | logger.i('401 Response: $jsonResponse');
122 |
123 | if (jsonResponse['message']?.contains('not verified')) {
124 | logger.i('Unverified email case detected');
125 | return jsonResponse;
126 | }
127 |
128 | throw UnauthorisedException(jsonResponse['message']);
129 |
130 | case 403:
131 | if (jsonResponse['message']?.contains('not verified')) {
132 | return jsonResponse;
133 | }
134 | throw UnauthorisedException(jsonResponse['message']);
135 |
136 | case 404:
137 | if (jsonResponse['success'] == false) {
138 | throw BadRequestException(jsonResponse['message']);
139 | }
140 | return jsonResponse;
141 |
142 | case 500:
143 | throw FetchDataException(jsonResponse['message']);
144 |
145 | default:
146 | throw UnauthorisedException();
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/lib/data/repositories/auth_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:note_app/data/exceptions/app_exceptions.dart';
2 | import 'package:note_app/data/models/cloud_note_models/user_model.dart';
3 | import 'package:note_app/data/network/network_services_api.dart';
4 | import 'package:note_app/helpers/hive_manager.dart';
5 | import 'package:note_app/utils/const_values.dart';
6 |
7 | class AuthRepository {
8 | final NetworkServicesApi _api;
9 | final HiveManager _hiveManager;
10 |
11 | AuthRepository({
12 | required NetworkServicesApi api,
13 | required HiveManager hiveManager,
14 | }) : _api = api,
15 | _hiveManager = hiveManager;
16 |
17 | Future login(String email, String password) async {
18 | final response = await _api.postApi(
19 | requestEnd: 'user/auth/authenticate',
20 | params: {
21 | 'identifier': email,
22 | 'password': password,
23 | },
24 | );
25 |
26 | logger.i(response);
27 |
28 | if (response['success'] == false &&
29 | response['message'].contains('not yet verified')) {
30 | final token = UserModel.fromJsonLocalToken(response);
31 | final user = UserModel.fromJsonUserDetails(response);
32 | await _hiveManager.userModelBox.put(tokenKey, token);
33 | await _hiveManager.userModelBox.put(userKey, user);
34 |
35 | // Check what's stored in Hive
36 | logger.i('Stored Token: ${_hiveManager.userModelBox.get(tokenKey)}');
37 | logger.i('Stored User: ${_hiveManager.userModelBox.get(userKey)}');
38 | logger.i('Stored User Email: ${_hiveManager.userModelBox.get(userKey)?.email}');
39 |
40 | throw UnauthorisedException(response['message']);
41 | }
42 |
43 | if (response['success'] == true) {
44 | final token = UserModel.fromJsonLocalToken(response);
45 | final user = UserModel.fromJsonUserDetails(response);
46 | await _hiveManager.userModelBox.put(tokenKey, token);
47 | await _hiveManager.userModelBox.put(userKey, user);
48 | return user;
49 | }
50 |
51 | throw response['message'];
52 | }
53 |
54 | Future register({
55 | required String email,
56 | required String password,
57 | required String firstName,
58 | required String lastName,
59 | required String userName,
60 | }) async {
61 | final response = await _api.postApi(
62 | requestEnd: 'user/auth/register',
63 | params: {
64 | 'email': email,
65 | 'password': password,
66 | 'first_name': firstName,
67 | 'last_name': lastName,
68 | 'username': userName,
69 | },
70 | );
71 |
72 | logger.i(response);
73 |
74 | if (response['success'] == true) {
75 | final token = UserModel.fromJsonLocalToken(response);
76 | final user = UserModel.fromJsonUserDetails(response);
77 | await _hiveManager.userModelBox.put(tokenKey, token);
78 | await _hiveManager.userModelBox.put(userKey, user);
79 | return user;
80 | } else {
81 | throw response['message'];
82 | }
83 | }
84 |
85 | Future verifyEmail(String code) async {
86 | final response = await _api.postApi(
87 | requestEnd: 'user/auth/verify_code',
88 | params: {
89 | 'otp_code': code,
90 | },
91 | );
92 |
93 | logger.i(response);
94 |
95 | if (response['success'] != true) {
96 | throw response['message'];
97 | }
98 |
99 | if (response['success'] == true) {
100 | final currentUser = _hiveManager.userModelBox.get(userKey);
101 | if (currentUser != null) {
102 | // Create new user model with verified status
103 | final updatedUser = UserModel(
104 | id: currentUser.id,
105 | uuid: currentUser.uuid,
106 | firstName: currentUser.firstName,
107 | lastName: currentUser.lastName,
108 | userName: currentUser.userName,
109 | email: currentUser.email,
110 | emailVerified: DateTime.now().toString(), // Update verification status
111 | hasSubscription: currentUser.hasSubscription,
112 | hasExceedTier: currentUser.hasExceedTier,
113 | planDuration: currentUser.planDuration,
114 | planSubDate: currentUser.planSubDate,
115 | );
116 | await _hiveManager.userModelBox.put(userKey, updatedUser);
117 | }
118 | }
119 | }
120 |
121 | Future resendVerificationCode({String? email}) async {
122 | final user = _hiveManager.userModelBox.get(userKey);
123 | logger.i('Stored User: $user');
124 | logger.i('User Email: ${user?.email}');
125 |
126 | if (user?.email == null && email == null) {
127 | logger.e('User email is null');
128 | throw 'User email not found';
129 | }
130 | final response = await _api.postApi(
131 | requestEnd: 'user/auth/send_code',
132 | params: {
133 | 'email': email ?? user!.email,
134 | },
135 | );
136 |
137 | logger.i(response);
138 |
139 | if (response['success'] == false) {
140 | throw response['message'];
141 | }
142 | }
143 |
144 | Future forgotPassword(String email) async {
145 | final response = await _api.postApi(
146 | requestEnd: 'user/auth/forgot_password',
147 | params: {
148 | 'email': email,
149 | },
150 | );
151 |
152 | logger.i(response);
153 |
154 | if (response['success'] != true) {
155 | throw response['message'];
156 | }
157 | // return true;
158 | }
159 |
160 | Future resetPassword(String otpCode, String newPassword) async {
161 | final response = await _api.postApi(
162 | requestEnd: 'user/auth/reset_password',
163 | params: {
164 | 'otp_code': otpCode,
165 | 'new_password': newPassword,
166 | },
167 | );
168 |
169 | logger.i(response);
170 |
171 | if (response['success'] != true) {
172 | throw response['message'];
173 | }
174 |
175 | // return true;
176 | }
177 |
178 | // ======================================== //
179 |
180 | Future logout() async {
181 | await _hiveManager.userModelBox.delete(tokenKey);
182 | await _hiveManager.cloudNoteModelBox.clear();
183 | }
184 |
185 | UserModel? getCurrentUser() {
186 | return _hiveManager.userModelBox.get(userKey);
187 | }
188 |
189 | UserModel? getCurrentUserToken() {
190 | return _hiveManager.userModelBox.get(tokenKey);
191 | }
192 |
193 | Future fetchUserDetails() async {
194 | try {
195 | final token = _hiveManager.userModelBox.get(tokenKey)?.accessToken;
196 |
197 | final response = await _api.getApi(
198 | requestEnd: 'user/fetch_user',
199 | bearer: token,
200 | );
201 |
202 | if (response['success'] == false) {
203 |
204 | if (response['message'].contains('not verified') &&
205 | response['data'] != null &&
206 | response['data']['user'] != null) {
207 |
208 | final user = UserModel.fromJsonUserDetails(response);
209 | await _hiveManager.userModelBox.put(userKey, user);
210 |
211 | return user;
212 | }
213 | }
214 |
215 | if (response['success'] == true) {
216 | final user = UserModel.fromJsonUserDetails(response);
217 | await _hiveManager.userModelBox.put(userKey, user);
218 | await _hiveManager.userModelBox.flush();
219 | return user;
220 | }
221 |
222 | return null;
223 | } catch (e) {
224 | logger.e('Error in fetchUserDetails: $e');
225 | throw e.toString();
226 | }
227 | }
228 |
229 | }
--------------------------------------------------------------------------------
/lib/data/repositories/cloud_note_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:note_app/data/models/cloud_note_models/cloud_note_model.dart';
2 | import 'package:note_app/data/network/network_services_api.dart';
3 | import 'package:note_app/helpers/hive_manager.dart';
4 | import 'package:note_app/utils/const_values.dart';
5 |
6 | class CloudNoteRepository {
7 | final NetworkServicesApi _api;
8 | final HiveManager _hiveManager;
9 |
10 | CloudNoteRepository({
11 | required NetworkServicesApi api,
12 | required HiveManager hiveManager,
13 | }) : _api = api,
14 | _hiveManager = hiveManager;
15 |
16 | String? _getToken() {
17 | return _hiveManager.userModelBox.get(tokenKey)?.accessToken;
18 | }
19 |
20 | Future> fetchNotes() async {
21 | try {
22 | final token = _getToken();
23 |
24 | final response = await _api.getApi(
25 | requestEnd: 'user/fetch_notes',
26 | bearer: token,
27 | );
28 |
29 | // logger.i('Fetch Notes Response: $response');
30 |
31 | if (response['success'] == true) {
32 | List cloudNotes = CloudNoteModel.parseResponse(response);
33 |
34 | await _hiveManager.cloudNoteModelBox.clear();
35 | await _hiveManager.cloudNoteModelBox.addAll(cloudNotes);
36 |
37 | return cloudNotes;
38 | }
39 |
40 | throw response['message'] ?? 'Failed to fetch notes';
41 | } catch (e) {
42 | logger.e('Error in fetchNotes: $e');
43 | throw e.toString();
44 | }
45 | }
46 |
47 | Future createNote(String title, String content) async {
48 | try {
49 | final token = _getToken();
50 |
51 | final response = await _api.postApi(
52 | requestEnd: 'user/create_notes',
53 | params: {
54 | 'note_title': title,
55 | 'note_content': content,
56 | },
57 | bearer: token,
58 | );
59 |
60 | logger.i('Create Note Response: $response');
61 |
62 | if (response['success'] == true) {
63 | return CloudNoteModel.fromJson(response['data']);
64 | }
65 |
66 | throw response['message'] ?? 'Failed to create note';
67 | } catch (e) {
68 | logger.e('Error in createNote: $e');
69 | throw e.toString();
70 | }
71 | }
72 |
73 | Future updateNote(CloudNoteModel note, int noteKey) async {
74 | try {
75 | final token = _getToken();
76 |
77 | final response = await _api.postApi(
78 | requestEnd: 'user/edit_notes',
79 | params: {
80 | 'note_uuid': note.uuid,
81 | 'note_title': note.title,
82 | 'note_content': note.notes,
83 | },
84 | bearer: token,
85 | );
86 |
87 | logger.i('Update Note Response: $response');
88 |
89 | if (response['success'] == true) {
90 | // _hiveManager.cloudNoteModelBox.put(noteKey, note);
91 | return CloudNoteModel.fromJson(response['data']);
92 | }
93 |
94 | throw response['message'] ?? 'Failed to update note';
95 | } catch (e) {
96 | logger.e('Error in updateNote: $e');
97 | throw e.toString();
98 | }
99 | }
100 |
101 | Future moveToTrash(CloudNoteModel note) async {
102 | try {
103 | final token = _getToken();
104 |
105 | final response = await _api.postApi(
106 | requestEnd: 'user/trash_note',
107 | params: {
108 | 'note_uuid': note.uuid,
109 | },
110 | bearer: token,
111 | );
112 |
113 | logger.i('Update Note Response: $response');
114 |
115 | if (response['success'] == true) {
116 | return;
117 | }
118 |
119 | throw response['message'] ?? 'Failed to update note';
120 | } catch (e) {
121 | logger.e('Error in deleteNote: $e');
122 | throw e.toString();
123 | }
124 | }
125 |
126 | Future moveToCloud(String title, String content, int noteKey) async {
127 | try {
128 | final token = _getToken();
129 |
130 | final response = await _api.postApi(
131 | requestEnd: 'user/move_to_cloud',
132 | params: {
133 | 'note_title': title,
134 | 'note_content': content,
135 | },
136 | bearer: token,
137 | );
138 |
139 | logger.i('Create Note Response: $response');
140 |
141 | if (response['success'] == true) {
142 | _hiveManager.noteModelBox.delete(noteKey);
143 | return CloudNoteModel.fromJson(response['data']);
144 | }
145 |
146 | throw response['message'] ?? 'Failed to create note';
147 | } catch (e) {
148 | logger.e('Error in createNote: $e');
149 | throw e.toString();
150 | }
151 | }
152 |
153 |
154 | List getHiveKeys() {
155 | return _hiveManager.cloudNoteModelBox.keys.cast().toList();
156 | }
157 |
158 | }
--------------------------------------------------------------------------------
/lib/helpers/hive_manager.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:hive_flutter/hive_flutter.dart';
4 | import 'package:note_app/data/models/cloud_note_models/cloud_note_model.dart';
5 | import 'package:note_app/data/models/cloud_note_models/user_model.dart';
6 | import 'package:note_app/data/models/local_note_model/note_model.dart';
7 | import 'package:note_app/utils/const_values.dart';
8 | import 'package:path_provider/path_provider.dart';
9 |
10 | class HiveManager {
11 | static final HiveManager _instance = HiveManager._internal();
12 |
13 | factory HiveManager() {
14 | return _instance;
15 | }
16 |
17 | HiveManager._internal();
18 |
19 | static late Box _noteModelBox;
20 | static late Box _deleteNoteModelBox;
21 | static late Box _cloudNoteModelBox;
22 | static late Box _userModelBox;
23 |
24 | Future init() async {
25 | Directory directory = await getApplicationDocumentsDirectory();
26 | Hive
27 | ..init(directory.path)
28 | ..registerAdapter(NoteModelAdapter());
29 | _noteModelBox = await Hive.openBox(noteBox);
30 | _deleteNoteModelBox = await Hive.openBox(deletedNotes);
31 |
32 | Hive
33 | ..init(directory.path)
34 | ..registerAdapter(CloudNoteModelAdapter());
35 | _cloudNoteModelBox = await Hive.openBox(cloudNoteBox);
36 |
37 | Hive
38 | ..init(directory.path)
39 | ..registerAdapter(UserModelAdapter());
40 | _userModelBox = await Hive.openBox(userBox);
41 |
42 | }
43 |
44 |
45 | Box get noteModelBox => _noteModelBox;
46 | Box get deleteNoteModelBox => _deleteNoteModelBox;
47 | Box get cloudNoteModelBox => _cloudNoteModelBox;
48 | Box get userModelBox => _userModelBox;
49 |
50 | }
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:flutter_bloc/flutter_bloc.dart';
4 | import 'package:note_app/app/src/app.dart';
5 | import 'package:note_app/helpers/hive_manager.dart';
6 | import 'package:note_app/services/service_locator.dart';
7 | import 'package:note_app/state/cubits/auth_cubit/auth_cubit.dart';
8 | import 'package:note_app/state/cubits/cloud_note_cubit/cloud_note_cubit.dart';
9 | import 'package:note_app/state/cubits/note_style_cubit/note_style_cubit.dart';
10 | import 'package:note_app/state/cubits/play_button_cubit/play_button_cubit.dart';
11 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
12 | import 'package:note_app/utils/constant/api_constant.dart';
13 | import 'package:shared_preferences/shared_preferences.dart';
14 | import 'package:yaml/yaml.dart';
15 | import 'package:provider/provider.dart';
16 |
17 | void main() async {
18 | WidgetsFlutterBinding.ensureInitialized();
19 |
20 | final prefs = await SharedPreferences.getInstance();
21 |
22 | // initialize hive
23 | await HiveManager().init();
24 |
25 | loadApiCredentials();
26 |
27 | // Setup dependency injection
28 | await setupLocator();
29 |
30 | await SystemChrome.setPreferredOrientations([
31 | DeviceOrientation.portraitUp,
32 | DeviceOrientation.portraitDown,
33 | ]);
34 |
35 | runApp(
36 | MultiProvider(
37 | providers: [
38 | BlocProvider(
39 | create: (context) => ThemeCubit(prefs),
40 | ),
41 | BlocProvider(
42 | create: (context) => PlayButtonCubit(),
43 | ),
44 | BlocProvider(
45 | create: (context) => NoteStyleCubit(),
46 | ),
47 | BlocProvider(
48 | create: (context) => getIt(),
49 | ),
50 | BlocProvider(
51 | create: (context) => getIt(),
52 | ),
53 | ],
54 | child: const App(),
55 | ),
56 | );
57 | }
58 |
59 | Future loadApiCredentials() async {
60 | String yamlString = await rootBundle.loadString('api_credentials.yaml');
61 | Map yamlMap = loadYaml(yamlString);
62 | Map credentialsMap = Map.from(yamlMap);
63 | ApiConstants.apiUrl = credentialsMap['api_url'];
64 | }
65 |
--------------------------------------------------------------------------------
/lib/presentation/pages/cloud_notes/auth/forgot_password.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ForgotPassword extends StatefulWidget {
4 | const ForgotPassword({super.key});
5 |
6 | @override
7 | State createState() => _ForgotPasswordState();
8 | }
9 |
10 | class _ForgotPasswordState extends State {
11 | @override
12 | Widget build(BuildContext context) {
13 | return const Placeholder();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/presentation/pages/cloud_notes/auth/login_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_bloc/flutter_bloc.dart';
3 | import 'package:flutter_screenutil/flutter_screenutil.dart';
4 | import 'package:note_app/config/router/navigates_to.dart';
5 | import 'package:note_app/config/router/routes_name.dart';
6 | import 'package:note_app/presentation/widget/mNew_text_widget.dart';
7 | import 'package:note_app/presentation/widget/mbutton.dart';
8 | import 'package:note_app/state/cubits/auth_cubit/auth_cubit.dart';
9 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
10 | import 'package:note_app/utils/colors/m_colors.dart';
11 | import 'package:note_app/utils/tools/message_dialog.dart';
12 |
13 | class LoginScreen extends StatefulWidget {
14 | const LoginScreen({super.key});
15 |
16 | @override
17 | State createState() => _LoginScreenState();
18 | }
19 |
20 | class _LoginScreenState extends State {
21 | final formKey = GlobalKey();
22 |
23 | bool offText = true;
24 | bool isLoading = false;
25 | bool isAuthLoading = false;
26 |
27 | final TextEditingController _identifierController = TextEditingController();
28 | final TextEditingController _passwordController = TextEditingController();
29 |
30 | @override
31 | void dispose() {
32 | _identifierController.dispose();
33 | _passwordController.dispose();
34 | super.dispose();
35 | }
36 |
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | return BlocListener(
41 | listener: (context, state) {
42 | if (state is AuthAuthenticated) {
43 | // Get saved route and navigate
44 | final attemptedRoute =
45 | context.read().getAndClearAttemptedRoute();
46 | navigateReplaceTo(context, destination: attemptedRoute ?? RoutesName.cloud_notes);
47 | } else if(state is AuthEmailUnverified) {
48 | navigateReplaceTo(context, destination: RoutesName.verify_code_screen, arguments: {
49 | 'from': 'login',
50 | });
51 | } else if (state is AuthError) {
52 | showError(state.message);
53 | }
54 | },
55 | child: Scaffold(
56 | appBar: AppBar(
57 | title: const Text('Login'),
58 | ),
59 | body: SingleChildScrollView(
60 | padding: const EdgeInsets.symmetric(horizontal: 16),
61 | child: Form(
62 | key: formKey,
63 | child: Column(
64 | children: [
65 | MNewTextField(
66 | fieldName: 'Username / Email',
67 | controller: _identifierController,
68 | ),
69 | MNewTextField(
70 | fieldName: 'Password',
71 | controller: _passwordController,
72 | isPasswordField: true,
73 | offText: offText,
74 | togglePasswordView: () {
75 | setState(() {
76 | offText = !offText;
77 | });
78 | },
79 | ),
80 | BlocBuilder(
81 | builder: (context, state) {
82 | return MButton(
83 | title: 'Login',
84 | isLoading: state is AuthLoading,
85 | onPressed: state is! AuthLoading
86 | ? () {
87 | var form = formKey.currentState;
88 |
89 | if (form!.validate()) {
90 | form.save();
91 | context.read().login(
92 | _identifierController.text!,
93 | _passwordController.text!,
94 | );
95 | }
96 | }
97 | : null,
98 | );
99 | },
100 | ),
101 | SizedBox(
102 | height: 20.h,
103 | ),
104 | RichText(
105 | text: TextSpan(
106 | children: [
107 | TextSpan(
108 | text: 'Don\'t have an Account? ',
109 | style: TextStyle(
110 | color:
111 | context.watch().state.isDarkTheme ==
112 | false
113 | ? AppColors.defaultBlack
114 | : AppColors.defaultWhite,
115 | ),
116 | ),
117 | WidgetSpan(
118 | child: GestureDetector(
119 | onTap: () {
120 | navigateTo(context, destination: RoutesName.register_screen);
121 | },
122 | child: Text(
123 | 'Create One',
124 | style: TextStyle(
125 | color: context
126 | .watch()
127 | .state
128 | .isDarkTheme ==
129 | false
130 | ? AppColors.defaultBlack
131 | : AppColors.defaultWhite,
132 | fontWeight: FontWeight.bold,
133 | ),
134 | ),
135 | ),
136 | ),
137 | ],
138 | ),
139 | ),
140 | ],
141 | ),
142 | ),
143 | ),
144 | ),
145 | );
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/lib/presentation/pages/cloud_notes/auth/register_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_bloc/flutter_bloc.dart';
3 | import 'package:flutter_screenutil/flutter_screenutil.dart';
4 | import 'package:note_app/config/router/navigates_to.dart';
5 | import 'package:note_app/config/router/routes_name.dart';
6 | import 'package:note_app/presentation/widget/mNew_text_widget.dart';
7 | import 'package:note_app/presentation/widget/mbutton.dart';
8 | import 'package:note_app/state/cubits/auth_cubit/auth_cubit.dart';
9 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
10 | import 'package:note_app/utils/colors/m_colors.dart';
11 | import 'package:note_app/utils/tools/message_dialog.dart';
12 |
13 | class RegisterScreen extends StatefulWidget {
14 | const RegisterScreen({super.key});
15 |
16 | @override
17 | State createState() => _RegisterScreenState();
18 | }
19 |
20 | class _RegisterScreenState extends State {
21 | final formKey = GlobalKey();
22 |
23 | bool offText = true;
24 |
25 | final TextEditingController firstNameController = TextEditingController();
26 | final TextEditingController lastNameController = TextEditingController();
27 | final TextEditingController emailController = TextEditingController();
28 | final TextEditingController usernameController = TextEditingController();
29 | final TextEditingController passwordController = TextEditingController();
30 |
31 | @override
32 | void dispose() {
33 | firstNameController.dispose();
34 | lastNameController.dispose();
35 | emailController.dispose();
36 | usernameController.dispose();
37 | passwordController.dispose();
38 | super.dispose();
39 | }
40 |
41 | @override
42 | Widget build(BuildContext context) {
43 | return BlocListener(
44 | listener: (context, state) {
45 | if (state is AuthAuthenticated) {
46 | final attemptedRoute =
47 | context.read().getAndClearAttemptedRoute();
48 | Navigator.pop(context);
49 | navigateReplaceTo(context,
50 | destination: attemptedRoute ?? RoutesName.cloud_notes);
51 | } else if (state is AuthEmailUnverified) {
52 | navigateReplaceTo(context,
53 | destination: RoutesName.verify_code_screen,
54 | arguments: {
55 | 'from': 'login',
56 | });
57 | } else if (state is AuthError) {
58 | showError(state.message);
59 | }
60 | },
61 | child: Scaffold(
62 | appBar: AppBar(
63 | title: const Text('Register'),
64 | ),
65 | body: SingleChildScrollView(
66 | padding: const EdgeInsets.symmetric(horizontal: 16),
67 | child: Form(
68 | key: formKey,
69 | child: Column(
70 | children: [
71 | Row(
72 | children: [
73 | Expanded(
74 | child: MNewTextField(
75 | fieldName: '',
76 | sideText: 'First Name',
77 | controller: firstNameController,
78 | ),
79 | ),
80 | SizedBox(
81 | width: 10.w,
82 | ),
83 | Expanded(
84 | child: MNewTextField(
85 | fieldName: '',
86 | sideText: 'Last Name',
87 | controller: lastNameController,
88 | ),
89 | ),
90 | ],
91 | ),
92 | MNewTextField(
93 | fieldName: '',
94 | sideText: 'Email',
95 | controller: emailController,
96 | ),
97 | MNewTextField(
98 | fieldName: '',
99 | sideText: 'Username',
100 | controller: usernameController,
101 | ),
102 | MNewTextField(
103 | fieldName: '',
104 | sideText: 'Password',
105 | isPasswordField: true,
106 | offText: offText,
107 | togglePasswordView: () {
108 | setState(() {
109 | offText = !offText;
110 | });
111 | },
112 | controller: passwordController,
113 | ),
114 | BlocBuilder(
115 | builder: (context, state) {
116 | return MButton(
117 | title: 'Continue',
118 | isLoading: state is AuthLoading,
119 | onPressed: state is! AuthLoading
120 | ? () {
121 | var form = formKey.currentState;
122 |
123 | if (form!.validate()) {
124 | context.read().register(
125 | firstName: firstNameController.text,
126 | lastName: lastNameController.text,
127 | email: emailController.text,
128 | userName: usernameController.text,
129 | password: passwordController.text,
130 | );
131 | }
132 | }
133 | : null,
134 | );
135 | },
136 | ),
137 | SizedBox(
138 | height: 15.h,
139 | ),
140 | RichText(
141 | text: TextSpan(
142 | children: [
143 | TextSpan(
144 | text: 'Already have an Account? ',
145 | style: TextStyle(
146 | color:
147 | context.watch().state.isDarkTheme ==
148 | false
149 | ? AppColors.defaultBlack
150 | : AppColors.defaultWhite,
151 | ),
152 | ),
153 | WidgetSpan(
154 | child: GestureDetector(
155 | onTap: () {
156 | // context.pop();
157 | },
158 | child: Text(
159 | 'Login',
160 | style: TextStyle(
161 | color: context
162 | .watch()
163 | .state
164 | .isDarkTheme ==
165 | false
166 | ? AppColors.defaultBlack
167 | : AppColors.defaultWhite,
168 | fontWeight: FontWeight.bold,
169 | ),
170 | ),
171 | ),
172 | ),
173 | ],
174 | ),
175 | ),
176 | ],
177 | ),
178 | ),
179 | ),
180 | ),
181 | );
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/lib/presentation/pages/cloud_notes/auth/reset_password.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ResetPassword extends StatefulWidget {
4 | const ResetPassword({super.key});
5 |
6 | @override
7 | State createState() => _ResetPasswordState();
8 | }
9 |
10 | class _ResetPasswordState extends State {
11 | @override
12 | Widget build(BuildContext context) {
13 | return const Placeholder();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/presentation/pages/cloud_notes/auth/verify_code.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bloc/flutter_bloc.dart';
6 | import 'package:flutter_screenutil/flutter_screenutil.dart';
7 | import 'package:fluttertoast/fluttertoast.dart';
8 | import 'package:note_app/config/router/navigates_to.dart';
9 | import 'package:note_app/config/router/routes_name.dart';
10 | import 'package:note_app/presentation/widget/mbutton.dart';
11 | import 'package:note_app/state/cubits/auth_cubit/auth_cubit.dart';
12 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
13 | import 'package:note_app/utils/colors/m_colors.dart';
14 | import 'package:note_app/utils/const_values.dart';
15 | import 'package:note_app/utils/tools/message_dialog.dart';
16 | import 'package:note_app/utils/tools/sized_box_ex.dart';
17 | import 'package:otp_text_field_v2/otp_text_field_v2.dart';
18 |
19 | class VerifyCode extends StatefulWidget {
20 | final String? email;
21 | final String? from;
22 |
23 | const VerifyCode({
24 | super.key,
25 | this.email,
26 | this.from,
27 | });
28 |
29 | @override
30 | State createState() => _VerifyCodeState();
31 | }
32 |
33 | class _VerifyCodeState extends State {
34 | final formKey = GlobalKey();
35 | OtpFieldControllerV2 otpController = OtpFieldControllerV2();
36 |
37 | Timer? _timer;
38 | int _remainingSeconds = 0;
39 |
40 | bool offText = true;
41 | bool isLoading = false;
42 | bool isLoadingVerify = false;
43 | String? code;
44 | String? email;
45 |
46 | void startTimer() {
47 | setState(() {
48 | _remainingSeconds = 60;
49 | });
50 |
51 | _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
52 | setState(() {
53 | if (_remainingSeconds > 0) {
54 | _remainingSeconds--;
55 | } else {
56 | _timer?.cancel();
57 | }
58 | });
59 | });
60 | }
61 |
62 | @override
63 | void dispose() {
64 | _timer?.cancel();
65 | super.dispose();
66 | }
67 |
68 | @override
69 | Widget build(BuildContext context) {
70 | return BlocBuilder(
71 | builder: (context, state) {
72 | return BlocListener(
73 | listener: (context, state) {
74 | if (state is AuthAuthenticated) {
75 | final attemptedRoute = context.read().getAndClearAttemptedRoute();
76 | navigateReplaceTo(
77 | context,
78 | destination: attemptedRoute ?? RoutesName.cloud_notes,
79 | );
80 | } else if (state is AuthError) {
81 | showError(state.message);
82 | }
83 | },
84 | child: Scaffold(
85 | appBar: AppBar(
86 | title: const Text('Verify Code'),
87 | ),
88 | body: SingleChildScrollView(
89 | padding: const EdgeInsets.symmetric(horizontal: 16),
90 | child: Column(
91 | children: [
92 | 10.toHeight,
93 | OTPTextFieldV2(
94 | controller: otpController,
95 | length: 4,
96 | width: MediaQuery.of(context).size.width,
97 | textFieldAlignment: MainAxisAlignment.spaceAround,
98 | fieldWidth: 60,
99 | fieldStyle: FieldStyle.box,
100 | otpFieldStyle: OtpFieldStyle(
101 | backgroundColor: transparent,
102 | enabledBorderColor:
103 | context.watch().state.isDarkTheme == false
104 | ? AppColors.defaultBlack
105 | : AppColors.defaultWhite,
106 | ),
107 | outlineBorderRadius: 8,
108 | style: TextStyle(
109 | fontSize: 17.sp,
110 | ),
111 | onChanged: (pin) {
112 | print("Changed: " + pin);
113 | setState(() {
114 | code = pin;
115 | });
116 | },
117 | onCompleted: (pin) {
118 | print("Completed: " + pin);
119 | setState(() {
120 | code = pin;
121 | });
122 | },
123 | ),
124 | 15.toHeight,
125 | MButton(
126 | title: 'Verify OTP',
127 | hasIcon: true,
128 | btnIcon: Icons.arrow_forward_ios,
129 | isLoading: state is AuthLoading,
130 | onPressed: state is! AuthLoading
131 | ? () {
132 | if (code == null || code!.length < 4) {
133 | Fluttertoast.showToast(
134 | msg: 'Code not complete',
135 | gravity: ToastGravity.TOP,
136 | );
137 | return;
138 | }
139 | context.read().verifyEmail(code!);
140 | }
141 | : null,
142 | ),
143 | 15.toHeight,
144 | GestureDetector(
145 | onTap: _remainingSeconds == 0 && state is! AuthLoading
146 | ? () {
147 | context.read().resendVerificationCode();
148 | startTimer();
149 | }
150 | : null,
151 | child: Text(
152 | state is AuthLoading
153 | ? 'Sending code...'
154 | : _remainingSeconds == 0
155 | ? 'Send Code'
156 | : 'Resend code in $_remainingSeconds',
157 | style: TextStyle(
158 | fontSize: 16.sp,
159 | ),
160 | overflow: TextOverflow.clip,
161 | ),
162 | ),
163 | ],
164 | ),
165 | ),
166 | ),
167 | );
168 | },
169 | );
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/lib/presentation/pages/cloud_notes/cloud_create_note.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bloc/flutter_bloc.dart';
6 | import 'package:fluttertoast/fluttertoast.dart';
7 | import 'package:note_app/config/router/navigates_to.dart';
8 | import 'package:note_app/config/router/routes_name.dart';
9 | import 'package:note_app/state/cubits/auth_cubit/auth_cubit.dart';
10 | import 'package:note_app/state/cubits/cloud_note_cubit/cloud_note_cubit.dart';
11 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
12 | import 'package:note_app/utils/tools/message_dialog.dart';
13 |
14 | class CloudCreateNote extends StatefulWidget {
15 | const CloudCreateNote({super.key});
16 |
17 | @override
18 | State createState() => _CloudCreateNoteState();
19 | }
20 |
21 | class _CloudCreateNoteState extends State {
22 | final TextEditingController _noteTitle = TextEditingController();
23 | final TextEditingController _noteText = TextEditingController();
24 | final scaffoldKey = GlobalKey();
25 |
26 | TextStyle? myTextStyle;
27 | TextAlign? myTextAlign;
28 |
29 | bool? _isNotEmpty;
30 |
31 | final goToNotes = FocusNode();
32 |
33 | Future checkIfNoteIsNotEmptyWhenGoingBack() async {
34 | if (_noteText.text.isNotEmpty || _noteTitle.text.isNotEmpty) {
35 | final String noteTitle = _noteTitle.text;
36 | final String note = _noteText.text;
37 |
38 | context.read().createNote(noteTitle, note);
39 |
40 | await Fluttertoast.showToast(
41 | msg: 'Note Saved',
42 | toastLength: Toast.LENGTH_SHORT,
43 | );
44 | if (mounted) {
45 | navigateReplaceTo(context, destination: RoutesName.cloud_notes);
46 | }
47 | _isNotEmpty = true;
48 | } else {
49 | await Fluttertoast.showToast(
50 | msg: 'Note was empty, nothing was saved',
51 | toastLength: Toast.LENGTH_SHORT,
52 | );
53 | if (mounted) {
54 | navigateReplaceTo(context, destination: RoutesName.cloud_notes);
55 | }
56 | _isNotEmpty = false;
57 | }
58 | return _isNotEmpty!;
59 | }
60 |
61 | void checkIfNoteIsNotEmptyAndSaveNote() {
62 | if (_noteTitle.text.isEmpty || _noteText.text.isEmpty) {
63 | Fluttertoast.showToast(
64 | msg: 'Title or note body cannot be empty',
65 | toastLength: Toast.LENGTH_SHORT,
66 | );
67 | return;
68 | } else {
69 | final String noteTitle = _noteTitle.text;
70 | final String note = _noteText.text;
71 |
72 | // createNote(noteTitle, note);
73 | context.read().createNote(noteTitle, note);
74 |
75 | Fluttertoast.showToast(
76 | msg: 'Note Saved',
77 | toastLength: Toast.LENGTH_SHORT,
78 | );
79 | navigateReplaceTo(context, destination: RoutesName.cloud_notes);
80 | }
81 | }
82 |
83 | @override
84 | void initState() {
85 | super.initState();
86 | myTextStyle = const TextStyle(
87 | fontSize: 18.5,
88 | );
89 | myTextAlign = TextAlign.left;
90 | }
91 |
92 | @override
93 | void dispose() {
94 | super.dispose();
95 | _noteTitle.dispose();
96 | _noteText.dispose();
97 | }
98 |
99 | @override
100 | Widget build(BuildContext context) {
101 | var height = MediaQuery.of(context).size.height;
102 | return WillPopScope(
103 | onWillPop: checkIfNoteIsNotEmptyWhenGoingBack,
104 | child: BlocListener(
105 | listener: (context, state) {
106 | if (state is AuthEmailUnverified) {
107 | navigateReplaceTo(context,
108 | destination: RoutesName.verify_code_screen,
109 | arguments: {
110 | 'from': 'login',
111 | });
112 | } else if (state is AuthError) {
113 | showError(state.message);
114 | }
115 | },
116 | child: Scaffold(
117 | key: scaffoldKey,
118 | appBar: AppBar(
119 | // used a text form field for the app bar
120 | leading: Platform.isIOS
121 | ? IconButton(
122 | icon: const Icon(CupertinoIcons.back),
123 | onPressed: checkIfNoteIsNotEmptyWhenGoingBack,
124 | )
125 | : null,
126 | title: TextFormField(
127 | autofocus: true,
128 | controller: _noteTitle,
129 | decoration: const InputDecoration(
130 | hintText: 'Create Note Title...',
131 | hintStyle: TextStyle(
132 | fontSize: 20,
133 | fontWeight: FontWeight.bold,
134 | ),
135 | border: InputBorder.none,
136 | ),
137 | style: const TextStyle(
138 | fontSize: 20,
139 | fontWeight: FontWeight.bold,
140 | ),
141 | onFieldSubmitted: (_) {
142 | FocusScope.of(context).requestFocus(goToNotes);
143 | },
144 | keyboardType: TextInputType.text,
145 | textCapitalization: TextCapitalization.sentences,
146 | textInputAction: TextInputAction.next,
147 | ),
148 | // ends here //
149 | centerTitle: false,
150 | actions: [
151 | TextButton.icon(
152 | onPressed: () {
153 | checkIfNoteIsNotEmptyAndSaveNote();
154 | },
155 | icon: Icon(
156 | Icons.done,
157 | color: context.watch().state.isDarkTheme == false
158 | ? Colors.black45
159 | : Colors.white38,
160 | ),
161 | label: Text(
162 | 'Save',
163 | style: TextStyle(
164 | color:
165 | context.watch().state.isDarkTheme == false
166 | ? Colors.black45
167 | : Colors.white38,
168 | ),
169 | ),
170 | ),
171 | ],
172 | ),
173 | body: SingleChildScrollView(
174 | child: Padding(
175 | padding: const EdgeInsets.symmetric(horizontal: 10.0),
176 | child: TextField(
177 | controller: _noteText,
178 | decoration: const InputDecoration(
179 | hintText: 'Type Note...',
180 | hintStyle: TextStyle(),
181 | border: InputBorder.none,
182 | ),
183 | textCapitalization: TextCapitalization.sentences,
184 | focusNode: goToNotes,
185 | style: myTextStyle,
186 | textAlign: myTextAlign!,
187 | maxLines: height.toInt(),
188 | ),
189 |
190 | //TODO! trying to add styling functionality, having issues
191 | //TODO! persisting the style for a saved note
192 | ),
193 | ),
194 | ),
195 | ),
196 | );
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/lib/presentation/pages/cloud_notes/cloud_edit_note.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_bloc/flutter_bloc.dart';
3 | import 'package:fluttertoast/fluttertoast.dart';
4 | import 'package:note_app/config/router/navigates_to.dart';
5 | import 'package:note_app/config/router/routes_name.dart';
6 | import 'package:note_app/data/models/cloud_note_models/cloud_note_model.dart';
7 | import 'package:note_app/helpers/hive_manager.dart';
8 | import 'package:note_app/presentation/pages/cloud_notes/cloud_read_note_screen.dart';
9 | import 'package:note_app/state/cubits/cloud_note_cubit/cloud_note_cubit.dart';
10 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
11 | import 'package:note_app/utils/const_values.dart';
12 | import 'package:note_app/utils/tools/message_dialog.dart';
13 | import 'package:flutter_bloc/flutter_bloc.dart';
14 |
15 | class CloudEditNote extends StatefulWidget {
16 | final CloudNoteModel? notes;
17 | final int? noteKey;
18 |
19 | const CloudEditNote({
20 | super.key,
21 | @required this.notes,
22 | @required this.noteKey,
23 | });
24 |
25 | @override
26 | State createState() => _CloudEditNoteState();
27 | }
28 |
29 | class _CloudEditNoteState extends State {
30 | final goToNotes = FocusNode();
31 | final scaffoldKey = GlobalKey();
32 |
33 | TextStyle? myTextStyle;
34 | TextAlign? myTextAlign;
35 |
36 | dynamic coText;
37 | var _initValue = {'notes': '', 'conText': ''};
38 |
39 | var _isInit = true;
40 |
41 | @override
42 | void didChangeDependencies() {
43 | super.didChangeDependencies();
44 | // _noteText = widget.notes.notes;
45 | if (_isInit) {
46 | _initValue = {
47 | 'title': widget.notes!.title.toString(),
48 | 'notes': widget.notes!.notes.toString(),
49 | 'conText': widget.notes!.notes.toString()
50 | };
51 | }
52 | _isInit = false;
53 | }
54 |
55 | @override
56 | void initState() {
57 | super.initState();
58 | myTextStyle = const TextStyle(
59 | fontSize: 18.5,
60 | );
61 | myTextAlign = TextAlign.left;
62 | }
63 |
64 | bool? isEdited;
65 |
66 | @override
67 | Widget build(BuildContext context) {
68 | var height = MediaQuery.of(context).size.height;
69 | return Scaffold(
70 | key: scaffoldKey,
71 | appBar: AppBar(
72 | title: TextFormField(
73 | initialValue: _initValue['title'],
74 | autofocus: true,
75 | onChanged: (val) {
76 | _initValue['title'] = val;
77 | },
78 | decoration: const InputDecoration(
79 | hintText: 'Create Note Title...',
80 | hintStyle: TextStyle(
81 | fontSize: 20,
82 | fontWeight: FontWeight.bold,
83 | ),
84 | border: InputBorder.none,
85 | ),
86 | style: const TextStyle(
87 | fontSize: 20,
88 | fontWeight: FontWeight.bold,
89 | ),
90 | onFieldSubmitted: (_) {
91 | FocusScope.of(context).requestFocus(goToNotes);
92 | },
93 | keyboardType: TextInputType.text,
94 | textCapitalization: TextCapitalization.sentences,
95 | textInputAction: TextInputAction.next,
96 | ),
97 | centerTitle: false,
98 | actions: [
99 | TextButton.icon(
100 | onPressed: () {
101 | if (_initValue['title']!.isEmpty ||
102 | _initValue['notes']!.isEmpty) {
103 | Fluttertoast.showToast(
104 | msg: 'Title or note body cannot be empty',
105 | toastLength: Toast.LENGTH_SHORT,
106 | );
107 | return;
108 | } else {
109 | var key = widget.noteKey;
110 | String? title = _initValue['title'];
111 | String? note = _initValue['notes'];
112 |
113 | CloudNoteModel noteM = CloudNoteModel(
114 | uuid: widget.notes!.uuid!,
115 | title: title!,
116 | notes: note!,
117 | );
118 |
119 | context.read().editNote(noteM, key!);
120 |
121 | Fluttertoast.showToast(
122 | msg: 'Note Saved',
123 | toastLength: Toast.LENGTH_SHORT,
124 | );
125 |
126 | navigateReplaceTo(context, destination: RoutesName.cloud_read_notes_screen , arguments: {
127 | 'note': noteM,
128 | 'noteKey': key
129 | });
130 | }
131 | },
132 | icon: Icon(
133 | Icons.done,
134 | color: context.watch().state.isDarkTheme == false
135 | ? Colors.black45
136 | : Colors.white38,
137 | ),
138 | label: Text(
139 | 'Update',
140 | style: TextStyle(
141 | color: context.watch().state.isDarkTheme == false
142 | ? Colors.black45
143 | : Colors.white38,
144 | ),
145 | ),
146 | ),
147 | ],
148 | ),
149 | body: Padding(
150 | padding: const EdgeInsets.symmetric(horizontal: 10.0),
151 | child: TextFormField(
152 | initialValue: _initValue['notes'],
153 | autofocus: true,
154 | onChanged: (value) {
155 | _initValue['notes'] = value;
156 | },
157 | decoration: const InputDecoration(
158 | border: InputBorder.none,
159 | ),
160 | focusNode: goToNotes,
161 | style: myTextStyle,
162 | textCapitalization: TextCapitalization.sentences,
163 | textAlign: myTextAlign!,
164 | maxLines: height.toInt(),
165 | ),
166 |
167 | //TODO! trying to add styling functionality, having issues
168 | //TODO! persisting the style for a saved note
169 | ),
170 | );
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/lib/presentation/pages/cloud_notes/cloud_read_note_screen.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_tts/flutter_tts.dart';
6 | import 'package:note_app/config/router/navigates_to.dart';
7 | import 'package:note_app/config/router/routes_name.dart';
8 | import 'package:note_app/data/models/cloud_note_models/cloud_note_model.dart';
9 | import 'package:note_app/state/cubits/cloud_note_cubit/cloud_note_cubit.dart';
10 | import 'package:note_app/state/cubits/play_button_cubit/play_button_cubit.dart';
11 | import 'package:note_app/utils/const_values.dart';
12 | import 'package:provider/provider.dart';
13 |
14 | class CloudReadNote extends StatefulWidget {
15 | final CloudNoteModel? note;
16 | final int? noteKey;
17 |
18 | const CloudReadNote({
19 | super.key,
20 | @required this.note,
21 | @required this.noteKey,
22 | });
23 |
24 | @override
25 | State createState() => _CloudReadNoteState();
26 | }
27 |
28 | enum TtsCloudState {
29 | playing,
30 | stopped,
31 | paused,
32 | continued,
33 | }
34 |
35 | class _CloudReadNoteState extends State {
36 | FlutterTts? flutterTts;
37 | dynamic languages;
38 | String? language;
39 | double? volume = 0.5;
40 | double? pitch = 0.2;
41 | double? rate = 0.4;
42 |
43 | String? _newVoiceText;
44 |
45 | TtsCloudState ttsState = TtsCloudState.stopped;
46 |
47 | bool get isIOS => !kIsWeb && Platform.isIOS;
48 |
49 | bool get isAndroid => !kIsWeb && Platform.isAndroid;
50 |
51 | bool get isWeb => kIsWeb;
52 |
53 | @override
54 | void initState() {
55 | super.initState();
56 | initTts();
57 | _onChange(widget.note!.notes.toString());
58 | }
59 |
60 | void initTts() {
61 | flutterTts = FlutterTts();
62 |
63 | _getLanguages();
64 |
65 | // flutterTts.setVoice("en-us-x-sfg#male_1-local");
66 | flutterTts!.setLanguage('en-Us');
67 |
68 | if (isAndroid) {
69 | _getEngines();
70 | }
71 |
72 | flutterTts!.setStartHandler(() {
73 | setState(() {
74 | logger.i('Playing');
75 | ttsState = TtsCloudState.playing;
76 | });
77 | });
78 |
79 | flutterTts!.setCompletionHandler(() {
80 | setState(() {
81 | logger.i('Complete');
82 | ttsState = TtsCloudState.stopped;
83 | });
84 | });
85 |
86 | flutterTts!.setCancelHandler(() {
87 | setState(() {
88 | logger.i('Cancel');
89 | ttsState = TtsCloudState.stopped;
90 | });
91 | });
92 |
93 | if (isWeb || isIOS) {
94 | flutterTts!.setPauseHandler(() {
95 | setState(() {
96 | logger.i('Paused');
97 | ttsState = TtsCloudState.paused;
98 | });
99 | });
100 |
101 | flutterTts!.setContinueHandler(() {
102 | setState(() {
103 | logger.i('Continued');
104 | ttsState = TtsCloudState.continued;
105 | });
106 | });
107 | }
108 |
109 | flutterTts!.setErrorHandler((msg) {
110 | setState(() {
111 | logger.i('error: $msg');
112 | ttsState = TtsCloudState.stopped;
113 | });
114 | });
115 | }
116 |
117 | Future _getLanguages() async {
118 | languages = await flutterTts!.getLanguages;
119 | if (languages != null) setState(() => languages);
120 | }
121 |
122 | Future _getEngines() async {
123 | var engines = await flutterTts!.getEngines;
124 | if (engines != null) {
125 | for (dynamic engine in engines) {
126 | logger.i(engine);
127 | }
128 | }
129 | }
130 |
131 | Future _speak() async {
132 | await flutterTts!.setVolume(volume!);
133 | await flutterTts!.setSpeechRate(rate!);
134 | await flutterTts!.setPitch(pitch!);
135 |
136 | if (_newVoiceText != null) {
137 | if (_newVoiceText!.isNotEmpty) {
138 | await flutterTts!.awaitSpeakCompletion(true);
139 | await flutterTts!.speak(_newVoiceText!);
140 | }
141 | }
142 | }
143 |
144 | Future _stop() async {
145 | var result = await flutterTts!.stop();
146 | if (result == 1) setState(() => ttsState = TtsCloudState.stopped);
147 | }
148 |
149 | // Android does not support pause as of this moment
150 | // Future _pause() async {
151 | // var result =
152 | // await flutterTts.setSilence(widget.note.notes.toString().length);
153 | // if (result == 1) setState(() => ttsState = TtsState.paused);
154 | // }
155 |
156 | @override
157 | void dispose() {
158 | super.dispose();
159 | flutterTts!.stop();
160 | }
161 |
162 | void _onChange(String text) {
163 | setState(() {
164 | _newVoiceText = text;
165 | });
166 | }
167 |
168 | final bool isEditing = true;
169 |
170 | Widget showText() {
171 | dynamic test;
172 | setState(() {
173 | test = SelectableText(
174 | widget.note!.notes.toString(),
175 | style: const TextStyle(
176 | fontSize: 18.0,
177 | ),
178 | );
179 | });
180 | return test;
181 | }
182 |
183 | @override
184 | Widget build(BuildContext context) {
185 | return WillPopScope(
186 | onWillPop: () async {
187 | await context.read().fetchNotes();
188 | return true;
189 | },
190 | child: Scaffold(
191 | appBar: AppBar(
192 | title: const Text(
193 | 'Read Note',
194 | ),
195 | shadowColor: Colors.transparent,
196 | backgroundColor: Colors.transparent,
197 | centerTitle: true,
198 | actions: [
199 | IconButton(
200 | icon: const Icon(Icons.mode_edit),
201 | onPressed: () {
202 | navigateReplaceTo(context, destination: RoutesName.cloud_edit_notes_screen , arguments: {
203 | 'notes': widget.note,
204 | 'noteKey': widget.noteKey
205 | });
206 | },
207 | )
208 | ],
209 | ),
210 | body: SingleChildScrollView(
211 | child: Padding(
212 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
213 | child: Column(
214 | crossAxisAlignment: CrossAxisAlignment.start,
215 | children: [
216 | SizedBox(
217 | child: Center(
218 | child: Text(
219 | '${widget.note!.title}',
220 | style: const TextStyle(
221 | fontSize: 20,
222 | fontWeight: FontWeight.bold,
223 | ),
224 | softWrap: true,
225 | textAlign: TextAlign.center,
226 | ),
227 | ),
228 | ),
229 | const SizedBox(
230 | height: 15,
231 | ),
232 | showText(),
233 | ],
234 | ),
235 | ),
236 | ),
237 | floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
238 | floatingActionButton: context.watch().state.canPlay
239 | ? FloatingActionButton(
240 | backgroundColor: Colors.white60,
241 | onPressed: () {
242 | ttsState == TtsCloudState.stopped ? _speak() : _stop();
243 | },
244 | child: Icon(
245 | ttsState == TtsCloudState.stopped
246 | ? Icons.play_circle_outline
247 | : Icons.stop_circle_outlined,
248 | color: Colors.black45,
249 | ),
250 | )
251 | : null,
252 | ),
253 | );
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/lib/presentation/pages/home/home.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter/services.dart';
5 | import 'package:flutter_screenutil/flutter_screenutil.dart';
6 | import 'package:note_app/config/router/navigates_to.dart';
7 | import 'package:note_app/config/router/routes_name.dart';
8 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
9 | import 'package:note_app/utils/colors/m_colors.dart';
10 | import 'package:note_app/utils/greetings.dart';
11 | import 'package:provider/provider.dart';
12 |
13 | class HomeScreen extends StatefulWidget {
14 | const HomeScreen({super.key});
15 |
16 | @override
17 | _HomeScreenState createState() => _HomeScreenState();
18 | }
19 |
20 | class _HomeScreenState extends State {
21 |
22 | @override
23 | void initState() {
24 | super.initState();
25 | }
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return Scaffold(
30 | appBar: AppBar(
31 | title: const Text('VNotes'),
32 | // TODO:* adding support for localization soon.
33 | actions: [
34 | IconButton(
35 | onPressed: () {
36 | navigateTo(context, destination: RoutesName.settings_screen);
37 | },
38 | icon: const Icon(
39 | Icons.settings,
40 | ),
41 | ),
42 | ],
43 | ),
44 | body: SingleChildScrollView(
45 | child: Padding(
46 | padding: const EdgeInsets.symmetric(horizontal: 20),
47 | child: Center(
48 | child: Column(
49 | mainAxisAlignment: MainAxisAlignment.center,
50 | children: [
51 | SizedBox(
52 | height: 30.h,
53 | ),
54 | Text(
55 | 'Hi 👋🏾, ${greetingMessage()}',
56 | style: TextStyle(
57 | fontWeight: FontWeight.bold,
58 | fontSize: 20.sp,
59 | ),
60 | ),
61 | SizedBox(
62 | height: 30.h,
63 | ),
64 | Card(
65 | shape: RoundedRectangleBorder(
66 | borderRadius: BorderRadius.circular(20),
67 | ),
68 | color: context.watch().state.isDarkTheme == false
69 | ? AppColors.primaryColor.withOpacity(0.7)
70 | : AppColors.cardColor,
71 | child: InkWell(
72 | onTap: () {
73 | navigateTo(context, destination: RoutesName.local_notes);
74 | },
75 | child: Container(
76 | height: 200.h,
77 | width: 300.w,
78 | decoration: BoxDecoration(
79 | borderRadius: BorderRadius.circular(10),
80 | ),
81 | child: Center(
82 | child: Column(
83 | mainAxisAlignment: MainAxisAlignment.center,
84 | children: [
85 | Icon(
86 | Icons.sd_storage_outlined,
87 | size: 100.r,
88 | color: context.watch().state.isDarkTheme == false
89 | ? AppColors.defaultBlack
90 | : AppColors.defaultWhite,
91 | ),
92 | Text(
93 | 'Local Note',
94 | style: TextStyle(
95 | fontSize: 20.sp,
96 | fontWeight: FontWeight.bold,
97 | ),
98 | ),
99 | ],
100 | ),
101 | ),
102 | ),
103 | ),
104 | ),
105 | SizedBox(
106 | height: 20.h,
107 | ),
108 | Card(
109 | shape: RoundedRectangleBorder(
110 | borderRadius: BorderRadius.circular(20),
111 | ),
112 | color: context.watch().state.isDarkTheme == false
113 | ? AppColors.primaryColor.withOpacity(0.7)
114 | : AppColors.cardColor,
115 | child: InkWell(
116 | onTap: () {
117 | navigateTo(context, destination: RoutesName.cloud_notes);
118 | },
119 | child: Container(
120 | height: 200.h,
121 | width: 300.w,
122 | decoration: BoxDecoration(
123 | borderRadius: BorderRadius.circular(10),
124 | ),
125 | child: Center(
126 | child: Column(
127 | mainAxisAlignment: MainAxisAlignment.center,
128 | children: [
129 | Icon(
130 | Icons.cloud_queue_outlined,
131 | size: 100.r,
132 | color: context.watch().state.isDarkTheme == false
133 | ? AppColors.defaultBlack
134 | : AppColors.defaultWhite,
135 | ),
136 | Text(
137 | 'Cloud Note',
138 | style: TextStyle(
139 | fontSize: 20.sp,
140 | fontWeight: FontWeight.bold,
141 | ),
142 | ),
143 | ],
144 | ),
145 | ),
146 | ),
147 | ),
148 | ),
149 | ],
150 | ),
151 | ),
152 | ),
153 | ),
154 | );
155 | }
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/lib/presentation/pages/local_notes/create_note_screen.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'dart:convert';
3 |
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:fluttertoast/fluttertoast.dart';
7 | import 'package:note_app/data/models/local_note_model/note_model.dart';
8 | import 'package:note_app/helpers/hive_manager.dart';
9 | import 'package:note_app/presentation/pages/local_notes/local_notes.dart';
10 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
11 | import 'package:note_app/utils/tools/slide_transition.dart';
12 | import 'package:provider/provider.dart';
13 | import 'package:flutter_quill/flutter_quill.dart';
14 |
15 | class CreateNoteScreen extends StatefulWidget {
16 | const CreateNoteScreen({Key? key}) : super(key: key);
17 | @override
18 | _CreateNoteScreenState createState() => _CreateNoteScreenState();
19 | }
20 |
21 | class _CreateNoteScreenState extends State {
22 | final TextEditingController _noteTitle = TextEditingController();
23 |
24 | // Quill controllers
25 | late QuillController _quillController;
26 | final FocusNode _quillFocusNode = FocusNode();
27 | final ScrollController _scrollController = ScrollController();
28 |
29 | final scaffoldKey = GlobalKey();
30 | bool? _isNotEmpty;
31 | final goToNotes = FocusNode();
32 |
33 | @override
34 | void initState() {
35 | super.initState();
36 | // Initialize the Quill controller with empty document
37 | _quillController = QuillController.basic();
38 | }
39 |
40 | @override
41 | void dispose() {
42 | _noteTitle.dispose();
43 | _quillController.dispose();
44 | _quillFocusNode.dispose();
45 | _scrollController.dispose();
46 | super.dispose();
47 | }
48 |
49 | // Convert Quill document to JSON string for storage
50 | String _getQuillContentAsJson() {
51 | return jsonEncode(_quillController.document.toDelta().toJson());
52 | }
53 |
54 | // Check if Quill document is empty
55 | bool _isQuillContentEmpty() {
56 | final delta = _quillController.document.toDelta();
57 | // Check if there's only one empty line or no content
58 | return delta.length <= 1 &&
59 | (delta.isEmpty ||
60 | (delta.length == 1 && delta.first.data == '\n'));
61 | }
62 |
63 | Future checkIfNoteIsNotEmptyWhenGoingBack() async {
64 | final storeData = HiveManager().noteModelBox;
65 |
66 | if (!_isQuillContentEmpty() || _noteTitle.text.isNotEmpty) {
67 | final String noteTitle = _noteTitle.text;
68 | final String noteContent = _getQuillContentAsJson();
69 |
70 | NoteModel noteM = NoteModel(
71 | title: noteTitle,
72 | notes: noteContent,
73 | // dateTime: DateTime.now().toString(),
74 | );
75 |
76 | await storeData.add(noteM);
77 | await Fluttertoast.showToast(
78 | msg: 'Note Saved',
79 | toastLength: Toast.LENGTH_SHORT,
80 | );
81 |
82 | if(mounted) {
83 | Navigator.of(context).pop();
84 | await Navigator.of(context).push(MaterialPageRoute(builder: (_) {
85 | return const LocalNotesScreen();
86 | }));
87 | }
88 | _isNotEmpty = true;
89 | } else {
90 | await Fluttertoast.showToast(
91 | msg: 'Note was empty, nothing was saved',
92 | toastLength: Toast.LENGTH_SHORT,
93 | );
94 |
95 | if(mounted) {
96 | Navigator.of(context).pop();
97 | await Navigator.of(context).push(MaterialPageRoute(builder: (_) {
98 | return const LocalNotesScreen();
99 | }));
100 | }
101 | _isNotEmpty = false;
102 | }
103 | return _isNotEmpty!;
104 | }
105 |
106 | void checkIfNoteIsNotEmptyAndSaveNote() {
107 | final storeData = HiveManager().noteModelBox;
108 |
109 | if (_noteTitle.text.isEmpty || _isQuillContentEmpty()) {
110 | Fluttertoast.showToast(
111 | msg: 'Title or note body cannot be empty',
112 | toastLength: Toast.LENGTH_SHORT,
113 | );
114 | return;
115 | } else {
116 | final String noteTitle = _noteTitle.text;
117 | final String noteContent = _getQuillContentAsJson();
118 |
119 | NoteModel noteM = NoteModel(
120 | title: noteTitle,
121 | notes: noteContent,
122 | // dateTime: DateTime.now().toString(),
123 | );
124 |
125 | storeData.add(noteM);
126 | Fluttertoast.showToast(
127 | msg: 'Note Saved',
128 | toastLength: Toast.LENGTH_SHORT,
129 | );
130 |
131 | Navigator.of(context).pop();
132 | Navigator.of(context).push(MySlide(builder: (_) {
133 | return const LocalNotesScreen();
134 | }));
135 | }
136 | }
137 |
138 | @override
139 | Widget build(BuildContext context) {
140 | return WillPopScope(
141 | onWillPop: checkIfNoteIsNotEmptyWhenGoingBack,
142 | child: Scaffold(
143 | key: scaffoldKey,
144 | appBar: AppBar(
145 | title: TextFormField(
146 | autofocus: true,
147 | controller: _noteTitle,
148 | decoration: const InputDecoration(
149 | hintText: 'Create Note Title...',
150 | hintStyle: TextStyle(
151 | fontSize: 20,
152 | fontWeight: FontWeight.bold,
153 | ),
154 | border: InputBorder.none,
155 | ),
156 | style: const TextStyle(
157 | fontSize: 20,
158 | fontWeight: FontWeight.bold,
159 | ),
160 | onFieldSubmitted: (_) {
161 | FocusScope.of(context).requestFocus(_quillFocusNode);
162 | },
163 | keyboardType: TextInputType.text,
164 | textCapitalization: TextCapitalization.sentences,
165 | textInputAction: TextInputAction.next,
166 | ),
167 | centerTitle: false,
168 | actions: [
169 | TextButton.icon(
170 | onPressed: checkIfNoteIsNotEmptyAndSaveNote,
171 | icon: Icon(
172 | Icons.done,
173 | color: context.watch().state.isDarkTheme == false
174 | ? Colors.black45
175 | : Colors.white38,
176 | ),
177 | label: Text(
178 | 'Save',
179 | style: TextStyle(
180 | color: context.watch().state.isDarkTheme == false
181 | ? Colors.black45
182 | : Colors.white38,
183 | ),
184 | ),
185 | ),
186 | ],
187 | ),
188 | body: Column(
189 | children: [
190 | // Quill Toolbar
191 | QuillSimpleToolbar(
192 | controller: _quillController,
193 | config: const QuillSimpleToolbarConfig(
194 | showFontFamily: true,
195 | showFontSize: false,
196 | showBackgroundColorButton: false,
197 | showColorButton: true,
198 | showClearFormat: true,
199 | showCodeBlock: false,
200 | showInlineCode: false,
201 | showListCheck: true,
202 | showListBullets: true,
203 | showListNumbers: true,
204 | showQuote: true,
205 | showStrikeThrough: true,
206 | showUnderLineButton: true,
207 | showDividers: false,
208 | showHeaderStyle: true,
209 | showLink: false,
210 | showSearchButton: false,
211 | showAlignmentButtons: true,
212 | ),
213 | ),
214 | // Quill Editor
215 | Expanded(
216 | child: Container(
217 | padding: const EdgeInsets.symmetric(horizontal: 10.0),
218 | child: QuillEditor(
219 | focusNode: _quillFocusNode,
220 | scrollController: _scrollController,
221 | controller: _quillController,
222 | config: const QuillEditorConfig(
223 | placeholder: 'Type Note...',
224 | scrollable: true,
225 | padding: EdgeInsets.all(0),
226 | autoFocus: false,
227 | expands: false,
228 | ),
229 | ),
230 | ),
231 | ),
232 | ],
233 | ),
234 | ),
235 | );
236 | }
237 | }
--------------------------------------------------------------------------------
/lib/presentation/pages/local_notes/edit_note_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:fluttertoast/fluttertoast.dart';
3 | import 'package:note_app/data/models/local_note_model/note_model.dart';
4 | import 'package:note_app/helpers/hive_manager.dart';
5 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
6 | import 'package:provider/provider.dart';
7 |
8 | class EditNoteScreen extends StatefulWidget {
9 | final NoteModel? notes;
10 | final int? noteKey;
11 |
12 | const EditNoteScreen({
13 | super.key,
14 | @required this.notes,
15 | @required this.noteKey,
16 | });
17 |
18 | @override
19 | _EditNoteScreenState createState() => _EditNoteScreenState();
20 | }
21 |
22 | class _EditNoteScreenState extends State {
23 | final goToNotes = FocusNode();
24 | final scaffoldKey = GlobalKey();
25 |
26 | TextStyle? myTextStyle;
27 | TextAlign? myTextAlign;
28 |
29 | dynamic coText;
30 | var _initValue = {'notes': '', 'conText': ''};
31 |
32 | var _isInit = true;
33 |
34 | @override
35 | void didChangeDependencies() {
36 | super.didChangeDependencies();
37 | // _noteText = widget.notes.notes;
38 | if (_isInit) {
39 | _initValue = {
40 | 'title': widget.notes!.title.toString(),
41 | 'notes': widget.notes!.notes.toString(),
42 | 'conText': widget.notes!.notes.toString()
43 | };
44 | }
45 | _isInit = false;
46 | }
47 |
48 | @override
49 | void initState() {
50 | super.initState();
51 | myTextStyle = const TextStyle(
52 | fontSize: 18.5,
53 | );
54 | myTextAlign = TextAlign.left;
55 | }
56 |
57 | bool? isEdited;
58 |
59 | @override
60 | Widget build(BuildContext context) {
61 | var height = MediaQuery.of(context).size.height;
62 | final storeData = HiveManager().noteModelBox;
63 | return Scaffold(
64 | key: scaffoldKey,
65 | appBar: AppBar(
66 | title: TextFormField(
67 | initialValue: _initValue['title'],
68 | autofocus: true,
69 | onChanged: (val) {
70 | _initValue['title'] = val;
71 | },
72 | decoration: const InputDecoration(
73 | hintText: 'Create Note Title...',
74 | hintStyle: TextStyle(
75 | fontSize: 20,
76 | fontWeight: FontWeight.bold,
77 | ),
78 | border: InputBorder.none,
79 | ),
80 | style: const TextStyle(
81 | fontSize: 20,
82 | fontWeight: FontWeight.bold,
83 | ),
84 | onFieldSubmitted: (_) {
85 | FocusScope.of(context).requestFocus(goToNotes);
86 | },
87 | keyboardType: TextInputType.text,
88 | textCapitalization: TextCapitalization.sentences,
89 | textInputAction: TextInputAction.next,
90 | ),
91 | centerTitle: false,
92 | actions: [
93 | TextButton.icon(
94 | onPressed: () {
95 | if (_initValue['title']!.isEmpty ||
96 | _initValue['notes']!.isEmpty) {
97 | Fluttertoast.showToast(
98 | msg: 'Title or note body cannot be empty',
99 | toastLength: Toast.LENGTH_SHORT,
100 | );
101 | return;
102 | } else {
103 | var key = widget.noteKey;
104 | String? title = _initValue['title'];
105 | String? note = _initValue['notes'];
106 | NoteModel noteM = NoteModel(
107 | title: title!,
108 | notes: note!,
109 | // dateTime: DateTime.now().toString(),
110 | );
111 | storeData.put(key, noteM);
112 | Fluttertoast.showToast(
113 | msg: 'Note Saved',
114 | toastLength: Toast.LENGTH_SHORT,
115 | );
116 | Navigator.pop(context);
117 | // navigateTo(context,
118 | // destination: ReadNotesScreen(note: noteM, noteKey: key));
119 | }
120 | },
121 | icon: Icon(
122 | Icons.done,
123 | color:
124 | context.watch().state.isDarkTheme == false ? Colors.black45 : Colors.white38,
125 | ),
126 | label: Text(
127 | 'Update',
128 | style: TextStyle(
129 | color: context.watch().state.isDarkTheme == false
130 | ? Colors.black45
131 | : Colors.white38,
132 | ),
133 | ),
134 | ),
135 | ],
136 | ),
137 | body: Padding(
138 | padding: const EdgeInsets.symmetric(horizontal: 10.0),
139 | child: TextFormField(
140 | initialValue: _initValue['notes'],
141 | autofocus: true,
142 | onChanged: (value) {
143 | _initValue['notes'] = value;
144 | },
145 | decoration: const InputDecoration(
146 | border: InputBorder.none,
147 | ),
148 | focusNode: goToNotes,
149 | style: myTextStyle,
150 | textCapitalization: TextCapitalization.sentences,
151 | textAlign: myTextAlign!,
152 | maxLines: height.toInt(),
153 | ),
154 |
155 | //TODO! trying to add styling functionality, having issues
156 | //TODO! persisting the style for a saved note
157 | ),
158 | );
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/lib/presentation/pages/notifications/notifications_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_screenutil/flutter_screenutil.dart';
3 |
4 | class NotificationView extends StatefulWidget {
5 | final String? title;
6 | final String? body;
7 |
8 | const NotificationView({Key? key, this.title, this.body}) : super(key: key);
9 |
10 | @override
11 | State createState() => _NotificationViewState();
12 | }
13 |
14 | class _NotificationViewState extends State {
15 | @override
16 | Widget build(BuildContext context) {
17 | return Scaffold(
18 | appBar: AppBar(),
19 | body: SingleChildScrollView(
20 | padding: const EdgeInsets.symmetric(horizontal: 16),
21 | child: Column(
22 | crossAxisAlignment: CrossAxisAlignment.start,
23 | children: [
24 | Center(
25 | child: Text(
26 | '${widget.title}',
27 | style: TextStyle(
28 | fontSize: 30.sp,
29 | fontWeight: FontWeight.bold,
30 | ),
31 | ),
32 | ),
33 | const SizedBox(
34 | height: 10,
35 | ),
36 | Text(
37 | '${widget.body}',
38 | style: TextStyle(
39 | fontSize: 17.sp,
40 | ),
41 | textAlign: TextAlign.justify,
42 | )
43 | ],
44 | ),
45 | ),
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/presentation/pages/settings/settings_screen.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_bloc/flutter_bloc.dart';
5 | import 'package:note_app/config/router/navigates_to.dart';
6 | import 'package:note_app/config/router/routes_name.dart';
7 | import 'package:note_app/state/cubits/auth_cubit/auth_cubit.dart';
8 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
9 | import 'package:note_app/utils/tools/message_dialog.dart';
10 | import 'package:note_app/utils/tools/sized_box_ex.dart';
11 | import 'package:package_info_plus/package_info_plus.dart';
12 | import 'package:provider/provider.dart';
13 |
14 | class SettingsScreen extends StatefulWidget {
15 | const SettingsScreen({Key? key}) : super(key: key);
16 |
17 | @override
18 | _SettingsScreenState createState() => _SettingsScreenState();
19 | }
20 |
21 | class _SettingsScreenState extends State {
22 | String? appName;
23 | String? packageName;
24 | String? version;
25 | String? buildNumber;
26 |
27 | bool isLoading = false;
28 | bool isLoadingRemoveKeys = false;
29 |
30 | @override
31 | void initState() {
32 | super.initState();
33 | PackageInfo.fromPlatform().then((PackageInfo packageInfo) {
34 | setState(() {
35 | appName = packageInfo.appName;
36 | packageName = packageInfo.packageName;
37 | version = packageInfo.version;
38 | buildNumber = packageInfo.buildNumber;
39 | });
40 | });
41 | }
42 |
43 | @override
44 | Widget build(BuildContext context) {
45 | return Scaffold(
46 | appBar: AppBar(
47 | title: const Text('App Settings'),
48 | ),
49 | body: Padding(
50 | padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
51 | child: Column(
52 | children: [
53 | SingleChildScrollView(
54 | child: Column(
55 | children: [
56 | //Body here
57 | SizedBox(
58 | child: Column(
59 | children: [
60 | Text(
61 | 'VNotes',
62 | style: TextStyle(
63 | fontSize: 30,
64 | fontWeight: FontWeight.bold,
65 | ),
66 | ),
67 | 10.toHeight,
68 | Text(
69 | 'VNotes is a simple lite Note taking app, here to make Note taking easy.',
70 | style: TextStyle(),
71 | textAlign: TextAlign.center,
72 | )
73 | ],
74 | ),
75 | ),
76 | 10.toHeight,
77 | const Divider(),
78 | 10.toHeight,
79 | ListTile(
80 | leading: Icon(
81 | context.watch().state.isDarkTheme == false
82 | ? Icons.brightness_3
83 | : Icons.brightness_6),
84 | title: const Text(
85 | 'Enable Dark Theme',
86 | style: TextStyle(),
87 | ),
88 | trailing: Switch(
89 | value: context.watch().state.isDarkTheme,
90 | onChanged: (val) {
91 | context.read().toggleTheme();
92 | },
93 | ),
94 | ),
95 | 10.toHeight,
96 | ListTile(
97 | leading: const Icon(Icons.delete),
98 | title: const Text(
99 | 'Trash',
100 | style: TextStyle(),
101 | ),
102 | subtitle: const Text(
103 | 'You can recover any note you delete and'
104 | ' also delete them permanently',
105 | style: TextStyle(),
106 | ),
107 | onTap: () {
108 | navigateTo(context, destination: RoutesName.trash_notes);
109 | },
110 | ),
111 | 10.toHeight,
112 | BlocBuilder(
113 | builder: (context, state) {
114 | if (state is AuthAuthenticated) {
115 | return ListTile(
116 | leading: const Icon(Icons.logout),
117 | title: const Text(
118 | 'Logout',
119 | ),
120 | subtitle: const Text(
121 | 'This will log you out of you cloud account',
122 | style: TextStyle(),
123 | ),
124 | onTap: () {
125 | context.read().logout().then((value) {
126 | showSuccess('Logout successful');
127 | });
128 | },
129 | );
130 | } else {
131 | return const SizedBox.shrink();
132 | }
133 | },
134 | ),
135 | ],
136 | ),
137 | ),
138 | Flexible(
139 | child: Align(
140 | alignment: Alignment.bottomCenter,
141 | child: Platform.isAndroid
142 | ? Text('$packageName Version $version')
143 | : Padding(
144 | padding: const EdgeInsets.only(bottom: 20),
145 | child: Text('$packageName Version $version'),
146 | ),
147 | ),
148 | )
149 | ],
150 | ),
151 | ),
152 | );
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/lib/presentation/views.dart:
--------------------------------------------------------------------------------
1 | export 'pages/home/home.dart';
2 |
3 | // local notes
4 | export 'pages/local_notes/local_notes.dart';
5 | export 'pages/local_notes/create_note_screen.dart';
6 | export 'pages/local_notes/edit_note_screen.dart';
7 | export 'pages/local_notes/read_notes_screens.dart';
8 |
9 | // cloud notes
10 | export 'pages/cloud_notes/cloud_notes.dart';
11 | export 'pages/cloud_notes/cloud_create_note.dart';
12 | export 'pages/cloud_notes/cloud_edit_note.dart';
13 | export 'pages/cloud_notes/cloud_read_note_screen.dart';
14 |
15 | // cloud auth
16 | export 'pages/cloud_notes/auth/login_screen.dart';
17 | export 'pages/cloud_notes/auth/register_screen.dart';
18 | export 'pages/cloud_notes/auth/verify_code.dart';
19 |
20 | // settings
21 | export 'pages/settings/settings_screen.dart';
22 |
23 | // trash
24 | export 'pages/trash/trashed_notes.dart';
25 |
26 | // notifications
27 | export 'pages/notifications/notifications_view.dart';
--------------------------------------------------------------------------------
/lib/presentation/widget/mbutton.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_bloc/flutter_bloc.dart';
3 | import 'package:flutter_screenutil/flutter_screenutil.dart';
4 | import 'package:note_app/state/cubits/theme_cubit/theme_cubit.dart';
5 | import 'package:note_app/utils/colors/m_colors.dart';
6 | import 'package:note_app/utils/const_values.dart';
7 |
8 | class MButton extends StatefulWidget {
9 | final String? title;
10 | final String? secondTitle;
11 | final double? radius;
12 | final double? width;
13 | final double? height;
14 | final Color? btnColor;
15 | final Color? textColor;
16 | final double? textSize;
17 | final Color? borderColor;
18 | final IconData? btnIcon;
19 | final bool isLoading;
20 | final bool isTwoText;
21 | final bool hasIcon;
22 | final Widget? btnSVGIcon;
23 | final bool isTextBold;
24 | final VoidCallback? onPressed;
25 |
26 | const MButton({
27 | super.key,
28 | required this.title,
29 | this.secondTitle,
30 | this.radius,
31 | this.width,
32 | this.height,
33 | this.btnColor,
34 | this.textColor,
35 | this.textSize,
36 | this.borderColor,
37 | this.btnIcon,
38 | this.btnSVGIcon,
39 | this.isLoading = false,
40 | this.isTwoText = false,
41 | this.hasIcon = false,
42 | this.isTextBold = false,
43 | required this.onPressed,
44 | }) : assert(
45 | title != null,
46 | onPressed != null,
47 | );
48 |
49 | @override
50 | _MButtonState createState() => _MButtonState();
51 | }
52 |
53 | class _MButtonState extends State {
54 | @override
55 | Widget build(BuildContext context) {
56 | return BlocBuilder(
57 | builder: (context, state) {
58 | return SizedBox(
59 | width: widget.width ?? 327.w,
60 | height: widget.height ?? 55.h,
61 | child: ElevatedButton(
62 | style: ElevatedButton.styleFrom(
63 | elevation: 0,
64 | backgroundColor: widget.btnColor ?? transparent,
65 | shape: RoundedRectangleBorder(
66 | borderRadius: BorderRadius.circular(widget.radius ?? 8),
67 | side: BorderSide(
68 | color: state.isDarkTheme == false
69 | ? AppColors.defaultBlack
70 | : AppColors.defaultWhite,
71 | ),
72 | ),
73 | ),
74 | onPressed: widget.onPressed,
75 | child: widget.isLoading == false
76 | ? widget.isTwoText == true
77 | ? Row(
78 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
79 | children: [
80 | Text(
81 | '${widget.title}',
82 | style: TextStyle(
83 | fontSize: widget.textSize ?? 16.sp,
84 | fontWeight: widget.isTextBold == true
85 | ? FontWeight.bold
86 | : FontWeight.normal,
87 | color: state.isDarkTheme == false
88 | ? AppColors.defaultBlack
89 | : AppColors.defaultWhite,
90 | ),
91 | textAlign: TextAlign.center,
92 | ),
93 | Text(
94 | '${widget.secondTitle}',
95 | style: TextStyle(
96 | fontSize: widget.textSize ?? 16.sp,
97 | fontWeight: widget.isTextBold == true
98 | ? FontWeight.bold
99 | : FontWeight.normal,
100 | color: state.isDarkTheme == false
101 | ? AppColors.defaultBlack
102 | : AppColors.defaultWhite,
103 | ),
104 | textAlign: TextAlign.center,
105 | ),
106 | ],
107 | )
108 | : widget.hasIcon == true
109 | ? Row(
110 | mainAxisAlignment: MainAxisAlignment.center,
111 | children: [
112 | Text(
113 | '${widget.title}',
114 | style: TextStyle(
115 | fontSize: widget.textSize ?? 16.sp,
116 | fontWeight: widget.isTextBold == true
117 | ? FontWeight.bold
118 | : FontWeight.normal,
119 | color: state.isDarkTheme == false
120 | ? AppColors.defaultBlack
121 | : AppColors.defaultWhite,
122 | ),
123 | textAlign: TextAlign.center,
124 | ),
125 | SizedBox(
126 | width: 10.w,
127 | ),
128 | Icon(
129 | widget.btnIcon!,
130 | color: AppColors.defaultWhite,
131 | ),
132 | ],
133 | )
134 | : Text(
135 | '${widget.title}',
136 | style: TextStyle(
137 | fontSize: widget.textSize ?? 13.sp,
138 | fontWeight: widget.isTextBold == true
139 | ? FontWeight.bold
140 | : FontWeight.normal,
141 | color: state.isDarkTheme == false
142 | ? AppColors.defaultBlack
143 | : AppColors.defaultWhite,
144 | ),
145 | textAlign: TextAlign.center,
146 | )
147 | : SizedBox(
148 | width: 20.w,
149 | height: 20.w,
150 | child: CircularProgressIndicator(
151 | color:
152 | state.isDarkTheme == false ? AppColors.defaultBlack : AppColors.defaultWhite,
153 | ),
154 | ),
155 | ),
156 | );
157 | },
158 | );
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/lib/services/service_locator.dart:
--------------------------------------------------------------------------------
1 | import 'package:get_it/get_it.dart';
2 | import 'package:note_app/data/network/network_services_api.dart';
3 | import 'package:note_app/data/repositories/auth_repository.dart';
4 | import 'package:note_app/data/repositories/cloud_note_repository.dart';
5 | import 'package:note_app/helpers/hive_manager.dart';
6 | import 'package:note_app/state/cubits/auth_cubit/auth_cubit.dart';
7 | import 'package:note_app/state/cubits/cloud_note_cubit/cloud_note_cubit.dart';
8 |
9 | final GetIt getIt = GetIt.instance;
10 |
11 | Future setupLocator() async {
12 | // Services
13 | getIt.registerLazySingleton(() => NetworkServicesApi());
14 | getIt.registerLazySingleton(() => HiveManager());
15 |
16 | // Repositories
17 | getIt.registerLazySingleton(
18 | () => AuthRepository(
19 | api: getIt(),
20 | hiveManager: getIt(),
21 | ),
22 | );
23 |
24 | getIt.registerLazySingleton(
25 | () => CloudNoteRepository(
26 | api: getIt(),
27 | hiveManager: getIt(),
28 | ),
29 | );
30 |
31 | // Cubits
32 | getIt.registerFactory(
33 | () => AuthCubit(
34 | authRepository: getIt(),
35 | ),
36 | );
37 |
38 | getIt.registerFactory(
39 | () => CloudNoteCubit(
40 | repository: getIt(),
41 | ),
42 | );
43 | }
--------------------------------------------------------------------------------
/lib/state/cubits/auth_cubit/auth_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:bloc/bloc.dart';
2 | import 'package:equatable/equatable.dart';
3 | import 'package:meta/meta.dart';
4 | import 'package:note_app/data/exceptions/app_exceptions.dart';
5 | import 'package:note_app/data/models/cloud_note_models/user_model.dart';
6 | import 'package:note_app/data/repositories/auth_repository.dart';
7 | import 'package:note_app/utils/const_values.dart';
8 | import 'package:note_app/utils/tools/message_dialog.dart';
9 |
10 | part 'auth_state.dart';
11 |
12 | class AuthCubit extends Cubit {
13 | final AuthRepository _authRepository;
14 | String? _attemptedRoute;
15 |
16 |
17 | AuthCubit({
18 | required AuthRepository authRepository,
19 | }) : _authRepository = authRepository,
20 | super(AuthInitial()) {
21 | _loadInitialState();
22 | }
23 |
24 | void saveAttemptedRoute(String route) {
25 | _attemptedRoute = route;
26 | }
27 |
28 | String? getAndClearAttemptedRoute() {
29 | final route = _attemptedRoute;
30 | _attemptedRoute = null;
31 | return route;
32 | }
33 |
34 | Future _loadInitialState() async {
35 | final token = _authRepository.getCurrentUserToken();
36 | final user = _authRepository.getCurrentUser();
37 |
38 | if (token == null) {
39 | emit(AuthUnauthenticated());
40 | return;
41 | }
42 |
43 | if (user != null) {
44 | logger.i('Email of user: ${user.emailVerified}');
45 | if (user.emailVerified == null || user.emailVerified == 'null') {
46 | emit(AuthEmailUnverified(user));
47 | } else {
48 | emit(AuthAuthenticated(user));
49 | }
50 | }
51 |
52 | checkAuthStatus();
53 | }
54 |
55 | Future checkAuthStatus() async {
56 | final token = _authRepository.getCurrentUserToken();
57 | if (token != null) {
58 | try {
59 | final freshUserData = await _authRepository.fetchUserDetails();
60 |
61 | if (freshUserData?.emailVerified == null ||
62 | freshUserData?.emailVerified == 'null') {
63 | emit(AuthEmailUnverified(freshUserData!));
64 | } else {
65 | emit(AuthAuthenticated(freshUserData!));
66 | }
67 | } catch (e) {
68 |
69 | if (e.toString().contains('not verified')) {
70 | final user = _authRepository.getCurrentUser();
71 | if (user != null) {
72 | emit(AuthEmailUnverified(user));
73 | }
74 | } else if (e.toString().contains('suspended')) {
75 | emit(const AuthError('Account suspended'));
76 | await _authRepository.logout();
77 | } else {
78 | emit(AuthError(e.toString()));
79 | }
80 | }
81 | } else {
82 | emit(AuthUnauthenticated());
83 | }
84 | }
85 |
86 | Future login(String email, String password) async {
87 | emit(AuthLoading());
88 | try {
89 | final user = await _authRepository.login(email, password);
90 | emit(AuthAuthenticated(user));
91 | } catch (e) {
92 |
93 | if (e is UnauthorisedException &&
94 | e.toString().contains('not yet verified')) {
95 | logger.i('Handling unverified user in cubit');
96 | final user = _authRepository.getCurrentUser();
97 | if (user != null) {
98 | emit(AuthError(e.toString()));
99 | emit(AuthEmailUnverified(user,));
100 | }
101 | } else if (e is NoInternetException) {
102 | emit(const AuthError('No internet connection'));
103 | } else {
104 | emit(AuthError(e.toString()));
105 | }
106 | }
107 | }
108 |
109 | Future register({
110 | required String email,
111 | required String password,
112 | required String firstName,
113 | required String lastName,
114 | required String userName,
115 | }) async {
116 | emit(AuthLoading());
117 | try {
118 | final user = await _authRepository.register(
119 | email: email,
120 | password: password,
121 | firstName: firstName,
122 | lastName: lastName,
123 | userName: userName,
124 | );
125 | emit(AuthEmailUnverified(user));
126 | } catch (e) {
127 | if (e is NoInternetException) {
128 | emit(const AuthError('No internet connection'));
129 | } else {
130 | emit(AuthError(e.toString()));
131 | }
132 | }
133 | }
134 |
135 | Future verifyEmail(String code) async {
136 | emit(AuthLoading());
137 | try {
138 | await _authRepository.verifyEmail(code);
139 | final user = _authRepository.getCurrentUser();
140 | if (user != null) {
141 | showSuccess('You have been verified successfully.');
142 | final attemptedRoute = _attemptedRoute;
143 | emit(AuthAuthenticated(user));
144 | if (attemptedRoute != null) {
145 | saveAttemptedRoute(attemptedRoute);
146 | }
147 | }
148 | } catch (e) {
149 | if (e is NoInternetException) {
150 | emit(const AuthError('No internet connection'));
151 | } else {
152 | emit(AuthError(e.toString()));
153 | }
154 | }
155 | }
156 |
157 | Future resendVerificationCode({String? email}) async {
158 | emit(AuthLoading());
159 | try {
160 | if(email != null) {
161 | await _authRepository.resendVerificationCode(email: email);
162 | emit(AuthInitial());
163 | showSuccess('Code sent');
164 | } else {
165 | await _authRepository.resendVerificationCode();
166 | emit(AuthEmailUnverified(_authRepository.getCurrentUser()!));
167 | showSuccess('Code sent');
168 | }
169 | } catch (e) {
170 | if (e is NoInternetException) {
171 | emit(const AuthError('No internet connection'));
172 | } else {
173 | emit(AuthError(e.toString()));
174 | }
175 | }
176 | }
177 |
178 | Future forgotPassword(String email) async {
179 | emit(AuthLoading());
180 | try {
181 | await _authRepository.forgotPassword(email);
182 | emit(AuthSuccess());
183 | showSuccess('Code sent');
184 | } catch (e) {
185 | if (e is NoInternetException) {
186 | emit(const AuthError('No internet connection'));
187 | } else {
188 | emit(AuthError(e.toString()));
189 | }
190 | }
191 | }
192 |
193 | Future resetPassword(String otpCode, String newPassword) async {
194 | emit(AuthLoading());
195 | try {
196 | await _authRepository.resetPassword(otpCode, newPassword);
197 | emit(AuthSuccess());
198 | showSuccess('Password reset');
199 | } catch (e) {
200 | if (e is NoInternetException) {
201 | emit(const AuthError('No internet connection'));
202 | } else {
203 | emit(AuthError(e.toString()));
204 | }
205 | }
206 | }
207 |
208 | Future logout() async {
209 | try {
210 | await _authRepository.logout();
211 | emit(AuthUnauthenticated());
212 | } catch (e) {
213 | emit(AuthError(e.toString()));
214 | }
215 | }
216 |
217 | }
218 |
--------------------------------------------------------------------------------
/lib/state/cubits/auth_cubit/auth_state.dart:
--------------------------------------------------------------------------------
1 | part of 'auth_cubit.dart';
2 |
3 | abstract class AuthState extends Equatable{
4 | const AuthState();
5 |
6 | @override
7 | List get props => [];
8 | }
9 |
10 | class AuthInitial extends AuthState {}
11 |
12 | class AuthLoading extends AuthState {}
13 |
14 | class AuthAuthenticated extends AuthState {
15 | final UserModel user;
16 | const AuthAuthenticated(this.user);
17 |
18 | @override
19 | List get props => [user];
20 | }
21 |
22 | class AuthUnauthenticated extends AuthState {}
23 |
24 | class AuthSuccess extends AuthState {}
25 |
26 | class AuthError extends AuthState {
27 | final String message;
28 | const AuthError(this.message);
29 |
30 | @override
31 | List get props => [message];
32 | }
33 |
34 | class AuthEmailUnverified extends AuthState {
35 | final UserModel user;
36 | const AuthEmailUnverified(this.user);
37 |
38 | @override
39 | List get props => [user];
40 | }
41 |
--------------------------------------------------------------------------------
/lib/state/cubits/cloud_note_cubit/cloud_note_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:bloc/bloc.dart';
2 | import 'package:equatable/equatable.dart';
3 | import 'package:meta/meta.dart';
4 | import 'package:note_app/data/models/cloud_note_models/cloud_note_model.dart';
5 | import 'package:note_app/data/repositories/cloud_note_repository.dart';
6 | import 'package:note_app/utils/const_values.dart';
7 |
8 | part 'cloud_note_state.dart';
9 |
10 | class CloudNoteCubit extends Cubit {
11 | final CloudNoteRepository _repository;
12 |
13 | CloudNoteCubit({
14 | required CloudNoteRepository repository,
15 | }) : _repository = repository,
16 | super(CloudNoteInitial());
17 |
18 | Future fetchNotes() async {
19 | try {
20 | emit(CloudNoteLoading());
21 |
22 | final notes = await _repository.fetchNotes();
23 | final hiveKeys = _repository.getHiveKeys();
24 | emit(CloudNoteLoaded(notes, hiveKeys),);
25 | } catch (e) {
26 | logger.e('Error in fetchNotes: $e');
27 | emit(CloudError(e.toString()));
28 | }
29 | }
30 |
31 | Future moveToCloud(String? title, String? content, int noteKey) async {
32 | try {
33 | emit(CloudNoteLoading());
34 |
35 | await _repository.moveToCloud(title!, content!, noteKey);
36 | emit(CloudNoteSuccess());
37 | } catch (e) {
38 | logger.e('Error in fetchNotes: $e');
39 | emit(CloudError(e.toString()));
40 | }
41 | }
42 |
43 | Future createNote(String? title, String? content) async {
44 | try {
45 | emit(CloudNoteLoading());
46 |
47 | final note = await _repository.createNote(title!, content!);
48 | emit(CloudNoteCreated(note));
49 |
50 | } catch (e) {
51 | logger.e('Error in fetchNotes: $e');
52 | emit(CloudError(e.toString()));
53 | }
54 | }
55 |
56 | Future editNote(CloudNoteModel cloudNote, int noteKey) async {
57 | try {
58 | emit(CloudNoteLoading());
59 |
60 | final updatedNote = await _repository.updateNote(cloudNote, noteKey);
61 |
62 | emit(CloudNoteUpdated(updatedNote));
63 |
64 | } catch (e) {
65 | logger.e('Error in fetchNotes: $e');
66 | emit(CloudError(e.toString()));
67 | }
68 | }
69 |
70 | Future deleteNote(CloudNoteModel cloudNote) async {
71 | try {
72 | emit(CloudNoteLoading());
73 |
74 | await _repository.moveToTrash(cloudNote);
75 |
76 | emit(CloudNoteDeleted());
77 |
78 | } catch (e) {
79 | logger.e('Error in fetchNotes: $e');
80 | emit(CloudError(e.toString()));
81 | }
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/lib/state/cubits/cloud_note_cubit/cloud_note_state.dart:
--------------------------------------------------------------------------------
1 | part of 'cloud_note_cubit.dart';
2 |
3 | abstract class CloudNoteState extends Equatable {
4 | const CloudNoteState();
5 |
6 | @override
7 | List get props => [];
8 | }
9 |
10 | class CloudNoteInitial extends CloudNoteState {}
11 |
12 | class CloudNoteLoading extends CloudNoteState {}
13 |
14 | class CloudNoteLoaded extends CloudNoteState {
15 | final List notes;
16 | final List hiveKeys;
17 |
18 | const CloudNoteLoaded(this.notes, this.hiveKeys);
19 |
20 | @override
21 | List get props => [notes, hiveKeys];
22 | }
23 |
24 | class CloudNoteSingleLoaded extends CloudNoteState {
25 | final CloudNoteModel note;
26 |
27 | const CloudNoteSingleLoaded(this.note);
28 |
29 | @override
30 | List get props => [note];
31 | }
32 |
33 | class CloudNoteCreated extends CloudNoteState {
34 | final CloudNoteModel note;
35 |
36 | const CloudNoteCreated(this.note);
37 |
38 | @override
39 | List get props => [note];
40 | }
41 |
42 | class CloudNoteUpdated extends CloudNoteState {
43 | final CloudNoteModel note;
44 |
45 | const CloudNoteUpdated(this.note);
46 |
47 | @override
48 | List get props => [note];
49 | }
50 |
51 | class CloudNoteDeleted extends CloudNoteState {}
52 |
53 | class CloudError extends CloudNoteState {
54 | final String message;
55 |
56 | const CloudError(this.message);
57 |
58 | @override
59 | List get props => [message];
60 | }
61 |
62 | class CloudNoteMoved extends CloudNoteState {
63 | final CloudNoteModel note;
64 |
65 | const CloudNoteMoved(this.note);
66 |
67 | @override
68 | List get props => [note];
69 | }
70 |
71 | class CloudNoteSuccess extends CloudNoteState {}
72 |
--------------------------------------------------------------------------------
/lib/state/cubits/note_style_cubit/note_style_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:bloc/bloc.dart';
2 | import 'package:equatable/equatable.dart';
3 | import 'package:meta/meta.dart';
4 | import 'package:shared_preferences/shared_preferences.dart';
5 |
6 | part 'note_style_state.dart';
7 |
8 | class NoteStyleCubit extends Cubit {
9 | String key = 'changeViewStyle';
10 | SharedPreferences? _pref;
11 |
12 | NoteStyleCubit() : super(NoteStyleState.initial()) {
13 | _loadFromPref();
14 | }
15 |
16 | // initialize the shared Preference Instance
17 | Future _initPrefs() async {
18 | _pref ??= await SharedPreferences.getInstance();
19 | }
20 |
21 | // Load the data from it and check the current value
22 | Future _loadFromPref() async {
23 | await _initPrefs();
24 | final bool viewStyle = _pref!.getBool(key) ?? false;
25 | emit(state.copyWith(viewStyle: viewStyle));
26 | }
27 |
28 | // save the new value to the key
29 | Future _saveToPref(bool value) async {
30 | await _initPrefs();
31 | await _pref!.setBool(key, value);
32 | }
33 |
34 | // toggle between light or dark mode
35 | void toggleNoteStyle() {
36 | final newViewStyle = !state.viewStyle;
37 | _saveToPref(newViewStyle);
38 | emit(state.copyWith(viewStyle: newViewStyle));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/state/cubits/note_style_cubit/note_style_state.dart:
--------------------------------------------------------------------------------
1 | part of 'note_style_cubit.dart';
2 |
3 | class NoteStyleState extends Equatable {
4 | final bool viewStyle;
5 |
6 | const NoteStyleState({
7 | this.viewStyle = false,
8 | });
9 |
10 | factory NoteStyleState.initial() {
11 | return const NoteStyleState();
12 | }
13 |
14 | @override
15 | List get props => [viewStyle];
16 |
17 | NoteStyleState copyWith({
18 | bool? viewStyle,
19 | }) {
20 | return NoteStyleState(
21 | viewStyle: viewStyle ?? this.viewStyle,
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/state/cubits/play_button_cubit/play_button_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:bloc/bloc.dart';
2 | import 'package:equatable/equatable.dart';
3 | import 'package:meta/meta.dart';
4 | import 'package:shared_preferences/shared_preferences.dart';
5 |
6 | part 'play_button_state.dart';
7 |
8 | class PlayButtonCubit extends Cubit {
9 | String key = 'playButton';
10 | SharedPreferences? _pref;
11 |
12 |
13 | PlayButtonCubit() : super(PlayButtonState.initial()) {
14 | _loadFromPref();
15 | }
16 |
17 | // initialize the shared Preference Instance
18 | Future _initPrefs() async {
19 | _pref ??= await SharedPreferences.getInstance();
20 | }
21 |
22 | // Load the data from it and check the current value
23 | Future _loadFromPref() async {
24 | await _initPrefs();
25 | final bool canPlay = _pref!.getBool(key) ?? false;
26 | emit(state.copyWith(canPlay: canPlay));
27 | }
28 |
29 | // save the new value to the key
30 | Future _saveToPref(bool value) async {
31 | await _initPrefs();
32 | await _pref!.setBool(key, value);
33 | }
34 |
35 | // toggle between light or dark mode
36 | void togglePlayButton() {
37 | final newCanPlay = !state.canPlay;
38 | _saveToPref(newCanPlay);
39 | emit(state.copyWith(canPlay: newCanPlay));
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/lib/state/cubits/play_button_cubit/play_button_state.dart:
--------------------------------------------------------------------------------
1 | part of 'play_button_cubit.dart';
2 |
3 | @immutable
4 | class PlayButtonState extends Equatable {
5 | final bool canPlay;
6 |
7 | const PlayButtonState({
8 | this.canPlay = false,
9 | });
10 |
11 | factory PlayButtonState.initial() {
12 | return const PlayButtonState();
13 | }
14 |
15 | @override
16 | List get props => [canPlay];
17 |
18 | PlayButtonState copyWith({
19 | bool? canPlay,
20 | }) {
21 | return PlayButtonState(
22 | canPlay: canPlay ?? this.canPlay,
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/state/cubits/theme_cubit/theme_cubit.dart:
--------------------------------------------------------------------------------
1 | import 'package:bloc/bloc.dart';
2 | import 'package:equatable/equatable.dart';
3 | import 'package:shared_preferences/shared_preferences.dart';
4 |
5 | part 'theme_state.dart';
6 |
7 | class ThemeCubit extends Cubit {
8 | String key = 'appTheme';
9 | SharedPreferences? _pref;
10 |
11 | ThemeCubit(this._pref)
12 | : super(ThemeState(isDarkTheme: _pref!.getBool('appTheme') ?? false));
13 |
14 | // initialize the shared Preference Instance
15 | Future _initPrefs() async {
16 | _pref ??= await SharedPreferences.getInstance();
17 | }
18 |
19 | // Load the data from it and check the current value
20 | /*
21 | This function is no longer needed but is kept for future reference
22 | */
23 | Future _loadFromPref() async {
24 | await _initPrefs();
25 | final bool isDarkTheme = _pref?.getBool(key) ?? false;
26 | emit(state.copyWith(isDarkTheme: isDarkTheme));
27 | }
28 |
29 | // save the new value to the key
30 | /*
31 | This function is no longer needed but is kept for future reference
32 | */
33 | Future _saveToPref(bool value) async {
34 | await _initPrefs();
35 | await _pref!.setBool(key, value);
36 | }
37 |
38 | // toggle between light or dark mode
39 | Future toggleTheme() async {
40 | final newThemeValue = !state.isDarkTheme;
41 | _pref!.setBool(key, newThemeValue);
42 | emit(state.copyWith(isDarkTheme: newThemeValue));
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/state/cubits/theme_cubit/theme_state.dart:
--------------------------------------------------------------------------------
1 | part of 'theme_cubit.dart';
2 |
3 | class ThemeState extends Equatable {
4 | final bool isDarkTheme;
5 |
6 | const ThemeState({
7 | this.isDarkTheme = false,
8 | });
9 |
10 | factory ThemeState.initial() {
11 | return const ThemeState();
12 | }
13 |
14 | @override
15 | List get props => [isDarkTheme];
16 |
17 | ThemeState copyWith({
18 | bool? isDarkTheme,
19 | }) {
20 | return ThemeState(
21 | isDarkTheme: isDarkTheme ?? this.isDarkTheme,
22 | );
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/lib/utils/colors/m_colors.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:note_app/utils/tools/hex_to_color.dart';
3 |
4 | class AppColors {
5 | static Color darkColor = hexToColor('#333333');
6 | static Color? cardColor = Colors.grey[850];
7 | static Color? cardGray = Colors.grey[900];
8 |
9 | //colors for the app
10 | static Color primaryColor = hexToColor('#E0D3AFFF');
11 | static Color secondaryColor = hexToColor('#171B1E');
12 | static Color subTextColor = hexToColor('#B5A006');
13 | static Color greyTextColor = hexToColor('#798084');
14 | static Color scaffoldLightColor = hexToColor('#FAFAFA');
15 |
16 | static Color primaryGrey = hexToColor('#F9F9F9');
17 | static Color secondaryGrey = hexToColor('#F5F5F5');
18 | static Color lightGrey = hexToColor('#F1F1F1');
19 | static Color primaryGold = hexToColor('#C9B902');
20 | static Color transparent = Colors.transparent;
21 | static Color btnBorderColor = hexToColor('#CCE8D4');
22 | static Color textFieldBorderColor = hexToColor('#938989');
23 |
24 | static Color borderColor = hexToColor('#EBECED');
25 | static Color green = hexToColor('#009A51');
26 | static Color darkGreen = hexToColor('#006D3A');
27 | static Color red = hexToColor('#F83446');
28 | static Color darkRed = hexToColor('#B02532');
29 | static Color redWarning = hexToColor('#FEEBED');
30 |
31 | static Color dialogColor = Colors.white;
32 | static Color backColorOne = Colors.white;
33 | static Color defaultTextColor = Colors.black;
34 |
35 |
36 | static Color boxColor = const Color.fromRGBO(235, 237, 242, 1);
37 |
38 | static Color grey = hexToColor('#AFACAB');
39 |
40 | //other colors
41 | static Color defaultWhite = const Color.fromRGBO(255, 255, 255, 1);
42 | static Color defaultBlack = const Color.fromRGBO(0, 0, 0, 1);
43 | //
44 |
45 | static Color btnSecondaryTextCode = const Color.fromRGBO(255, 255, 255, 1);
46 | static Color btnPrimaryTextCode = const Color.fromRGBO(0, 0, 0, 1);
47 |
48 | }
--------------------------------------------------------------------------------
/lib/utils/const_values.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:logger/logger.dart';
3 | import 'package:note_app/utils/constant/api_constant.dart';
4 | import 'package:note_app/utils/tools/hex_to_color.dart';
5 |
6 | var logger = Logger();
7 |
8 | // String apiUrl = dotenv.env['API_URL'].toString();
9 | // String payStackPubKey = dotenv.env['PAYSTACK_PUB_KEY'].toString();
10 | // String payStackSecKey = dotenv.env['PAYSTACK_SEC_KEY'].toString();
11 |
12 | String apiUrl = ApiConstants.apiUrl;
13 |
14 | const String androidID = 'com.viewus.v_notes';
15 | const String dialogTitle = 'Update V Notes';
16 | const String dialogText = 'There is a new update for V Notes, '
17 | 'would you like to update to check up '
18 | 'what we have improved about the app';
19 |
20 | // Hive details
21 | const String noteBox = 'notebox';
22 | const String cloudNoteBox = 'cloudNoteBox';
23 | const String userBox = 'userBox';
24 | const String deletedNotes = 'deletedNotes';
25 | const String appHiveKey = 'state';
26 | const String deleteNote = 'deleteNote';
27 |
28 |
29 | const String userKey = 'userKey';
30 | const String tokenKey = 'tokenKey';
31 |
32 | Color transparent = Colors.transparent;
33 | Color grey = hexToColor('#AFACAB');
34 | Color mGrey = grey.withOpacity(0.4);
35 |
--------------------------------------------------------------------------------
/lib/utils/constant/api_constant.dart:
--------------------------------------------------------------------------------
1 | class ApiConstants {
2 | static String apiUrl = '';
3 | }
--------------------------------------------------------------------------------
/lib/utils/constant/asset_dir.dart:
--------------------------------------------------------------------------------
1 |
2 | class AssetDir {
3 |
4 |
5 | // Lufga Font Family
6 | static String PublicSansBlack = 'PublicSansBlack';
7 | static String PublicSansBold = 'PublicSansBold';
8 | static String PublicSansExtraBold = 'PublicSansExtraBold';
9 | static String PublicSansExtraLight = 'PublicSansExtraLight';
10 | static String PublicSansLight = 'PublicSansLight';
11 | static String PublicSansMedium = 'PublicSansMedium';
12 | static String PublicSansRegular = 'PublicSansRegular';
13 | static String PublicSansSemiBold = 'PublicSansSemiBold';
14 | static String PublicSansThin = 'PublicSansThin';
15 |
16 | // SVGs
17 | // static String happyBird = '$svgAssets/undraw_happy_music_g6wc.svg';
18 | }
--------------------------------------------------------------------------------
/lib/utils/greetings.dart:
--------------------------------------------------------------------------------
1 | String greetingMessage() {
2 | var timeNow = DateTime.now().hour;
3 |
4 | if (timeNow < 12) {
5 | return 'Good Morning';
6 | } else if ((timeNow >= 12) && (timeNow <= 16)) {
7 | return 'Good Afternoon';
8 | } else if ((timeNow > 16) && (timeNow < 20)) {
9 | return 'Good Evening';
10 | } else {
11 | return 'It\'s Night';
12 | }
13 | }
--------------------------------------------------------------------------------
/lib/utils/text_style/m_text_style.dart:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/lib/utils/text_style/m_text_style.dart
--------------------------------------------------------------------------------
/lib/utils/themes/custom_theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:note_app/utils/colors/m_colors.dart';
3 | import 'package:note_app/utils/const_values.dart';
4 |
5 | ThemeData buildLightTheme() => ThemeData.light().copyWith(
6 | cardColor: Colors.white,
7 | scaffoldBackgroundColor: AppColors.primaryColor,
8 | iconTheme: IconThemeData(
9 | color: AppColors.defaultBlack,
10 | ),
11 | cardTheme: const CardTheme(
12 | color: Colors.white,
13 | ),
14 | dialogTheme: DialogTheme(
15 | backgroundColor: AppColors.primaryColor,
16 | titleTextStyle: const TextStyle(
17 | color: Colors.black,
18 | fontWeight: FontWeight.bold,
19 | ),
20 | contentTextStyle: const TextStyle(
21 | color: Colors.black,
22 | ),
23 | ),
24 | dividerColor: AppColors.defaultBlack,
25 | appBarTheme: AppBarTheme(
26 | centerTitle: true,
27 | elevation: 0.0,
28 | shadowColor: Colors.transparent,
29 | color: Colors.transparent,
30 | titleTextStyle: const TextStyle(
31 | fontSize: 20,
32 | fontWeight: FontWeight.bold,
33 | color: Colors.black,
34 | ),
35 | iconTheme: IconThemeData(
36 | color: Colors.grey[900],
37 | ),
38 | ),
39 | textButtonTheme: TextButtonThemeData(
40 | style: ButtonStyle(
41 | textStyle: MaterialStateProperty.resolveWith(
42 | (states) => TextStyle(
43 | color: AppColors.defaultBlack,
44 | ),
45 | ),
46 | ),
47 | ),
48 | dialogBackgroundColor: AppColors.primaryColor,
49 | textTheme: Typography.blackCupertino,
50 | );
51 |
52 | ThemeData buildDarkTheme() => ThemeData.dark().copyWith(
53 | cardColor: Colors.grey[850],
54 | scaffoldBackgroundColor: AppColors.darkColor,
55 | dividerColor: AppColors.defaultWhite,
56 | iconTheme: IconThemeData(
57 | color: AppColors.defaultWhite,
58 | ),
59 | cardTheme: CardTheme(
60 | color: AppColors.cardColor,
61 | ),
62 | dialogTheme: DialogTheme(
63 | backgroundColor: Colors.grey[900],
64 | titleTextStyle: const TextStyle(
65 | color: Colors.white,
66 | fontWeight: FontWeight.bold,
67 | ),
68 | contentTextStyle: const TextStyle(
69 | color: Colors.white,
70 | ),
71 | ),
72 | switchTheme: SwitchThemeData(
73 | thumbColor: MaterialStateProperty.resolveWith(
74 | (states) => Colors.grey[400],
75 | ),
76 | trackColor: MaterialStateProperty.resolveWith(
77 | (states) => Colors.white,
78 | ),
79 | ),
80 | appBarTheme: AppBarTheme(
81 | centerTitle: true,
82 | elevation: 0.0,
83 | titleTextStyle: const TextStyle(
84 | fontSize: 20,
85 | fontWeight: FontWeight.bold,
86 | color: Colors.white,
87 | ),
88 | color: Colors.grey[900],
89 | iconTheme: IconThemeData(
90 | color: Colors.grey[400],
91 | ),
92 | ),
93 | textButtonTheme: TextButtonThemeData(
94 | style: ButtonStyle(
95 | textStyle: MaterialStateProperty.resolveWith(
96 | (states) => TextStyle(
97 | color: AppColors.defaultBlack,
98 | ),
99 | ),
100 | ),
101 | ),
102 | textTheme: Typography.whiteCupertino,
103 | );
104 |
--------------------------------------------------------------------------------
/lib/utils/tools/hex_to_color.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | Color hexToColor(String code) {
4 | return Color(int.parse(code.substring(1, 7), radix: 16) + 0xFF000000);
5 | }
--------------------------------------------------------------------------------
/lib/utils/tools/message_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:flashy_flushbar/flashy_flushbar.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | void showError(String errorMsg) {
5 | FlashyFlushbar(
6 | leadingWidget: const Icon(
7 | Icons.error_outline,
8 | color: Colors.orange,
9 | size: 24,
10 | ),
11 | message: errorMsg,
12 | duration: const Duration(seconds: 5),
13 | trailingWidget: IconButton(
14 | icon: const Icon(
15 | Icons.close,
16 | color: Colors.black,
17 | size: 24,
18 | ),
19 | onPressed: () {
20 | FlashyFlushbar.cancel();
21 | },
22 | ),
23 | isDismissible: true,
24 | ).show();
25 | }
26 |
27 | void showSuccess(String msg) {
28 | FlashyFlushbar(
29 | leadingWidget: const Icon(
30 | Icons.check_circle_outline_outlined,
31 | color: Colors.green,
32 | size: 24,
33 | ),
34 | message: msg,
35 | duration: const Duration(seconds: 5),
36 | trailingWidget: IconButton(
37 | icon: const Icon(
38 | Icons.close,
39 | color: Colors.black,
40 | size: 24,
41 | ),
42 | onPressed: () {
43 | FlashyFlushbar.cancel();
44 | },
45 | ),
46 | isDismissible: true,
47 | ).show();
48 | }
--------------------------------------------------------------------------------
/lib/utils/tools/money_formatter.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/services.dart';
2 |
3 | class ThousandsSeparatorInputFormatter extends TextInputFormatter {
4 | static const separator = ','; // Change this to '.' for other locales
5 |
6 | @override
7 | TextEditingValue formatEditUpdate(
8 | TextEditingValue oldValue, TextEditingValue newValue) {
9 | // Short-circuit if the new value is empty
10 | if (newValue.text.isEmpty) {
11 | return newValue.copyWith(text: '');
12 | }
13 |
14 | // Handle "deletion" of separator character
15 | String oldValueText = oldValue.text.replaceAll(separator, '');
16 | String newValueText = newValue.text.replaceAll(separator, '');
17 |
18 | if (oldValue.text.endsWith(separator) &&
19 | oldValue.text.length == newValue.text.length + 1) {
20 | newValueText = newValueText.substring(0, newValueText.length - 1);
21 | }
22 |
23 | // Only process if the old value and new value are different
24 | if (oldValueText != newValueText) {
25 | int selectionIndex =
26 | newValue.text.length - newValue.selection.extentOffset;
27 | final chars = newValueText.split('');
28 |
29 | String newString = '';
30 | for (int i = chars.length - 1; i >= 0; i--) {
31 | if ((chars.length - 1 - i) % 3 == 0 && i != chars.length - 1) {
32 | newString = separator + newString;
33 | }
34 | newString = chars[i] + newString;
35 | }
36 |
37 | return TextEditingValue(
38 | text: newString.toString(),
39 | selection: TextSelection.collapsed(
40 | offset: newString.length - selectionIndex,
41 | ),
42 | );
43 | }
44 |
45 | // If the new value and old value are the same, just return as-is
46 | return newValue;
47 | }
48 | }
--------------------------------------------------------------------------------
/lib/utils/tools/sized_box_ex.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_screenutil/flutter_screenutil.dart';
3 |
4 | extension SizedBoxExtension on int {
5 | Widget get toHeight {
6 | return SizedBox(
7 | height: toDouble().h,
8 | );
9 | }
10 |
11 | Widget get toWidth {
12 | return SizedBox(
13 | width: toDouble().w,
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/lib/utils/tools/slide_transition.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class MySlide extends MaterialPageRoute {
4 | MySlide({
5 | WidgetBuilder? builder,
6 | RouteSettings? settings,
7 | }) : super(builder: builder!, settings: settings);
8 |
9 | @override
10 | Widget buildTransitions(BuildContext context, Animation animation,
11 | Animation secondaryAnimation, Widget child) {
12 | Animation custom =
13 | Tween(begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0))
14 | .animate(animation);
15 | return SlideTransition(
16 | position: custom,
17 | child: child,
18 | );
19 | // return super.buildTransitions(context, animation, secondaryAnimation, child);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: note_app
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 2.0.17+24
19 |
20 | environment:
21 | sdk: ^3.5.4
22 |
23 | dependencies:
24 | flutter:
25 | sdk: flutter
26 | cupertino_icons: ^1.0.3
27 | flutter_staggered_grid_view: ^0.7.0
28 | flutter_tts: ^4.0.2
29 | hive: ^2.0.4
30 | hive_flutter: ^1.1.0
31 | path_provider: ^2.0.2
32 | provider: ^6.0.2
33 | shared_preferences: ^2.2.3
34 | fluttertoast: ^8.0.7
35 | logger: ^2.0.2
36 | intl: ^0.19.0
37 | flutter_screenutil: ^5.5.3+2
38 | package_info_plus: ^8.0.0
39 | flashy_flushbar:
40 | git:
41 | url: https://github.com/quiet-programmer/flashy_flushbar.git
42 | ref: 0cda07e
43 | font_awesome_flutter: ^10.7.0
44 | otp_text_field_v2: ^1.0.3
45 | yaml: ^3.1.2
46 | flutter_bloc: ^9.1.0
47 | equatable: ^2.0.5
48 | get_it: ^8.0.3
49 | flutter_quill: ^11.2.0
50 |
51 | dependency_overrides:
52 | flutter_inappwebview: ^6.1.3
53 | flutter_keyboard_visibility: ^6.0.0
54 |
55 | dev_dependencies:
56 | flutter_test:
57 | sdk: flutter
58 | build_runner:
59 | change_app_package_name: ^1.1.0
60 | hive_generator: ^2.0.0
61 | flutter_lints: ^5.0.0
62 |
63 | # For information on the generic Dart part of this file, see the
64 | # following page: https://dart.dev/tools/pub/pubspec
65 | # The following section is specific to Flutter.
66 | flutter:
67 | # The following line ensures that the Material Icons font is
68 | # included with your application, so that you can use the icons in
69 | # the material Icons class.
70 | assets:
71 | - shorebird.yaml
72 | - api_credentials.yaml
73 | uses-material-design: true
74 | # To add assets to your application, add an assets section, like this:
75 | # assets:
76 | # - .env
77 | # - images/a_dot_ham.jpeg
78 | # An image asset can refer to one or more resolution-specific "variants", see
79 | # https://flutter.dev/assets-and-images/#resolution-aware.
80 | # For details regarding adding assets from package dependencies, see
81 | # https://flutter.dev/assets-and-images/#from-packages
82 | # To add custom fonts to your application, add a fonts section here,
83 | # in this "flutter" section. Each entry in this list should have a
84 | # "family" key with the font family name, and a "fonts" key with a
85 | # list giving the asset and other descriptors for the font. For
86 | # example:
87 | # fonts:
88 | # - family: Schyler
89 | # fonts:
90 | # - asset: fonts/Schyler-Regular.ttf
91 | # - asset: fonts/Schyler-Italic.ttf
92 | # style: italic
93 | # - family: Trajan Pro
94 | # fonts:
95 | # - asset: fonts/TrajanPro.ttf
96 | # - asset: fonts/TrajanPro_Bold.ttf
97 | # weight: 700
98 | #
99 | # For details regarding fonts from package dependencies,
100 | # see https://flutter.dev/custom-fonts/#from-packages
101 |
--------------------------------------------------------------------------------
/shorebird.yaml:
--------------------------------------------------------------------------------
1 | # This file is used to configure the Shorebird updater used by your app.
2 | # Learn more at https://docs.shorebird.dev
3 | # This file should be checked into version control.
4 |
5 | # This is the unique identifier assigned to your app.
6 | # Your app_id is not a secret and is just used to identify your app
7 | # when requesting patches from Shorebird's servers.
8 | app_id: 02f3cd98-f488-4ec4-bbe3-f722d9131537
9 |
10 | # auto_update controls if Shorebird should automatically update in the background on launch.
11 | # If auto_update: false, you will need to use package:shorebird_code_push to trigger updates.
12 | # https://pub.dev/packages/shorebird_code_push
13 | # Uncomment the following line to disable automatic updates.
14 | # auto_update: false
15 |
--------------------------------------------------------------------------------
/ss/145535058_221013679682765_5476232847126577393_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/ss/145535058_221013679682765_5476232847126577393_n.png
--------------------------------------------------------------------------------
/ss/145944556_3545455032189582_7968282205009447548_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sudo-which-qp/hive_note_app/7cdad3dbd23c38ac8532109b6014a5e4e9d7fd55/ss/145944556_3545455032189582_7968282205009447548_n.png
--------------------------------------------------------------------------------
/test/unit_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_test/flutter_test.dart';
2 |
3 | void main() {
4 | test('String should not be empty', () async {});
5 | }
6 |
--------------------------------------------------------------------------------
/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 | import 'package:note_app/app/src/app.dart';
11 |
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(App());
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 |
--------------------------------------------------------------------------------