├── .gitignore
├── .metadata
├── LICENSE
├── README.md
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── history_of_me
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-hdpi
│ │ │ └── ic_launcher_foreground.png
│ │ │ ├── drawable-mdpi
│ │ │ └── ic_launcher_foreground.png
│ │ │ ├── drawable-xhdpi
│ │ │ └── ic_launcher_foreground.png
│ │ │ ├── drawable-xxhdpi
│ │ │ └── ic_launcher_foreground.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ └── ic_launcher_foreground.png
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ └── ic_launcher.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
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── assets
├── fonts
│ └── PlayfairDisplay
│ │ ├── PlayfairDisplay-Black.ttf
│ │ ├── PlayfairDisplay-BlackItalic.ttf
│ │ ├── PlayfairDisplay-Bold.ttf
│ │ ├── PlayfairDisplay-BoldItalic.ttf
│ │ ├── PlayfairDisplay-Italic.ttf
│ │ └── PlayfairDisplay-Regular.ttf
├── icon
│ ├── Launcher_Icon_Adaptive.png
│ ├── Launcher_Icon_Static.png
│ └── Launcher_Icon_Static_Dev.png
├── images
│ ├── Cloud.png
│ ├── Curtain_Left.png
│ ├── Curtain_Right.png
│ ├── History_Of_Me_Key_64px-01.png
│ ├── History_Of_Me_Key_Icon_256px-01.png
│ ├── History_Of_Me_Logo_Final-01.png
│ ├── History_Of_Me_Window_Artwork_Small.png
│ ├── Key.png
│ ├── Window.png
│ └── lit_Life_Software_Dark_Mode-01.png
└── misc
│ ├── Google_Playstore_Promo_Image_2.png
│ ├── History_Of_Me_Screenshot_1.png
│ ├── History_Of_Me_Screenshot_2.png
│ ├── History_Of_Me_Screenshot_3.png
│ ├── History_Of_Me_Screenshot_4.png
│ └── History_Of_Me_Screenshot_5.png
├── build_production.sh
├── flutter_launcher_icons.yaml
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-App-1024x1024@1x.png
│ │ ├── Icon-App-20x20@1x.png
│ │ ├── Icon-App-20x20@2x.png
│ │ ├── Icon-App-20x20@3x.png
│ │ ├── Icon-App-29x29@1x.png
│ │ ├── Icon-App-29x29@2x.png
│ │ ├── Icon-App-29x29@3x.png
│ │ ├── Icon-App-40x40@1x.png
│ │ ├── Icon-App-40x40@2x.png
│ │ ├── Icon-App-40x40@3x.png
│ │ ├── Icon-App-50x50@1x.png
│ │ ├── Icon-App-50x50@2x.png
│ │ ├── Icon-App-57x57@1x.png
│ │ ├── Icon-App-57x57@2x.png
│ │ ├── Icon-App-60x60@2x.png
│ │ ├── Icon-App-60x60@3x.png
│ │ ├── Icon-App-72x72@1x.png
│ │ ├── Icon-App-72x72@2x.png
│ │ ├── Icon-App-76x76@1x.png
│ │ ├── Icon-App-76x76@2x.png
│ │ └── Icon-App-83.5x83.5@2x.png
│ └── LaunchImage.imageset
│ │ ├── Contents.json
│ │ ├── LaunchImage.png
│ │ ├── LaunchImage@2x.png
│ │ ├── LaunchImage@3x.png
│ │ └── README.md
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
├── lib
├── api.dart
├── api
│ ├── all_data_provider.dart
│ ├── app_api.dart
│ ├── app_settings_provider.dart
│ ├── database_state_validator.dart
│ ├── debug_output_service.dart
│ ├── default_data.dart
│ ├── diary_entry_provider.dart
│ ├── query_controller.dart
│ ├── query_diary_entry_provider.dart
│ ├── user_created_color_provider.dart
│ └── user_data_provider.dart
├── app.dart
├── controller
│ ├── autosave_controller.dart
│ ├── diary_photo_picker.dart
│ ├── hom_navigator.dart
│ └── mood_translator.dart
├── controllers.dart
├── extensions.dart
├── extensions
│ ├── file_system_entity_extension.dart
│ └── string_extension.dart
├── localization.dart
├── localization
│ ├── app_localizations.dart
│ ├── app_localizations_delegate.dart
│ ├── app_localizations_keys.dart
│ └── languages
│ │ ├── de.dart
│ │ └── en.dart
├── main.dart
├── model
│ ├── app_settings.dart
│ ├── app_settings.g.dart
│ ├── diary_backup.dart
│ ├── diary_entry.dart
│ ├── diary_entry.g.dart
│ ├── diary_photo.dart
│ ├── diary_photo.g.dart
│ ├── models.dart
│ ├── user_created_color.dart
│ ├── user_created_color.g.dart
│ ├── user_data.dart
│ └── user_data.g.dart
├── models.dart
├── screens.dart
├── screens
│ ├── app_credits_screen.dart
│ ├── app_onboarding_screen.dart
│ ├── app_privacy_screen.dart
│ ├── backup_notice_screen.dart
│ ├── bookmark_editing_screen.dart
│ ├── diary_screen.dart
│ ├── entry_detail_screen.dart
│ ├── entry_editing_screen.dart
│ ├── home_screen.dart
│ ├── profile_screen.dart
│ ├── restore_diary_screen.dart
│ ├── select_backup_screen.dart
│ └── splash_screen.dart
├── static.dart
├── static
│ └── app_assets.dart
├── styles.dart
├── styles
│ └── app_colors.dart
├── widgets.dart
└── widgets
│ ├── animated_updated_label.dart
│ ├── app_about_dialog.dart
│ ├── app_artwork.dart
│ ├── bookmark_back.dart
│ ├── bookmark_back_preview.dart
│ ├── bookmark_container.dart
│ ├── bookmark_cover.dart
│ ├── bookmark_design.dart
│ ├── bookmark_front.dart
│ ├── bookmark_front_preview.dart
│ ├── bookmark_page_view.dart
│ ├── bookmark_preview_container.dart
│ ├── bookmark_title.dart
│ ├── cancel_restoring_dialog.dart
│ ├── change_name_dialog.dart
│ ├── clean_text_field.dart
│ ├── create_entry_dialog.dart
│ ├── create_new_diary_action_card.dart
│ ├── database_state_screen_builder.dart
│ ├── deletable_container.dart
│ ├── delete_all_photos_dialog.dart
│ ├── diary_backup_dialog.dart
│ ├── diary_bookmark_header.dart
│ ├── diary_entry_bottom_sheet.dart
│ ├── diary_filter_header.dart
│ ├── diary_filter_header_delegate.dart
│ ├── diary_list_tile.dart
│ ├── diary_list_view.dart
│ ├── diary_preview_card.dart
│ ├── dotted_design.dart
│ ├── editable_item_meta_info.dart
│ ├── ellipse_icon.dart
│ ├── entry_detail_backdrop.dart
│ ├── entry_detail_card.dart
│ ├── greetings_bar.dart
│ ├── history_of_me_app_logo.dart
│ ├── home_screen_drawer.dart
│ ├── image_preview_dialog.dart
│ ├── launcher_icon_art.dart
│ ├── lit_toggle_button_group.dart
│ ├── pattern_config_card.dart
│ ├── photos_missing_dialog.dart
│ ├── pick_photos_button.dart
│ ├── primary_color_selector_card.dart
│ ├── purple_pink_button.dart
│ ├── purple_pink_save_button.dart
│ ├── quote_card.dart
│ ├── secondary_color_selector_card.dart
│ ├── selectable_color_tile.dart
│ ├── selected_create_tile.dart
│ ├── statistics_card.dart
│ ├── striped_design.dart
│ ├── unselected_create_tile.dart
│ ├── unsupported_file_dialog.dart
│ ├── updated_label_text.dart
│ ├── user_icon.dart
│ ├── user_profile_card.dart
│ └── word_count_badge.dart
├── pubspec.lock
├── pubspec.yaml
└── web
├── favicon.png
├── icons
├── Icon-192.png
└── Icon-512.png
├── index.html
└── manifest.json
/.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 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 | # Production APK/bundle export directory
34 | /dist/
35 |
36 | # Web related
37 | lib/generated_plugin_registrant.dart
38 |
39 | # Symbolication related
40 | app.*.symbols
41 |
42 | # Obfuscation related
43 | app.*.map.json
44 |
45 | # Android Studio will place build artifacts here
46 | /android/app/debug
47 | /android/app/profile
48 | /android/app/release
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: 55289324c6d1db9071fb372f9f15f5ad5a064eeb
8 | channel: master
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2021 LitLifeSoftware. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without modification,
4 | are permitted provided that the following conditions are met:
5 |
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above
9 | copyright notice, this list of conditions and the following
10 | disclaimer in the documentation and/or other materials provided
11 | with the distribution.
12 | * Neither the name of LitLifeSoftware nor the names of its
13 | contributors may be used to endorse or promote products derived
14 | from this software without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | def keystoreProperties = new Properties()
29 | def keystorePropertiesFile = rootProject.file('key.properties')
30 | if (keystorePropertiesFile.exists()) {
31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
32 | }
33 |
34 | android {
35 | compileSdkVersion 33
36 |
37 | sourceSets {
38 | main.java.srcDirs += 'src/main/kotlin'
39 | }
40 |
41 | lintOptions {
42 | disable 'InvalidPackage'
43 | }
44 |
45 | defaultConfig {
46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
47 | applicationId "com.litlifesoftware.historyofme"
48 | minSdkVersion 23
49 | targetSdkVersion 33
50 | versionCode flutterVersionCode.toInteger()
51 | versionName flutterVersionName
52 | }
53 |
54 | signingConfigs {
55 | release {
56 | keyAlias keystoreProperties['keyAlias']
57 | keyPassword keystoreProperties['keyPassword']
58 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
59 | storePassword keystoreProperties['storePassword']
60 | }
61 | }
62 |
63 | buildTypes {
64 | release {
65 | signingConfig signingConfigs.release
66 | }
67 | }
68 |
69 | }
70 |
71 | flutter {
72 | source '../..'
73 | }
74 |
75 | dependencies {
76 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
77 | }
78 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
8 |
16 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/history_of_me/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.litlifesoftware.historyofme
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.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-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ffefe1
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | History of Me
4 |
--------------------------------------------------------------------------------
/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 | buildscript {
2 | ext.kotlin_version = '1.7.20'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.2.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/assets/fonts/PlayfairDisplay/PlayfairDisplay-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/fonts/PlayfairDisplay/PlayfairDisplay-Black.ttf
--------------------------------------------------------------------------------
/assets/fonts/PlayfairDisplay/PlayfairDisplay-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/fonts/PlayfairDisplay/PlayfairDisplay-BlackItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/PlayfairDisplay/PlayfairDisplay-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/fonts/PlayfairDisplay/PlayfairDisplay-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/PlayfairDisplay/PlayfairDisplay-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/fonts/PlayfairDisplay/PlayfairDisplay-BoldItalic.ttf
--------------------------------------------------------------------------------
/assets/fonts/PlayfairDisplay/PlayfairDisplay-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/fonts/PlayfairDisplay/PlayfairDisplay-Italic.ttf
--------------------------------------------------------------------------------
/assets/fonts/PlayfairDisplay/PlayfairDisplay-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/fonts/PlayfairDisplay/PlayfairDisplay-Regular.ttf
--------------------------------------------------------------------------------
/assets/icon/Launcher_Icon_Adaptive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/icon/Launcher_Icon_Adaptive.png
--------------------------------------------------------------------------------
/assets/icon/Launcher_Icon_Static.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/icon/Launcher_Icon_Static.png
--------------------------------------------------------------------------------
/assets/icon/Launcher_Icon_Static_Dev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/icon/Launcher_Icon_Static_Dev.png
--------------------------------------------------------------------------------
/assets/images/Cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/images/Cloud.png
--------------------------------------------------------------------------------
/assets/images/Curtain_Left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/images/Curtain_Left.png
--------------------------------------------------------------------------------
/assets/images/Curtain_Right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/images/Curtain_Right.png
--------------------------------------------------------------------------------
/assets/images/History_Of_Me_Key_64px-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/images/History_Of_Me_Key_64px-01.png
--------------------------------------------------------------------------------
/assets/images/History_Of_Me_Key_Icon_256px-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/images/History_Of_Me_Key_Icon_256px-01.png
--------------------------------------------------------------------------------
/assets/images/History_Of_Me_Logo_Final-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/images/History_Of_Me_Logo_Final-01.png
--------------------------------------------------------------------------------
/assets/images/History_Of_Me_Window_Artwork_Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/images/History_Of_Me_Window_Artwork_Small.png
--------------------------------------------------------------------------------
/assets/images/Key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/images/Key.png
--------------------------------------------------------------------------------
/assets/images/Window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/images/Window.png
--------------------------------------------------------------------------------
/assets/images/lit_Life_Software_Dark_Mode-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/images/lit_Life_Software_Dark_Mode-01.png
--------------------------------------------------------------------------------
/assets/misc/Google_Playstore_Promo_Image_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/misc/Google_Playstore_Promo_Image_2.png
--------------------------------------------------------------------------------
/assets/misc/History_Of_Me_Screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/misc/History_Of_Me_Screenshot_1.png
--------------------------------------------------------------------------------
/assets/misc/History_Of_Me_Screenshot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/misc/History_Of_Me_Screenshot_2.png
--------------------------------------------------------------------------------
/assets/misc/History_Of_Me_Screenshot_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/misc/History_Of_Me_Screenshot_3.png
--------------------------------------------------------------------------------
/assets/misc/History_Of_Me_Screenshot_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/misc/History_Of_Me_Screenshot_4.png
--------------------------------------------------------------------------------
/assets/misc/History_Of_Me_Screenshot_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/assets/misc/History_Of_Me_Screenshot_5.png
--------------------------------------------------------------------------------
/build_production.sh:
--------------------------------------------------------------------------------
1 | #/!/bin/bash
2 |
3 | now=`date +"%Y%m%d-%H%M"`
4 | appName="History of Me"
5 | appNameClean="HistoryOfMe"
6 | arm64="arm64-v8a"
7 | arm32="armeabi-v7a"
8 | x86="x86_64"
9 | platform="Android"
10 | green=`tput setaf 2`
11 | yellow=`tput setaf 3`
12 | blue=`tput setaf 4`
13 | reset=`tput sgr0`
14 |
15 | echo "${blue}Creating '${appName}' builds for ${now}.\n${reset}"
16 | echo "Building APK files ..."
17 | flutter build apk --split-per-abi
18 | echo "${green}Finished building APK files.${reset}"
19 | echo "Building AppBundle file ...\n"
20 | flutter build appbundle
21 | echo "${green}Finished building AppBundle file.\n"
22 | echo "${green}✓ Finished creating '${appName}' builds.\n${reset}"
23 | echo "Exporting files ...\n"
24 | mkdir -p -v ./dist/"${now}"
25 | mkdir -p -v ./dist/"${now}"/apk
26 | mkdir -p -v ./dist/"${now}"/appbundle
27 | cat <./dist/"${now}"/release_notes_play.txt
28 |
29 | - Bug fixes and stability improvements
30 |
31 |
32 | - Fehlerbehebungen und Stabilitätsverbesserungen
33 |
34 | EOF
35 | cat <./dist/"${now}"/release_notes_github.txt
36 | ## What's new?
37 |
38 | - Bug fixes and stability improvements
39 | EOF
40 | rsync -rvh --progress ./build/app/outputs/apk/ ./dist/"${now}"/apk
41 | rsync -rvh --progress ./build/app/outputs/bundle/ ./dist/"${now}"/appbundle
42 | echo "\n"
43 | echo "Renaming builds ...\n"
44 |
45 | # Rename and relocate arm64 build
46 | mv ./dist/"${now}"/apk/release/app-arm64-v8a-release.apk ./dist/"${now}"/apk/"${appNameClean}-${now}-${platform}-${arm64}.apk"
47 |
48 | # Rename and relocate arm32 build
49 | mv ./dist/"${now}"/apk/release/app-armeabi-v7a-release.apk ./dist/"${now}"/apk/"${appNameClean}-${now}-${platform}-${arm32}.apk"
50 |
51 | # Rename and relocate x86 build
52 | mv ./dist/"${now}"/apk/release/app-x86_64-release.apk ./dist/"${now}"/apk/"${appNameClean}-${now}-${platform}-${x86}.apk"
53 |
54 | # Rename and relocate app bundle
55 | mv ./dist/"${now}"/appbundle/release/app-release.aab ./dist/"${now}"/appbundle/"${appNameClean}-${now}-${platform}.aab"
56 |
57 | # Delete debug builds if existing
58 | rm -rf ./dist/"${now}"/apk/debug
59 | # Delete empty folders
60 | rm -rf ./dist/"${now}"/apk/release
61 | rm -rf ./dist/"${now}"/appbundle/release
62 |
63 | echo "${green}✓ Finished building and exporting '${appName}' releases.\n${reset}"
64 | echo "${yellow}Please edit the release notes before uploading your production builds.\n${reset}"
65 |
--------------------------------------------------------------------------------
/flutter_launcher_icons.yaml:
--------------------------------------------------------------------------------
1 | dev_dependencies:
2 | flutter_launcher_icons: "^0.10.0"
3 |
4 | flutter_icons:
5 | android: true
6 | ios: true
7 | image_path: "assets/icon/Launcher_Icon_Static.png"
8 | adaptive_icon_background: "#ffefe1"
9 | adaptive_icon_foreground: "assets/icon/Launcher_Icon_Adaptive.png"
10 | remove_alpha_ios: true
--------------------------------------------------------------------------------
/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 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
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 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | 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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/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/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | History of Me
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 |
45 |
46 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/lib/api.dart:
--------------------------------------------------------------------------------
1 | /// Classes implementing interfaces allowing History of Me to interact with
2 | /// a local `Hive` database instance.
3 | ///
4 | /// To use, import `package:history_of_me/api.dart`.
5 | library api;
6 |
7 | import 'dart:math';
8 |
9 | import 'package:flutter/foundation.dart';
10 | import 'package:flutter/widgets.dart';
11 | import 'package:history_of_me/extensions.dart';
12 | import 'package:history_of_me/models.dart';
13 | import 'package:hive_flutter/hive_flutter.dart';
14 | import 'package:leitmotif/leitmotif.dart';
15 |
16 | part 'api/all_data_provider.dart';
17 | part 'api/app_api.dart';
18 | part 'api/app_settings_provider.dart';
19 | part 'api/database_state_validator.dart';
20 | part 'api/debug_output_service.dart';
21 | part 'api/default_data.dart';
22 | part 'api/diary_entry_provider.dart';
23 | part 'api/query_controller.dart';
24 | part 'api/query_diary_entry_provider.dart';
25 | part 'api/user_created_color_provider.dart';
26 | part 'api/user_data_provider.dart';
27 |
--------------------------------------------------------------------------------
/lib/api/all_data_provider.dart:
--------------------------------------------------------------------------------
1 | part of api;
2 |
3 | /// An `api` widget allowing to pass all data stored on the `Hive` database
4 | /// into a child widget using the [builder] method.
5 | ///
6 | /// Accessing all data at once is rather expensive on memory but required to
7 | /// e.g. create backupable objects.
8 | class AllDataProvider extends StatefulWidget {
9 | /// `builder` method allowing to access all data available on its child.
10 | final Widget Function(
11 | BuildContext context,
12 | AppSettings appSettings,
13 | UserData? userData,
14 | List diaryEntries,
15 | List userCreatedColors,
16 | ) builder;
17 |
18 | /// Creates an [AllDataProvider] that provides all data stored on the `Hive`
19 | /// database.
20 | const AllDataProvider({
21 | Key? key,
22 | required this.builder,
23 | }) : super(key: key);
24 |
25 | @override
26 | _AllDataProviderState createState() => _AllDataProviderState();
27 | }
28 |
29 | class _AllDataProviderState extends State {
30 | /// The app api instance.
31 | final AppAPI _api = AppAPI();
32 |
33 | /// The database validator instance.
34 | late DatabaseStateValidator _validator;
35 |
36 | @override
37 | void initState() {
38 | _validator = DatabaseStateValidator(api: _api);
39 | super.initState();
40 | }
41 |
42 | @override
43 | Widget build(BuildContext context) {
44 | return AppSettingsProvider(
45 | validator: _validator,
46 | builder: (context, appSettings) {
47 | return UserDataProvider(
48 | builder: (context, userData) {
49 | return DiaryEntryProvider(
50 | builder: (context, diaryEntries) {
51 | return UserCreatedColorProvider(
52 | builder: (context, userCreatedColors) {
53 | return widget.builder(
54 | context,
55 | appSettings,
56 | userData,
57 | diaryEntries,
58 | userCreatedColors,
59 | );
60 | },
61 | );
62 | },
63 | );
64 | },
65 | );
66 | },
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/lib/api/app_settings_provider.dart:
--------------------------------------------------------------------------------
1 | part of api;
2 |
3 | /// A `api` widget providing [AppSettings] objects to the child widget accessed
4 | /// by on the builder method.
5 | class AppSettingsProvider extends StatelessWidget {
6 | /// The builder method providing access to [DiaryEntry] objects.
7 | final Widget Function(
8 | BuildContext context,
9 | AppSettings appSettings,
10 | ) builder;
11 |
12 | /// The [AppAPI] instance.
13 | final AppAPI api;
14 |
15 | /// The [DatabaseStateValidator] instance.
16 | final DatabaseStateValidator? validator;
17 |
18 | /// Creates a [AppSettingsProvider].
19 | const AppSettingsProvider({
20 | Key? key,
21 | required this.builder,
22 | this.api = const AppAPI(),
23 | this.validator,
24 | }) : super(key: key);
25 |
26 | /// Creates the initial [AppSettings] instance.
27 | void _createAppSettings() {
28 | api.createAppSettings();
29 | }
30 |
31 | /// Extracts the content stored inside the `Hive` box.
32 | AppSettings? extractContent(Box box) {
33 | AppSettings? appSettings;
34 | // Try to retrieve the `AppSettings` instance
35 | try {
36 | appSettings = box.getAt(AppAPI.defaultEntryIndex);
37 | // If none found, return null.
38 | if (appSettings == null) return null;
39 | // Validate the app settings object to enforce data integrity.
40 | if (validator != null) validator!.validateAppSettings(appSettings);
41 | } catch (e) {
42 | print(e);
43 | _createAppSettings();
44 | print('Error while accessing AppSettings object. '
45 | 'Creating backup AppSettings object ...');
46 | }
47 | return appSettings;
48 | }
49 |
50 | @override
51 | Widget build(BuildContext context) {
52 | return ValueListenableBuilder(
53 | valueListenable: api.getAppSettings(),
54 | builder: (context, Box appSettingsBox, _) {
55 | return builder(
56 | context,
57 | extractContent(appSettingsBox) ?? api.defaultAppSettings,
58 | );
59 | },
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/api/database_state_validator.dart:
--------------------------------------------------------------------------------
1 | part of api;
2 |
3 | /// A `api` class allowing to validate and correct the current database
4 | /// content to prevent inconsistency or missing values.
5 | ///
6 | /// Incorrect data could be created whenever the `model` classes are altered
7 | /// while the existing database still contains the deprecated state.
8 | ///
9 | /// Implement database-updating methods for each property added to the
10 | /// `model` classes.
11 |
12 | class DatabaseStateValidator {
13 | final AppAPI api;
14 |
15 | /// Creates a [DatabaseStateValidator].
16 | const DatabaseStateValidator({
17 | required this.api,
18 | });
19 |
20 | void createInstallationID(AppSettings appSettings) {
21 | final _appSettings = AppSettings(
22 | privacyPolicyAgreed: appSettings.privacyPolicyAgreed,
23 | darkMode: appSettings.darkMode,
24 | tabIndex: appSettings.tabIndex,
25 | installationID: AppAPI.generateInstallationID(),
26 | lastBackup: appSettings.lastBackup,
27 | backupNoticeIgnored: appSettings.backupNoticeIgnored,
28 | );
29 | api.updateAppSettings(_appSettings);
30 | }
31 |
32 | void createLastBackup(AppSettings appSettings) {
33 | final _appSettings = AppSettings(
34 | privacyPolicyAgreed: appSettings.privacyPolicyAgreed,
35 | darkMode: appSettings.darkMode,
36 | tabIndex: appSettings.tabIndex,
37 | installationID: appSettings.installationID,
38 | lastBackup: DefaultData.lastBackup,
39 | backupNoticeIgnored: appSettings.backupNoticeIgnored,
40 | );
41 | api.updateAppSettings(_appSettings);
42 | }
43 |
44 | void createBackupNoticeIgnored(AppSettings appSettings) {
45 | final _appSettings = AppSettings(
46 | privacyPolicyAgreed: appSettings.privacyPolicyAgreed,
47 | darkMode: appSettings.darkMode,
48 | tabIndex: appSettings.tabIndex,
49 | installationID: appSettings.installationID,
50 | lastBackup: appSettings.lastBackup,
51 | backupNoticeIgnored: DefaultData.backupNoticeIgnored,
52 | );
53 | api.updateAppSettings(_appSettings);
54 | }
55 |
56 | /// Validates the current app settings box state.
57 | ///
58 | /// Updates the app settings box according to current requirements.
59 | void validateAppSettings(AppSettings appSettings) {
60 | if (appSettings.installationID == null) {
61 | print("`InstallationID` setting missing.");
62 | createInstallationID(appSettings);
63 | print("`AppSettings` updated using generated `installationID`");
64 | }
65 |
66 | if (appSettings.lastBackup == null) {
67 | print("`lastBackup` setting missing.");
68 | createLastBackup(appSettings);
69 | print("`AppSettings` updated using default data.");
70 | }
71 |
72 | if (appSettings.backupNoticeIgnored == null) {
73 | print("`backupNoticeIgnored` setting missing.");
74 | createBackupNoticeIgnored(appSettings);
75 | print("`AppSettings` updated using default data.");
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/lib/api/debug_output_service.dart:
--------------------------------------------------------------------------------
1 | part of api;
2 |
3 | /// A class allowing to print debug output on various occasions.
4 | class DebugOutputService {
5 | /// Prints a debug message stating that a file does not exists anymore.
6 | static void printImageFileStorageError(String path) {
7 | print(
8 | "Diary Photo on " + "'" + path + "'" + " does not exists anymore.",
9 | );
10 | }
11 |
12 | /// Prints a debug message stating that a file is already backed up.
13 | static void printBackedUpImageFileDuplicateError(String path) {
14 | print("Image File on " + "'" + path + "'" " already backed up.");
15 | }
16 |
17 | /// Prints a debug message stating that a file has been copied.
18 | static void printImageFileCopied(String path) {
19 | print(path + " copied.");
20 | }
21 |
22 | /// Prints a debug message stating that a file has been linked to a diary
23 | /// entry.
24 | static void printImageFileLinked(String path) {
25 | print("File: " + path + " is linked to a DiaryEntry");
26 | }
27 |
28 | /// Prints a debug message stating that an unused file has been deleted.
29 | static void printStorageImageFileDeleted(String path) {
30 | print("Unused application file on " + path + " " + "deleted.");
31 | }
32 |
33 | /// Prints a debug message stating that deleting a file has failed.
34 | static void printStorageImageFileDeletionError(String path) {
35 | print("Deleting duplicate file on: " + path + " failed.");
36 | }
37 |
38 | /// Prints a debug message stating that an unused file has been deleted.
39 | static void printBackedUpImageFileDeleted(String path) {
40 | print("Unused backed up image file on " + path + " " + "deleted.");
41 | }
42 |
43 | static void printBackedUpImageFileDeletionError(String path) {
44 | print("Deleting duplicate file on: " + path + " failed.");
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/api/default_data.dart:
--------------------------------------------------------------------------------
1 | part of api;
2 |
3 | /// A `History of Me` `api` class containing default values applied on new
4 | /// Hive box objects.
5 | class DefaultData {
6 | static const int minStripeCount = 1;
7 | static int maxStripeCount = 32;
8 | static int minDotSize = 12;
9 | static int maxDotSize = 32;
10 |
11 | /// The initial quote.
12 | static const String quote =
13 | "The way to get started is to quit talking and begin doing.";
14 |
15 | /// The initial quote's author.
16 | static const String quoteAuthor = "Walt Disney";
17 |
18 | /// The initial design pattern index.
19 | static const int designPatternIndex = 0;
20 |
21 | /// The initial primary color value.
22 | static const int primaryColor = 0xFF757575;
23 |
24 | /// The initial secondary color value.
25 | static const int secondaryColors = 0xFFfff2b0;
26 |
27 | /// A list of initial user created color values.
28 | static const List userCreatedColorValues = [
29 | const Color(0xFFFAFAFA),
30 | const Color(0xFFF5F5F5),
31 | const Color(0xFFEEEEEE),
32 | const Color(0xFFE0E0E0),
33 | const Color(0xFFD6D6D6),
34 | const Color(0xFFBDBDBD),
35 | const Color(0xFF9E9E9E),
36 | const Color(0xFF757575),
37 | const Color(0xFF616161),
38 | const Color(0xFF424242),
39 | const Color(0xFF303030),
40 | const Color(0xFF212121),
41 | const Color(0xFFfff2b0),
42 | ];
43 |
44 | /// The default entry title.
45 | static const String diaryEntryTitle = "";
46 |
47 | /// the default entry content.
48 | static const String diaryEntryContent = "";
49 |
50 | /// The default diary entry's mood score.
51 | static const double diaryEntryMoodScore = 0.5;
52 |
53 | /// The default diary entry's backdrop id.
54 | static const int diaryEntryBackdropId = 1;
55 |
56 | static const bool agreedPrivacy = true;
57 | static const bool darkMode = false;
58 | static const int tabIndex = 0;
59 | static const String lastBackup = "";
60 | static const List photos = [];
61 | static const int visitCount = 0;
62 | static const int editCount = 0;
63 | static const bool backupNoticeIgnored = false;
64 | static const int maxDaysBackupOutdated = 2;
65 |
66 | static const Color colorGood = Color(0xFFECFFE9);
67 | static const Color colorBad = Color(0xFFF2E4E4);
68 | static const IconData iconGood = LitIcons.check;
69 | static const IconData iconBad = LitIcons.times;
70 | }
71 |
--------------------------------------------------------------------------------
/lib/api/diary_entry_provider.dart:
--------------------------------------------------------------------------------
1 | part of api;
2 |
3 | /// A `api` widget providing [DiaryEntry] objects to the child widget accessed
4 | /// by on the builder method.
5 | class DiaryEntryProvider extends StatelessWidget {
6 | /// The builder method providing access to [DiaryEntry] objects.
7 | final Widget Function(
8 | BuildContext context,
9 | List diaryEntries,
10 | ) builder;
11 |
12 | /// The [AppAPI] instance.
13 | final AppAPI api;
14 |
15 | /// Creates a [DiaryEntryProvider].
16 | const DiaryEntryProvider({
17 | Key? key,
18 | required this.builder,
19 | this.api = const AppAPI(),
20 | }) : super(key: key);
21 |
22 | /// Extracts the content stored inside the `Hive` box.
23 | List extractContent(Box box) {
24 | return box.isNotEmpty ? box.values.toList() : [];
25 | }
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return ValueListenableBuilder(
30 | valueListenable: api.getDiaryEntries(),
31 | builder: (BuildContext context, Box box, Widget? _) {
32 | return builder(
33 | context,
34 | extractContent(box),
35 | );
36 | },
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/api/query_diary_entry_provider.dart:
--------------------------------------------------------------------------------
1 | part of api;
2 |
3 | /// A `api` widget allowing to query for a single [DiaryEntry] object by its
4 | /// uid.
5 | ///
6 | /// The builder method provides additional access to metadata required to
7 | /// locate the object inside the collection.
8 | ///
9 | /// Returns null if no diary entry has been found.
10 | class QueryDiaryEntryProvider extends StatelessWidget {
11 | /// The [DiaryEntry]'s uid.
12 | ///
13 | /// Required to query the `Hive` database for a specific [DiaryEntry] object.
14 | final String diaryEntryUid;
15 |
16 | /// The builder method providing access to [DiaryEntry] objects.
17 | final Widget Function(
18 | BuildContext context,
19 | DiaryEntry? diaryEntry,
20 | bool isFirst,
21 | bool isLast,
22 | int boxLength,
23 | ) builder;
24 |
25 | /// The [AppAPI] instance.
26 | final AppAPI api;
27 |
28 | /// Creates a [DiaryEntryProvider].
29 | const QueryDiaryEntryProvider({
30 | Key? key,
31 | required this.diaryEntryUid,
32 | required this.builder,
33 | this.api = const AppAPI(),
34 | }) : super(key: key);
35 |
36 | /// Extracts the content stored inside the `Hive` box.
37 | DiaryEntry? extractContent(Box box) {
38 | final DiaryEntry? diaryEntry = box.get(diaryEntryUid);
39 |
40 | return diaryEntry;
41 | }
42 |
43 | /// Returns the last entry's index.
44 | int getLastIndex(Box box) {
45 | final int lastIndex = (box.length - 1);
46 | return lastIndex;
47 | }
48 |
49 | /// Returns the first entry's index.
50 | DiaryEntry? getFirstEntry(Box box) {
51 | return box.isNotEmpty ? box.getAt(0) : null;
52 | }
53 |
54 | /// Returns whether the queried entry is stored on the first index.
55 | bool getIsFirstIndex(Box box) {
56 | if (box.isEmpty) {
57 | return false;
58 | }
59 |
60 | DiaryEntry? first = getFirstEntry(box);
61 |
62 | if (first == null) return false;
63 |
64 | final bool? isFirst = first.uid == diaryEntryUid;
65 |
66 | return isFirst ?? false;
67 | }
68 |
69 | /// Returns whether the queried entry is stored on the last index.
70 | bool getIsLastIndex(Box box) {
71 | if (box.isEmpty) {
72 | return false;
73 | }
74 |
75 | final bool? isFirst = box.getAt(getLastIndex(box))!.uid == diaryEntryUid;
76 | return isFirst ?? false;
77 | }
78 |
79 | /// Returns the provided [Box]'s length.
80 | int getBoxLenth(Box box) {
81 | return box.length;
82 | }
83 |
84 | @override
85 | Widget build(BuildContext context) {
86 | return ValueListenableBuilder(
87 | valueListenable: api.getDiaryEntries(),
88 | builder: (BuildContext context, Box box, Widget? _) {
89 | return builder(
90 | context,
91 | extractContent(box),
92 | getIsFirstIndex(box),
93 | getIsLastIndex(box),
94 | getBoxLenth(box),
95 | );
96 | },
97 | );
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/lib/api/user_created_color_provider.dart:
--------------------------------------------------------------------------------
1 | part of api;
2 |
3 | /// A `api` widget providing [UserCreatedColor] objects to the child widget
4 | /// accessed by on the builder method.
5 | class UserCreatedColorProvider extends StatelessWidget {
6 | /// The builder method providing access to [UserCreatedColor] objects.
7 | final Widget Function(
8 | BuildContext context,
9 | List userCreatedColors,
10 | ) builder;
11 |
12 | /// The [AppAPI] instance.
13 | final AppAPI api;
14 |
15 | /// Creates a [UserCreatedColorProvider].
16 | const UserCreatedColorProvider({
17 | Key? key,
18 | required this.builder,
19 | this.api = const AppAPI(),
20 | }) : super(key: key);
21 |
22 | /// Extracts the content stored inside the `Hive` box.
23 | List extractContent(Box box) {
24 | return box.isNotEmpty ? box.values.toList() : [];
25 | }
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return ValueListenableBuilder(
30 | valueListenable: api.getUserCreatedColors(),
31 | builder: (
32 | BuildContext context,
33 | Box box,
34 | Widget? _,
35 | ) {
36 | return builder(
37 | context,
38 | extractContent(box),
39 | );
40 | },
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/api/user_data_provider.dart:
--------------------------------------------------------------------------------
1 | part of api;
2 |
3 | /// A `api` widget providing [UserData] objects to the child widget accessed
4 | /// by on the builder method.
5 | ///
6 | /// Accessing the user data using this provider should only be done once the
7 | /// user data is already present in the `Hive` database.
8 | class UserDataProvider extends StatelessWidget {
9 | /// The builder method providing access to the [UserData] object.
10 | ///
11 | /// The [isEmpty] values states whether the corresponding box is empty.
12 | final Widget Function(
13 | BuildContext context,
14 | UserData? userData,
15 | ) builder;
16 |
17 | /// The [AppAPI] instance.
18 | final AppAPI api;
19 |
20 | /// Creates a [UserDataProvider].
21 | const UserDataProvider({
22 | Key? key,
23 | required this.builder,
24 | this.api = const AppAPI(),
25 | }) : super(key: key);
26 |
27 | /// Extracts the content stored inside the `Hive` box.
28 | ///
29 | /// In case the user has not created an initial `UserData` object, null in
30 | /// favor of an empty object is returned to check whether or not it's the
31 | /// first app startup.
32 | UserData? extractContent(Box box) {
33 | return box.isEmpty ? null : box.getAt(0);
34 | }
35 |
36 | @override
37 | Widget build(BuildContext context) {
38 | return ValueListenableBuilder(
39 | valueListenable: api.getUserData(),
40 | builder: (context, Box userDataBox, _) {
41 | return builder(
42 | context,
43 | extractContent(userDataBox),
44 | );
45 | },
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/app.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_localizations/flutter_localizations.dart';
3 | import 'package:history_of_me/localization.dart';
4 | import 'package:history_of_me/static.dart';
5 | import 'package:history_of_me/widgets.dart';
6 | import 'package:leitmotif/leitmotif.dart';
7 |
8 | /// The main Flutter widget of `HistoryOfMe`.
9 | ///
10 | /// It's main purpose is to initialize the [MaterialApp] widget and to load the
11 | /// read-only content from the local storage and to provide basic meta data such
12 | /// as the app name.
13 | ///
14 | /// It also provides the option to restart the whole app if required using
15 | /// its `restartApp()` method.
16 | class App extends StatefulWidget {
17 | /// States whether the app runs in debug mode.
18 | ///
19 | /// This will prevent debug output to be printed.
20 | static const bool DEBUG = false;
21 |
22 | /// The application's name.
23 | static const String appName = "History Of Me";
24 |
25 | /// The application's slogan.
26 | static const String appSlogan = "Your own personal diary.";
27 |
28 | /// The application's developer.
29 | static const String appDeveloper = "LitLifeSoftware";
30 |
31 | static const supportedLocales = AppLocalizations.supportedLocales;
32 |
33 | static const supportedLanguages = const [
34 | EN.languageCode,
35 | DE.languageCode,
36 | ];
37 |
38 | /// Restarts the whole application by creating a new [UniqueKey] on the
39 | /// uppermost widget ([MaterialApp]).
40 | static void restartApp(BuildContext context) {
41 | context.findAncestorStateOfType<_AppState>()!.restartApp();
42 | }
43 |
44 | @override
45 | _AppState createState() => _AppState();
46 | }
47 |
48 | class _AppState extends State {
49 | Key _key = UniqueKey();
50 |
51 | /// The background photos fetched from local storage.
52 | List backdropPhotoUrlList = [];
53 |
54 | /// The list of images required to load from local storage.
55 | final List utilityImagesUrlList = const [
56 | AppAssets.keyLogo256px,
57 | AppAssets.keyIcon,
58 | AppAssets.curtainLeftImg,
59 | AppAssets.curtainRightImg,
60 | AppAssets.windowImg,
61 | AppAssets.cloudImg,
62 | AppAssets.historyOfMeArtworkSmall,
63 | ];
64 |
65 | /// Restart the app globally by creating a new [UniqueKey].
66 | void restartApp() {
67 | setState(() {
68 | _key = UniqueKey();
69 | });
70 | }
71 |
72 | @override
73 | void initState() {
74 | super.initState();
75 | ImageCacheController(
76 | context: context,
77 | assetImages: utilityImagesUrlList,
78 | );
79 | }
80 |
81 | @override
82 | Widget build(BuildContext context) {
83 | return KeyedSubtree(
84 | key: _key,
85 | child: MaterialApp(
86 | debugShowCheckedModeBanner: App.DEBUG,
87 | theme: ThemeData(
88 | brightness: Brightness.light,
89 | primarySwatch: Colors.grey,
90 | primaryColor: Colors.grey[50],
91 | useMaterial3: true,
92 | textTheme: LitSansSerifStyles.theme,
93 | iconTheme: IconThemeData(
94 | color: LitColors.grey500,
95 | ),
96 | appBarTheme: AppBarTheme(
97 | iconTheme: IconThemeData(color: LitColors.grey500),
98 | actionsIconTheme: IconThemeData(
99 | color: LitColors.grey500,
100 | ),
101 | ),
102 | ),
103 | localizationsDelegates: const [
104 | AppLocalizations.delegate,
105 | LeitmotifLocalizations.delegate,
106 | GlobalMaterialLocalizations.delegate,
107 | GlobalWidgetsLocalizations.delegate,
108 | GlobalCupertinoLocalizations.delegate,
109 | ],
110 |
111 | /// Supported languages
112 | supportedLocales: App.supportedLocales,
113 | title: App.appName,
114 | home: DatabaseStateScreenBuilder(),
115 | ),
116 | );
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/lib/controller/autosave_controller.dart:
--------------------------------------------------------------------------------
1 | part of controllers;
2 |
3 | /// A `controller` class handling autosave events.
4 | ///
5 | /// Executes the [saveChanges] methods whenenver the preferred
6 | /// duration has been reached.
7 | class AutosaveController {
8 | /// Handles the save action.
9 | final void Function() saveChanges;
10 |
11 | /// The timer's preferred duration.
12 | final Duration duration;
13 |
14 | /// Creates a [AutosaveController].
15 | ///
16 | /// Initializes the autosave timer.
17 | AutosaveController(
18 | this.saveChanges, {
19 | this.duration = defaultDuration,
20 | }) {
21 | _init();
22 | }
23 |
24 | /// The timer's default duration.
25 | static const defaultDuration = const Duration(seconds: 60);
26 |
27 | /// The timer to handle calling the [saveChanges] method.
28 | late Timer timer;
29 |
30 | /// Disposes the [AutosaveController] by canceling the timer.
31 | void dispose() {
32 | timer.cancel();
33 | }
34 |
35 | /// Initializes the [AutosaveController].
36 | void _init() {
37 | timer = Timer.periodic(
38 | duration,
39 | (_) => {
40 | saveChanges(),
41 | },
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/controller/hom_navigator.dart:
--------------------------------------------------------------------------------
1 | part of controllers;
2 |
3 | /// A controller class enabling to navigate through screens by its corresponding
4 | /// member method.
5 | ///
6 | /// Pass the [BuildContext] in order to manipulate the widget stack.
7 | class HOMNavigator {
8 | /// The [BuildContext] instance required to initialize the [LitRouteController.]
9 | final BuildContext context;
10 |
11 | /// Creates a [HOMNavigator].
12 | ///
13 | /// Pass on the [BuildContext] instance.
14 | HOMNavigator(this.context) {
15 | _routeController = LitRouteController(context);
16 | }
17 |
18 | /// The [LitRouteController] will handle the navigation logic.
19 | late LitRouteController _routeController;
20 |
21 | /// Navigates the user to the [EntryDetailScreen].
22 | ///
23 | /// Provide the arguments that should be passed to the screen widget.
24 | void toDiaryEntryDetailScreen({
25 | required int listIndex,
26 | required DiaryEntry diaryEntry,
27 | }) {
28 | final Widget pushedWidget = EntryDetailScreen(
29 | listIndex: listIndex,
30 | diaryEntry: diaryEntry,
31 | );
32 | _routeController.pushMaterialWidget(pushedWidget);
33 | }
34 |
35 | /// Navigates the user to the [EntryEditingScreen].
36 | ///
37 | /// Provide the arguments that should be passed to the screen widget.
38 | void toEntryEditingScreen({
39 | required DiaryEntry diaryEntry,
40 | }) {
41 | final Widget pushedWidget = EntryEditingScreen(
42 | diaryEntry: diaryEntry,
43 | );
44 | _routeController.pushCupertinoWidget(pushedWidget);
45 | }
46 |
47 | /// Navigates the user to the [BookmarkEditingScreen].
48 | ///
49 | /// Provide the arguments that should be passed to the screen widget.
50 | void toBookmarkEditingScreen({
51 | required UserData? userData,
52 | }) {
53 | final pushedWidget = BookmarkEditingScreen(
54 | initialUserDataModel: userData,
55 | );
56 | _routeController.pushMaterialWidget(pushedWidget);
57 | }
58 |
59 | void showCreateEntryDialog() {
60 | _routeController.showDialogWidget(
61 | CreateEntryDialog(),
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/lib/controller/mood_translator.dart:
--------------------------------------------------------------------------------
1 | part of controllers;
2 |
3 | /// A `controller` class translating the provided [moodScore] into a human
4 | /// readable label.
5 | class MoodTranslator {
6 | /// The current [BuildContext].
7 | final BuildContext context;
8 |
9 | /// A list of mood labels sorted accending according to the corresponding
10 | /// [moodScore] it should represent.
11 | ///
12 | /// Applies default labels if none are provided.
13 | final List? labels;
14 |
15 | /// The current mood score.
16 | final double moodScore;
17 | const MoodTranslator({
18 | required this.moodScore,
19 | required this.context,
20 | this.labels,
21 | });
22 |
23 | /// A list of mood labels sorted accending according to the corresponding
24 | /// [moodScore] it should represent.
25 | List get _labels =>
26 | labels ??
27 | [
28 | AppLocalizations.of(context).badLabel,
29 | AppLocalizations.of(context).mixedLabel,
30 | AppLocalizations.of(context).alrightLabel,
31 | AppLocalizations.of(context).goodLabel,
32 | ];
33 |
34 | /// Returns a [String] translating the [moodScore] into a human readable
35 | /// label.
36 | String get label {
37 | for (int i = 0; i < _labels.length; i++) {
38 | // States the limit the score should not exceed in order to fit
39 | // the value.
40 | double limit = (i + 1) / _labels.length;
41 | if (moodScore < limit) {
42 | return _labels[i];
43 | }
44 | }
45 |
46 | return _labels.last;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/controllers.dart:
--------------------------------------------------------------------------------
1 | /// A list of controller classes used to implement certain features using
2 | /// business logic.
3 | library controllers;
4 |
5 | import 'dart:async';
6 |
7 | import 'dart:io';
8 | import 'package:flutter/material.dart';
9 | import 'package:history_of_me/api.dart';
10 | import 'package:history_of_me/localization.dart';
11 | import 'package:history_of_me/models.dart';
12 | import 'package:history_of_me/screens.dart';
13 | import 'package:history_of_me/widgets.dart';
14 | import 'package:image_picker/image_picker.dart';
15 | import 'package:leitmotif/leitmotif.dart';
16 | import 'package:path/path.dart' as p;
17 | import 'package:path_provider/path_provider.dart';
18 |
19 | part 'controller/autosave_controller.dart';
20 | part 'controller/hom_navigator.dart';
21 | part 'controller/mood_translator.dart';
22 | part 'controller/diary_photo_picker.dart';
23 |
--------------------------------------------------------------------------------
/lib/extensions.dart:
--------------------------------------------------------------------------------
1 | /// Extensions implementing various additional methods on Dart or Flutter
2 | /// base classes.
3 | ///
4 | /// To use, import `package:history_of_me/extensions.dart`.
5 | library extensions;
6 |
7 | import 'dart:io';
8 |
9 | part 'extensions/file_system_entity_extension.dart';
10 | part 'extensions/string_extension.dart';
11 |
--------------------------------------------------------------------------------
/lib/extensions/file_system_entity_extension.dart:
--------------------------------------------------------------------------------
1 | part of extensions;
2 |
3 | /// Extends the abstract [FileSystemEntity] class using additional methods and
4 | /// properties.
5 | extension FileSystemEntityExtention on FileSystemEntity {
6 | String get _splitPattern => "/";
7 |
8 | String get name {
9 | return this.path.split(_splitPattern).last;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/lib/extensions/string_extension.dart:
--------------------------------------------------------------------------------
1 | part of extensions;
2 |
3 | //TODO: Integrate into Leitmotif
4 | /// Extension on [String] class.
5 | ///
6 | /// Includes various methods for word counting.
7 | extension StringExtension on String {
8 | // The pattern applied to split words inside the string.
9 | static const _wordSplitPattern = ' ';
10 |
11 | /// Validates whether the provided string does represent a valid word.
12 | ///
13 | /// Returns true if so.
14 | bool _validateWord(String word) {
15 | return word.isNotEmpty && !(word.contains(_wordSplitPattern));
16 | }
17 |
18 | /// Return the total number of words included in the string's value.
19 | int get wordCount {
20 | int counter = 0;
21 |
22 | // Iterate through the split string list and validate each item.
23 | for (String word in this.split(_wordSplitPattern)) {
24 | if (_validateWord(word)) {
25 | counter++;
26 | }
27 | }
28 |
29 | return counter;
30 | }
31 |
32 | /// Removes all whitespaces and returns the cleaned [String].
33 | String removeSpaces() {
34 | return this.split(" ").join();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/localization.dart:
--------------------------------------------------------------------------------
1 | /// Widgets implementing the localization of History of me.
2 | ///
3 | /// To use, import `package:history_of_me/localization.dart`.
4 | library localization;
5 |
6 | import 'package:flutter/foundation.dart';
7 | import 'package:flutter/material.dart';
8 |
9 | part 'localization/app_localizations.dart';
10 | part 'localization/app_localizations_delegate.dart';
11 | part 'localization/app_localizations_keys.dart';
12 | part 'localization/languages/de.dart';
13 | part 'localization/languages/en.dart';
14 |
--------------------------------------------------------------------------------
/lib/localization/app_localizations_delegate.dart:
--------------------------------------------------------------------------------
1 | part of localization;
2 |
3 | /// A Leitmotif delegate class initializing the [AppLocalizations].
4 | class AppLocalizationsDelegate extends LocalizationsDelegate {
5 | const AppLocalizationsDelegate();
6 |
7 | @override
8 | bool isSupported(Locale locale) =>
9 | AppLocalizations.languages.contains(locale.languageCode);
10 |
11 | @override
12 | Future load(Locale locale) {
13 | // Returning a SynchronousFuture here because an async "load" operation
14 | // isn't needed to produce an instance of AppLocalizations.
15 | return SynchronousFuture(
16 | AppLocalizations(locale),
17 | );
18 | }
19 |
20 | @override
21 | bool shouldReload(AppLocalizationsDelegate old) => false;
22 | }
23 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:history_of_me/app.dart';
3 | import 'package:history_of_me/api.dart';
4 |
5 | void main() async {
6 | await AppAPI().init();
7 | runApp(App());
8 | }
9 |
--------------------------------------------------------------------------------
/lib/model/app_settings.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 |
3 | part 'app_settings.g.dart';
4 |
5 | /// A model class storing app-specific data, which may only be client-specific.
6 | @HiveType(typeId: 3)
7 | class AppSettings {
8 | @HiveField(0)
9 | final bool privacyPolicyAgreed;
10 | @HiveField(1)
11 | final bool darkMode;
12 | @HiveField(2)
13 | final int tabIndex;
14 | @HiveField(3)
15 | final String? installationID;
16 | @HiveField(4)
17 | final String? lastBackup;
18 | @HiveField(5)
19 | final bool? backupNoticeIgnored;
20 |
21 | /// Creates a [AppSettings] object.
22 | const AppSettings({
23 | required this.privacyPolicyAgreed,
24 | required this.darkMode,
25 | required this.tabIndex,
26 | required this.installationID,
27 | required this.lastBackup,
28 | required this.backupNoticeIgnored,
29 | });
30 |
31 | /// Creates a [AppSettings] object by serializing the provided `Map` data.
32 | ///
33 | /// `JSON` decoding must be done before serialization.
34 | factory AppSettings.fromJson(Map json) {
35 | return AppSettings(
36 | privacyPolicyAgreed: json['privacyPolicyAgreed'] as bool,
37 | darkMode: json['darkMode'] as bool,
38 | tabIndex: json['tabIndex'] as int,
39 | installationID: json['installationID'] as String?,
40 | lastBackup: json['lastBackup'] as String?,
41 | backupNoticeIgnored: json['backupNoticeIgnored'] as bool,
42 | );
43 | }
44 |
45 | /// Creates a `Map` object based on this [AppSettings] object.
46 | ///
47 | /// `JSON` encoding must be done after serialization.
48 | Map toJson() => {
49 | 'privacyPolicyAgreed': privacyPolicyAgreed,
50 | 'darkMode': darkMode,
51 | 'tabIndex': tabIndex,
52 | 'installationID': installationID,
53 | 'lastBackup': lastBackup,
54 | 'backupNoticeIgnored': backupNoticeIgnored,
55 | };
56 | }
57 |
--------------------------------------------------------------------------------
/lib/model/app_settings.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'app_settings.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class AppSettingsAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 3;
12 |
13 | @override
14 | AppSettings 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 AppSettings(
20 | privacyPolicyAgreed: fields[0] as bool,
21 | darkMode: fields[1] as bool,
22 | tabIndex: fields[2] as int,
23 | installationID: fields[3] as String?,
24 | lastBackup: fields[4] as String?,
25 | backupNoticeIgnored: fields[5] as bool?,
26 | );
27 | }
28 |
29 | @override
30 | void write(BinaryWriter writer, AppSettings obj) {
31 | writer
32 | ..writeByte(6)
33 | ..writeByte(0)
34 | ..write(obj.privacyPolicyAgreed)
35 | ..writeByte(1)
36 | ..write(obj.darkMode)
37 | ..writeByte(2)
38 | ..write(obj.tabIndex)
39 | ..writeByte(3)
40 | ..write(obj.installationID)
41 | ..writeByte(4)
42 | ..write(obj.lastBackup)
43 | ..writeByte(5)
44 | ..write(obj.backupNoticeIgnored);
45 | }
46 |
47 | @override
48 | int get hashCode => typeId.hashCode;
49 |
50 | @override
51 | bool operator ==(Object other) =>
52 | identical(this, other) ||
53 | other is AppSettingsAdapter &&
54 | runtimeType == other.runtimeType &&
55 | typeId == other.typeId;
56 | }
57 |
--------------------------------------------------------------------------------
/lib/model/diary_backup.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:history_of_me/models.dart';
4 | import 'package:lit_backup_service/lit_backup_service.dart';
5 |
6 | /// A `model` class including all required data to create and read backups of
7 | /// the user's diary.
8 | ///
9 | /// It implements the [BackupModel] in order to be compatible to the
10 | /// `lit_backup_service` package.
11 | class DiaryBackup implements BackupModel {
12 | final String appVersion;
13 | final String backupDate;
14 | final AppSettings appSettings;
15 | final List diaryEntries;
16 | final List userCreatedColors;
17 | final UserData userData;
18 |
19 | /// Creates a [DiaryBackup].
20 | const DiaryBackup({
21 | required this.appVersion,
22 | required this.backupDate,
23 | required this.appSettings,
24 | required this.diaryEntries,
25 | required this.userCreatedColors,
26 | required this.userData,
27 | });
28 |
29 | /// Creates a [DiaryBackup] based on the provided `Map`.
30 | factory DiaryBackup.fromJson(Map json) {
31 | /// Extracts all `DiaryEntry` objects into a list.
32 | final diaryEntries = List.from(
33 | jsonDecode(json['diaryEntries']).map(
34 | (item) => DiaryEntry.fromJson(item),
35 | ),
36 | );
37 |
38 | /// Extracts all `UserCreatedColor` objects into a list.
39 | final userCreatedColors = List.from(
40 | jsonDecode(json['userCreatedColors']).map(
41 | (item) => UserCreatedColor.fromJson(item),
42 | ),
43 | );
44 |
45 | return DiaryBackup(
46 | appVersion: json['appVersion'] as String,
47 | backupDate: json['backupDate'] as String,
48 | appSettings: AppSettings.fromJson(json['appSettings']),
49 | diaryEntries: diaryEntries,
50 | userCreatedColors: userCreatedColors,
51 | userData: UserData.fromJson(json['userData']),
52 | );
53 | }
54 |
55 | @override
56 | Map toJson() {
57 | final diaryEntriesMap =
58 | jsonEncode(diaryEntries.map((item) => item.toJson()).toList());
59 | final userCreatedColorsMap =
60 | jsonEncode(userCreatedColors.map((item) => item.toJson()).toList());
61 | return {
62 | 'appVersion': appVersion,
63 | 'backupDate': backupDate,
64 | 'appSettings': appSettings.toJson(),
65 | 'diaryEntries': diaryEntriesMap,
66 | 'userCreatedColors': userCreatedColorsMap,
67 | 'userData': userData.toJson(),
68 | };
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/lib/model/diary_entry.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'diary_photo.dart';
4 | import 'package:hive/hive.dart';
5 | part 'diary_entry.g.dart';
6 |
7 | /// A model class storing data specific to an individual diary entry.
8 | ///
9 | /// It contains the diaries content as well as meta data.
10 | @HiveType(typeId: 2)
11 | class DiaryEntry {
12 | @HiveField(0)
13 | final String uid;
14 | @HiveField(1)
15 | final String date;
16 | @HiveField(2)
17 | final int created;
18 | @HiveField(3)
19 | final int lastUpdated;
20 | @HiveField(4)
21 | final String title;
22 | @HiveField(5)
23 | final String content;
24 | @HiveField(6)
25 | final double moodScore;
26 | @HiveField(7)
27 | final bool favorite;
28 | @HiveField(8)
29 | final int backdropPhotoId;
30 | @HiveField(9)
31 | final List? photos;
32 | @HiveField(10)
33 | final int? visitCount;
34 | @HiveField(11)
35 | final int? editCount;
36 |
37 | /// Creates a [DiaryEntry] object.
38 | const DiaryEntry({
39 | required this.uid,
40 | required this.date,
41 | required this.created,
42 | required this.lastUpdated,
43 | required this.title,
44 | required this.content,
45 | required this.moodScore,
46 | required this.favorite,
47 | required this.backdropPhotoId,
48 | required this.photos,
49 | required this.visitCount,
50 | required this.editCount,
51 | });
52 |
53 | /// Creates a [DiaryEntry] object by serializing the provided `Map` data.
54 | ///
55 | /// `JSON` decoding must be done before serialization.
56 | factory DiaryEntry.fromJson(Map json) {
57 | /// Contains all `DiaryPhoto` objects.
58 | List? photos = [];
59 |
60 | if (json.containsKey('photos')) {
61 | photos = List.from(
62 | jsonDecode(json['photos']).map(
63 | (item) => DiaryPhoto.fromJson(item),
64 | ),
65 | );
66 | }
67 |
68 | return DiaryEntry(
69 | uid: json['uid'] as String,
70 | date: json['date'] as String,
71 | created: json['created'] as int,
72 | lastUpdated: json['lastUpdated'] as int,
73 | title: json['title'] as String,
74 | content: json['content'] as String,
75 | moodScore: json['moodScore'] as double,
76 | favorite: json['favorite'] as bool,
77 | backdropPhotoId: json['backdropPhotoId'] as int,
78 | photos: photos,
79 | visitCount: json['visitCount'] as int,
80 | editCount: json['editCount'] as int,
81 | );
82 | }
83 |
84 | /// Creates a `Map` object based on this [DiaryEntry] object.
85 | ///
86 | /// `JSON` encoding must be done after serialization.
87 | Map toJson() {
88 | final photosMap = photos != null
89 | ? jsonEncode(photos!.map((item) => item.toJson()).toList())
90 | : Map();
91 | print(photosMap.toString());
92 |
93 | return {
94 | 'uid': uid,
95 | 'date': date,
96 | 'created': created,
97 | 'lastUpdated': lastUpdated,
98 | 'title': title,
99 | 'content': content,
100 | 'moodScore': moodScore,
101 | 'favorite': favorite,
102 | 'backdropPhotoId': backdropPhotoId,
103 | 'photos': photosMap,
104 | 'visitCount': visitCount,
105 | 'editCount': editCount,
106 | };
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/lib/model/diary_entry.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'diary_entry.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class DiaryEntryAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 2;
12 |
13 | @override
14 | DiaryEntry 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 DiaryEntry(
20 | uid: fields[0] as String,
21 | date: fields[1] as String,
22 | created: fields[2] as int,
23 | lastUpdated: fields[3] as int,
24 | title: fields[4] as String,
25 | content: fields[5] as String,
26 | moodScore: fields[6] as double,
27 | favorite: fields[7] as bool,
28 | backdropPhotoId: fields[8] as int,
29 | photos: (fields[9] as List?)?.cast(),
30 | visitCount: fields[10] as int?,
31 | editCount: fields[11] as int?,
32 | );
33 | }
34 |
35 | @override
36 | void write(BinaryWriter writer, DiaryEntry obj) {
37 | writer
38 | ..writeByte(12)
39 | ..writeByte(0)
40 | ..write(obj.uid)
41 | ..writeByte(1)
42 | ..write(obj.date)
43 | ..writeByte(2)
44 | ..write(obj.created)
45 | ..writeByte(3)
46 | ..write(obj.lastUpdated)
47 | ..writeByte(4)
48 | ..write(obj.title)
49 | ..writeByte(5)
50 | ..write(obj.content)
51 | ..writeByte(6)
52 | ..write(obj.moodScore)
53 | ..writeByte(7)
54 | ..write(obj.favorite)
55 | ..writeByte(8)
56 | ..write(obj.backdropPhotoId)
57 | ..writeByte(9)
58 | ..write(obj.photos)
59 | ..writeByte(10)
60 | ..write(obj.visitCount)
61 | ..writeByte(11)
62 | ..write(obj.editCount);
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 DiaryEntryAdapter &&
72 | runtimeType == other.runtimeType &&
73 | typeId == other.typeId;
74 | }
75 |
--------------------------------------------------------------------------------
/lib/model/diary_photo.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive_flutter/hive_flutter.dart';
2 |
3 | part 'diary_photo.g.dart';
4 |
5 | /// A `model` class storing data of a image file linked to a diary entry.
6 | @HiveType(typeId: 4)
7 | class DiaryPhoto {
8 | @HiveField(0)
9 | final String uid;
10 | @HiveField(1)
11 | final String date;
12 | @HiveField(2)
13 | final int created;
14 | @HiveField(3)
15 | final String name;
16 | @HiveField(4)
17 | final String path;
18 |
19 | /// Creates a [DiaryPhoto].
20 | const DiaryPhoto({
21 | required this.uid,
22 | required this.date,
23 | required this.created,
24 | required this.name,
25 | required this.path,
26 | });
27 |
28 | /// Creates an [DiaryPhoto] object by serializing the provided `Map`
29 | /// data.
30 | ///
31 | /// `JSON` decoding must be done before serialization.
32 | factory DiaryPhoto.fromJson(Map json) => DiaryPhoto(
33 | uid: json['uid'] as String,
34 | date: json['date'] as String,
35 | created: json['created'] as int,
36 | name: json['name'] as String,
37 | path: json['path'] as String,
38 | );
39 |
40 | /// Creates a `Map` object based on this [UserCreatedColor] object.
41 | ///
42 | /// `JSON` encoding must be done after serialization.
43 | Map toJson() => {
44 | 'uid': uid,
45 | 'date': date,
46 | 'created': created,
47 | 'name': name,
48 | 'path': path,
49 | };
50 | }
51 |
--------------------------------------------------------------------------------
/lib/model/diary_photo.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'diary_photo.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class DiaryPhotoAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 4;
12 |
13 | @override
14 | DiaryPhoto 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 DiaryPhoto(
20 | uid: fields[0] as String,
21 | date: fields[1] as String,
22 | created: fields[2] as int,
23 | name: fields[3] as String,
24 | path: fields[4] as String,
25 | );
26 | }
27 |
28 | @override
29 | void write(BinaryWriter writer, DiaryPhoto obj) {
30 | writer
31 | ..writeByte(5)
32 | ..writeByte(0)
33 | ..write(obj.uid)
34 | ..writeByte(1)
35 | ..write(obj.date)
36 | ..writeByte(2)
37 | ..write(obj.created)
38 | ..writeByte(3)
39 | ..write(obj.name)
40 | ..writeByte(4)
41 | ..write(obj.path);
42 | }
43 |
44 | @override
45 | int get hashCode => typeId.hashCode;
46 |
47 | @override
48 | bool operator ==(Object other) =>
49 | identical(this, other) ||
50 | other is DiaryPhotoAdapter &&
51 | runtimeType == other.runtimeType &&
52 | typeId == other.typeId;
53 | }
54 |
--------------------------------------------------------------------------------
/lib/model/models.dart:
--------------------------------------------------------------------------------
1 | // /// A list of model classes used to store and mutate data across the History of
2 | // /// me app.
3 | // library models;
4 |
5 | // export 'app_settings.dart';
6 | // export 'backdrop_photo.dart';
7 | // export 'diary_backup.dart';
8 | // export 'diary_entry.dart';
9 | // export 'user_created_color.dart';
10 | // export 'user_data.dart';
11 |
--------------------------------------------------------------------------------
/lib/model/user_created_color.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 |
3 | part 'user_created_color.g.dart';
4 |
5 | /// A model class storing an user-created `Color` object.
6 | ///
7 | /// Each color channel is extracted into its own property.
8 | @HiveType(typeId: 1)
9 | class UserCreatedColor {
10 | @HiveField(0)
11 | final String uid;
12 | @HiveField(1)
13 | final int alpha;
14 | @HiveField(2)
15 | final int red;
16 | @HiveField(3)
17 | final int green;
18 | @HiveField(4)
19 | final int blue;
20 |
21 | /// Creates an [UserCreatedColor] object.
22 | const UserCreatedColor({
23 | required this.uid,
24 | required this.alpha,
25 | required this.red,
26 | required this.green,
27 | required this.blue,
28 | });
29 |
30 | /// Creates an [UserCreatedColor] object by serializing the provided `Map`
31 | /// data.
32 | ///
33 | /// `JSON` decoding must be done before serialization.
34 | factory UserCreatedColor.fromJson(Map json) {
35 | return UserCreatedColor(
36 | uid: json['uid'] as String,
37 | alpha: json['alpha'] as int,
38 | red: json['red'] as int,
39 | green: json['green'] as int,
40 | blue: json['blue'] as int,
41 | );
42 | }
43 |
44 | /// Creates a `Map` object based on this [UserCreatedColor] object.
45 | ///
46 | /// `JSON` encoding must be done after serialization.
47 | Map toJson() => {
48 | 'uid': uid,
49 | 'alpha': alpha,
50 | 'red': red,
51 | 'green': green,
52 | 'blue': blue,
53 | };
54 | }
55 |
--------------------------------------------------------------------------------
/lib/model/user_created_color.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'user_created_color.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class UserCreatedColorAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 1;
12 |
13 | @override
14 | UserCreatedColor 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 UserCreatedColor(
20 | uid: fields[0] as String,
21 | alpha: fields[1] as int,
22 | red: fields[2] as int,
23 | green: fields[3] as int,
24 | blue: fields[4] as int,
25 | );
26 | }
27 |
28 | @override
29 | void write(BinaryWriter writer, UserCreatedColor obj) {
30 | writer
31 | ..writeByte(5)
32 | ..writeByte(0)
33 | ..write(obj.uid)
34 | ..writeByte(1)
35 | ..write(obj.alpha)
36 | ..writeByte(2)
37 | ..write(obj.red)
38 | ..writeByte(3)
39 | ..write(obj.green)
40 | ..writeByte(4)
41 | ..write(obj.blue);
42 | }
43 |
44 | @override
45 | int get hashCode => typeId.hashCode;
46 |
47 | @override
48 | bool operator ==(Object other) =>
49 | identical(this, other) ||
50 | other is UserCreatedColorAdapter &&
51 | runtimeType == other.runtimeType &&
52 | typeId == other.typeId;
53 | }
54 |
--------------------------------------------------------------------------------
/lib/model/user_data.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 |
3 | part 'user_data.g.dart';
4 |
5 | /// A model class storing user-specific data as well as the currently edited
6 | /// bookmark configuration.
7 | @HiveType(typeId: 0)
8 | class UserData {
9 | @HiveField(0)
10 | final String name;
11 | @HiveField(1)
12 | final int primaryColor;
13 | @HiveField(2)
14 | final int secondaryColor;
15 | @HiveField(3)
16 | final int stripeCount;
17 | @HiveField(4)
18 | final int dotSize;
19 | @HiveField(5)
20 | final bool animated;
21 | @HiveField(6)
22 | final String quote;
23 | @HiveField(7)
24 | final int designPatternIndex;
25 | @HiveField(8)
26 | final String quoteAuthor;
27 | @HiveField(9)
28 | final int lastUpdated;
29 | @HiveField(10)
30 | final int created;
31 |
32 | /// Creates an [UserData] object.
33 | const UserData({
34 | required this.name,
35 | required this.primaryColor,
36 | required this.secondaryColor,
37 | required this.stripeCount,
38 | required this.dotSize,
39 | required this.animated,
40 | required this.quote,
41 | required this.designPatternIndex,
42 | required this.quoteAuthor,
43 | required this.lastUpdated,
44 | required this.created,
45 | });
46 |
47 | /// Creates an [UserData] object by serializing the provided `Map` data.
48 | ///
49 | /// `JSON` decoding must be done before serialization.
50 | factory UserData.fromJson(Map json) {
51 | return UserData(
52 | name: json['name'] as String,
53 | primaryColor: json['primaryColor'] as int,
54 | secondaryColor: json['secondaryColor'] as int,
55 | stripeCount: json['stripeCount'] as int,
56 | dotSize: json['dotSize'] as int,
57 | animated: json['animated'] as bool,
58 | quote: json['quote'] as String,
59 | designPatternIndex: json['designPatternIndex'] as int,
60 | quoteAuthor: json['quoteAuthor'] as String,
61 | lastUpdated: json['lastUpdated'] as int,
62 | created: json['created'] as int,
63 | );
64 | }
65 |
66 | /// Creates a `Map` object based on this [UserData] object.
67 | ///
68 | /// `JSON` encoding must be done after serialization.
69 | Map toJson() => {
70 | 'name': name,
71 | 'primaryColor': primaryColor,
72 | 'secondaryColor': secondaryColor,
73 | 'stripeCount': stripeCount,
74 | 'dotSize': dotSize,
75 | 'animated': animated,
76 | 'quote': quote,
77 | 'designPatternIndex': designPatternIndex,
78 | 'quoteAuthor': quoteAuthor,
79 | 'lastUpdated': lastUpdated,
80 | 'created': created,
81 | };
82 | }
83 |
--------------------------------------------------------------------------------
/lib/model/user_data.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'user_data.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class UserDataAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 0;
12 |
13 | @override
14 | UserData 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 UserData(
20 | name: fields[0] as String,
21 | primaryColor: fields[1] as int,
22 | secondaryColor: fields[2] as int,
23 | stripeCount: fields[3] as int,
24 | dotSize: fields[4] as int,
25 | animated: fields[5] as bool,
26 | quote: fields[6] as String,
27 | designPatternIndex: fields[7] as int,
28 | quoteAuthor: fields[8] as String,
29 | lastUpdated: fields[9] as int,
30 | created: fields[10] as int,
31 | );
32 | }
33 |
34 | @override
35 | void write(BinaryWriter writer, UserData obj) {
36 | writer
37 | ..writeByte(11)
38 | ..writeByte(0)
39 | ..write(obj.name)
40 | ..writeByte(1)
41 | ..write(obj.primaryColor)
42 | ..writeByte(2)
43 | ..write(obj.secondaryColor)
44 | ..writeByte(3)
45 | ..write(obj.stripeCount)
46 | ..writeByte(4)
47 | ..write(obj.dotSize)
48 | ..writeByte(5)
49 | ..write(obj.animated)
50 | ..writeByte(6)
51 | ..write(obj.quote)
52 | ..writeByte(7)
53 | ..write(obj.designPatternIndex)
54 | ..writeByte(8)
55 | ..write(obj.quoteAuthor)
56 | ..writeByte(9)
57 | ..write(obj.lastUpdated)
58 | ..writeByte(10)
59 | ..write(obj.created);
60 | }
61 |
62 | @override
63 | int get hashCode => typeId.hashCode;
64 |
65 | @override
66 | bool operator ==(Object other) =>
67 | identical(this, other) ||
68 | other is UserDataAdapter &&
69 | runtimeType == other.runtimeType &&
70 | typeId == other.typeId;
71 | }
72 |
--------------------------------------------------------------------------------
/lib/models.dart:
--------------------------------------------------------------------------------
1 | /// A collection of `model` classes storing different kinds of data.
2 | ///
3 | /// Includes the Hive-compatible adapters.
4 | library models;
5 |
6 | export 'model/app_settings.dart';
7 | export 'model/diary_backup.dart';
8 | export 'model/diary_entry.dart';
9 | export 'model/user_created_color.dart';
10 | export 'model/user_data.dart';
11 | export 'model/diary_photo.dart';
12 |
--------------------------------------------------------------------------------
/lib/screens.dart:
--------------------------------------------------------------------------------
1 | /// Widgets implementing the navigatable screen widgets of History of me.
2 | ///
3 | /// To use, import `package:history_of_me/screens.dart`.
4 | library screens;
5 |
6 | export 'screens/app_onboarding_screen.dart';
7 | export 'screens/bookmark_editing_screen.dart';
8 | export 'screens/diary_screen.dart';
9 | export 'screens/entry_detail_screen.dart';
10 | export 'screens/entry_editing_screen.dart';
11 | export 'screens/home_screen.dart';
12 | export 'screens/profile_screen.dart';
13 | export 'screens/restore_diary_screen.dart';
14 | export 'screens/select_backup_screen.dart';
15 | export 'screens/splash_screen.dart';
16 | export 'screens/app_privacy_screen.dart';
17 | export 'screens/app_credits_screen.dart';
18 | export 'screens/backup_notice_screen.dart';
19 |
--------------------------------------------------------------------------------
/lib/screens/app_credits_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:history_of_me/app.dart';
3 | import 'package:history_of_me/localization.dart';
4 | import 'package:history_of_me/widgets.dart';
5 | import 'package:leitmotif/leitmotif.dart';
6 |
7 | class AppCreditsScreen extends StatelessWidget {
8 | const AppCreditsScreen({Key? key}) : super(key: key);
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return LitCreditsScreen(
13 | art: AppLauncherIconArt(
14 | boxShadow: LitBoxShadows.sm,
15 | ),
16 | appName: App.appName,
17 | appDescription: AppLocalizations.of(context).aboutAppDescr,
18 | credits: [
19 | CreditData(
20 | role: LeitmotifLocalizations.of(context).creatorLabel,
21 | names: [
22 | App.appDeveloper,
23 | ],
24 | ),
25 | CreditData(
26 | role: LeitmotifLocalizations.of(context).userExpericenceDesignLabel,
27 | names: [
28 | AppLocalizations.of(context).creatorName,
29 | ],
30 | ),
31 | CreditData(
32 | role: LeitmotifLocalizations.of(context).developmentLabel,
33 | names: [
34 | AppLocalizations.of(context).creatorName,
35 | ],
36 | ),
37 | CreditData(
38 | role: AppLocalizations.of(context).inspiredByLabel,
39 | names: [
40 | AppLocalizations.of(context).inspiredByMovieTitle,
41 | ],
42 | ),
43 | ],
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/screens/app_onboarding_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:history_of_me/localization.dart';
3 | import 'package:history_of_me/widgets.dart';
4 | import 'package:leitmotif/leitmotif.dart';
5 |
6 | /// A `screen` widget returning a customized [LitOnboardingScreen].
7 | ///
8 | /// The screen will display all main features of `History of Me` on a card
9 | /// view.
10 | class AppOnboardingScreen extends StatefulWidget {
11 | final void Function()? onDismiss;
12 | const AppOnboardingScreen({
13 | Key? key,
14 | this.onDismiss,
15 | }) : super(key: key);
16 | @override
17 | _AppOnboardingScreenState createState() => _AppOnboardingScreenState();
18 | }
19 |
20 | class _AppOnboardingScreenState extends State {
21 | /// Returns customized localizations for the onboarding screen.
22 | LitOnboardingScreenLocalization get localizations =>
23 | LitOnboardingScreenLocalization(
24 | title: LeitmotifLocalizations.of(context).onboardingLabel,
25 | nextLabel: LeitmotifLocalizations.of(context).nextLabel,
26 | dismissLabel: AppLocalizations.of(context).continueLabel,
27 | );
28 |
29 | /// Handles the `dismiss` action.
30 | void _onDismiss() {
31 | if (widget.onDismiss != null) {
32 | widget.onDismiss!();
33 | } else {
34 | LitRouteController(context).pop();
35 | }
36 | }
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | return LitOnboardingScreen(
41 | localization: localizations,
42 | showButtonIcon: false,
43 | art: Padding(
44 | padding: const EdgeInsets.symmetric(vertical: 16.0),
45 | child: SizedBox(
46 | width: MediaQuery.of(context).size.width * 0.55,
47 | child: LayoutBuilder(
48 | builder: (context, constraints) {
49 | return Row(
50 | crossAxisAlignment: CrossAxisAlignment.start,
51 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
52 | children: [
53 | HistoryOfMeAppLogo(
54 | width: constraints.maxWidth * 0.25,
55 | showKeyImage: true,
56 | color: Colors.white,
57 | ),
58 | AppArtwork(
59 | width: constraints.maxWidth * 0.65,
60 | ),
61 | ],
62 | );
63 | },
64 | ),
65 | ),
66 | ),
67 | textItems: [
68 | TextPageContent(
69 | subtitle: AppLocalizations.of(context).organizeLabel,
70 | title: AppLocalizations.of(context).browseDiaryTitle,
71 | text: AppLocalizations.of(context).browseDiaryDescr,
72 | ),
73 | TextPageContent(
74 | subtitle: AppLocalizations.of(context).reliveLabel,
75 | title: AppLocalizations.of(context).readDiaryTitle,
76 | text: AppLocalizations.of(context).readDiaryDescr,
77 | ),
78 | TextPageContent(
79 | subtitle: AppLocalizations.of(context).personalizeLabel,
80 | title: AppLocalizations.of(context).customizeBookmarkTitle,
81 | text: AppLocalizations.of(context).customizeBookmarkDescr,
82 | ),
83 | TextPageContent(
84 | subtitle: AppLocalizations.of(context).privateLabel,
85 | title: LeitmotifLocalizations.of(context).privacyLabel,
86 | text: AppLocalizations.of(context).privacyDescr,
87 | ),
88 | ],
89 | onDismiss: _onDismiss,
90 | );
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/lib/screens/app_privacy_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:history_of_me/localization.dart';
3 | import 'package:history_of_me/widgets.dart';
4 | import 'package:leitmotif/leitmotif.dart';
5 |
6 | class AppPrivacyScreen extends StatelessWidget {
7 | const AppPrivacyScreen({Key? key}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return LitPrivacyPolicyScreen(
12 | onAgreeCallback: () => LitRouteController(context).pop(),
13 | privacyBody: AppLocalizations.of(context).privacyDescr,
14 | art: AppLauncherIconArt(),
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/screens/home_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:history_of_me/api.dart';
3 | import 'package:history_of_me/localization.dart';
4 | import 'package:history_of_me/models.dart';
5 | import 'package:history_of_me/screens.dart';
6 | import 'package:history_of_me/widgets.dart';
7 |
8 | import 'package:leitmotif/leitmotif.dart';
9 |
10 | /// The app's home screen widget allowing to navigate between multiple tabs.
11 | ///
12 | /// Accessing and creating (if required) the [AppSettings] will be handled
13 | /// within the app.
14 | class HomeScreen extends StatefulWidget {
15 | /// The bookmark animation's [Duration].
16 | final Duration bookmarkAnimationDuration;
17 |
18 | /// Creates a [HomeScreen].
19 | ///
20 | /// * [bookmarkAnimationDuration] will determine the bookmark's animation
21 | /// duration on each tab.
22 | const HomeScreen({
23 | Key? key,
24 | this.bookmarkAnimationDuration = const Duration(milliseconds: 5000),
25 | }) : super(key: key);
26 | @override
27 | _HomeScreenState createState() => _HomeScreenState();
28 | }
29 |
30 | class _HomeScreenState extends State with TickerProviderStateMixin {
31 | late AnimationController _bookmarkAnimation;
32 |
33 | late AppAPI _api;
34 |
35 | /// Persists the `tabIndex` on the the corresponding [AppSettings] instance.
36 | void _onTabSwitch(int tabIndex, AppSettings appSettings) {
37 | _api.updateTabIndex(appSettings, tabIndex);
38 | }
39 |
40 | @override
41 | void initState() {
42 | super.initState();
43 | _api = AppAPI();
44 | _bookmarkAnimation = AnimationController(
45 | duration: widget.bookmarkAnimationDuration,
46 | vsync: this,
47 | )..repeat(reverse: true);
48 | }
49 |
50 | @override
51 | void dispose() {
52 | _bookmarkAnimation.dispose();
53 | super.dispose();
54 | }
55 |
56 | @override
57 | Widget build(BuildContext context) {
58 | return AppSettingsProvider(
59 | api: _api,
60 | validator: DatabaseStateValidator(api: _api),
61 | builder: (BuildContext context, AppSettings appSettings) {
62 | return LitTabView(
63 | materialAppBar: AppBar(
64 | centerTitle: true,
65 | title: Text(
66 | AppLocalizations.of(context).greetingLabel,
67 | textAlign: TextAlign.center,
68 | style: LitSansSerifStyles.subtitle2.copyWith(
69 | fontWeight: FontWeight.bold,
70 | ),
71 | ),
72 | ),
73 | materialDrawer: HomeScreenDrawer(appSettings: appSettings),
74 | initialTabIndex: appSettings.tabIndex,
75 | transitionListener: (index) => _onTabSwitch(index, appSettings),
76 | tabs: [
77 | LitNavigableTab(
78 | tabData: LitBottomNavigationItemData(
79 | index: 0,
80 | icon: LitIcons.home_alt,
81 | iconAlt: LitIcons.home,
82 | title: AppLocalizations.of(context).homeLabel,
83 | ),
84 | screen: DiaryScreen(
85 | bookmarkAnimation: _bookmarkAnimation,
86 | appSettings: appSettings,
87 | ),
88 | ),
89 | LitNavigableTab(
90 | tabData: LitBottomNavigationItemData(
91 | index: 1,
92 | icon: LitIcons.person,
93 | iconAlt: LitIcons.person_solid,
94 | title: AppLocalizations.of(context).profileLabel,
95 | ),
96 | screen: ProfileScreen(
97 | bookmarkAnimation: _bookmarkAnimation,
98 | ),
99 | ),
100 | ],
101 | );
102 | },
103 | );
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/lib/screens/restore_diary_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:history_of_me/localization.dart';
3 | import 'package:history_of_me/screens.dart';
4 | import 'package:history_of_me/styles.dart';
5 | import 'package:history_of_me/widgets.dart';
6 | import 'package:leitmotif/leitmotif.dart';
7 |
8 | /// A Flutter `screen` widget allowing to restore an existing diary by reading
9 | /// its backup file or to create a new diary (if restoring the diary is not
10 | /// possible).
11 | ///
12 | class RestoreDiaryScreen extends StatefulWidget {
13 | /// Handles the creation of a new diary.
14 | final void Function() onCreateNewInstance;
15 |
16 | /// Creates a [RestoreDiaryScreen].
17 | const RestoreDiaryScreen({
18 | Key? key,
19 | required this.onCreateNewInstance,
20 | }) : super(key: key);
21 |
22 | @override
23 | _RestoreDiaryScreenState createState() => _RestoreDiaryScreenState();
24 | }
25 |
26 | class _RestoreDiaryScreenState extends State {
27 | late LitRouteController _routeController;
28 | late ScrollController _scrollController;
29 |
30 | /// Handles the `restore` action by navigating to the [SelectBackupScreen].
31 | void onRestoreBackup() {
32 | _routeController.pushCupertinoWidget(
33 | SelectBackupScreen(
34 | onCreateNewInstance: widget.onCreateNewInstance,
35 | ),
36 | );
37 | }
38 |
39 | @override
40 | void initState() {
41 | _routeController = LitRouteController(context);
42 | _scrollController = ScrollController();
43 | super.initState();
44 | }
45 |
46 | @override
47 | Widget build(BuildContext context) {
48 | return LitScaffold(
49 | appBar: FixedOnScrollTitledAppbar(
50 | scrollController: _scrollController,
51 | title: AppLocalizations.of(context).newDiaryTitle,
52 | ),
53 | body: Container(
54 | height: MediaQuery.of(context).size.height,
55 | width: MediaQuery.of(context).size.width,
56 | decoration: BoxDecoration(
57 | gradient: LitGradients.greyGradient,
58 | ),
59 | child: ScrollableColumn(
60 | controller: _scrollController,
61 | crossAxisAlignment: CrossAxisAlignment.start,
62 | padding: const EdgeInsets.symmetric(
63 | vertical: 16.0,
64 | horizontal: 16.0,
65 | ),
66 | children: [
67 | LitScreenTitle(
68 | title: AppLocalizations.of(context).newDiaryTitle,
69 | subtitle: AppLocalizations.of(context).newDiarySubtitle,
70 | ),
71 | Padding(
72 | padding: const EdgeInsets.symmetric(
73 | vertical: 16.0,
74 | ),
75 | child: Column(
76 | children: [
77 | CreateNewActionCard(
78 | onCreate: widget.onCreateNewInstance,
79 | ),
80 | SizedBox(height: 24.0),
81 | _RestoreBackupActionCard(
82 | onRestore: onRestoreBackup,
83 | ),
84 | ],
85 | ),
86 | )
87 | ],
88 | ),
89 | ),
90 | );
91 | }
92 | }
93 |
94 | class _RestoreBackupActionCard extends StatelessWidget {
95 | final void Function() onRestore;
96 |
97 | const _RestoreBackupActionCard({
98 | Key? key,
99 | required this.onRestore,
100 | }) : super(key: key);
101 |
102 | @override
103 | Widget build(BuildContext context) {
104 | return LitTitledActionCard(
105 | title: AppLocalizations.of(context).restoreDiaryTitle,
106 | subtitle: AppLocalizations.of(context).continueJourneyTitle,
107 | actionButtonData: [
108 | ActionButtonData(
109 | title: LeitmotifLocalizations.of(context).restoreLabel,
110 | onPressed: onRestore,
111 | backgroundColor: AppColors.pastelGreen,
112 | accentColor: AppColors.pastelBlue,
113 | ),
114 | ],
115 | );
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/lib/screens/splash_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:history_of_me/widgets.dart';
3 | import 'package:leitmotif/leitmotif.dart';
4 |
5 | /// A screen widget to display a minimalist HistoryOfMe logo while the
6 | /// application being loaded.
7 | class SplashScreen extends StatelessWidget {
8 | /// Creates a [SplashScreen].
9 | const SplashScreen();
10 | @override
11 | Widget build(BuildContext context) {
12 | return LitStaticLoadingScreen(
13 | child: HistoryOfMeAppLogo(
14 | color: LitColors.grey350,
15 | showKeyImage: false,
16 | ),
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/lib/static.dart:
--------------------------------------------------------------------------------
1 | /// Classes containing static and default values which will be constant during
2 | /// the entire runtime.
3 | ///
4 | /// To use, import `package:history_of_me/static.dart`.
5 | library static;
6 |
7 | part 'static/app_assets.dart';
8 |
--------------------------------------------------------------------------------
/lib/static/app_assets.dart:
--------------------------------------------------------------------------------
1 | part of static;
2 |
3 | /// A `History of Me` static class containg paths to the app's assets.
4 | class AppAssets {
5 | static const String _assetImagesRoot = "assets/images/";
6 | static const String _iconsRoot = "assets/icon/";
7 |
8 | /// The `History of Me` key logo.
9 | static const keyLogo64px = _assetImagesRoot + "History_Of_Me_Key_64px-01.png";
10 |
11 | /// The `History of Me` key logo.
12 | static const keyLogo256px =
13 | _assetImagesRoot + "History_Of_Me_Key_Icon_256px-01.png";
14 |
15 | static const appIconAdaptive = _iconsRoot + "Launcher_Icon_Adaptive.png";
16 |
17 | static const keyIcon = _assetImagesRoot + "Key.png";
18 |
19 | static const curtainLeftImg = _assetImagesRoot + "Curtain_Left.png";
20 |
21 | static const curtainRightImg = _assetImagesRoot + "Curtain_Right.png";
22 |
23 | static const windowImg = _assetImagesRoot + "Window.png";
24 |
25 | static const cloudImg = _assetImagesRoot + "Cloud.png";
26 |
27 | static const historyOfMeArtworkSmall =
28 | _assetImagesRoot + "History_Of_Me_Window_Artwork_Small.png";
29 | }
30 |
--------------------------------------------------------------------------------
/lib/styles.dart:
--------------------------------------------------------------------------------
1 | /// A collection of styling components and elements applied on History of me.
2 | ///
3 | /// To use, import `package:history_of_me/styles.dart`
4 |
5 | library styles;
6 |
7 | import 'package:flutter/material.dart';
8 |
9 | part 'styles/app_colors.dart';
10 |
--------------------------------------------------------------------------------
/lib/styles/app_colors.dart:
--------------------------------------------------------------------------------
1 | part of styles;
2 |
3 | /// A collection of [Color] objects used in `History of Me`.
4 | class AppColors {
5 | /// A purple `Color`.
6 | static const Color purple = const Color(0xFFDE8FFA);
7 |
8 | /// A pink `Color`.
9 | static const Color pink = const Color(0xFFFA72AA);
10 |
11 | /// A pastel blue `Color`.
12 | static const Color pastelBlue = const Color(0xFFD7ECF4);
13 |
14 | /// A pastel green `Color`.
15 | static const Color pastelGreen = const Color(0xFFC7EBD3);
16 |
17 | /// A pastel pink `Color`.
18 | static const Color pastelPink = const Color(0xFFEBC7CF);
19 |
20 | /// A pastel purple `Color`.
21 | static const Color pastelPurple = const Color(0xFFDFD7F4);
22 | }
23 |
--------------------------------------------------------------------------------
/lib/widgets.dart:
--------------------------------------------------------------------------------
1 | /// Widgets implementing various user interface components of History of me.
2 | ///
3 | /// To use, import `package:history_of_me/widgets.dart`.
4 | library widgets;
5 |
6 | import 'dart:convert';
7 | import 'dart:io';
8 | import 'dart:math';
9 | import 'dart:ui' as ui;
10 |
11 | import 'package:date_colors/date_colors.dart';
12 | import 'package:flutter/material.dart';
13 | import 'package:history_of_me/api.dart';
14 | import 'package:history_of_me/app.dart';
15 | import 'package:history_of_me/controllers.dart';
16 | import 'package:history_of_me/extensions.dart';
17 | import 'package:history_of_me/localization.dart';
18 | import 'package:history_of_me/models.dart';
19 | import 'package:history_of_me/screens.dart';
20 | import 'package:history_of_me/static.dart';
21 | import 'package:history_of_me/styles.dart';
22 | import 'package:leitmotif/leitmotif.dart';
23 | import 'package:lit_backup_service/lit_backup_service.dart';
24 | import 'package:lit_relative_date_time/lit_relative_date_time.dart';
25 | import 'package:package_info_plus/package_info_plus.dart';
26 | import 'package:path_provider/path_provider.dart';
27 |
28 | part 'widgets/animated_updated_label.dart';
29 | part 'widgets/app_artwork.dart';
30 | part 'widgets/bookmark_back.dart';
31 | part 'widgets/bookmark_back_preview.dart';
32 | part 'widgets/bookmark_container.dart';
33 | part 'widgets/bookmark_cover.dart';
34 | part 'widgets/bookmark_design.dart';
35 | part 'widgets/bookmark_front.dart';
36 | part 'widgets/bookmark_front_preview.dart';
37 | part 'widgets/bookmark_page_view.dart';
38 | part 'widgets/bookmark_preview_container.dart';
39 | part 'widgets/bookmark_title.dart';
40 | part 'widgets/cancel_restoring_dialog.dart';
41 | part 'widgets/change_name_dialog.dart';
42 | part 'widgets/clean_text_field.dart';
43 | part 'widgets/create_entry_dialog.dart';
44 | part 'widgets/create_new_diary_action_card.dart';
45 | part 'widgets/database_state_screen_builder.dart';
46 | part 'widgets/deletable_container.dart';
47 | part 'widgets/delete_all_photos_dialog.dart';
48 | part 'widgets/diary_backup_dialog.dart';
49 | part 'widgets/diary_bookmark_header.dart';
50 | part 'widgets/diary_filter_header.dart';
51 | part 'widgets/diary_filter_header_delegate.dart';
52 | part 'widgets/diary_list_tile.dart';
53 | part 'widgets/diary_list_view.dart';
54 | part 'widgets/diary_preview_card.dart';
55 | part 'widgets/dotted_design.dart';
56 | part 'widgets/editable_item_meta_info.dart';
57 | part 'widgets/ellipse_icon.dart';
58 | part 'widgets/entry_detail_backdrop.dart';
59 | part 'widgets/entry_detail_card.dart';
60 | part 'widgets/greetings_bar.dart';
61 | part 'widgets/history_of_me_app_logo.dart';
62 | part 'widgets/launcher_icon_art.dart';
63 | part 'widgets/home_screen_drawer.dart';
64 | part 'widgets/image_preview_dialog.dart';
65 | part 'widgets/lit_toggle_button_group.dart';
66 | part 'widgets/pattern_config_card.dart';
67 | part 'widgets/photos_missing_dialog.dart';
68 | part 'widgets/pick_photos_button.dart';
69 | part 'widgets/primary_color_selector_card.dart';
70 | part 'widgets/purple_pink_button.dart';
71 | part 'widgets/purple_pink_save_button.dart';
72 | part 'widgets/quote_card.dart';
73 | part 'widgets/secondary_color_selector_card.dart';
74 | part 'widgets/selectable_color_tile.dart';
75 | part 'widgets/selected_create_tile.dart';
76 | part 'widgets/statistics_card.dart';
77 | part 'widgets/striped_design.dart';
78 | part 'widgets/unselected_create_tile.dart';
79 | part 'widgets/unsupported_file_dialog.dart';
80 | part 'widgets/updated_label_text.dart';
81 | part 'widgets/user_icon.dart';
82 | part 'widgets/user_profile_card.dart';
83 | part 'widgets/word_count_badge.dart';
84 | part 'widgets/app_about_dialog.dart';
85 | part 'widgets/diary_entry_bottom_sheet.dart';
86 |
--------------------------------------------------------------------------------
/lib/widgets/animated_updated_label.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class AnimatedUpdatedLabel extends StatefulWidget {
4 | final int? lastUpdateTimestamp;
5 | final EdgeInsets padding;
6 |
7 | const AnimatedUpdatedLabel({
8 | Key? key,
9 | required this.lastUpdateTimestamp,
10 | this.padding = const EdgeInsets.symmetric(
11 | vertical: 8.0,
12 | horizontal: 16.0,
13 | ),
14 | }) : super(key: key);
15 |
16 | @override
17 | _AnimatedUpdatedLabelState createState() => _AnimatedUpdatedLabelState();
18 | }
19 |
20 | class _AnimatedUpdatedLabelState extends State
21 | with TickerProviderStateMixin {
22 | late AnimationController _animationController;
23 |
24 | @override
25 | void initState() {
26 | super.initState();
27 | _animationController = AnimationController(
28 | duration: Duration(milliseconds: 1000),
29 | vsync: this,
30 | );
31 | _animationController.repeat(reverse: true);
32 | }
33 |
34 | @override
35 | void dispose() {
36 | _animationController.dispose();
37 | super.dispose();
38 | }
39 |
40 | @override
41 | Widget build(BuildContext context) {
42 | return AnimatedBuilder(
43 | animation: _animationController,
44 | builder: (BuildContext context, Widget? _) {
45 | return Padding(
46 | padding: widget.padding,
47 | child: Align(
48 | alignment: Alignment.centerLeft,
49 | child: AnimatedOpacity(
50 | opacity: 0.35 + (0.65 * _animationController.value),
51 | duration: _animationController.duration!,
52 | child: UpdatedLabelText(
53 | lastUpdateTimestamp: widget.lastUpdateTimestamp,
54 | ),
55 | ),
56 | ),
57 | );
58 | },
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/widgets/app_about_dialog.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A customized [LitAboutDialog] widget displaying the app's about dialog.
4 | class AppAboutDialog extends StatelessWidget {
5 | /// Creates a [AppAboutDialog].
6 | const AppAboutDialog({Key? key}) : super(key: key);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return LitAboutDialog(
11 | title: LeitmotifLocalizations.of(context).aboutAppLabel,
12 | appName: App.appName,
13 | art: AppLauncherIconArt(
14 | boxShadow: LitBoxShadows.sm,
15 | ),
16 | infoDescription: AppLocalizations.of(context).aboutAppDescr,
17 | copyrightNotice: AppLocalizations.of(context).copyrightNotice,
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/widgets/app_artwork.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class AppArtwork extends StatefulWidget {
4 | final double width;
5 |
6 | const AppArtwork({
7 | Key? key,
8 | this.width = 280.0,
9 | }) : super(key: key);
10 |
11 | @override
12 | _AppArtworkState createState() => _AppArtworkState();
13 | }
14 |
15 | class _AppArtworkState extends State with TickerProviderStateMixin {
16 | late AnimationController _animationController;
17 |
18 | @override
19 | void initState() {
20 | super.initState();
21 | _animationController = AnimationController(
22 | duration: Duration(milliseconds: 1500),
23 | vsync: this,
24 | );
25 | _animationController.repeat(reverse: true);
26 | }
27 |
28 | @override
29 | void dispose() {
30 | _animationController.dispose();
31 | super.dispose();
32 | }
33 |
34 | @override
35 | Widget build(BuildContext context) {
36 | return SizedBox(
37 | width: widget.width,
38 | child: Stack(
39 | //alignment: Alignment.center,
40 | children: [
41 | Image(
42 | image: AssetImage(
43 | "assets/images/Window.png",
44 | ),
45 | fit: BoxFit.scaleDown,
46 |
47 | //color: Colors.black,
48 | ),
49 | AnimatedBuilder(
50 | animation: _animationController,
51 | builder: (context, _) {
52 | return Align(
53 | alignment: Alignment.topCenter,
54 | child: SizedBox(
55 | width: widget.width * 0.4,
56 | child: Transform(
57 | transform: Matrix4.translationValues(
58 | 0.0,
59 | (widget.width * 0.306) +
60 | (-10.0 + (20.0 * _animationController.value)),
61 | 0.0,
62 | ),
63 | child: Image(
64 | image: AssetImage(
65 | "assets/images/Cloud.png",
66 | ),
67 | fit: BoxFit.scaleDown,
68 |
69 | //color: Colors.black,
70 | ),
71 | ),
72 | ),
73 | );
74 | })
75 | ],
76 | ),
77 | );
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/widgets/bookmark_back_preview.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class BookmarkBackPreview extends StatelessWidget {
4 | final EdgeInsets padding;
5 | final bool transformed;
6 | final UserData? userData;
7 | final AnimationController? animationController;
8 | const BookmarkBackPreview({
9 | Key? key,
10 | required this.userData,
11 | this.padding = const EdgeInsets.only(
12 | left: 16.0,
13 | right: 16.0,
14 | top: 30.0,
15 | bottom: 30.0,
16 | ),
17 | this.transformed = true,
18 | this.animationController,
19 | }) : super(key: key);
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return BookmarkPreviewContainer(
24 | transformed: transformed,
25 | reveredAnimation: true,
26 | animationController: animationController,
27 | padding: padding,
28 | child: BookmarkBack(
29 | userData: userData,
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/widgets/bookmark_container.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class BookmarkFittedBox extends StatelessWidget {
4 | final double maxWidth;
5 | final Widget child;
6 | final BoxDecoration boxDecoration;
7 | const BookmarkFittedBox({
8 | Key? key,
9 | required this.maxWidth,
10 | required this.child,
11 | this.boxDecoration = const BoxDecoration(
12 | boxShadow: [
13 | BoxShadow(
14 | color: Colors.black26,
15 | offset: Offset(-3.0, 3.0),
16 | blurRadius: 8.0,
17 | spreadRadius: 1.0,
18 | )
19 | ],
20 | ),
21 | }) : super(key: key);
22 | @override
23 | Widget build(BuildContext context) {
24 | return SizedBox(
25 | width: maxWidth,
26 | child: AspectRatio(
27 | aspectRatio: BookmarkCover.aspectRatio,
28 | child: Container(
29 | decoration: boxDecoration,
30 | child: child,
31 | ),
32 | ),
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/widgets/bookmark_cover.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | abstract class BookmarkCover extends Widget {
4 | static const Size dimensions = const Size(8.5, 2.5);
5 | static double get aspectRatio => dimensions.aspectRatio;
6 | static double get height => dimensions.height;
7 | static double get width => dimensions.width;
8 | }
9 |
--------------------------------------------------------------------------------
/lib/widgets/bookmark_design.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | abstract class BookmarkDesign extends Widget {}
4 |
5 | enum DesignType {
6 | stiped,
7 | dotted,
8 | }
9 |
--------------------------------------------------------------------------------
/lib/widgets/bookmark_front.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class BookmarkFront extends StatelessWidget implements BookmarkCover {
4 | final UserData userData;
5 | final double maxWidth;
6 | final double radius;
7 | const BookmarkFront({
8 | Key? key,
9 | required this.userData,
10 | this.maxWidth = 400.0,
11 | this.radius = 6.0,
12 | }) : super(key: key);
13 | @override
14 | Widget build(BuildContext context) {
15 | return BookmarkFittedBox(
16 | maxWidth: maxWidth,
17 | child: Stack(
18 | children: [
19 | _BookmarkFrontArt(
20 | userData: userData,
21 | radius: radius,
22 | ),
23 | BookmarkTitle(userData: userData),
24 | ],
25 | ),
26 | );
27 | }
28 | }
29 |
30 | class _BookmarkFrontArt extends StatelessWidget {
31 | final UserData userData;
32 | final double radius;
33 | const _BookmarkFrontArt({
34 | Key? key,
35 | required this.userData,
36 | required this.radius,
37 | }) : super(key: key);
38 |
39 | /// Returns the current design type selected by the user.
40 | DesignType get type => DesignType.values[userData.designPatternIndex];
41 |
42 | /// Returns the appropriate design implementation based on the [type] value.
43 | Widget get design {
44 | switch (type) {
45 | case DesignType.stiped:
46 | return StripedDesign(
47 | radius: radius,
48 | userData: userData,
49 | );
50 | case DesignType.dotted:
51 | return DottedDesign(
52 | radius: radius,
53 | userData: userData,
54 | );
55 | default:
56 | return StripedDesign(
57 | radius: radius,
58 | userData: userData,
59 | );
60 | }
61 | }
62 |
63 | Widget build(BuildContext context) {
64 | return Stack(
65 | children: [
66 | Container(
67 | decoration: BoxDecoration(
68 | borderRadius: BorderRadius.all(
69 | Radius.circular(
70 | radius,
71 | ),
72 | ),
73 | ),
74 | ),
75 | design
76 | ],
77 | );
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/widgets/bookmark_front_preview.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class BookmarkFrontPreview extends StatelessWidget {
4 | final EdgeInsets padding;
5 | final bool transformed;
6 | final UserData userData;
7 | final AnimationController? animationController;
8 | const BookmarkFrontPreview({
9 | Key? key,
10 | required this.userData,
11 | this.padding = const EdgeInsets.only(
12 | left: 16.0,
13 | right: 16.0,
14 | top: 30.0,
15 | bottom: 30.0,
16 | ),
17 | this.transformed = true,
18 | this.animationController,
19 | }) : super(key: key);
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return BookmarkPreviewContainer(
24 | transformed: transformed,
25 | animationController: animationController,
26 | padding: padding,
27 | child: BookmarkFront(
28 | userData: userData,
29 | ),
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/widgets/bookmark_page_view.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A History of Me widget displaying the bookmark's front and back on a page
4 | /// view.
5 | class BookmarkPageView extends StatelessWidget {
6 | /// The data containing the bookmark configuration.
7 | final UserData userData;
8 |
9 | /// The bookmark's animation.
10 | final AnimationController animationController;
11 |
12 | /// The page view's total height.
13 | final double height;
14 |
15 | final EdgeInsets padding;
16 |
17 | /// Creates a [BookmarkPageView].
18 | const BookmarkPageView({
19 | Key? key,
20 | required this.userData,
21 | required this.animationController,
22 | this.height = 180.0,
23 | this.padding = LitEdgeInsets.screen,
24 | }) : super(key: key);
25 |
26 | @override
27 | Widget build(BuildContext context) {
28 | return IndexedPageView(
29 | height: height,
30 | indicatorSpacingTop: 0.0,
31 | tapToNavigate: true,
32 | children: [
33 | BookmarkFrontPreview(
34 | userData: userData,
35 | animationController: animationController,
36 | padding: padding,
37 | ),
38 | BookmarkBackPreview(
39 | userData: userData,
40 | animationController: animationController,
41 | padding: padding,
42 | ),
43 | ],
44 | indicatorColor: LitColors.grey400,
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/lib/widgets/bookmark_preview_container.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class BookmarkPreviewContainer extends StatefulWidget {
4 | final bool transformed;
5 | final bool reveredAnimation;
6 | final EdgeInsets padding;
7 | final BookmarkCover child;
8 | final AnimationController? animationController;
9 | const BookmarkPreviewContainer({
10 | Key? key,
11 | required this.transformed,
12 | this.reveredAnimation = false,
13 | required this.padding,
14 | required this.child,
15 | this.animationController,
16 | }) : super(key: key);
17 | @override
18 | _BookmarkPreviewContainerState createState() =>
19 | _BookmarkPreviewContainerState();
20 | }
21 |
22 | class _BookmarkPreviewContainerState extends State
23 | with TickerProviderStateMixin {
24 | //late AnimationController _animationController;
25 |
26 | // @override
27 | // void initState() {
28 | // super.initState();
29 | // _animationController = AnimationController(
30 | // duration: Duration(milliseconds: 5000),
31 | // vsync: this,
32 | // );
33 | // if (widget.transformed) {
34 | // _animationController.repeat(reverse: true);
35 | // }
36 | // }
37 |
38 | // @override
39 | // void dispose() {
40 | // _animationController.dispose();
41 | // super.dispose();
42 | // }
43 | //
44 | //
45 |
46 | double get _animationValue {
47 | return widget.reveredAnimation
48 | ? (1.0 - widget.animationController!.value)
49 | : (widget.animationController!.value);
50 | }
51 |
52 | double get _rotation {
53 | return (2 * pi / 360) * 3 * (_animationValue);
54 | }
55 |
56 | Matrix4 get _transform {
57 | return Matrix4.translationValues(
58 | -20 + (20 * _animationValue), -20 + (20 * _animationValue), 0);
59 | }
60 |
61 | @override
62 | Widget build(BuildContext context) {
63 | return Padding(
64 | padding: widget.padding,
65 | child: widget.animationController != null
66 | ? AnimatedBuilder(
67 | animation: widget.animationController!,
68 | builder: (context, _) {
69 | return Transform.rotate(
70 | angle: _rotation,
71 | child: Transform(
72 | transform: widget.transformed
73 | ? _transform
74 | : Matrix4.translationValues(0, 0, 0),
75 | child: _Bookmark(
76 | child: widget.child,
77 | ),
78 | //UserBookmark(userData: ud)
79 | ),
80 | );
81 | })
82 | : _Bookmark(
83 | child: widget.child,
84 | ),
85 | );
86 | }
87 | }
88 |
89 | class _Bookmark extends StatelessWidget {
90 | final Widget child;
91 |
92 | const _Bookmark({
93 | Key? key,
94 | required this.child,
95 | }) : super(key: key);
96 | @override
97 | Widget build(BuildContext context) {
98 | return Stack(alignment: Alignment.center, children: [child]);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/lib/widgets/bookmark_title.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A History of Me widget displaying the bookmark's title, which is derived
4 | /// by the provided username.
5 | class BookmarkTitle extends StatelessWidget {
6 | /// Creates a [BookmarkTitle].
7 |
8 | /// The [UserData] containing the username.
9 | final UserData userData;
10 |
11 | /// Specifies the surrounding [BorderRadius].
12 | final BorderRadiusGeometry borderRadius;
13 |
14 | /// Specifies the alignment on the bookmark.
15 | final Alignment alignment;
16 |
17 | const BookmarkTitle({
18 | Key? key,
19 | required this.userData,
20 | this.borderRadius = const BorderRadius.only(
21 | topLeft: Radius.circular(6.0),
22 | bottomLeft: Radius.circular(6.0),
23 | ),
24 | this.alignment = Alignment.centerLeft,
25 | }) : super(key: key);
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return LayoutBuilder(
30 | builder: (context, constraints) {
31 | return Align(
32 | alignment: alignment,
33 | child: AspectRatio(
34 | aspectRatio: 3 / 4,
35 | child: Container(
36 | decoration: BoxDecoration(
37 | color: LitColors.grey120,
38 | borderRadius: borderRadius,
39 | ),
40 | child: Stack(
41 | children: [
42 | _KeyIcon(constraints: constraints),
43 | _HistoryOfLabel(userData: userData),
44 | ],
45 | ),
46 | ),
47 | ),
48 | );
49 | },
50 | );
51 | }
52 | }
53 |
54 | /// A History of Me widget displaying the `History Of Me`'s key icon.
55 | class _KeyIcon extends StatelessWidget {
56 | final BoxConstraints constraints;
57 |
58 | /// Creates a [_KeyIcon].
59 | const _KeyIcon({
60 | Key? key,
61 | required this.constraints,
62 | }) : super(key: key);
63 |
64 | @override
65 | Widget build(BuildContext context) {
66 | return Padding(
67 | padding: const EdgeInsets.only(
68 | left: 4.0,
69 | ),
70 | child: Align(
71 | alignment: Alignment.center,
72 | child: Image(
73 | color: LitColors.grey150,
74 | image: AssetImage(
75 | AppAssets.keyLogo64px,
76 | ),
77 | fit: BoxFit.fitHeight,
78 | //color: Colors.black,
79 | height: constraints.maxHeight * 0.8,
80 | ),
81 | ),
82 | );
83 | }
84 | }
85 |
86 | /// A History of Me widget displaying a stylized `History of` label.
87 | class _HistoryOfLabel extends StatelessWidget {
88 | final UserData userData;
89 |
90 | /// Creates a [_HistoryOfLabel].
91 | const _HistoryOfLabel({
92 | Key? key,
93 | required this.userData,
94 | }) : super(key: key);
95 |
96 | /// A constant string stating `History` due to it being the same on all
97 | /// locales.
98 | static const historyLabel = "History";
99 |
100 | /// A constant string stating `of` due to it being the same on all
101 | /// locales.
102 | static const ofLabel = "of";
103 |
104 | @override
105 | Widget build(BuildContext context) {
106 | return RotatedBox(
107 | quarterTurns: 1,
108 | child: Align(
109 | alignment: Alignment.centerRight,
110 | child: Column(
111 | mainAxisSize: MainAxisSize.min,
112 | children: [
113 | Row(
114 | mainAxisAlignment: MainAxisAlignment.center,
115 | children: [
116 | RotatedBox(
117 | quarterTurns: 3,
118 | child: ScaledDownText(
119 | ofLabel,
120 | style: LitSerifStyles.caption.copyWith(
121 | fontSize: 8.0,
122 | ),
123 | textAlign: TextAlign.center,
124 | ),
125 | ),
126 | ScaledDownText(
127 | historyLabel,
128 | style: LitSerifStyles.caption.copyWith(
129 | fontSize: 8.0,
130 | ),
131 | textAlign: TextAlign.center,
132 | ),
133 | ],
134 | ),
135 | Padding(
136 | padding: const EdgeInsets.symmetric(
137 | horizontal: 8.0,
138 | ),
139 | child: ClippedText(
140 | userData.name,
141 | style: LitSerifStyles.subtitle2.copyWith(
142 | fontSize: 11.0,
143 | ),
144 | textAlign: TextAlign.center,
145 | ),
146 | ),
147 | ],
148 | ),
149 | ),
150 | );
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/lib/widgets/cancel_restoring_dialog.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A Flutter widget allowing the user to confirm the creation of a new diary
4 | /// while canceling the restoring of a backuped diary.
5 | ///
6 | class CancelRestoringDialog extends StatelessWidget {
7 | /// Handles the `cancel` action.
8 | final void Function() onCancel;
9 |
10 | /// Creates a [CancelRestoringDialog].
11 | const CancelRestoringDialog({
12 | Key? key,
13 | required this.onCancel,
14 | }) : super(key: key);
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return LitTitledDialog(
19 | child: LitDescriptionTextBox(
20 | padding: const EdgeInsets.symmetric(
21 | horizontal: 16.0,
22 | vertical: 8.0,
23 | ),
24 | text: AppLocalizations.of(context).cancelRestoreDescr,
25 | ),
26 | titleText: LeitmotifLocalizations.of(context).cancelLabel + "?",
27 | actionButtons: [
28 | Padding(
29 | padding: const EdgeInsets.symmetric(horizontal: 8.0),
30 | child: LitPushedThroughButton(
31 | child: Text(
32 | AppLocalizations.of(context).createLabel.toUpperCase(),
33 | style: LitSansSerifStyles.button,
34 | textAlign: TextAlign.center,
35 | ),
36 | onPressed: onCancel,
37 | ),
38 | )
39 | ],
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/widgets/clean_text_field.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A clean [TextField] without border decoration applied.
4 | ///
5 | /// Allows to provide a custom text style.
6 | class CleanTextField extends StatelessWidget {
7 | final FocusNode focusNode;
8 | final TextEditingController controller;
9 | final TextStyle style;
10 | final bool showCounter;
11 | final int? maxLines;
12 | final int? minLines;
13 |
14 | /// Creates a [CleanTextField].
15 | const CleanTextField({
16 | Key? key,
17 | required this.controller,
18 | required this.focusNode,
19 | required this.style,
20 | this.maxLines,
21 | this.minLines,
22 | this.showCounter = false,
23 | }) : super(key: key);
24 |
25 | /// Returns the hint's text style
26 | TextStyle get hintStyle => style.copyWith(color: LitColors.grey200);
27 |
28 | @override
29 | Widget build(BuildContext context) {
30 | return TextField(
31 | controller: controller,
32 | cursorColor: LitColors.grey200,
33 | cursorRadius: Radius.circular(2.0),
34 | decoration: InputDecoration(
35 | border: InputBorder.none,
36 | counter: showCounter ? WordCountBadge(controller: controller) : null,
37 | hintText: AppLocalizations.of(context).hintText + "...",
38 | hintStyle: hintStyle,
39 | ),
40 | focusNode: focusNode,
41 | maxLines: maxLines,
42 | minLines: minLines,
43 | style: style,
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/widgets/create_new_diary_action_card.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A Flutter widget displaying an action card to allow the user to create a
4 | /// new diary.
5 | class CreateNewActionCard extends StatelessWidget {
6 | /// Handles the `create` action.
7 | final void Function() onCreate;
8 |
9 | /// Creates a [CreateNewActionCard].
10 | const CreateNewActionCard({
11 | Key? key,
12 | required this.onCreate,
13 | }) : super(key: key);
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return LitTitledActionCard(
18 | title: AppLocalizations.of(context).newDiaryTitle,
19 | subtitle: AppLocalizations.of(context).startJourneyTitle,
20 | actionButtonData: [
21 | ActionButtonData(
22 | title: AppLocalizations.of(context).createLabel,
23 | onPressed: onCreate,
24 | backgroundColor: AppColors.pastelPink,
25 | accentColor: AppColors.pastelPurple,
26 | ),
27 | ],
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/widgets/delete_all_photos_dialog.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A dialog widgets allowing to confirm the deletion of all photos related to
4 | /// a diary entry. The corresponding diary entry's photo list will be updated
5 | /// using an empty list.
6 | class DeleteAllPhotosDialog extends StatefulWidget {
7 | /// The diary entry, whose photos should be deleted.
8 | final DiaryEntry entry;
9 |
10 | /// Creates a [DeleteAllPhotosDialog].
11 | const DeleteAllPhotosDialog({
12 | Key? key,
13 | required this.entry,
14 | }) : super(key: key);
15 |
16 | @override
17 | State createState() => _DeleteAllPhotosDialogState();
18 | }
19 |
20 | class _DeleteAllPhotosDialogState extends State {
21 | /// The [AppAPI] instance.
22 | final AppAPI _api = AppAPI();
23 |
24 | /// Deletes the photos and closes the dialog.
25 | void _deletePhotos() {
26 | DiaryEntry updated = DiaryEntry(
27 | uid: widget.entry.uid,
28 | date: widget.entry.date,
29 | created: widget.entry.created,
30 | lastUpdated: widget.entry.lastUpdated,
31 | title: widget.entry.title,
32 | content: widget.entry.content,
33 | moodScore: widget.entry.moodScore,
34 | favorite: widget.entry.favorite,
35 | backdropPhotoId: widget.entry.backdropPhotoId,
36 | photos: DefaultData.photos,
37 | visitCount: widget.entry.visitCount,
38 | editCount: widget.entry.editCount,
39 | );
40 | // Update the diary entry to delete all photos.
41 | _api.updateDiaryEntry(updated);
42 | Navigator.of(context).pop();
43 | }
44 |
45 | @override
46 | Widget build(BuildContext context) {
47 | return LitTitledDialog(
48 | titleText: AppLocalizations.of(context).noPhotosSelectedLabel,
49 | margin: LitEdgeInsets.dialogMargin,
50 | child: Column(
51 | children: [
52 | LitDescriptionTextBox(
53 | text: AppLocalizations.of(context).deletePhotosDescr,
54 | ),
55 | SizedBox(height: 4.0),
56 | Text(
57 | AppLocalizations.of(context).deletePhotosActionLabel,
58 | style: LitSansSerifStyles.subtitle2,
59 | ),
60 | ],
61 | ),
62 | actionButtons: [
63 | DialogActionButton(
64 | data: ActionButtonData(
65 | title: LeitmotifLocalizations.of(context).deleteLabel,
66 | backgroundColor: LitColors.red200,
67 | accentColor: LitColors.red200,
68 | onPressed: _deletePhotos,
69 | ),
70 | )
71 | ],
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/lib/widgets/diary_bookmark_header.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class DiaryBookmarkHeader extends StatelessWidget {
4 | final UserData userData;
5 | final AnimationController bookmarkAnimation;
6 | const DiaryBookmarkHeader({
7 | Key? key,
8 | required this.userData,
9 | required this.bookmarkAnimation,
10 | }) : super(key: key);
11 | @override
12 | Widget build(BuildContext context) {
13 | return SliverList(
14 | delegate: SliverChildBuilderDelegate(
15 | (BuildContext context, int index) {
16 | return Column(
17 | children: [
18 | Container(
19 | color: Colors.white,
20 | child: Stack(
21 | children: [
22 | BookmarkPageView(
23 | animationController: bookmarkAnimation,
24 | userData: userData,
25 | padding: const EdgeInsets.only(
26 | top: 0,
27 | bottom: 32.0,
28 | left: 16.0,
29 | right: 16.0,
30 | ),
31 | ),
32 | ],
33 | ),
34 | )
35 | ],
36 | );
37 | },
38 | childCount: 1,
39 | ),
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/widgets/diary_entry_bottom_sheet.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class DiaryEntryBottomSheet extends StatelessWidget {
4 | final String? title;
5 | final void Function() onPressedEdit;
6 | final void Function() onPressedDelete;
7 | const DiaryEntryBottomSheet({
8 | Key? key,
9 | this.title,
10 | required this.onPressedEdit,
11 | required this.onPressedDelete,
12 | }) : super(key: key);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Padding(
17 | padding: const EdgeInsets.symmetric(
18 | horizontal: 16.0,
19 | ),
20 | child: Column(
21 | mainAxisSize: MainAxisSize.min,
22 | children: [
23 | SizedBox(height: 16.0),
24 | ScrollableText(
25 | title ?? AppLocalizations.of(context).optionsLabel.capitalize(),
26 | style: LitSansSerifStyles.h6.copyWith(
27 | color: LitColors.grey380,
28 | ),
29 | ),
30 | Divider(),
31 | SizedBox(
32 | height: 142.0,
33 | child: ListView(
34 | padding: const EdgeInsets.symmetric(
35 | vertical: 16.0,
36 | ),
37 | children: [
38 | LitPushedThroughButton(
39 | child: Row(
40 | mainAxisAlignment: MainAxisAlignment.center,
41 | children: [
42 | Icon(
43 | LitIcons.pencil_alt,
44 | color: LitSansSerifStyles.button.color,
45 | size: LitSansSerifStyles.button.fontSize,
46 | ),
47 | SizedBox(width: 8.0),
48 | ClippedText(
49 | AppLocalizations.of(context).editLabel.toUpperCase(),
50 | textAlign: TextAlign.center,
51 | style: LitSansSerifStyles.button,
52 | ),
53 | ],
54 | ),
55 | accentColor: LitColors.grey100,
56 | onPressed: onPressedEdit,
57 | ),
58 | SizedBox(height: 16.0),
59 | LitDeleteButton(
60 | textAlign: TextAlign.center,
61 | showIcon: true,
62 | onPressed: onPressedDelete,
63 | )
64 | ],
65 | ),
66 | )
67 | ],
68 | ),
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/widgets/diary_filter_header_delegate.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class DiaryFilterHeaderDelegate extends SliverPersistentHeaderDelegate {
4 | DiaryFilterHeaderDelegate(
5 | this.child,
6 | );
7 | final Widget child;
8 |
9 | @override
10 | Widget build(
11 | BuildContext context, double shrinkOffset, bool overlapsContent) {
12 | return child;
13 | }
14 |
15 | @override
16 | double get maxExtent => 52.0;
17 |
18 | @override
19 | double get minExtent => 52.0;
20 |
21 | @override
22 | bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
23 | return false;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/widgets/editable_item_meta_info.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class EditableItemMetaInfo extends StatefulWidget {
4 | final int? lastUpdateTimestamp;
5 | final bool showUnsavedBadge;
6 |
7 | const EditableItemMetaInfo({
8 | Key? key,
9 | required this.lastUpdateTimestamp,
10 | required this.showUnsavedBadge,
11 | }) : super(key: key);
12 |
13 | @override
14 | _EditableItemMetaInfoState createState() => _EditableItemMetaInfoState();
15 | }
16 |
17 | class _EditableItemMetaInfoState extends State
18 | with TickerProviderStateMixin {
19 | AnimationController? _animationController;
20 |
21 | @override
22 | void initState() {
23 | _animationController = AnimationController(
24 | duration: Duration(
25 | milliseconds: 3000,
26 | ),
27 | vsync: this,
28 | );
29 | _animationController!.repeat(reverse: true);
30 | super.initState();
31 | }
32 |
33 | @override
34 | void dispose() {
35 | _animationController!.dispose();
36 | super.dispose();
37 | }
38 |
39 | @override
40 | Widget build(BuildContext context) {
41 | return Row(
42 | mainAxisAlignment: MainAxisAlignment.end,
43 | children: [
44 | widget.showUnsavedBadge
45 | ? _AnimatedUnchangedBadge(
46 | animation: _animationController,
47 | )
48 | : SizedBox(),
49 | AnimatedUpdatedLabel(
50 | lastUpdateTimestamp: widget.lastUpdateTimestamp,
51 | ),
52 | ],
53 | );
54 | }
55 | }
56 |
57 | class _AnimatedUnchangedBadge extends StatelessWidget {
58 | final Animation? animation;
59 |
60 | const _AnimatedUnchangedBadge({
61 | Key? key,
62 | required this.animation,
63 | }) : super(key: key);
64 | @override
65 | Widget build(BuildContext context) {
66 | return ConstrainedBox(
67 | constraints: BoxConstraints(
68 | maxWidth: 86.0,
69 | ),
70 | child: AnimatedBuilder(
71 | animation: animation!,
72 | builder: (context, _) {
73 | return LitTextBadge(
74 | backgroundColor: Color.lerp(LitColors.mediumGrey, Colors.white,
75 | 0.8 * animation!.value) ??
76 | LitColors.mediumGrey,
77 | label: LeitmotifLocalizations.of(context).unsavedLabel.capitalize(),
78 | textColor: Color.lerp(
79 | Colors.white,
80 | LitColors.mediumGrey,
81 | 0.1 + (animation!.value * 0.9),
82 | ) ??
83 | Colors.white,
84 | );
85 | },
86 | ),
87 | );
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/lib/widgets/ellipse_icon.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class EllipseIcon extends StatefulWidget {
4 | final bool animated;
5 | final Axis axis;
6 | final double dotHeight;
7 | final double dotWidth;
8 | final Color dotColor;
9 | final List boxShadow;
10 |
11 | const EllipseIcon(
12 | {Key? key,
13 | this.animated = true,
14 | this.axis = Axis.horizontal,
15 | this.dotHeight = 8.0,
16 | this.dotWidth = 8.0,
17 | this.dotColor = LitColors.mediumGrey,
18 | this.boxShadow = const [
19 | BoxShadow(
20 | blurRadius: 3.0,
21 | color: Colors.black12,
22 | offset: Offset(
23 | 1.0,
24 | 1.0,
25 | ),
26 | spreadRadius: 1.0,
27 | )
28 | ]})
29 | : super(key: key);
30 |
31 | @override
32 | _EllipsisIconState createState() => _EllipsisIconState();
33 | }
34 |
35 | class _EllipsisIconState extends State
36 | with TickerProviderStateMixin {
37 | AnimationController? _animationController;
38 |
39 | @override
40 | void initState() {
41 | super.initState();
42 | _animationController = AnimationController(
43 | duration: Duration(
44 | milliseconds: 590,
45 | ),
46 | vsync: this,
47 | );
48 | if (widget.animated) {
49 | _animationController!.forward();
50 | }
51 | }
52 |
53 | @override
54 | void dispose() {
55 | _animationController!.dispose();
56 | super.dispose();
57 | }
58 |
59 | @override
60 | Widget build(BuildContext context) {
61 | return AnimatedBuilder(
62 | animation: _animationController!,
63 | builder: (context, _) {
64 | final List children = [];
65 | for (int i = 0; i < 3; i++) {
66 | children.add(
67 | _Dot(
68 | animated: widget.animated,
69 | axis: widget.axis,
70 | animationController: _animationController,
71 | height: widget.dotHeight,
72 | width: widget.dotWidth,
73 | color: widget.dotColor,
74 | boxShadow: widget.boxShadow,
75 | ),
76 | );
77 | }
78 | return widget.axis == Axis.horizontal
79 | ? Row(
80 | mainAxisSize: MainAxisSize.min,
81 | children: children,
82 | )
83 | : Column(
84 | mainAxisSize: MainAxisSize.min,
85 | children: children,
86 | );
87 | },
88 | );
89 | }
90 | }
91 |
92 | class _Dot extends StatelessWidget {
93 | final AnimationController? animationController;
94 | final bool animated;
95 | final double height;
96 | final double width;
97 | final Color color;
98 | final List boxShadow;
99 | final Axis axis;
100 | const _Dot({
101 | Key? key,
102 | required this.animationController,
103 | required this.animated,
104 | required this.height,
105 | required this.width,
106 | required this.color,
107 | required this.boxShadow,
108 | required this.axis,
109 | }) : super(key: key);
110 | @override
111 | Widget build(BuildContext context) {
112 | return Transform(
113 | transform: animated
114 | ? Matrix4.translationValues(
115 | -8.0 + 8.0 * (animationController!.value),
116 | 0,
117 | 0,
118 | )
119 | : Matrix4.translationValues(
120 | 0,
121 | 0,
122 | 0,
123 | ),
124 | child: Padding(
125 | padding: const EdgeInsets.symmetric(
126 | horizontal: 2.0,
127 | vertical: 1.0,
128 | ),
129 | child: Container(
130 | height: height,
131 | width: width,
132 | decoration: BoxDecoration(
133 | borderRadius: BorderRadius.circular(4),
134 | color: color,
135 | boxShadow: boxShadow,
136 | ),
137 | ),
138 | ),
139 | );
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/lib/widgets/greetings_bar.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class GreetingsBar extends StatelessWidget {
4 | @override
5 | Widget build(BuildContext context) {
6 | return Container(
7 | width: MediaQuery.of(context).size.width,
8 | height: Scaffold.of(context).appBarMaxHeight,
9 | decoration: BoxDecoration(
10 | color: Colors.white,
11 | boxShadow: [
12 | BoxShadow(
13 | blurRadius: 8.0,
14 | offset: Offset(2, 4),
15 | color: Colors.black,
16 | spreadRadius: 2.0,
17 | )
18 | ],
19 | ),
20 | child: Padding(
21 | padding: const EdgeInsets.symmetric(
22 | vertical: 16.0,
23 | ),
24 | child: Text(
25 | AppLocalizations.of(context).greetingLabel,
26 | textAlign: TextAlign.center,
27 | style: LitSansSerifStyles.subtitle2.copyWith(
28 | fontWeight: FontWeight.bold,
29 | ),
30 | ),
31 | ),
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/widgets/history_of_me_app_logo.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class HistoryOfMeAppLogo extends StatelessWidget {
4 | final double width;
5 | final double height;
6 | final bool showKeyImage;
7 | final Color color;
8 | const HistoryOfMeAppLogo(
9 | {Key? key,
10 | this.width = 100.0,
11 | this.height = 120.0,
12 | this.showKeyImage = false,
13 | this.color = const Color(0xFFadadad)})
14 | : super(key: key);
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return SizedBox(
19 | width: width,
20 | height: height,
21 | child: FittedBox(
22 | fit: BoxFit.scaleDown,
23 | child: Container(
24 | child: Stack(
25 | children: [
26 | Column(
27 | children: [
28 | RotatedBox(
29 | quarterTurns: 3,
30 | child: Text(
31 | "History",
32 | style: TextStyle(
33 | fontFamily: "Playfair Display",
34 | color: color,
35 | fontSize: 24.0,
36 | letterSpacing: 0.0,
37 | fontWeight: FontWeight.w300,
38 | ),
39 | ),
40 | ),
41 | Text(
42 | "of",
43 | style: TextStyle(
44 | fontFamily: "Playfair Display",
45 | color: color,
46 | fontSize: 20.0,
47 | letterSpacing: -0.65,
48 | fontWeight: FontWeight.w500,
49 | ),
50 | ),
51 | ],
52 | ),
53 | Transform(
54 | transform: Matrix4.translationValues(12, 27.5, 0),
55 | child: RotatedBox(
56 | quarterTurns: 3,
57 | child: Text(
58 | "me",
59 | style: LitTextStyles.sansSerif.copyWith(
60 | color: color,
61 | fontSize: 52.0,
62 | letterSpacing: -4.75,
63 | fontWeight: FontWeight.w500,
64 | ),
65 | ),
66 | ),
67 | ),
68 | showKeyImage
69 | ? Transform(
70 | transform: Matrix4.translationValues(58, 57.5, 0),
71 | child: Padding(
72 | padding: const EdgeInsets.symmetric(
73 | horizontal: 2.0,
74 | ),
75 | child: Image(
76 | image: AssetImage(
77 | "assets/images/History_Of_Me_Key_Icon_256px-01.png",
78 | ),
79 | // fit: BoxFit.fitWidth,
80 | color: HexColor('#c9b5b5'),
81 | //color: Colors.black,
82 | height: 46.0,
83 | ),
84 | ),
85 | )
86 | : SizedBox(),
87 | ],
88 | ),
89 | ),
90 | ),
91 | );
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/lib/widgets/image_preview_dialog.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A dialog `widget` displaying a preview of the provided image's path.
4 | class ImagePreviewDialog extends StatefulWidget {
5 | /// The images's path.
6 | final String path;
7 |
8 | /// Creates a [ImagePreviewDialog].
9 | const ImagePreviewDialog({
10 | Key? key,
11 | required this.path,
12 | }) : super(key: key);
13 |
14 | @override
15 | State createState() => _ImagePreviewDialogState();
16 | }
17 |
18 | class _ImagePreviewDialogState extends State {
19 | /// Pops the dialog from the navigation stack.
20 | void _onTap() {
21 | Navigator.of(context).pop();
22 | }
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | return Dialog(
27 | backgroundColor: Colors.black38,
28 | insetPadding: EdgeInsets.all(8.0),
29 | child: InkWell(
30 | onTap: _onTap,
31 | child: Container(
32 | child: Image.file(
33 | File(
34 | widget.path,
35 | ),
36 | fit: BoxFit.fitWidth,
37 | ),
38 | ),
39 | ),
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/widgets/launcher_icon_art.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class AppLauncherIconArt extends StatelessWidget {
4 | final double height;
5 | final double width;
6 | final List boxShadow;
7 | const AppLauncherIconArt({
8 | Key? key,
9 | this.height = 96.0,
10 | this.width = 96.0,
11 | this.boxShadow = const [],
12 | }) : super(key: key);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return SizedBox(
17 | child: Container(
18 | height: height,
19 | width: width,
20 | decoration: BoxDecoration(
21 | borderRadius: BorderRadius.all(
22 | Radius.circular(height / 3),
23 | ),
24 | color: Color(0xffffefe1),
25 | boxShadow: boxShadow,
26 | ),
27 | child: Image(
28 | image: AssetImage(
29 | AppAssets.appIconAdaptive,
30 | ),
31 | fit: BoxFit.scaleDown,
32 | ),
33 | ),
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/widgets/pattern_config_card.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class PatternConfigCard extends StatefulWidget {
4 | final int designPattern;
5 | //final String patternLabel;
6 | final int? patternValue;
7 | final void Function(double) onPatternSliderChange;
8 | final int min;
9 | final int max;
10 | const PatternConfigCard({
11 | Key? key,
12 | required this.designPattern,
13 | //required this.patternLabel,
14 | required this.patternValue,
15 | required this.onPatternSliderChange,
16 | required this.min,
17 | required this.max,
18 | }) : super(key: key);
19 |
20 | @override
21 | _PatternConfigCardState createState() => _PatternConfigCardState();
22 | }
23 |
24 | class _PatternConfigCardState extends State {
25 | bool get _showMetaData {
26 | return widget.designPattern == 0;
27 | }
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | return Padding(
32 | padding: EdgeInsets.symmetric(
33 | horizontal: 16.0,
34 | vertical: 8.0,
35 | ),
36 | child: LitTitledActionCard(
37 | child: LitSlider(
38 | displayValue: _showMetaData,
39 | displayRangeBadges: _showMetaData,
40 | max: widget.max.toDouble(),
41 | min: widget.min.toDouble(),
42 | onChanged: widget.onPatternSliderChange,
43 | value: widget.patternValue!.toDouble(),
44 | ),
45 | ),
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/widgets/photos_missing_dialog.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A dialog widgets shown in case some photos cannot be restored from the
4 | /// backup directory.
5 | class PhotosMissingDialog extends StatelessWidget {
6 | /// Creates a [PhotosMissingDialog].
7 | const PhotosMissingDialog({Key? key}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return LitTitledDialog(
12 | titleText: AppLocalizations.of(context).photosMissingLabel,
13 | margin: const EdgeInsets.symmetric(
14 | vertical: 8.0,
15 | horizontal: 16.0,
16 | ),
17 | minHeight: 128.0,
18 | child: LitDescriptionTextBox(
19 | maxLines: 6,
20 | text: AppLocalizations.of(context).photosMissingDescr,
21 | ),
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/widgets/pick_photos_button.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A button widget allowing to pick image files.
4 | class PickPhotosButton extends StatelessWidget {
5 | final void Function() onPressed;
6 |
7 | /// Creates a [PickPhotosButton].
8 | const PickPhotosButton({
9 | Key? key,
10 | required this.onPressed,
11 | }) : super(key: key);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return LitPushedThroughButton(
16 | child: Row(
17 | mainAxisSize: MainAxisSize.min,
18 | children: [
19 | Icon(
20 | LitIcons.photos,
21 | color: LitColors.grey500,
22 | size: 16.0,
23 | ),
24 | SizedBox(width: 8.0),
25 | Text(
26 | AppLocalizations.of(context).pickPhotoLabel.toUpperCase(),
27 | style: LitSansSerifStyles.button,
28 | ),
29 | ],
30 | ),
31 | onPressed: onPressed,
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/widgets/purple_pink_button.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A Flutter widget displaying a gradient-decorated button.
4 | class PurplePinkButton extends StatelessWidget {
5 | /// The button's label.
6 | final String label;
7 |
8 | /// Handles the `onPressed` action.
9 | final void Function() onPressed;
10 |
11 | /// Creates a [PurplePinkButton].
12 | const PurplePinkButton({
13 | Key? key,
14 | required this.label,
15 | required this.onPressed,
16 | }) : super(key: key);
17 | @override
18 | Widget build(BuildContext context) {
19 | return LitGradientButton(
20 | accentColor: AppColors.purple,
21 | color: AppColors.pink,
22 | child: Text(
23 | label.toUpperCase(),
24 | style: LitTextStyles.sansSerifStyles[button].copyWith(
25 | color: Colors.white,
26 | fontSize: 13.0,
27 | fontWeight: FontWeight.w700,
28 | letterSpacing: 1.3,
29 | ),
30 | ),
31 | onPressed: onPressed,
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/widgets/purple_pink_save_button.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A draggable purple pink button showing a [LitIcons.disk] icon as button
4 | /// label.
5 | ///
6 | /// It is designed to be used for saving changes.
7 | class PurplePinkSaveButton extends StatelessWidget {
8 | final bool disabled;
9 | final void Function() onSaveChanges;
10 |
11 | /// Creates a [PurplePinkSaveButton].
12 | ///
13 | /// * [disabled] states whether to hide the button (e.g. if no changes have
14 | /// been made).
15 | ///
16 | /// * [onSaveChanges] is the 'save' callback triggering the process to write
17 | /// to the database.
18 | const PurplePinkSaveButton({
19 | Key? key,
20 | required this.disabled,
21 | required this.onSaveChanges,
22 | }) : super(key: key);
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | return !disabled
27 | ? LitDraggable(
28 | initialDragOffset: Offset(
29 | MediaQuery.of(context).size.width - 90.0,
30 | MediaQuery.of(context).size.height - 156.0,
31 | ),
32 | child: LitGradientButton(
33 | accentColor: const Color(0xFFDE8FFA),
34 | color: const Color(0xFFFA72AA),
35 | child: Icon(
36 | LitIcons.disk,
37 | size: 28.0,
38 | color: Colors.white,
39 | ),
40 | onPressed: onSaveChanges,
41 | ),
42 | )
43 | : SizedBox();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/widgets/quote_card.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class QuoteCard extends StatefulWidget {
4 | final TextEditingController authorController;
5 | final TextEditingController quoteController;
6 | const QuoteCard({
7 | Key? key,
8 | required this.authorController,
9 | required this.quoteController,
10 | }) : super(key: key);
11 |
12 | @override
13 | _QuoteCardState createState() => _QuoteCardState();
14 | }
15 |
16 | class _QuoteCardState extends State {
17 | final FocusNode _quoteFocus = FocusNode();
18 | final FocusNode _authorFocus = FocusNode();
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return Padding(
23 | padding: EdgeInsets.symmetric(
24 | horizontal: 16.0,
25 | vertical: 8.0,
26 | ),
27 | child: LitTitledActionCard(
28 | title: AppLocalizations.of(context).quoteLabel.capitalize(),
29 | subtitle: AppLocalizations.of(context).quoteSubtitle,
30 | child: Column(
31 | crossAxisAlignment: CrossAxisAlignment.start,
32 | children: [
33 | ConstrainedBox(
34 | constraints: BoxConstraints(minHeight: 78.0),
35 | child: Padding(
36 | padding: const EdgeInsets.symmetric(
37 | vertical: 8.0,
38 | ),
39 | child: CleanTextField(
40 | focusNode: _quoteFocus,
41 | style: LitTextStyles.sansSerif.copyWith(
42 | height: 1.5,
43 | letterSpacing: 0.32,
44 | ),
45 | controller: widget.quoteController,
46 | ),
47 | ),
48 | ),
49 | Padding(
50 | padding: const EdgeInsets.symmetric(
51 | vertical: 8.0,
52 | ),
53 | child: Column(
54 | crossAxisAlignment: CrossAxisAlignment.start,
55 | children: [
56 | Text(
57 | AppLocalizations.of(context).byLabel,
58 | style: LitTextStyles.sansSerif.copyWith(
59 | fontSize: 13.0,
60 | fontWeight: FontWeight.w600,
61 | color: LitColors.mediumGrey.withOpacity(
62 | 0.6,
63 | ),
64 | ),
65 | ),
66 | CleanTextField(
67 | controller: widget.authorController,
68 | focusNode: _authorFocus,
69 | style: LitTextStyles.sansSerif.copyWith(
70 | fontWeight: FontWeight.w700,
71 | ),
72 | maxLines: 1,
73 | ),
74 | ],
75 | ),
76 | ),
77 | ],
78 | ),
79 | ),
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/lib/widgets/secondary_color_selector_card.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A card widget displaying a scrolling list of selectable color cards.
4 | ///
5 | /// The selected color's values are set to be the bookmark's secondary color.
6 | class SecondaryColorSelectorCard extends StatefulWidget {
7 | const SecondaryColorSelectorCard({
8 | Key? key,
9 | this.buttonBoxShadow = const [
10 | const BoxShadow(
11 | blurRadius: 2.0,
12 | color: Colors.black12,
13 | offset: Offset(2, 1),
14 | spreadRadius: 0.5,
15 | ),
16 | ],
17 | required this.userCreatedColors,
18 | required this.selectedSecondaryColorValue,
19 | required this.onSelectSecondaryColor,
20 | }) : super(key: key);
21 |
22 | /// The box shadow applied to the buttons.
23 | final List buttonBoxShadow;
24 |
25 | /// The list of selectable colors.
26 | final List userCreatedColors;
27 |
28 | /// The currently selected color (as an integer value).
29 | final int selectedSecondaryColorValue;
30 |
31 | /// The callback to update the selected color.
32 | final void Function(Color color) onSelectSecondaryColor;
33 |
34 | @override
35 | _SecondaryColorSelectorCardState createState() =>
36 | _SecondaryColorSelectorCardState();
37 | }
38 |
39 | class _SecondaryColorSelectorCardState
40 | extends State {
41 | /// States whether the provided [color] is currently selected.
42 | bool _colorIsSelected(Color color) {
43 | return color == Color(widget.selectedSecondaryColorValue);
44 | }
45 |
46 | /// Returns the color values as a [Color] object of an specific item on the
47 | /// provided color list (based on its index).
48 | Color _mapSelectedColor(int index) {
49 | return Color.fromARGB(
50 | widget.userCreatedColors[index].alpha,
51 | widget.userCreatedColors[index].red,
52 | widget.userCreatedColors[index].green,
53 | widget.userCreatedColors[index].blue,
54 | );
55 | }
56 |
57 | @override
58 | Widget build(BuildContext context) {
59 | return Padding(
60 | padding: const EdgeInsets.symmetric(
61 | horizontal: 16.0,
62 | vertical: 8.0,
63 | ),
64 | child: LitTitledActionCard(
65 | title: AppLocalizations.of(context).accentColorLabel.capitalize(),
66 | subtitle: AppLocalizations.of(context).selectAccentColorLabel,
67 | child: Column(
68 | crossAxisAlignment: CrossAxisAlignment.start,
69 | children: [
70 | Padding(
71 | padding: const EdgeInsets.only(top: 8.0),
72 | child: SizedBox(
73 | height: 64.0,
74 | child: LitScrollbar(
75 | child: ListView.builder(
76 | padding: const EdgeInsets.symmetric(
77 | horizontal: 8.0,
78 | ),
79 | physics: BouncingScrollPhysics(),
80 | scrollDirection: Axis.horizontal,
81 | itemCount: widget.userCreatedColors.length,
82 | itemBuilder: (BuildContext context, int index) {
83 | return SelectableColorTile(
84 | onSelectCallback: widget.onSelectSecondaryColor,
85 | color: _mapSelectedColor(index),
86 | selected: _colorIsSelected(
87 | _mapSelectedColor(index),
88 | ),
89 | );
90 | },
91 | ),
92 | ),
93 | ),
94 | ),
95 | ],
96 | ),
97 | ),
98 | );
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/lib/widgets/selectable_color_tile.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class SelectableColorTile extends StatefulWidget {
4 | final Color color;
5 | final List boxShadow;
6 | final double height;
7 | final double width;
8 | final bool selected;
9 | final void Function(Color) onSelectCallback;
10 |
11 | const SelectableColorTile({
12 | Key? key,
13 | required this.color,
14 | this.boxShadow = const [
15 | const BoxShadow(
16 | blurRadius: 2.0,
17 | color: Colors.black12,
18 | offset: Offset(2, 1),
19 | spreadRadius: 0.5,
20 | ),
21 | ],
22 | this.height = 64.0,
23 | this.width = 64.0,
24 | required this.selected,
25 | required this.onSelectCallback,
26 | }) : super(key: key);
27 |
28 | @override
29 | _SelectableColorTileState createState() => _SelectableColorTileState();
30 | }
31 |
32 | class _SelectableColorTileState extends State
33 | with TickerProviderStateMixin {
34 | late AnimationController _animationController;
35 |
36 | void _onSelect() {
37 | _animationController.reverse(from: 1.0).then(
38 | (_) => _animationController.forward().then(
39 | (__) => widget.onSelectCallback(widget.color),
40 | ),
41 | );
42 | }
43 |
44 | @override
45 | void initState() {
46 | super.initState();
47 | _animationController = AnimationController(
48 | duration: Duration(milliseconds: 120),
49 | vsync: this,
50 | );
51 | _animationController.forward();
52 | }
53 |
54 | @override
55 | Widget build(BuildContext context) {
56 | return AnimatedBuilder(
57 | animation: _animationController,
58 | builder: (context, _) {
59 | return InkWell(
60 | onTap: _onSelect,
61 | child: SizedBox(
62 | height: widget.height,
63 | width: widget.width,
64 | child: Padding(
65 | padding: const EdgeInsets.all(4.0),
66 | child: Stack(
67 | children: [
68 | Container(
69 | decoration: BoxDecoration(
70 | color: Color.lerp(Colors.white, widget.color,
71 | (_animationController.value) * 0.85),
72 | borderRadius: BorderRadius.circular(15.0),
73 | boxShadow: widget.boxShadow,
74 | ),
75 | ),
76 | widget.selected
77 | ? Align(
78 | alignment: Alignment.center,
79 | child: Container(
80 | decoration: BoxDecoration(
81 | color: Colors.white,
82 | borderRadius: BorderRadius.all(
83 | Radius.circular(
84 | 6.0,
85 | ),
86 | ),
87 | ),
88 | child: Padding(
89 | padding: const EdgeInsets.symmetric(
90 | horizontal: 6.0,
91 | vertical: 6.0,
92 | ),
93 | child: Icon(
94 | LitIcons.check,
95 | color: LitColors.mediumGrey,
96 | size: 16.0,
97 | ),
98 | ),
99 | ),
100 | )
101 | : SizedBox(),
102 | ],
103 | )),
104 | ),
105 | );
106 | });
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/lib/widgets/selected_create_tile.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class SelectedCreateTile extends StatelessWidget {
4 | final String label;
5 |
6 | const SelectedCreateTile({
7 | Key? key,
8 | required this.label,
9 | }) : super(key: key);
10 | @override
11 | Widget build(BuildContext context) {
12 | return Padding(
13 | padding: const EdgeInsets.symmetric(
14 | vertical: 12.0,
15 | horizontal: 16.0,
16 | ),
17 | child: Container(
18 | decoration: BoxDecoration(
19 | borderRadius: BorderRadius.circular(
20 | 12.0,
21 | ),
22 | color: HexColor('#8e8e8e')),
23 | child: Padding(
24 | padding: const EdgeInsets.symmetric(
25 | vertical: 8.0,
26 | horizontal: 32.0,
27 | ),
28 | child: Text(
29 | "$label",
30 | style: LitTextStyles.sansSerif.copyWith(
31 | fontSize: 16.0,
32 | color: Colors.white,
33 | fontWeight: FontWeight.w700),
34 | ),
35 | ),
36 | ),
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/widgets/striped_design.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A `History of Me` widget implementing a striped [BookmarkDesign] to be
4 | /// used as a bookmark preview based on the [UserData] provided.
5 | class StripedDesign extends StatelessWidget implements BookmarkDesign {
6 | /// The bookmark's radius.
7 | final double radius;
8 |
9 | /// The user data containing the bookmark configuration.
10 | final UserData userData;
11 |
12 | /// Creates a [StripedDesign].
13 | const StripedDesign({
14 | Key? key,
15 | required this.radius,
16 | required this.userData,
17 | }) : super(key: key);
18 |
19 | /// The total stripes count.
20 | int get _stripeCount {
21 | return userData.stripeCount;
22 | }
23 |
24 | /// The bookmark height.
25 | double get _height {
26 | return BookmarkCover.height;
27 | }
28 |
29 | /// The bookmark width.
30 | double get _width {
31 | return BookmarkCover.width;
32 | }
33 |
34 | /// Returns the stripe's aspect ratio.
35 | double get _stripeAspectRatio {
36 | return _width / (_height / _stripeCount);
37 | }
38 |
39 | Color get _primaryColor => Color(userData.primaryColor);
40 |
41 | Color get _accentColor => Colors.white;
42 |
43 | /// Returns a list of [_Stripe] widgets.
44 | List get stripes {
45 | List stripeList = [];
46 |
47 | for (int i = 0; i < _stripeCount; i++) {
48 | // Create a two-color pattern by only coloring every second stripe.
49 | if (i % 2 == 0) {
50 | /// Add a colored stripe
51 | stripeList.add(
52 | _Stripe(
53 | aspectRatio: _stripeAspectRatio,
54 | color: _primaryColor,
55 | ),
56 | );
57 | } else {
58 | /// Add a white stripe
59 | stripeList.add(
60 | _Stripe(
61 | aspectRatio: _stripeAspectRatio,
62 | color: _accentColor,
63 | ),
64 | );
65 | }
66 | }
67 |
68 | return stripeList;
69 | }
70 |
71 | @override
72 | Widget build(BuildContext context) {
73 | return ClipRRect(
74 | borderRadius: BorderRadius.all(
75 | Radius.circular(radius),
76 | ),
77 | child: Stack(
78 | children: [
79 | // Ensure to paint on a solid background.
80 | Container(color: _accentColor),
81 | Column(children: stripes),
82 | ],
83 | ),
84 | );
85 | }
86 | }
87 |
88 | /// A stripe on a stripped bookmark design.
89 | class _Stripe extends StatelessWidget {
90 | final double aspectRatio;
91 | final Color color;
92 | const _Stripe({
93 | Key? key,
94 | required this.aspectRatio,
95 | required this.color,
96 | }) : super(key: key);
97 |
98 | @override
99 | Widget build(BuildContext context) {
100 | return AspectRatio(
101 | aspectRatio: aspectRatio,
102 | child: Container(
103 | decoration: BoxDecoration(
104 | color: color,
105 | ),
106 | ),
107 | );
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/lib/widgets/unselected_create_tile.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class UnselectedCreateTile extends StatelessWidget {
4 | final String? label;
5 |
6 | const UnselectedCreateTile({Key? key, this.label}) : super(key: key);
7 | @override
8 | Widget build(BuildContext context) {
9 | return Padding(
10 | padding: const EdgeInsets.symmetric(
11 | vertical: 8.0,
12 | horizontal: 24.0,
13 | ),
14 | child: Text(
15 | "$label",
16 | style: LitTextStyles.sansSerif.copyWith(
17 | fontSize: 16.0,
18 | color: LitColors.darkGrey,
19 | fontWeight: FontWeight.w700,
20 | ),
21 | ),
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/widgets/unsupported_file_dialog.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A dialog widget showing a information message that the desired file is not
4 | /// supported by this app.
5 | class UnsupportedFileDialog extends StatefulWidget {
6 | /// Creates a [UnsupportedFileDialog].
7 | const UnsupportedFileDialog({Key? key}) : super(key: key);
8 |
9 | @override
10 | State createState() => _UnsupportedFileDialogState();
11 | }
12 |
13 | class _UnsupportedFileDialogState extends State {
14 | @override
15 | Widget build(BuildContext context) {
16 | return LitTitledDialog(
17 | titleText: LeitmotifLocalizations.of(context).unsupportedFileTitle,
18 | margin: LitEdgeInsets.none,
19 | child: CleanInkWell(
20 | onTap: () => Navigator.of(context).pop(),
21 | child: Padding(
22 | padding: LitEdgeInsets.dialogMargin,
23 | child: LitDescriptionTextBox(
24 | text: LeitmotifLocalizations.of(context).unsupportedFileDescr,
25 | ),
26 | ),
27 | ),
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/widgets/updated_label_text.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class UpdatedLabelText extends StatelessWidget {
4 | final TextAlign textAlign;
5 | final TextStyle textStyle;
6 |
7 | final int? lastUpdateTimestamp;
8 | const UpdatedLabelText({
9 | Key? key,
10 | this.textAlign = TextAlign.left,
11 | required this.lastUpdateTimestamp,
12 | this.textStyle = LitSansSerifStyles.caption,
13 | }) : super(key: key);
14 | @override
15 | Widget build(BuildContext context) {
16 | final RelativeDateTime relativeDateTime = RelativeDateTime(
17 | dateTime: DateTime.now(),
18 | other: DateTime.fromMillisecondsSinceEpoch(lastUpdateTimestamp!),
19 | );
20 | final RelativeDateFormat relativeDateFormatter = RelativeDateFormat(
21 | Localizations.localeOf(context),
22 | );
23 | //final String formatter = DateFormat().toString();
24 | return ClippedText(
25 | "${relativeDateFormatter.format(relativeDateTime)}",
26 | textAlign: textAlign,
27 | style: textStyle,
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/widgets/user_icon.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | class UserIcon extends StatelessWidget {
4 | final UserData userData;
5 | final double size;
6 | final void Function()? onPressed;
7 | const UserIcon({
8 | Key? key,
9 | required this.size,
10 | required this.userData,
11 | this.onPressed,
12 | }) : super(key: key);
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return LitUserIcon(
17 | size: size,
18 | name: userData.name,
19 | color: Color(userData.primaryColor),
20 | accentColor: Color(userData.secondaryColor),
21 | onPressed: onPressed,
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/widgets/user_profile_card.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A widget displaying the user's analytics and basic customization options.
4 | ///
5 | /// This includes an user icon, a button allowing to change the username and a
6 | /// card listing the user's statistics.
7 | class UserProfileCard extends StatefulWidget {
8 | /// The provided [UserData].
9 | final UserData userData;
10 |
11 | /// A callback to handle the 'on pressed' action.
12 | final void Function() onPressedUserIcon;
13 |
14 | /// Creates a [UserProfileCard].
15 | const UserProfileCard({
16 | Key? key,
17 | required this.userData,
18 | required this.onPressedUserIcon,
19 | }) : super(key: key);
20 | @override
21 | _UserProfileCardState createState() => _UserProfileCardState();
22 | }
23 |
24 | class _UserProfileCardState extends State {
25 | /// Returns a localized date string based the provided `created` timestamp.
26 | String get localizedCreatedLabel => DateTime.fromMillisecondsSinceEpoch(
27 | widget.userData.created,
28 | ).formatAsLocalizedDate(context);
29 |
30 | @override
31 | Widget build(BuildContext context) {
32 | return LitConstrainedSizedBox(
33 | child: Padding(
34 | padding: const EdgeInsets.symmetric(
35 | horizontal: 16.0,
36 | vertical: 8.0,
37 | ),
38 | child: Column(
39 | mainAxisAlignment: MainAxisAlignment.center,
40 | crossAxisAlignment: CrossAxisAlignment.center,
41 | children: [
42 | UserIcon(
43 | size: 96.0,
44 | userData: widget.userData,
45 | onPressed: widget.onPressedUserIcon,
46 | ),
47 | Padding(
48 | padding: LitEdgeInsets.card,
49 | child: ClippedText(
50 | widget.userData.name,
51 | style: LitSansSerifStyles.h6,
52 | ),
53 | ),
54 | LayoutBuilder(
55 | builder: (context, constraints) {
56 | return SizedBox(
57 | width: constraints.maxWidth,
58 | child: Row(
59 | mainAxisAlignment: MainAxisAlignment.center,
60 | children: [
61 | SizedBox(
62 | width: constraints.maxWidth / 2,
63 | child: Column(
64 | mainAxisAlignment: MainAxisAlignment.center,
65 | crossAxisAlignment: CrossAxisAlignment.center,
66 | children: [
67 | Padding(
68 | padding:
69 | const EdgeInsets.symmetric(vertical: 4.0),
70 | child: ClippedText(
71 | LeitmotifLocalizations.of(context)
72 | .createdOnLabel,
73 | style: LitSansSerifStyles.caption,
74 | ),
75 | ),
76 | Padding(
77 | padding:
78 | const EdgeInsets.symmetric(vertical: 4.0),
79 | child: ClippedText(
80 | localizedCreatedLabel,
81 | style: LitSansSerifStyles.subtitle2,
82 | ),
83 | ),
84 | ],
85 | ),
86 | ),
87 | ],
88 | ),
89 | );
90 | },
91 | ),
92 | ],
93 | ),
94 | ),
95 | );
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/lib/widgets/word_count_badge.dart:
--------------------------------------------------------------------------------
1 | part of widgets;
2 |
3 | /// A [LitBadge] widget displaying the total word count provided by the text
4 | /// editing controller's current input value.
5 | ///
6 | /// Applies validation to ensure only valid words are counted.
7 | class WordCountBadge extends StatelessWidget {
8 | final TextEditingController controller;
9 |
10 | /// Creates a [WordCountBadge].
11 | const WordCountBadge({
12 | Key? key,
13 | required this.controller,
14 | }) : super(key: key);
15 |
16 | /// Returns the total word count.
17 | int get _totalWords => controller.text.wordCount;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return LitBadge(
22 | backgroundColor: LitColors.grey300,
23 | child: Text(
24 | _totalWords.toString() +
25 | " " +
26 | AppLocalizations.of(context).wordsWrittenLabel,
27 | style: LitSansSerifStyles.caption.copyWith(
28 | color: Colors.white,
29 | ),
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: history_of_me
2 | description: Your own personal diary.
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: 1.6.0+10
19 |
20 | environment:
21 | sdk: ">=2.12.0 <3.0.0"
22 |
23 | dependencies:
24 | flutter:
25 | sdk: flutter
26 | flutter_localizations:
27 | sdk: flutter
28 |
29 | intl: ^0.17.0
30 | hive_flutter: ^1.1.0
31 | hive_generator: ^1.1.1
32 | build_runner: ^2.1.7
33 | path_provider: ^2.0.3
34 | image_picker: ^0.8.4+10
35 | package_info_plus: ^1.0.6
36 | leitmotif:
37 | ^2.2.0
38 | # Specify alternative leitmotif-flutter locations
39 | # git:
40 | # url: https://github.com/litlifesoftware/leitmotif-flutter.git
41 | # path: ../leitmotif-flutter/
42 | date_colors:
43 | ^0.0.1
44 | # Specify alternative date_colors locations
45 | # git:
46 | # url: https://github.com/litlifesoftware/date_colors.git
47 | # path: ../date_colors/
48 | lit_relative_date_time: ^1.2.0
49 | lit_backup_service:
50 | ^1.0.3
51 | # Specify alternative leitmotif-flutter locations
52 | # git:
53 | # url: https://github.com/litlifesoftware/lit_backup_service.git
54 | # path: ../lit_backup_service/
55 |
56 | dev_dependencies:
57 | # For information on the generic Dart part of this file, see the
58 | # following page: https://dart.dev/tools/pub/pubspec
59 |
60 | flutter_launcher_icons: ^0.10.0
61 |
62 | # The following section is specific to Flutter.
63 | flutter:
64 | # The following line ensures that the Material Icons font is
65 | # included with your application, so that you can use the icons in
66 | # the material Icons class.
67 | uses-material-design: true
68 |
69 | # To add assets to your application, add an assets section, like this:
70 | # assets:
71 | # - images/a_dot_burr.jpeg
72 | # - images/a_dot_ham.jpeg
73 | assets:
74 | - assets/icon/
75 | - assets/images/
76 |
77 | # An image asset can refer to one or more resolution-specific "variants", see
78 | # https://flutter.dev/assets-and-images/#resolution-aware.
79 |
80 | # For details regarding adding assets from package dependencies, see
81 | # https://flutter.dev/assets-and-images/#from-packages
82 |
83 | # To add custom fonts to your application, add a fonts section here,
84 | # in this "flutter" section. Each entry in this list should have a
85 | # "family" key with the font family name, and a "fonts" key with a
86 | # list giving the asset and other descriptors for the font. For
87 | # example:
88 | fonts:
89 | - family: Playfair Display
90 | fonts:
91 | - asset: assets/fonts/PlayfairDisplay/PlayfairDisplay-Black.ttf
92 | - asset: assets/fonts/PlayfairDisplay/PlayfairDisplay-BlackItalic.ttf
93 | - asset: assets/fonts/PlayfairDisplay/PlayfairDisplay-Bold.ttf
94 | - asset: assets/fonts/PlayfairDisplay/PlayfairDisplay-BoldItalic.ttf
95 | - asset: assets/fonts/PlayfairDisplay/PlayfairDisplay-Italic.ttf
96 | - asset: assets/fonts/PlayfairDisplay/PlayfairDisplay-Regular.ttf
97 | # fonts:
98 | # - family: Schyler
99 | # fonts:
100 | # - asset: fonts/Schyler-Regular.ttf
101 | # - asset: fonts/Schyler-Italic.ttf
102 | # style: italic
103 | # - family: Trajan Pro
104 | # fonts:
105 | # - asset: fonts/TrajanPro.ttf
106 | # - asset: fonts/TrajanPro_Bold.ttf
107 | # weight: 700
108 | #
109 | # For details regarding fonts from package dependencies,
110 | # see https://flutter.dev/custom-fonts/#from-packages
111 |
--------------------------------------------------------------------------------
/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/web/favicon.png
--------------------------------------------------------------------------------
/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/litlifesoftware/HistoryOfMe/aa7eae85732059bbab743dc6145cd09b03dfe17e/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | history_of_me
30 |
31 |
32 |
33 |
36 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "history_of_me",
3 | "short_name": "history_of_me",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------