Repository: floranguyen0/mmas-money-tracker
Branch: main
Commit: c886e0fce272
Files: 141
Total size: 408.3 KB
Directory structure:
gitextract_sxwtmaf3/
├── .gitignore
├── .metadata
├── LICENSE
├── README.md
├── android/
│ ├── .gitignore
│ ├── app/
│ │ ├── build.gradle
│ │ └── src/
│ │ ├── debug/
│ │ │ └── AndroidManifest.xml
│ │ ├── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── mmas/
│ │ │ │ └── money_assistant_2608/
│ │ │ │ └── MainActivity.kt
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ └── launch_background.xml
│ │ │ ├── drawable-v21/
│ │ │ │ └── launch_background.xml
│ │ │ ├── values/
│ │ │ │ └── styles.xml
│ │ │ └── values-night/
│ │ │ └── styles.xml
│ │ └── profile/
│ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ └── settings.gradle
├── flutter
├── ios/
│ ├── .gitignore
│ ├── Flutter/
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Runner/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ ├── LaunchBackground.imageset/
│ │ │ │ └── Contents.json
│ │ │ └── LaunchImage.imageset/
│ │ │ ├── Contents.json
│ │ │ └── README.md
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── GoogleService-Info.plist
│ │ ├── Info.plist
│ │ ├── Runner-Bridging-Header.h
│ │ └── Runner.entitlements
│ ├── 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
│ └── build/
│ └── Pods.build/
│ └── Release-iphonesimulator/
│ ├── DKImagePickerController-DKImagePickerController.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── DKImagePickerController.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── DKPhotoGallery-DKPhotoGallery.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── DKPhotoGallery.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── FMDB.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── Flutter.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── Pods-Runner.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── SDWebImage.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── SwiftyGif.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── Toast.build/
│ │ └── dgph
│ ├── file_picker.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── fluttertoast.build/
│ │ └── dgph
│ ├── in_app_review.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── local_auth.build/
│ │ └── dgph
│ ├── path_provider.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── rate_my_app.build/
│ │ └── dgph
│ ├── share_plus.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── shared_preferences.build/
│ │ ├── dgph
│ │ └── dgph~
│ ├── sqflite.build/
│ │ ├── dgph
│ │ └── dgph~
│ └── url_launcher.build/
│ ├── dgph
│ └── dgph~
├── lib/
│ ├── main.dart
│ └── project/
│ ├── app_pages/
│ │ ├── add_category.dart
│ │ ├── analysis.dart
│ │ ├── calendar.dart
│ │ ├── currency.dart
│ │ ├── edit.dart
│ │ ├── edit_expense_category.dart
│ │ ├── edit_income_category.dart
│ │ ├── expense_category.dart
│ │ ├── income_category.dart
│ │ ├── input.dart
│ │ ├── others.dart
│ │ ├── parent_category.dart
│ │ ├── report.dart
│ │ ├── select_date_format.dart
│ │ ├── select_icon.dart
│ │ └── select_language.dart
│ ├── auth_pages/
│ │ ├── loading_page.dart
│ │ ├── sign_in.dart
│ │ ├── sign_up.dart
│ │ ├── user_account.dart
│ │ ├── welcome_page.dart
│ │ └── wrapper.dart
│ ├── auth_services/
│ │ └── firebase_authentication.dart
│ ├── classes/
│ │ ├── alert_dialog.dart
│ │ ├── app_bar.dart
│ │ ├── category_item.dart
│ │ ├── chart_pie.dart
│ │ ├── constants.dart
│ │ ├── custom_toast.dart
│ │ ├── dropdown_box.dart
│ │ ├── icons.dart
│ │ ├── input_model.dart
│ │ ├── keyboard.dart
│ │ ├── lockscreen.dart
│ │ └── saveOrSaveAndDeleteButtons.dart
│ ├── database_management/
│ │ ├── database.dart
│ │ ├── shared_preferences_services.dart
│ │ └── sqflite_services.dart
│ ├── draft1.dart
│ ├── home.dart
│ ├── localization/
│ │ ├── app_localization.dart
│ │ ├── lang/
│ │ │ ├── ar.json
│ │ │ ├── de.json
│ │ │ ├── en.json
│ │ │ ├── es.json
│ │ │ ├── fr.json
│ │ │ ├── hi.json
│ │ │ ├── ja.json
│ │ │ ├── ko.json
│ │ │ ├── ne.json
│ │ │ ├── pt.json
│ │ │ ├── ru.json
│ │ │ ├── tr.json
│ │ │ ├── vi.json
│ │ │ └── zh.json
│ │ ├── language.dart
│ │ └── methods.dart
│ ├── provider.dart
│ └── real_main.dart
├── pubspec.yaml
└── test/
└── widget_test.dart
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
================================================
FILE: .metadata
================================================
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: f4abaa0735eba4dfd8f33f73363911d63931fe03
channel: stable
project_type: app
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Hoa Nguyen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# MMAS: Money Tracker
> An optimized application for daily expense tracking and finance management.

## Setup
Run the following commands from your terminal:
1) `git clone https://github.com/floranguyen0/mmas-money-tracker` to clone this repository
2) `flutter pub get` in the project root directory to install all the required dependencies.
## Download MMAS at:
https://apps.apple.com/vn/app/mmas-money-tracker/id1582638369
## Screenshots
## LICENSE
MMAS: Money Tracker is [MIT-licensed](https://github.com/floranguyen0/mmas-money-tracker/blob/main/LICENSE).
================================================
FILE: android/.gitignore
================================================
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
app/upload-keystore.jks
================================================
FILE: android/app/build.gradle
================================================
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
compileSdkVersion 30
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.mmas.money_assistant_2608"
minSdkVersion 16
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
================================================
FILE: android/app/src/debug/AndroidManifest.xml
================================================
================================================
FILE: android/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: android/app/src/main/kotlin/com/mmas/money_assistant_2608/MainActivity.kt
================================================
package com.mmas.money_assistant_2608
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
================================================
FILE: android/app/src/main/res/drawable/launch_background.xml
================================================
-
-
================================================
FILE: android/app/src/main/res/drawable-v21/launch_background.xml
================================================
-
-
================================================
FILE: android/app/src/main/res/values/styles.xml
================================================
================================================
FILE: android/app/src/main/res/values-night/styles.xml
================================================
================================================
FILE: android/app/src/profile/AndroidManifest.xml
================================================
================================================
FILE: android/build.gradle
================================================
buildscript {
ext.kotlin_version = '1.8.0'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: android/gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
================================================
FILE: android/gradle.properties
================================================
#org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
org.gradle.java.home=C:/Program Files/Java/jdk-19
org.gradle.jvmargs=--add-opens java.base/java.io=ALL-UNNAMED
================================================
FILE: android/settings.gradle
================================================
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
================================================
FILE: flutter
================================================
================================================
FILE: ios/.gitignore
================================================
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
================================================
FILE: ios/Flutter/AppFrameworkInfo.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
App
CFBundleIdentifier
io.flutter.flutter.app
CFBundleInfoDictionaryVersion
6.0
CFBundleName
App
CFBundlePackageType
FMWK
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1.0
MinimumOSVersion
9.0
================================================
FILE: ios/Flutter/Debug.xcconfig
================================================
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
================================================
FILE: ios/Flutter/Release.xcconfig
================================================
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
================================================
FILE: ios/Podfile
================================================
# Uncomment this line to define a global platform for your project
platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
================================================
FILE: ios/Runner/AppDelegate.swift
================================================
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
================================================
FILE: ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
================================================
{
"images" : [
{
"filename" : "background.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
================================================
{
"images" : [
{
"filename" : "LaunchImage.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "LaunchImage@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "LaunchImage@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
================================================
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
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.
================================================
FILE: ios/Runner/Base.lproj/LaunchScreen.storyboard
================================================
================================================
FILE: ios/Runner/Base.lproj/Main.storyboard
================================================
================================================
FILE: ios/Runner/GoogleService-Info.plist
================================================
CLIENT_ID
189781709351-2fgub7ut4f44e7seqvdgaebid05e8mgd.apps.googleusercontent.com
REVERSED_CLIENT_ID
com.googleusercontent.apps.189781709351-2fgub7ut4f44e7seqvdgaebid05e8mgd
API_KEY
AIzaSyC_4qm52Vkak1c_a-WucmgTY1GLhQvIp-g
GCM_SENDER_ID
189781709351
PLIST_VERSION
1
BUNDLE_ID
com.example.moneyAssistant2608
PROJECT_ID
mmas-moneytracker
STORAGE_BUCKET
mmas-moneytracker.appspot.com
IS_ADS_ENABLED
IS_ANALYTICS_ENABLED
IS_APPINVITE_ENABLED
IS_GCM_ENABLED
IS_SIGNIN_ENABLED
GOOGLE_APP_ID
1:189781709351:ios:53aeb605dc2b087285b402
================================================
FILE: ios/Runner/Info.plist
================================================
CFBundleDevelopmentRegion
en-US
CFBundleDisplayName
MMAS
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleLocalizations
en
zh
zh_CN
fr
de
ja
ko
CFBundleName
MMAS
CFBundlePackageType
APPL
CFBundleShortVersionString
$(FLUTTER_BUILD_NAME)
CFBundleSignature
????
CFBundleVersion
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
NSPhotoLibraryUsageDescription
We access your photo library to add a photo of your bills to your expense/income records
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
Main
UIStatusBarHidden
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UIInterfaceOrientationPortraitUpsideDown
UISupportedInterfaceOrientations~ipad
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
CFBundleURLTypes
CFBundleTypeRole
Editor
CFBundleURLSchemes
com.googleusercontent.apps.189781709351-2fgub7ut4f44e7seqvdgaebid05e8mgd
================================================
FILE: ios/Runner/Runner-Bridging-Header.h
================================================
#import "GeneratedPluginRegistrant.h"
================================================
FILE: ios/Runner/Runner.entitlements
================================================
aps-environment
development
================================================
FILE: ios/Runner.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
23D73B63272C62B1009E72A7 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 23D73B62272C62B1009E72A7 /* GoogleService-Info.plist */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
61499E5D1D041FF00E95B707 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B99D98016620F7A28ACDC882 /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
23D73B62272C62B1009E72A7 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
23E42C6126E530110040CEF6 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
3CAD1B05AD9D13F3D49A238F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
6F42B7B7C5C3973C91F87BBA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
B99D98016620F7A28ACDC882 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B9A5FB7A6583188949FF87EB /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
61499E5D1D041FF00E95B707 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
5A2F1614F901E5E08066321B /* Frameworks */ = {
isa = PBXGroup;
children = (
B99D98016620F7A28ACDC882 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
E1BD7345BC0DA67D9B5D118F /* Pods */,
5A2F1614F901E5E08066321B /* Frameworks */,
);
sourceTree = "";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
23D73B62272C62B1009E72A7 /* GoogleService-Info.plist */,
23E42C6126E530110040CEF6 /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "";
};
E1BD7345BC0DA67D9B5D118F /* Pods */ = {
isa = PBXGroup;
children = (
B9A5FB7A6583188949FF87EB /* Pods-Runner.debug.xcconfig */,
3CAD1B05AD9D13F3D49A238F /* Pods-Runner.release.xcconfig */,
6F42B7B7C5C3973C91F87BBA /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
79D9EC2F681B8FDF039DE67A /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
D1539A2B591CC9A875AC7664 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
23D73B63272C62B1009E72A7 /* GoogleService-Info.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
79D9EC2F681B8FDF039DE67A /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
D1539A2B591CC9A875AC7664 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 2U5AVM796N;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.moneyAssistant2608;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 2U5AVM796N;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.moneyAssistant2608;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 2U5AVM796N;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.moneyAssistant2608;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
IDEDidComputeMac32BitWarning
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
PreviewsEnabled
================================================
FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
================================================
================================================
FILE: ios/Runner.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
IDEDidComputeMac32BitWarning
================================================
FILE: ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
PreviewsEnabled
================================================
FILE: lib/main.dart
================================================
// @dart=2.9
import 'package:money_assistant_2608/project/real_main.dart';
void main() async {
realMain();
}
================================================
FILE: lib/project/app_pages/add_category.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:money_assistant_2608/project/app_pages/parent_category.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/category_item.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/classes/saveOrSaveAndDeleteButtons.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:money_assistant_2608/project/provider.dart';
import 'package:provider/provider.dart';
import 'select_icon.dart';
class AddCategory extends StatelessWidget {
final BuildContext? contextEx, contextExEdit, contextInEdit, contextIn;
final String type, appBarTitle;
final String? categoryName, description;
final IconData? categoryIcon;
final CategoryItem? parentItem;
static final _formKey4 = GlobalKey(debugLabel: '_formKey4'),
_formKey5 = GlobalKey(debugLabel: '_formKey5');
AddCategory(
{this.contextExEdit,
this.contextEx,
this.contextInEdit,
this.contextIn,
required this.type,
required this.appBarTitle,
this.categoryName,
this.categoryIcon,
this.parentItem,
this.description});
void unFocusNode(BuildContext context) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
unFocusNode(context);
},
child: Scaffold(
backgroundColor: blue1,
appBar: BasicAppBar(this.appBarTitle),
body: Form(
key: this.type == 'Income' ? _formKey4 : _formKey5,
child: ChangeNotifierProvider(
create: (context) => ChangeCategory(),
child: ListView(
children: [
CategoryName(
this.type,
this.categoryName == null
? null
: getTranslated(context, this.categoryName!) ??
this.categoryName,
this.categoryIcon),
// Hide ParentCategoryCard when either type is Income or users press on parent category
// Fix this? why not (this.categoryName == null && this.parentCategory != null)
this.type == 'Income' ||
(this.categoryName != null && this.parentItem == null)
? SizedBox()
: ParentCategoryCard(this.parentItem),
SizedBox(
height: 20.h,
),
Description(this.description),
Padding(
padding: EdgeInsets.symmetric(vertical: 90.h),
child: Save(
formKey:
this.type == 'Income' ? _formKey4 : _formKey5,
contextEx: this.contextEx,
contextExEdit: this.contextExEdit,
contextIn: this.contextIn,
contextInEdit: this.contextInEdit,
categoryName: this.categoryName,
categoryIcon: this.categoryIcon,
parentItem: this.parentItem,
description: this.description))
],
),
),
),
));
}
}
class CategoryName extends StatefulWidget {
final String type;
final String? categoryName;
final IconData? categoryIcon;
CategoryName(this.type, this.categoryName, this.categoryIcon);
@override
_CategoryNameState createState() => _CategoryNameState();
}
class _CategoryNameState extends State {
// late final FocusNode categoryNameFocusNode;
static late TextEditingController categoryNameController;
final FocusNode categoryNameFocusNode = FocusNode();
@override
void initState() {
super.initState();
// categoryNameFocusNode = FocusNode();
categoryNameController =
TextEditingController(text: widget.categoryName ?? '');
}
// @override
// void dispose() {
// categoryNameFocusNode.dispose();
// super.dispose();
// }
@override
Widget build(BuildContext context) {
return Card(
elevation: 7,
child: Padding(
padding: EdgeInsets.only(left: 10.w, top: 8.h, bottom: 8.h),
child: TextFormField(
controller: categoryNameController,
focusNode: categoryNameFocusNode,
onChanged: (value) => {},
maxLines: null,
cursorColor: blue1,
textCapitalization: TextCapitalization.sentences,
style: TextStyle(fontSize: 22.sp, fontWeight: FontWeight.bold),
validator: (categoryNameInput) {
categoryNameInput = categoryNameInput!.trim();
if (categoryNameInput.isEmpty) {
return getTranslated(context, 'Please fill a category name');
} else {
bool isIdenticalCategory() {
if (widget.type == 'Income') {
for (CategoryItem incomeItem in incomeItems) {
if (categoryNameInput == incomeItem.text) {
return true;
}
}
} else {
List expenseItems = [];
sharedPrefs.getAllExpenseItemsLists().forEach(
(parentExpenseItem) => parentExpenseItem.forEach(
(expenseItem) => expenseItems.add(expenseItem)));
for (CategoryItem expenseItem in expenseItems) {
if (categoryNameInput == expenseItem.text) {
return true;
}
}
}
return false;
}
// Show an error if users want to edit to or add an existing category
if ((categoryNameInput != widget.categoryName) &&
(isIdenticalCategory())) {
return getTranslated(context, 'Category already exists');
}
}
},
decoration: InputDecoration(
border: InputBorder.none,
hintText: getTranslated(context, 'Category name'),
hintStyle: TextStyle(
fontSize: 22.sp,
color: grey,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.normal),
suffixIcon: categoryNameController.text.length > 0
? IconButton(
icon: Icon(Icons.clear),
onPressed: () {
categoryNameController.clear();
})
: SizedBox(),
icon: Selector(
selector: (_, provider) => provider.selectedCategoryIcon,
builder: (context, selectedCategoryIcon, child) {
return GestureDetector(
onTap: () async {
IconData? selectedIcon = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SelectIcon(widget.type))) ??
selectedCategoryIcon;
context
.read()
.changeCategoryIcon(selectedIcon);
},
child: Column(
children: [
CircleAvatar(
radius: 20.r,
backgroundColor: Color.fromRGBO(215, 223, 231, 1),
child: Icon(
selectedCategoryIcon ??
widget.categoryIcon ??
Icons.category_outlined,
size: 25.sp,
color: widget.type == 'Income' ? green : red,
)),
SizedBox(
height: 2,
),
Text(
'select icon',
style:
TextStyle(fontSize: 11, color: Colors.blueGrey),
)
],
),
);
})),
),
),
);
}
}
class ParentCategoryCard extends StatefulWidget {
final CategoryItem? parentItem;
const ParentCategoryCard(this.parentItem);
@override
_ParentCategoryCardState createState() => _ParentCategoryCardState();
}
class _ParentCategoryCardState extends State {
@override
Widget build(BuildContext context) {
return Selector(
selector: (_, provider) => provider.parentItem,
builder: (context, selectedParentItem, child) {
selectedParentItem ??= widget.parentItem ??
categoryItem(Icons.category_outlined,
getTranslated(context, 'Parent category')!);
context.read().parentItem = selectedParentItem;
return GestureDetector(
onTap: () async {
CategoryItem newParentItem = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ParentCategoryList()));
context.read().changeParentItem(newParentItem);
},
child: Card(
elevation: 7,
child: Padding(
padding: EdgeInsets.only(left: 18.w, top: 6.h, bottom: 6.h),
child: Row(
children: [
CircleAvatar(
radius: 20.r,
backgroundColor: Color.fromRGBO(215, 223, 231, 1),
child: Icon(
iconData(selectedParentItem),
size: 25.sp,
color: red,
)),
SizedBox(
width: 28.w,
),
Text(
getTranslated(context, selectedParentItem.text) ??
selectedParentItem.text,
style: selectedParentItem.text ==
getTranslated(context, 'Parent category')!
? TextStyle(
fontSize: 22.sp,
color: grey,
fontStyle: FontStyle.italic,
)
: TextStyle(
fontSize: 22.sp,
color: red,
fontWeight: FontWeight.bold),
),
Spacer(),
Icon(
Icons.arrow_forward_ios,
size: 22.sp,
),
SizedBox(
width: 10.h,
)
],
),
),
));
});
}
}
class Description extends StatefulWidget {
final String? description;
Description(this.description);
@override
_DescriptionState createState() => _DescriptionState();
}
class _DescriptionState extends State {
final FocusNode descriptionFocusNode = FocusNode();
static late TextEditingController descriptionController;
@override
void initState() {
super.initState();
descriptionController =
TextEditingController(text: widget.description ?? '');
}
@override
Widget build(BuildContext context) {
return Card(
elevation: 7,
child: SizedBox(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 6.h),
child: TextFormField(
controller: descriptionController,
focusNode: descriptionFocusNode,
maxLines: null,
minLines: 1,
cursorColor: blue1,
textCapitalization: TextCapitalization.sentences,
style: TextStyle(fontSize: 22.sp),
decoration: InputDecoration(
border: InputBorder.none,
hintText:
getTranslated(context, 'Description') ?? 'Description',
hintStyle: GoogleFonts.cousine(
fontSize: 21.5.sp,
fontStyle: FontStyle.italic,
),
suffixIcon: descriptionController.text.length > 0
? IconButton(
icon: Icon(Icons.clear),
onPressed: () {
descriptionController.clear();
})
: SizedBox(),
icon: Padding(
padding: EdgeInsets.only(right: 10.w),
child: Icon(
Icons.description_outlined,
size: 35.sp,
color: Colors.blueGrey,
),
))),
),
),
);
}
}
class Save extends StatefulWidget {
final GlobalKey formKey;
final BuildContext? contextEx, contextExEdit, contextIn, contextInEdit;
final String? categoryName, description;
final IconData? categoryIcon;
final CategoryItem? parentItem;
const Save(
{required this.formKey,
this.contextEx,
this.contextExEdit,
this.contextIn,
this.contextInEdit,
this.categoryName,
this.categoryIcon,
this.parentItem,
this.description});
@override
_SaveState createState() => _SaveState();
}
class _SaveState extends State {
@override
Widget build(BuildContext context) {
void saveCategoryFunction() {
if (widget.formKey.currentState!.validate()) {
String? finalDescription = _DescriptionState.descriptionController.text;
String finalCategoryName =
_CategoryNameState.categoryNameController.text;
IconData? finalCategoryIcon =
Provider.of(context, listen: false)
.selectedCategoryIcon;
// trim to get a real input
finalCategoryName = finalCategoryName.trim();
finalDescription = finalDescription.trim();
CategoryItem categoryItem(IconData iconData) => CategoryItem(
iconData.codePoint,
iconData.fontPackage,
iconData.fontFamily,
finalCategoryName,
finalDescription);
void updateCategory() {
Provider.of(widget.contextExEdit!,
listen: false)
.getAllExpenseItems();
Provider.of(widget.contextEx!, listen: false)
.getAllExpenseItems();
}
if (widget.contextInEdit != null) {
print('income');
if (widget.categoryName != null) {
print('edit');
if (finalCategoryName != widget.categoryName ||
widget.categoryIcon != finalCategoryIcon ||
widget.description != finalDescription) {
print('something changed');
incomeItems
.removeWhere((item) => item.text == widget.categoryName);
}
}
incomeItems.add(categoryItem(finalCategoryIcon ??
widget.categoryIcon ??
Icons.category_outlined));
sharedPrefs.saveItems('income items', incomeItems);
Provider.of(widget.contextInEdit!,
listen: false)
.getIncomeItems();
if (widget.contextIn != null) {
Provider.of(widget.contextIn!, listen: false)
.getIncomeItems();
}
} else {
print('expense');
CategoryItem? finalParent = context.read().parentItem;
if ((widget.categoryName == null) && (widget.parentItem == null)) {
CategoryItem item =
categoryItem(finalCategoryIcon ?? Icons.category_outlined);
print('add expense');
if (finalParent!.text ==
getTranslated(context, 'Parent category')!) {
print('add parent');
sharedPrefs.saveItems(finalCategoryName, [item]);
var parentExpenseItemNames = sharedPrefs.parentExpenseItemNames;
parentExpenseItemNames.add(finalCategoryName);
sharedPrefs.parentExpenseItemNames = parentExpenseItemNames;
} else {
print('add new expense category to an existing parent');
List items = sharedPrefs.getItems(finalParent.text);
items.add(item);
sharedPrefs.saveItems(finalParent.text, items);
}
updateCategory();
} else {
print('edit');
if (widget.parentItem == null) {
print('edit parent only');
if (finalCategoryName != widget.categoryName ||
widget.categoryIcon != finalCategoryIcon ||
widget.description != finalDescription) {
print('something changed');
List items =
sharedPrefs.getItems(widget.categoryName!);
items.removeAt(0);
items.insert(
0, categoryItem(finalCategoryIcon ?? widget.categoryIcon!));
sharedPrefs.saveItems(finalCategoryName, items);
if (finalCategoryName != widget.categoryName) {
print('parent name changed');
var parentExpenseItemNames =
sharedPrefs.parentExpenseItemNames;
parentExpenseItemNames.removeWhere((parentExpenseItemName) =>
widget.categoryName == parentExpenseItemName);
parentExpenseItemNames.insert(0, finalCategoryName);
sharedPrefs.parentExpenseItemNames = parentExpenseItemNames;
}
updateCategory();
}
} else {
print('edit category');
if (finalParent!.text != widget.parentItem!.text ||
widget.categoryIcon != finalCategoryIcon ||
finalCategoryName != widget.categoryName ||
widget.description != finalDescription) {
print('something changed');
void itemsAdd(List items) {
items.add(
categoryItem(finalCategoryIcon ?? widget.categoryIcon!));
}
List items =
sharedPrefs.getItems(widget.parentItem!.text);
items.removeWhere((item) => item.text == widget.categoryName);
if (finalParent.text != widget.parentItem!.text) {
print('edit parent of expense category');
List itemsMovedTo =
sharedPrefs.getItems(finalParent.text);
itemsAdd(itemsMovedTo);
sharedPrefs.saveItems(finalParent.text, itemsMovedTo);
} else {
print('edit other things');
itemsAdd(items);
}
sharedPrefs.saveItems(widget.parentItem!.text, items);
updateCategory();
}
}
}
}
Navigator.pop(context);
}
}
if (widget.categoryName == null && widget.parentItem == null) {
return SaveButton(false, saveCategoryFunction, null);
} else {
return SaveAndDeleteButton(
saveAndDeleteInput: false,
saveCategory: saveCategoryFunction,
categoryName: widget.categoryName,
parentExpenseItem:
widget.parentItem == null ? null : widget.parentItem!.text,
contextEx: widget.contextEx,
contextExEdit: widget.contextExEdit,
contextIn: widget.contextIn,
contextInEdit: widget.contextInEdit);
}
}
}
================================================
FILE: lib/project/app_pages/analysis.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/category_item.dart';
import 'package:money_assistant_2608/project/classes/chart_pie.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/classes/dropdown_box.dart';
import 'package:money_assistant_2608/project/classes/input_model.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/database_management/sqflite_services.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:provider/provider.dart';
import '../provider.dart';
import 'report.dart';
final List chartDataNull = [
InputModel(
id: null,
type: null,
amount: 1,
category: '',
description: null,
date: null,
time: null,
color: const Color.fromRGBO(0, 220, 252, 1))
];
class Analysis extends StatefulWidget {
@override
_AnalysisState createState() => _AnalysisState();
}
class _AnalysisState extends State {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => ChangeSelectedDate(),
child: DefaultTabController(
initialIndex: 0,
length: 2,
child: Scaffold(
backgroundColor: blue1,
appBar: InExAppBar(false),
body: Selector(
selector: (_, changeSelectedDate) =>
changeSelectedDate.selectedAnalysisDate,
builder: (context, selectedAnalysisDate, child) {
selectedAnalysisDate ??= sharedPrefs.selectedDate;
ListView listViewChild(String type) => ListView(
children: [
ShowDate(true, selectedAnalysisDate!),
ShowDetails(type, selectedAnalysisDate),
],
);
return TabBarView(
children: [
listViewChild('Expense'),
listViewChild('Income')
],
);
})),
),
);
}
}
class ShowDate extends StatelessWidget {
final bool forAnalysis;
final String selectedDate;
const ShowDate(this.forAnalysis, this.selectedDate);
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: 10.w,
vertical: 25.h,
),
child: Row(
children: [
Icon(
Icons.calendar_today,
size: 27.sp,
color: Color.fromRGBO(82, 179, 252, 1),
),
SizedBox(
width: 10.w,
),
DateDisplay(this.selectedDate),
Spacer(),
DropDownBox(this.forAnalysis, this.selectedDate)
],
));
}
}
class DateDisplay extends StatelessWidget {
final String selectedDate;
DateDisplay(this.selectedDate);
@override
Widget build(BuildContext context) {
final String today = DateFormat(sharedPrefs.dateFormat).format(todayDT);
String since = getTranslated(context, 'Since')!;
TextStyle style =
GoogleFonts.aBeeZee(fontSize: 20.sp, fontWeight: FontWeight.bold);
Map dateMap = {
'Today': Text('$today', style: style),
'This week': Text(
'$since ${DateFormat(sharedPrefs.dateFormat).format(startOfThisWeek)}',
style: style,
),
'This month': Text(
'$since ${DateFormat(sharedPrefs.dateFormat).format(startOfThisMonth)}',
style: style),
'This quarter': Text(
'$since ${DateFormat(sharedPrefs.dateFormat).format(startOfThisQuarter)}',
style: style,
),
'This year': Text(
'$since ${DateFormat(sharedPrefs.dateFormat).format(startOfThisYear)}',
style: style,
),
'All': Text('${getTranslated(context, 'All')!}', style: style)
};
var dateListKey = dateMap.keys.toList();
var dateListValue = dateMap.values.toList();
for (int i = 0; i < dateListKey.length; i++) {
if (selectedDate == dateListKey[i]) {
return dateListValue[i];
}
}
return Container();
}
}
class ShowMoneyFrame extends StatelessWidget {
final String type;
final double typeValue, balance;
const ShowMoneyFrame(this.type, this.typeValue, this.balance);
@override
Widget build(BuildContext context) {
Widget rowFrame(String typeName, double value) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
getTranslated(context, typeName)!,
style: TextStyle(fontSize: 22.sp),
),
Expanded(
child: Text(
format(value) + ' ' + currency,
style: GoogleFonts.aBeeZee(
fontSize: format(value.toDouble()).length > 22
? 16.5.sp
: format(value.toDouble()).length > 17
? 19.5.sp
: 22.sp),
// fix here: Overflow is a temporary parameter, fix whatever it is so that the money value will never overflow
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.end,
),
),
],
);
}
return Container(
decoration: BoxDecoration(
color: Color.fromRGBO(239, 247, 253, 1),
borderRadius: BorderRadius.circular(40.r),
border: Border.all(
color: grey,
width: 0.4.w,
)),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 18.5.h),
child: Column(
children: [
rowFrame(this.type, typeValue),
SizedBox(
height: 12.5.h,
),
rowFrame('Balance', this.balance)
],
),
),
);
}
}
class ShowDetails extends StatefulWidget {
final String type, selectedDate;
ShowDetails(this.type, this.selectedDate);
@override
_ShowDetailsState createState() => _ShowDetailsState();
}
class _ShowDetailsState extends State {
Widget showInExDetails(
BuildContext context,
List transactionsSorted,
) {
List itemList = widget.type == 'Income'
? createItemList(
transactions: transactionsSorted,
forAnalysisPage: true,
isIncomeType: true,
forSelectIconPage: false)
: createItemList(
transactions: transactionsSorted,
forAnalysisPage: true,
isIncomeType: false,
forSelectIconPage: false);
return Column(
children: List.generate(itemList.length, (int) {
return
// SwipeActionCell(
// backgroundColor: Colors.transparent,
// key: ObjectKey(transactionsSorted[int]),
// performsFirstActionWithFullSwipe: true,
// trailingActions: [
// SwipeAction(
// title: "Delete",
// onTap: (CompletionHandler handler) async {
// Future onDeletion() async {
// await handler(true);
// transactionsSorted.removeAt(int);
// customToast(context, 'Transactions has been deleted');
// setState(() {});
// }
//
// Platform.isIOS
// ? await iosDialog(
// context,
// 'Deleted data can not be recovered. Are you sure you want to Delete All Transactions In This Category?',
// 'Delete',
// onDeletion)
// : await androidDialog(
// context,
// 'Deleted data can not be recovered. Are you sure you want to Delete All Transactions In This Category?',
// 'Delete',
// onDeletion);
// },
// color: red),
// ], child:
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Report(
type: widget.type,
category: itemList[int].text,
selectedDate: widget.selectedDate,
icon: iconData(itemList[int]),
))).then((value) => setState(() {}));
},
child: CategoryDetails(
widget.type,
getTranslated(context, itemList[int].text) ??
itemList[int].text,
transactionsSorted[int].amount!,
transactionsSorted[int].color,
iconData(itemList[int]),
false));
}));
}
@override
Widget build(BuildContext context) {
late Map chartDataMap;
return FutureBuilder>(
initialData: [],
future: DB.inputModelList(),
builder:
(BuildContext context, AsyncSnapshot> snapshot) {
connectionUI(snapshot);
if (snapshot.connectionState == ConnectionState.waiting) {
return ShowNullDetail(0, null, this.widget.type, false);
}
if (snapshot.data == null) {
return ShowNullDetail(0, chartDataNull, this.widget.type, true);
} else {
double income = 0, expense = 0, balance = 0;
List allTransactions =
filterData(context, snapshot.data!, widget.selectedDate);
if (allTransactions.length > 0) {
//prepare for MoneyFrame
List incomeList = [], expenseList = [];
incomeList = allTransactions
.map((data) {
if (data.type == 'Income') {
return data.amount;
}
})
.where((element) => element != null)
.toList();
expenseList = allTransactions
.map((data) {
if (data.type == 'Expense') {
return data.amount;
}
})
.where((element) => element != null)
.toList();
if (incomeList.length > 0) {
for (int i = 0; i < incomeList.length; i++) {
income = income + incomeList[i]!;
}
}
if (expenseList.length > 0) {
for (int i = 0; i < expenseList.length; i++) {
expense = expense + expenseList[i]!;
}
}
balance = income - expense;
// prepare for InExDetails
if (this.widget.type == 'Income') {
allTransactions = allTransactions
.map((data) {
if (data.type == 'Income') {
return inputModel(data);
}
})
.where((element) => element != null)
.cast()
.toList();
} else {
allTransactions = allTransactions
.map((data) {
if (data.type == 'Expense') {
return inputModel(data);
}
})
.where((element) => element != null)
.cast()
.toList();
}
}
if (allTransactions.length == 0) {
return ShowNullDetail(
balance, chartDataNull, this.widget.type, true);
} else {
List transactionsSorted = [
InputModel(
type: this.widget.type,
amount: allTransactions[0].amount,
category: allTransactions[0].category,
)
];
int i = 1;
//cmt: chartDataListDetailed.length must be greater than 2 to execute
while (i < allTransactions.length) {
allTransactions
.sort((a, b) => a.category!.compareTo(b.category!));
if (i == 1) {
chartDataMap = {
allTransactions[0].category!: allTransactions[0].amount!
};
}
if (allTransactions[i].category ==
allTransactions[i - 1].category) {
chartDataMap.update(allTransactions[i].category!,
(value) => (value + allTransactions[i].amount!),
ifAbsent: () => (allTransactions[i - 1].amount! +
allTransactions[i].amount!));
i++;
} else {
chartDataMap.addAll({
allTransactions[i].category!: allTransactions[i].amount!
});
i++;
}
transactionsSorted = chartDataMap.entries
.map((entry) => InputModel(
type: this.widget.type,
category: entry.key,
amount: entry.value,
))
.toList();
}
void recurringFunc({required int i, n}) {
if (n > i) {
for (int c = 1; c <= n - i; c++) {
transactionsSorted[i + c - 1].color = chartPieColors[c - 1];
recurringFunc(i: i, n: c);
}
}
}
for (int n = 1; n <= transactionsSorted.length; n++) {
transactionsSorted[n - 1].color = chartPieColors[n - 1];
recurringFunc(i: chartPieColors.length, n: n);
}
return Column(
children: [
ShowMoneyFrame(this.widget.type,
this.widget.type == 'Income' ? income : expense, balance),
SizedBox(height: 360.h, child: ChartPie(transactionsSorted)),
showInExDetails(
context,
// sum value of transactions having a same category to one
transactionsSorted,
)
],
);
}
}
});
}
}
class ShowNullDetail extends StatelessWidget {
final double balanceValue;
final List? chartData;
final String type;
final bool connection;
ShowNullDetail(this.balanceValue, this.chartData, this.type, this.connection);
@override
Widget build(BuildContext context) {
return Column(
children: [
ShowMoneyFrame(this.type, 0, this.balanceValue),
SizedBox(
height: 360.h,
child: connection == false ? null : ChartPie(this.chartData!)),
CategoryDetails(
this.type,
getTranslated(context, 'Category') ?? 'Category',
0,
this.type == 'Income' ? green : red,
Icons.category_outlined,
true)
],
);
}
}
class CategoryDetails extends StatelessWidget {
final String type, category;
final double amount;
final Color? color;
final IconData icon;
final bool forNullDetail;
CategoryDetails(this.type, this.category, this.amount, this.color, this.icon,
this.forNullDetail);
@override
Widget build(BuildContext context) {
return Card(
color: white,
elevation: 3,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.r)),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 15.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(
this.icon,
color: forNullDetail
? this.type == 'Income'
? green
: red
: this.color,
size: 23.sp,
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 15.w, right: 10.w),
child: Text(
this.category,
style: TextStyle(fontSize: 20.sp),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.start,
),
),
),
// attention: This widget will never overflow
Flexible(
flex: 0,
child: Text(
// '${this.color!.red},' + '${this.color!.green},' + '${this.color!.blue},',
format(amount) + ' ' + currency,
style: GoogleFonts.aBeeZee(fontSize: 20.sp),
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.end,
),
),
SizedBox(
width: 10.w,
),
forNullDetail
? SizedBox()
: Icon(
Icons.arrow_forward_ios,
size: 18.sp,
),
],
),
));
}
}
================================================
FILE: lib/project/app_pages/calendar.dart
================================================
import 'dart:collection';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_swipe_action_cell/core/cell.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:money_assistant_2608/project/classes/alert_dialog.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/category_item.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/classes/custom_toast.dart';
import 'package:money_assistant_2608/project/classes/input_model.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/database_management/sqflite_services.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:table_calendar/table_calendar.dart';
import 'dart:io' show Platform;
import '../classes/input_model.dart';
import 'edit.dart';
class Calendar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: blue1,
appBar: BasicAppBar(getTranslated(context, 'Calendar')!),
body: CalendarBody());
}
}
class CalendarBody extends StatefulWidget {
@override
_CalendarBodyState createState() => _CalendarBodyState();
}
class _CalendarBodyState extends State {
CalendarFormat _calendarFormat = CalendarFormat.month;
RangeSelectionMode _rangeSelectionMode = RangeSelectionMode
.toggledOff; // Can be toggled on/off by longpressing a date
DateTime _focusedDay = DateTime.now(),
today = DateTime(
DateTime.now().year, DateTime.now().month, DateTime.now().day);
DateTime? _selectedDay, _rangeStart, _rangeEnd;
late Map> transactions = {};
late ValueNotifier> _selectedEvents;
@override
void initState() {
super.initState();
_selectedDay = _focusedDay;
}
@override
void dispose() {
// _calendarController.dispose();
super.dispose();
}
int getHashCode(DateTime key) {
return key.day * 1000000 + key.month * 10000 + key.year;
}
/// Returns a list of [DateTime] objects from [first] to [last], inclusive.
List daysInRange(DateTime first, DateTime last) {
final dayCount = last.difference(first).inDays + 1;
return List.generate(
dayCount,
(index) => DateTime.utc(first.year, first.month, first.day + index),
);
}
Widget buildEvents(List? transactions) {
Color colorCategory;
if (transactions == null) {
return Container();
}
List itemList = createItemList(
transactions: transactions,
forAnalysisPage: false,
forSelectIconPage: false,
isIncomeType: false,
);
return ListView.builder(
shrinkWrap: true,
itemCount: itemList.length,
itemBuilder: (context, int) {
colorCategory =
transactions[int].type == 'Income' ? Colors.lightGreen : red;
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Edit(
inputModel: transactions[int],
categoryIcon: iconData(itemList[int]),
))).then((value) => setState(() {}));
},
child: SwipeActionCell(
key: ObjectKey(transactions[int]),
performsFirstActionWithFullSwipe: true,
trailingActions: [
SwipeAction(
title: getTranslated(context, 'Delete') ?? 'Delete',
//setState makes handler experience lagging
onTap: (CompletionHandler handler) {
Platform.isIOS
? iosDialog(
context,
'Are you sure you want to delete this transaction?',
'Delete', () {
DB.delete(transactions[int].id!);
setState(() {});
customToast(
context, 'Transaction has been deleted');
})
: androidDialog(
context,
'Are you sure you want to delete this transaction?',
'Delete', () {
DB.delete(transactions[int].id!);
setState(() {});
customToast(
context, 'Transaction has been deleted');
});
},
color: red),
SwipeAction(
title: getTranslated(context, 'Add') ?? 'Add',
onTap: (CompletionHandler handler) {
var model = transactions[int];
model.id = null;
DB.insert(model);
setState(() {});
customToast(context, 'Transaction has been updated');
},
color: Color.fromRGBO(255, 183, 121, 1)),
],
child: Column(
children: [
Container(
color: white,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 15.w, vertical: 15.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(
iconData(itemList[int]),
color: colorCategory,
),
SizedBox(
width: 150.w,
child: Padding(
padding:
EdgeInsets.only(left: 15.w, right: 10.w),
child: Text(
getTranslated(
context, itemList[int].text) ??
itemList[int].text,
style: TextStyle(
fontSize: 18.sp,
),
overflow: TextOverflow.ellipsis),
),
),
Expanded(
child: Text('(${transactions[int].description})',
style: TextStyle(
fontSize: 14.sp,
fontStyle: FontStyle.italic),
overflow: TextOverflow.fade),
),
//this widget will never overflow
Flexible(
flex: 0,
child: Padding(
padding:
EdgeInsets.only(right: 10.w, left: 7.w),
child: Text(
format(transactions[int].amount!) +
' ' +
currency,
style: GoogleFonts.aBeeZee(
fontSize:
format(transactions[int].amount!)
.length >
15
? 16.sp
: 17.sp,
),
overflow: TextOverflow.ellipsis),
),
),
Icon(
Icons.arrow_forward_ios,
size: 15.5.sp,
),
],
),
),
),
Divider(
height: 0,
thickness: 0.25.h,
indent: 20.w,
color: grey,
// color: Color.fromRGBO(213, 215, 217, 1),
),
],
),
));
});
}
@override
Widget build(BuildContext context) {
return FutureBuilder>(
initialData: [],
future: DB.inputModelList(),
builder: (context, snapshot) {
connectionUI(snapshot);
Map> map = {};
if (snapshot.data != null) {
for (int i = 0; i < snapshot.data!.length; i++) {
String description = snapshot.data![i].description!;
InputModel map1 = InputModel(
id: snapshot.data![i].id,
type: snapshot.data![i].type,
amount: snapshot.data![i].amount,
category: snapshot.data![i].category,
description: description,
date: snapshot.data![i].date,
time: snapshot.data![i].time);
void updateMapValue(Map> map, K key, V value) =>
map.update(key, (list) => list..add(value),
ifAbsent: () => [value]);
updateMapValue(
map,
'${snapshot.data![i].date}',
map1,
);
}
transactions = map.map((key, value) =>
MapEntry(DateFormat('dd/MM/yyyy').parse(key), value));
}
late LinkedHashMap linkedHashedMapTransactions =
LinkedHashMap>(
equals: isSameDay,
hashCode: getHashCode,
)..addAll(transactions);
List transactionsForDay(DateTime? day) =>
linkedHashedMapTransactions[day] ?? [];
if (_selectedDay != null) {
_selectedEvents = ValueNotifier(transactionsForDay(_selectedDay));
}
List _getEventsForRange(DateTime start, DateTime end) {
final days = daysInRange(start, end);
return [
for (final d in days) ...transactionsForDay(d),
];
}
void _onDaySelected(DateTime selectedDay, DateTime focusedDay) {
if (!isSameDay(_selectedDay, selectedDay)) {
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
_rangeStart = null; // Important to clean those
_rangeEnd = null;
_rangeSelectionMode = RangeSelectionMode.toggledOff;
});
_selectedEvents.value = transactionsForDay(selectedDay);
}
}
void _onRangeSelected(
DateTime? start, DateTime? end, DateTime focusedDay) {
setState(() {
_selectedDay = null;
_focusedDay = focusedDay;
_rangeStart = start;
_rangeEnd = end;
_rangeSelectionMode = RangeSelectionMode.toggledOn;
if (start != null && end != null) {
_selectedEvents = ValueNotifier(_getEventsForRange(start, end));
} else if (start != null) {
_selectedEvents = ValueNotifier(transactionsForDay(start));
} else if (end != null) {
_selectedEvents = ValueNotifier(transactionsForDay(end));
}
});
}
return Column(children: [
TableCalendar(
availableCalendarFormats: {
CalendarFormat.month: getTranslated(context, 'Month')!,
CalendarFormat.twoWeeks: getTranslated(context, '2 weeks')!,
CalendarFormat.week: getTranslated(context, 'Week')!
},
locale: Localizations.localeOf(context).languageCode,
// sixWeekMonthsEnforced: true,
// shouldFillViewport: true,
rowHeight: 52.h,
daysOfWeekHeight: 22.h,
firstDay: DateTime.utc(2000, 01, 01),
lastDay: DateTime.utc(2050, 01, 01),
focusedDay: _focusedDay,
calendarFormat: _calendarFormat,
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
rangeStartDay: _rangeStart,
rangeEndDay: _rangeEnd,
rangeSelectionMode: _rangeSelectionMode,
eventLoader: transactionsForDay,
startingDayOfWeek: StartingDayOfWeek.monday,
calendarStyle: CalendarStyle(
// weekendTextStyle:
// TextStyle().copyWith(color: Colors.blue[800]),
),
headerStyle: HeaderStyle(
formatButtonTextStyle: TextStyle(fontSize: 18.sp),
formatButtonDecoration: BoxDecoration(
boxShadow: [BoxShadow()],
color: blue2,
borderRadius: BorderRadius.circular(25.r)),
),
calendarBuilders: CalendarBuilders(
selectedBuilder: (context, date, _) {
return Container(
//see difference between margin and padding below: Margin: Out (for itself), padding: In (for its child)
// margin: EdgeInsets.all(4.0.w),
padding: EdgeInsets.only(top: 6.0.h, left: 6.0.w),
color: Color.fromRGBO(255, 168, 68, 1),
width: 46.w,
height: 46.h,
child: Text(
'${date.day}',
style: TextStyle().copyWith(fontSize: 17.0.sp),
),
);
},
todayBuilder: (context, date, _) {
return Container(
padding: EdgeInsets.only(top: 6.0.w, left: 6.0.w),
color: blue2,
width: 46.w,
height: 46.h,
child: Text(
'${date.day}',
style: TextStyle().copyWith(fontSize: 17.0.sp),
),
);
},
markerBuilder: (context, date, events) {
if (events.isNotEmpty) {
return Positioned(
right: 1.w,
bottom: 1.h,
child: _buildEventsMarker(date, events),
);
}
},
),
onDaySelected: _onDaySelected,
onRangeSelected: _onRangeSelected,
onFormatChanged: (format) {
if (_calendarFormat != format) {
setState(() {
_calendarFormat = format;
});
}
},
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
},
pageJumpingEnabled: true,
),
SizedBox(height: 8.0.h),
Expanded(
child: ValueListenableBuilder>(
valueListenable: _selectedEvents,
builder: (context, value, _) {
return Column(children: [
Balance(value),
Expanded(child: buildEvents(value))
]);
},
),
)
]);
});
}
}
Widget _buildEventsMarker(DateTime date, List events) {
double width = events.length < 100 ? 18.w : 28.w;
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Color.fromRGBO(67, 125, 229, 1),
),
width: width,
height: 18.0.h,
child: Center(
child: Text(
'${events.length}',
style: TextStyle().copyWith(
color: white,
fontSize: 13.0.sp,
),
),
),
);
}
class Balance extends StatefulWidget {
final List? events;
Balance(this.events);
@override
_BalanceState createState() => _BalanceState();
}
class _BalanceState extends State {
@override
Widget build(BuildContext context) {
double income = 0, expense = 0, balance = 0;
if (widget.events != null) {
for (int i = 0; i < widget.events!.length; i++) {
if (widget.events![i].type == 'Income') {
income = income + widget.events![i].amount;
} else {
expense = expense + widget.events![i].amount;
}
balance = income - expense;
}
}
Widget summaryFrame(String type, double amount, color) => Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// this widget will never overflow
Text(
getTranslated(context, type)!,
style: TextStyle(
color: color,
fontSize: 15.sp,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold),
),
Text(format(amount.toDouble()) + ' ' + currency,
style: GoogleFonts.aBeeZee(
color: color,
fontSize: (format(amount.toDouble()).length > 19)
? 11.5.sp
: format(amount.toDouble()).length > 14
? 14.sp
: 18.sp,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis)
],
);
return Container(
color: Colors.white54,
height: 69.h,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 10.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
summaryFrame(
'INCOME',
income,
Colors.lightGreen,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: summaryFrame('EXPENSE', expense, red)),
Flexible(
child: summaryFrame('TOTAL BALANCE', balance, Colors.black)),
],
),
),
);
}
}
================================================
FILE: lib/project/app_pages/currency.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/localization/language.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:provider/provider.dart';
import '../provider.dart';
class Currency extends StatelessWidget {
@override
Widget build(BuildContext context) {
List languageList = Language.languageList;
return Scaffold(
appBar: AppBar(
backgroundColor: blue3,
title: Text(
getTranslated(context, 'Select a currency') ??
'Select a currency',
style: TextStyle(fontSize: 21.sp)),
actions: [
Padding(
padding: EdgeInsets.only(right: 5.w),
child: TextButton(
child: Text(
getTranslated(context, 'Save') ?? 'Save',
style: TextStyle(fontSize: 18.5.sp, color: white),
),
onPressed: () => Navigator.pop(context)),
)
]),
body: ChangeNotifierProvider(
create: (context) => OnCurrencySelected(),
builder: (context, widget) => ListView.builder(
itemCount: languageList.length,
itemBuilder: (context, int) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
context.read().onCurrencySelected(
'${languageList[int].languageCode}_${languageList[int].countryCode}');
},
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(
horizontal: 15.w, vertical: 10.h),
child: Row(
children: [
Text(
Language.languageList[int].flag,
style: TextStyle(fontSize: 45.sp),
),
SizedBox(width: 30.w),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(Language.languageList[int].currencyName,
style: TextStyle(fontSize: 20.sp)),
SizedBox(height: 2.5.h),
Text(Language.languageList[int].currencyCode,
style: TextStyle(fontSize: 15.sp))
],
),
Spacer(),
context.watch().appCurrency ==
'${languageList[int].languageCode}_${languageList[int].countryCode}'
? Icon(Icons.check_circle,
size: 25.sp, color: blue3)
: SizedBox(),
SizedBox(width: 25.w),
Text(
Language.languageList[int].currencySymbol,
style: TextStyle(fontSize: 23.sp),
),
SizedBox(width: 15.w)
],
),
),
Divider(
indent: 75.w,
height: 0,
thickness: 0.25.h,
color: grey,
),
],
),
);
})),
);
}
}
================================================
FILE: lib/project/app_pages/edit.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/classes/input_model.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'input.dart';
class Edit extends StatelessWidget {
static final _formKey3 = GlobalKey(debugLabel: '_formKey3');
final InputModel? inputModel;
final IconData categoryIcon;
const Edit({
this.inputModel,
required this.categoryIcon,
});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: blue1,
appBar: BasicAppBar(getTranslated(context, 'Edit')!),
body: GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: PanelForKeyboard(AddEditInput(
formKey: _formKey3,
inputModel: this.inputModel,
categoryIcon: this.categoryIcon,
),)
),
);
}
}
================================================
FILE: lib/project/app_pages/edit_expense_category.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:provider/provider.dart';
import '../provider.dart';
import 'add_category.dart';
import 'expense_category.dart';
class EditExpenseCategory extends StatefulWidget {
final BuildContext buildContext;
EditExpenseCategory(this.buildContext);
@override
_EditExpenseCategoryState createState() => _EditExpenseCategoryState();
}
class _EditExpenseCategoryState extends State {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => ChangeExpenseItemEdit(),
child: Builder(
builder: (contextEdit) => Scaffold(
backgroundColor: blue1,
appBar: EditCategoryAppBar(
AddCategory(
contextEx: widget.buildContext,
contextExEdit: contextEdit,
type: 'Expense',
appBarTitle:
getTranslated(context, 'Add Expense Category')!,
description: ''),
),
body: ExpenseCategoryBody(
contextExEdit: contextEdit,
contextEx: widget.buildContext,
))));
}
}
================================================
FILE: lib/project/app_pages/edit_income_category.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:money_assistant_2608'
'/project/classes/app_bar.dart';
import 'package:money_assistant_2608'
'/project/classes/constants.dart';
import 'package:money_assistant_2608'
'/project/localization/methods.dart';
import 'package:provider/provider.dart';
import '../provider.dart';
import 'add_category.dart';
import 'income_category.dart';
class EditIncomeCategory extends StatelessWidget {
final BuildContext? buildContext;
EditIncomeCategory(this.buildContext);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => ChangeIncomeItemEdit(),
child: Builder(
builder: (contextEdit) => Scaffold(
backgroundColor: blue1,
appBar: EditCategoryAppBar(
AddCategory(
contextIn: this.buildContext,
contextInEdit: contextEdit ,
type: 'Income',
appBarTitle: getTranslated(context, 'Add Income Category')!,
description: ''),
),
body: IncomeCategoryBody( context: this.buildContext, contextEdit: contextEdit,editIncomeCategory: true))));
}
}
================================================
FILE: lib/project/app_pages/expense_category.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/category_item.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:money_assistant_2608/project/provider.dart';
import 'package:provider/provider.dart';
import 'add_category.dart';
import 'edit_expense_category.dart';
class ExpenseCategory extends StatefulWidget {
@override
_ExpenseCategoryState createState() => _ExpenseCategoryState();
}
class _ExpenseCategoryState extends State {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => ChangeExpenseItem(),
child: Builder(
builder: (buildContext) => Scaffold(
backgroundColor: blue1,
appBar: CategoryAppBar(EditExpenseCategory(buildContext)),
body: ExpenseCategoryBody(
contextEx: buildContext,
)),
));
}
}
class ExpenseCategoryBody extends StatefulWidget {
final BuildContext? contextExEdit,contextEx;
ExpenseCategoryBody(
{this.contextExEdit, this.contextEx});
@override
_ExpenseCategoryBodyState createState() => _ExpenseCategoryBodyState();
}
class _ExpenseCategoryBodyState extends State {
@override
Widget build(BuildContext context) {
var exItemsLists = widget.contextExEdit == null ?
Provider.of(widget.contextEx!).exItemsLists : Provider.of(widget.contextExEdit!).exItemsLists;
return SingleChildScrollView(
child: Padding(
padding: EdgeInsets.only(left: 10.w, right: 10.w, bottom: 20.h),
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: exItemsLists.length,
itemBuilder: (context, int) {
return Padding(
padding: EdgeInsets.only(top: 20.h),
child: CategoryContainer( contextEx: widget.contextEx , contextExEdit: widget.contextExEdit,
itemsList: exItemsLists[int]),
);
}),
],
),
));
}
}
// The named parameter 'body' is required, but there's no corresponding argument. Try adding the required argument.
// Widget searchBar() {
// return FloatingSearchAppBar(
// title: const Text('Enter Category Name'),
// transitionDuration: const Duration(milliseconds: 800),
// color: Colors.orangeAccent.shade100,
// colorOnScroll: Colors.greenAccent.shade200,
// height: 55,
// );
// }
class CategoryContainer extends StatefulWidget {
final BuildContext? contextEx,contextExEdit;
final List itemsList;
const CategoryContainer(
{ this.contextEx, this.contextExEdit, required this.itemsList});
@override
_CategoryContainerState createState() => _CategoryContainerState();
}
class _CategoryContainerState extends State {
@override
Widget build(BuildContext context) {
if (widget.itemsList.length < 2) {
return ParentCategory(
contextEx: widget.contextEx,
contextExEdit: widget.contextExEdit,
noChildren: true,
parentCategory: widget.itemsList[0],
);
}
return Container(
decoration: BoxDecoration(
color: white,
borderRadius: BorderRadius.circular(40.r),
),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
ParentCategory(
contextEx: widget.contextEx,
contextExEdit: widget.contextExEdit,
noChildren: false,
parentCategory: widget.itemsList[0],
),
CategoryItems(contextEx: widget.contextEx, contextExEdit: widget.contextExEdit,
categoryItemChildren: widget.itemsList.sublist(1),parentItem: widget.itemsList[0]),
]),
);
}
}
class CategoryItems extends StatefulWidget {
final BuildContext? contextEx,contextExEdit;
final List categoryItemChildren;
final CategoryItem parentItem;
const CategoryItems({this.contextEx, this.contextExEdit,
required this.categoryItemChildren, required this.parentItem});
@override
_CategoryItemsState createState() => _CategoryItemsState();
}
class _CategoryItemsState extends State {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(bottom: 25.h, top: 10.h),
child: Wrap(
children: List.generate(widget.categoryItemChildren.length, (index) {
final cellWidth = (1.sw - 20.w) / 4;
return SizedBox(
width: cellWidth,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
GestureDetector(
onLongPress:(){
if (widget.contextExEdit != null) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddCategory(
contextEx: widget.contextEx,
contextExEdit: widget.contextExEdit,
type: 'Expense',
appBarTitle: 'Add Expense Category',
categoryName:
widget.categoryItemChildren[index].text,
categoryIcon: iconData(
widget.categoryItemChildren[index]),
parentItem: widget.parentItem,
description: widget
.categoryItemChildren[index]
.description!)));
}
},
onTap: () {
if (widget.contextExEdit != null) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddCategory(
contextEx: widget.contextEx,
contextExEdit: widget.contextExEdit,
type: 'Expense',
appBarTitle: 'Add Expense Category',
categoryName:
widget.categoryItemChildren[index].text,
categoryIcon: iconData(
widget.categoryItemChildren[index]),
parentItem: widget.parentItem,
description: widget
.categoryItemChildren[index]
.description!)));
} else {
Navigator.pop(
context, widget.categoryItemChildren[index]);
}
},
child: Padding(
padding: EdgeInsets.symmetric(vertical: 15.h),
child: CircleAvatar(
radius: 24.r,
backgroundColor: Color.fromRGBO(215, 223, 231, 1),
child: Icon(
iconData(widget.categoryItemChildren[index]),
color: red,
size: 30.sp,
)),
),
),
Text(
getTranslated(
context, widget.categoryItemChildren[index].text) ??
widget.categoryItemChildren[index].text,
style: TextStyle(
fontSize: 16.sp,
),
textAlign: TextAlign.center,
),
],
),
);
}),
),
);
}
}
class ParentCategory extends StatefulWidget {
final BuildContext? contextEx, contextExEdit;
final bool noChildren;
final CategoryItem parentCategory;
ParentCategory(
{ this.contextEx,
this.contextExEdit,
required this.noChildren,
required this.parentCategory,}
);
@override
_ParentCategoryState createState() => _ParentCategoryState();
}
class _ParentCategoryState extends State {
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onLongPress:(){
if (widget.contextExEdit != null) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddCategory(
contextEx: widget.contextEx,
contextExEdit: widget.contextExEdit,
type: 'Expense',
appBarTitle: 'Add Expense Category',
categoryName: widget.parentCategory.text,
categoryIcon: iconData(widget.parentCategory),
description: widget.parentCategory.description)));
}
},
onTap: () {
if (widget.contextExEdit != null) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddCategory(
contextEx: widget.contextEx,
contextExEdit: widget.contextExEdit,
type: 'Expense',
appBarTitle: 'Add Expense Category',
categoryName: widget.parentCategory.text,
categoryIcon: iconData(widget.parentCategory),
description: widget.parentCategory.description)));
}
// consider category as parent category for simplicity, need to change later
else {
Navigator.pop(context, widget.parentCategory);
}
},
child: Container(
decoration: BoxDecoration(
color: Color.fromRGBO(222, 174, 112, 1),
borderRadius: widget.noChildren
? BorderRadius.circular(40.r)
: BorderRadius.vertical(
top: Radius.circular(40.r), bottom: Radius.zero),
),
child: Padding(
padding: EdgeInsets.symmetric(vertical: 18.h, horizontal: 15.w),
child: Row(
children: [
CircleAvatar(
backgroundColor: Color.fromRGBO(215, 223, 231, 1),
radius: 26.r,
child: Icon(
iconData(widget.parentCategory),
size: 33.sp,
color: red,
)),
SizedBox(
width: 23.5.w,
),
Expanded(
child: Text(
getTranslated(context, widget.parentCategory.text) ??
widget.parentCategory.text,
style:
TextStyle(fontSize: 22.sp, fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
),
)
],
),
),
),
);
}
}
================================================
FILE: lib/project/app_pages/income_category.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:money_assistant_2608/project/provider.dart';
import 'package:provider/provider.dart';
import 'add_category.dart';
import 'edit_income_category.dart';
class IncomeCategory extends StatefulWidget {
@override
_IncomeCategoryState createState() => _IncomeCategoryState();
}
class _IncomeCategoryState extends State {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => ChangeIncomeItem(),
child: Builder(
builder: (buildContext) => Scaffold(
backgroundColor: blue1,
appBar: CategoryAppBar(EditIncomeCategory(buildContext)),
body: IncomeCategoryBody(
context: buildContext, editIncomeCategory: false))));
}
}
class IncomeCategoryBody extends StatefulWidget {
final BuildContext? context, contextEdit;
final bool editIncomeCategory;
IncomeCategoryBody(
{this.context, this.contextEdit, required this.editIncomeCategory});
@override
_IncomeCategoryBodyState createState() => _IncomeCategoryBodyState();
}
class _IncomeCategoryBodyState extends State {
@override
Widget build(BuildContext context) {
var incomeList = widget.contextEdit == null
? Provider.of(widget.context!).incomeItems
: Provider.of(widget.contextEdit!).incomeItems;
return Padding(
padding: EdgeInsets.only(top: 30.h),
child: ListView.builder(
itemCount: incomeList.length,
itemBuilder: (context, int) {
return Padding(
padding: EdgeInsets.only(top: 3.h, left: 10.w, right: 10.w),
child: GestureDetector(
onLongPress: () {
if (this.widget.editIncomeCategory) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddCategory(
contextIn: widget.context,
contextInEdit: widget.contextEdit,
type: 'Income',
appBarTitle: 'Add Income Category',
categoryName: incomeList[int].text,
categoryIcon: iconData(incomeList[int]),
description: incomeList[int].description!)));
}
},
onTap: () {
if (this.widget.editIncomeCategory) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddCategory(
contextIn: widget.context,
contextInEdit: widget.contextEdit,
type: 'Income',
appBarTitle: 'Add Income Category',
categoryName: incomeList[int].text,
categoryIcon: iconData(incomeList[int]),
description: incomeList[int].description!)));
} else {
Navigator.pop(context, incomeList[int]);
}
},
child: Card(
elevation: 5,
color: white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(35.r),
),
child: Padding(
padding: EdgeInsets.symmetric(vertical: 15.h),
child: Row(
children: [
SizedBox(
width: 40.h,
),
CircleAvatar(
backgroundColor: Color.fromRGBO(215, 223, 231, 1),
radius: 25.r,
child: Icon(
iconData(incomeList[int]),
size: 33.sp,
color: green,
),
),
SizedBox(
width: 25.w,
),
Text(
getTranslated(context, incomeList[int].text) ??
incomeList[int].text,
style: TextStyle(
fontSize: 20.sp, fontWeight: FontWeight.bold),
)
],
),
),
),
),
);
},
),
);
}
}
================================================
FILE: lib/project/app_pages/input.dart
================================================
import 'dart:core';
import 'dart:io' show Platform;
import 'package:day_night_time_picker/day_night_time_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_material_pickers/flutter_material_pickers.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:money_assistant_2608/project/classes/alert_dialog.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/category_item.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/classes/custom_toast.dart';
import 'package:money_assistant_2608/project/classes/input_model.dart';
import 'package:money_assistant_2608/project/classes/keyboard.dart';
import 'package:money_assistant_2608/project/classes/saveOrSaveAndDeleteButtons.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/database_management/sqflite_services.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:provider/provider.dart';
import 'package:sliding_up_panel/sliding_up_panel.dart';
import '../provider.dart';
import 'expense_category.dart';
import 'income_category.dart';
late CategoryItem defaultCategory;
var selectedTime = TimeOfDay.now();
var selectedDate = DateTime.now();
InputModel model = InputModel();
PanelController _pc = PanelController();
late TextEditingController _amountController;
FocusNode? amountFocusNode, descriptionFocusNode;
class AddInput extends StatefulWidget {
@override
_AddInputState createState() => _AddInputState();
}
class _AddInputState extends State {
static final _formKey1 = GlobalKey(debugLabel: '_formKey1'),
_formKey2 = GlobalKey(debugLabel: '_formKey2');
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasFocus || !currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
if (_pc.isPanelOpen) {
_pc.close();
}
}
},
child: DefaultTabController(
initialIndex: 0,
length: 2,
child: Scaffold(
backgroundColor: blue1,
appBar: InExAppBar(true),
body:
// ChangeNotifierProvider(
// create: (context) => ChangeModelType(),
// child:
PanelForKeyboard(
TabBarView(
children: [
AddEditInput(
type: 'Expense',
formKey: _formKey2,
),
AddEditInput(
type: 'Income',
formKey: _formKey1,
)
],
),
))),
)
// )
;
}
}
class PanelForKeyboard extends StatelessWidget {
const PanelForKeyboard(
this.body,
);
final Widget body;
void _insertText(String myText) {
final text = _amountController.text;
TextSelection textSelection = _amountController.selection;
String newText = text.replaceRange(
textSelection.start,
textSelection.end,
myText,
);
if (newText.length > 13) {
newText = newText.substring(0, 13);
}
// if input starts to have '.' => Don't need to reformat
if (newText.contains('.')) {
String fractionalNumber = newText.split('.').last;
// input can not have more than 2 numbers after a decimal point
if (fractionalNumber.length > 2) {
String wholeNumber = newText.split('.').first;
newText = wholeNumber + '.' + fractionalNumber.substring(0, 2);
}
if (newText.substring(newText.length - 1) == '.') {
// input can not have more than 1 dot
if ('.'.allMatches(newText).length == 2) {
newText = newText.substring(0, newText.length - 1);
}
}
_amountController.text = newText;
} else {
_amountController.text =
format(double.parse(newText.replaceAll(',', '')));
}
//define text input and cursor position
textSelection = TextSelection.fromPosition(
TextPosition(offset: _amountController.text.length));
_amountController.selection = textSelection;
}
void _backspace() {
final text = _amountController.text;
TextSelection textSelection = _amountController.selection;
// The cursor is at the beginning.
if (textSelection.start == 0) {
return;
}
final selectionLength = textSelection.end - textSelection.start;
// There is a selection.
if (selectionLength > 0) {
final newText = text.replaceRange(
textSelection.start,
textSelection.end,
'',
);
// if users delete all input or if input has '.'
// => Don't need to reformat when deleting
if (newText == '' || newText.contains('.')) {
_amountController.text = newText;
} else {
_amountController.text =
format(double.parse(newText.replaceAll(',', '')));
}
textSelection = TextSelection.fromPosition(
TextPosition(offset: _amountController.text.length));
_amountController.selection = textSelection;
return;
}
// Delete the previous character
final previousCodeUnit = text.codeUnitAt(textSelection.start - 1);
final offset = _isUtf16Surrogate(previousCodeUnit) ? 2 : 1;
final newStart = textSelection.start - offset;
final newEnd = textSelection.start;
final newText = text.replaceRange(
newStart,
newEnd,
'',
);
if (newText == '' || newText.contains('.')) {
_amountController.text = newText;
} else {
_amountController.text =
format(double.parse(newText.replaceAll(',', '')));
}
textSelection = TextSelection.fromPosition(
TextPosition(offset: _amountController.text.length));
_amountController.selection = textSelection;
}
bool _isUtf16Surrogate(int value) {
return value & 0xF800 == 0xD800;
}
@override
Widget build(BuildContext context) {
return SlidingUpPanel(
controller: _pc,
minHeight: 0,
maxHeight: 300.h,
parallaxEnabled: true,
isDraggable: false,
panelSnapping: true,
panel: CustomKeyboard(
panelController: _pc,
mainFocus: amountFocusNode,
nextFocus: descriptionFocusNode,
onTextInput: (myText) {
_insertText(myText);
},
onBackspace: () {
_backspace();
},
page: model.type == 'Income'
// Provider.of(context).modelType == 'Income'
? IncomeCategory()
: ExpenseCategory(),
),
body: this.body);
}
}
class AddEditInput extends StatelessWidget {
final GlobalKey formKey;
final InputModel? inputModel;
final String? type;
final IconData? categoryIcon;
const AddEditInput({
required this.formKey,
this.inputModel,
this.type,
this.categoryIcon,
});
@override
Widget build(BuildContext context) {
if (this.inputModel != null) {
model = this.inputModel!;
defaultCategory = categoryItem(this.categoryIcon!, model.category!);
// Provider.of(context, listen: false)
// .changeModelType(this.inputModel!.type!);
} else {
model = InputModel(
type: this.type,
);
defaultCategory = categoryItem(Icons.category_outlined, 'Category');
// Provider.of(context, listen: false)
// .changeModelType(this.type!);
}
return ChangeNotifierProvider(
create: (context) => ChangeCategoryA(),
child: ListView(children: [
AmountCard(),
SizedBox(
height: 30.h,
),
Container(
decoration: BoxDecoration(
color: white,
border: Border.all(
color: grey,
width: 0.6.w,
)),
child: Column(
children: [
CategoryCard(),
DescriptionCard(),
DateCard(),
],
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 70.h),
child: this.inputModel != null
? SaveAndDeleteButton(
saveAndDeleteInput: true,
formKey: this.formKey,
)
: SaveButton(true, null, true),
)
]));
}
}
class AmountCard extends StatefulWidget {
@override
_AmountCardState createState() => _AmountCardState();
}
class _AmountCardState extends State {
@override
void initState() {
super.initState();
amountFocusNode = FocusNode();
_amountController = TextEditingController(
text: model.id == null ? '' : format(model.amount!),
);
}
// @override
// void dispose(){
// amountFocusNode!.dispose();
// super.dispose();
// }
@override
Widget build(BuildContext context) {
Color colorMain = model.type == 'Income' ? green : red;
return Container(
decoration: BoxDecoration(
color: white,
border: Border(
bottom: BorderSide(
color: grey,
width: 0.6.h,
))),
child: Padding(
padding:
EdgeInsets.only(top: 15.h, bottom: 30.h, right: 20.w, left: 20.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'${getTranslated(context, 'Amount')}',
style: TextStyle(
fontSize: 22.sp,
),
),
TextFormField(
controller: _amountController,
readOnly: true,
showCursor: true,
maxLines: null,
minLines: 1,
// maxLength: ,
// inputFormatters: [
// FilteringTextInputFormatter.allow(
// RegExp(r'^\d*(.?|,?)\d{0,2}')),
// ],
onTap: () => _pc.open(),
cursorColor: colorMain,
style: GoogleFonts.aBeeZee(
color: colorMain,
fontSize: 35.sp,
fontWeight: FontWeight.bold),
focusNode: amountFocusNode,
decoration: InputDecoration(
hintText: '0',
hintStyle: GoogleFonts.aBeeZee(
color: colorMain,
fontSize: 35.sp,
fontWeight: FontWeight.bold),
icon: Padding(
padding: EdgeInsets.only(right: 5.w),
child: Icon(
Icons.monetization_on,
size: 45.sp,
color: colorMain,
),
),
suffixIcon: _amountController.text.length > 0
? IconButton(
icon: Icon(
Icons.clear,
size: 24.sp,
),
onPressed: () {
_amountController.clear();
})
: SizedBox(),
),
),
],
),
),
);
}
}
class CategoryCard extends StatefulWidget {
@override
_CategoryCardState createState() => _CategoryCardState();
}
class _CategoryCardState extends State {
@override
Widget build(BuildContext context) {
return Consumer(builder: (_, changeCategoryA, __) {
changeCategoryA.categoryItemA ??= defaultCategory;
var categoryItem = changeCategoryA.categoryItemA;
model.category = categoryItem!.text;
return GestureDetector(
onTap: () async {
if (_pc.isPanelOpen) {
_pc.close();
}
CategoryItem newCategoryItem = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => model.type == 'Income'
? IncomeCategory()
: ExpenseCategory()),
);
changeCategoryA.changeCategory(newCategoryItem);
},
child: Column(children: [
Padding(
padding: EdgeInsets.only(
left: 20.w, right: 20.w, top: 20.h, bottom: 21.h),
child: Row(
children: [
Icon(
iconData(categoryItem),
size: 40.sp,
color: model.type == 'Income' ? green : red,
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 31.w),
child: Text(
getTranslated(context, categoryItem.text) ??
categoryItem.text,
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
),
overflow: TextOverflow.ellipsis,
),
),
),
// Spacer(),
Icon(
Icons.arrow_forward_ios_outlined,
size: 20.sp,
),
],
),
),
Divider(
height: 0,
thickness: 0.25.w,
color: grey,
indent: 85.w,
),
]));
});
}
}
class DescriptionCard extends StatefulWidget {
@override
_DescriptionCardState createState() => _DescriptionCardState();
}
class _DescriptionCardState extends State {
static late TextEditingController descriptionController;
@override
void initState() {
super.initState();
descriptionFocusNode = FocusNode();
descriptionController =
TextEditingController(text: model.description ?? '');
}
// @override
// void dispose(){
// descriptionFocusNode!.dispose();
// super.dispose();
// }
KeyboardActionsConfig _buildConfig(BuildContext context) {
return KeyboardActionsConfig(
nextFocus: false,
keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
keyboardBarColor: Colors.grey[200],
actions: [
KeyboardActionsItem(
focusNode: descriptionFocusNode!,
toolbarButtons: [
(node) {
return SizedBox(
width: 1.sw,
child: Padding(
padding: EdgeInsets.only(left: 5.w, right: 16.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
FocusScope.of(context)
.requestFocus(amountFocusNode);
_pc.open();
},
child: SizedBox(
height: 35.h,
width: 60.w,
child: Icon(Icons.keyboard_arrow_up,
size: 25.sp, color: Colors.blueGrey),
),
),
// GestureDetector(
// onTap: () {
// node.unfocus();
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => model.type == 'Income'
// ? IncomeCategory()
// : ExpenseCategory()));
// },
// child: Text(
// getTranslated(context, 'Choose Category')!,
// style: TextStyle(
// fontSize: 16.sp,
// fontWeight: FontWeight.bold,
// color: Colors.blueGrey),
// ),
// ),
GestureDetector(
onTap: () => node.unfocus(),
child: Text(
getTranslated(context, "Done")!,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Colors.blue),
))
],
)),
);
},
])
]);
}
@override
Widget build(BuildContext context) {
return KeyboardActions(
overscroll: 0,
disableScroll: true,
tapOutsideBehavior: TapOutsideBehavior.translucentDismiss,
autoScroll: false,
config: _buildConfig(context),
child: Column(children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 8.5.h),
child: TextFormField(
controller: descriptionController,
maxLines: null,
minLines: 1,
keyboardType: TextInputType.multiline,
keyboardAppearance: Brightness.light,
// maxLength: ,
onTap: () {
if (_pc.isPanelOpen) {
_pc.close();
}
},
cursorColor: blue1,
textCapitalization: TextCapitalization.sentences,
style: TextStyle(fontSize: 20.sp),
focusNode: descriptionFocusNode,
textInputAction: TextInputAction.newline,
decoration: InputDecoration(
border: InputBorder.none,
hintText: getTranslated(context, 'Description'),
hintStyle: GoogleFonts.cousine(
fontSize: 22.sp,
fontStyle: FontStyle.italic,
),
suffixIcon: descriptionController.text.length > 0
? IconButton(
icon: Icon(
Icons.clear,
size: 20.sp,
),
onPressed: () {
descriptionController.clear();
})
: SizedBox(),
icon: Padding(
padding: EdgeInsets.only(right: 15.w),
child: Icon(
Icons.description_outlined,
size: 40.sp,
color: Colors.blueGrey,
),
)),
),
),
Divider(
height: 0,
thickness: 0.25.w,
color: grey,
indent: 85.w,
)
]),
);
}
}
class DateCard extends StatefulWidget {
const DateCard();
@override
_DateCardState createState() => _DateCardState();
}
class _DateCardState extends State {
@override
Widget build(BuildContext context) {
if (model.date == null) {
model.date = DateFormat('dd/MM/yyyy').format(selectedDate);
model.time = selectedTime.format(context);
}
return Padding(
padding:
EdgeInsets.only(left: 20.w, right: 20.w, top: 17.5.h, bottom: 19.h),
child: Row(
children: [
GestureDetector(
onTap: () {
if (_pc.isPanelOpen) {
_pc.close();
}
showMaterialDatePicker(
headerColor: blue3,
headerTextColor: Colors.black,
backgroundColor: white,
buttonTextColor: Color.fromRGBO(80, 157, 253, 1),
cancelText: getTranslated(context, 'CANCEL'),
confirmText: getTranslated(context, 'OK') ?? 'OK',
maxLongSide: 450.w,
maxShortSide: 300.w,
title: getTranslated(context, 'Select a date'),
context: context,
firstDate: DateTime(1990, 1, 1),
lastDate: DateTime(2050, 12, 31),
selectedDate: DateFormat('dd/MM/yyyy').parse(model.date!),
onChanged: (value) => setState(() {
selectedDate = value;
model.date = DateFormat('dd/MM/yyyy').format(value);
}),
);
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.only(right: 30.w),
child: Icon(
Icons.event,
size: 40.sp,
color: Colors.blue,
),
),
Text(
DateFormat(sharedPrefs.dateFormat).format(
DateFormat('dd/MM/yyyy').parse(
model.date!)),
style: GoogleFonts.aBeeZee(
fontSize: 21.5.sp,
),
),
],
),
),
Spacer(),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
if (_pc.isPanelOpen) {
_pc.close();
}
Navigator.of(context).push(
showPicker(
cancelText: getTranslated(context, 'Cancel') ?? 'Cancel',
okText: getTranslated(context, 'Ok') ?? 'Ok',
unselectedColor: grey,
dialogInsetPadding: EdgeInsets.symmetric(
horizontal: 50.w, vertical: 30.0.h),
elevation: 12,
context: context,
value: selectedTime,
is24HrFormat: true,
onChange: (value) => setState(() {
selectedTime = value;
model.time = value.format(context);
})),
);
},
child: Text(
model.time!,
style: GoogleFonts.aBeeZee(
fontSize: 21.5.sp,
),
),
)
],
),
);
}
}
void saveInputFunc(BuildContext context, bool saveFunction) {
model.amount = _amountController.text.isEmpty
? 0
: double.parse(_amountController.text.replaceAll(',', ''));
model.description = _DescriptionCardState.descriptionController.text;
if (saveFunction) {
DB.insert(model);
_amountController.clear();
if (_DescriptionCardState.descriptionController.text.length > 0) {
_DescriptionCardState.descriptionController.clear();
}
customToast(context, 'Data has been saved');
} else {
DB.update(model);
Navigator.pop(context);
customToast(context, getTranslated(context, 'Transaction has been updated') ?? 'Transaction has been updated');
}
}
Future deleteInputFunction(
BuildContext context,
) async {
void onDeletion() {
DB.delete(model.id!);
Navigator.pop(context);
customToast(context, 'Transaction has been deleted');
}
Platform.isIOS
? await iosDialog(
context,
'Are you sure you want to delete this transaction?',
'Delete',
onDeletion)
: await androidDialog(
context,
'Are you sure you want to delete this transaction?',
'Delete',
onDeletion);
}
================================================
FILE: lib/project/app_pages/others.dart
================================================
import 'dart:core';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:in_app_review/in_app_review.dart';
import 'package:intl/intl.dart';
import 'package:money_assistant_2608/project/app_pages/select_date_format.dart';
import 'package:money_assistant_2608/project/app_pages/select_language.dart';
import 'package:money_assistant_2608/project/auth_pages/user_account.dart';
import 'package:money_assistant_2608/project/classes/alert_dialog.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/classes/custom_toast.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/database_management/sqflite_services.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:provider/provider.dart';
import 'package:share_plus/share_plus.dart';
import 'dart:io' show Platform;
import '../provider.dart';
import 'currency.dart';
class Other extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
primary: true,
appBar: PreferredSize(
preferredSize: Size.fromHeight(
150.h,
),
child: Container(
color: blue3,
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 0),
height: 200.h,
child: Padding(
padding: EdgeInsets.only(top: 30.w),
child: Row(
children: [
CircleAvatar(
child: CircleAvatar(
child: Icon(
FontAwesomeIcons.smileBeam,
color: Colors.black,
size: 71.sp,
),
radius: 35.r,
backgroundColor: blue1),
radius: 40.r,
backgroundColor: Colors.orangeAccent,
),
SizedBox(
width: 20.w,
),
Text(
'${getTranslated(context, 'Hi you')!}!',
style: TextStyle(fontSize: 30.sp),
),
// Spacer(),
// Icon(
// Icons.notifications_rounded,
// size: 25.sp,
// )
],
),
),
),
),
body: ChangeNotifierProvider(
create: (context) => OnSwitch(),
builder: (context, widget) => Settings(providerContext: context)));
}
}
class Settings extends StatefulWidget {
final BuildContext providerContext;
const Settings({required this.providerContext});
@override
State createState() => _SettingsState();
}
class _SettingsState extends State {
@override
Widget build(BuildContext context) {
List pageRoute = [
UserAccount(),
SelectLanguage(),
Currency(),
];
List settingsIcons = [
Icon(
Icons.account_circle,
size: 35,
color: Colors.lightBlue,
),
// Icon(
// Icons.settings,
// size: 32,
// color: Colors.blueGrey[800],
// ),
// Icon(
// Icons.feedback,
// size: 35.sp,
// color: Colors.black54,
// ),
Icon(
Icons.language,
size: 32.sp,
color: Colors.lightBlue,
),
Icon(
Icons.monetization_on,
size: 32.sp,
color: Colors.orangeAccent,
),
Icon(Icons.format_align_center, size: 32.sp, color: Colors.lightBlue),
Icon(Icons.refresh, size: 32.sp, color: Colors.lightBlue),
Icon(Icons.delete_forever, size: 32.sp, color: red),
// Icon(Icons.lock, size: 32.sp, color: Colors.blueGrey),
Icon(
Icons.share,
size: 28.sp,
color: Colors.lightBlue,
),
Icon(
Icons.star,
size: 32.sp,
color: Colors.amber,
),
];
List settingsList = [
getTranslated(context, 'My Account')!,
// getTranslated(context, 'General Settings')!,
// getTranslated(context, 'Feedback')!,
getTranslated(context, 'Language') ?? 'Language',
getTranslated(context, 'Currency') ?? 'Currency',
(getTranslated(context, 'Date format') ??
'Date format') +
' (${DateFormat(sharedPrefs.dateFormat).format(now)})',
getTranslated(context, 'Reset All Categories') ?? 'Reset All Categories',
getTranslated(context, 'Delete All Data') ?? 'Delete All Data',
// getTranslated(context, 'Enable Passcode') ?? 'Enable Passcode',
getTranslated(context, 'Share Friends') ?? 'Share Friends',
getTranslated(context, 'Rate App') ?? 'Rate App',
];
return ListView.builder(
itemCount: settingsList.length,
itemBuilder: (context, int) {
// void onPasscodeSwitched() {
// context.read().onSwitch();
// if (context.read().isPasscodeOn) {
// showDialog(
// context: context,
// builder: (providerContext) =>
// OtherLockScreen(providerContext: this.providerContext));
// } else {
// customToast(context, 'Passcode has been disabled');
// }
// }
return GestureDetector(
onTap: () async {
if ((int == 0) || (int == 1) || (int == 2)) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => pageRoute[int]));
} else if (int == 3) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => FormatDate()))
.then((value) => setState(() {}));
} else if (int == 4) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => EditIncomeCategory(null)));
void onReset() {
sharedPrefs.setItems(setCategoriesToDefault: true);
customToast(context, 'Categories have been reset');
}
Platform.isIOS
? await iosDialog(
context,
'This action cannot be undone. Are you sure you want to reset all categories?',
'Reset',
onReset)
: await androidDialog(
context,
'This action cannot be undone. Are you sure you want to reset all categories?',
'reset',
onReset);
} else if (int == 5) {
Future onDeletion() async {
await DB.deleteAll();
customToast(context, 'All data has been deleted');
}
Platform.isIOS
? await iosDialog(
context,
'Deleted data can not be recovered. Are you sure you want to delete all data?',
'Delete',
onDeletion)
: await androidDialog(
context,
'Deleted data can not be recovered. Are you sure you want to delete all data?',
'Delete',
onDeletion);
}
// else if (int == 4) {
// onPasscodeSwitched();
// }
else if (int == 6) {
Share.share(
'https://apps.apple.com/us/app/mmas-money-tracker-bookkeeper/id1582638369');
} else {
final InAppReview inAppReview = InAppReview.instance;
await inAppReview.openStoreListing(
appStoreId: Platform.isIOS
? '1582638369'
: 'com.mmas.money_assistant_2608',
);
}
},
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(vertical: 7.h),
child: SizedBox(
child: Center(
child: ListTile(
title: Padding(
padding: EdgeInsets.symmetric(horizontal: 8.w),
child: Text(
'${settingsList[int]}',
style: TextStyle(fontSize: 18.5.sp),
),
),
leading: CircleAvatar(
radius: 24.r,
backgroundColor: Color.fromRGBO(229, 231, 234, 1),
child: settingsIcons[int]),
trailing:
// int == 4
// ? Switch(
// value: context.watch().isPasscodeOn,
// onChanged: (value) {
// onPasscodeSwitched();
// },
// activeTrackColor: blue1,
// activeColor: Color.fromRGBO(71, 131, 192, 1),
// ) :
Icon(
Icons.arrow_forward_ios,
size: 20.sp,
color: Colors.blueGrey,
),
)),
),
),
Divider(
indent: 78.w,
height: 0.1.h,
thickness: 0.4.h,
color: grey,
),
],
),
);
});
}
}
// class Upgrade extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return Stack(
// alignment: Alignment.center,
// children: [
// Container(
// height: 165.h,
// color: Color.fromRGBO(234, 234, 234, 1),
// ),
// Container(
// alignment: Alignment.center,
// height: 115.h,
// decoration: BoxDecoration(
// image: DecorationImage(
// fit: BoxFit.fill, image: AssetImage('images/image13.jpg'))),
// ),
// Container(
// alignment: Alignment.center,
// decoration: BoxDecoration(
// color: Color.fromRGBO(255, 255, 255, 1),
// borderRadius: BorderRadius.circular(40),
// border: Border.all(
// color: Colors.grey,
// width: 0.5.w,
// )),
// height: 55.h,
// width: 260.w,
// child: Text(
// getTranslated(context, 'VIEW UPGRADE OPTIONS')!,
// style: TextStyle(fontSize: 4.206, fontWeight: FontWeight.bold),
// ),
// ),
// ],
// );
// }
// }
================================================
FILE: lib/project/app_pages/parent_category.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/category_item.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
class ParentCategoryList extends StatelessWidget {
@override
Widget build(BuildContext context) {
List parentCategories = sharedPrefs.getAllExpenseItemsLists()
.map((item) => CategoryItem(
item[0].iconCodePoint,
item[0].iconFontPackage,
item[0].iconFontFamily,
item[0].text,
item[0].description))
.toList();
return Scaffold(
appBar: BasicAppBar(getTranslated(context, 'Parent category')!),
body: ListView.builder(
itemCount: parentCategories.length,
itemBuilder: (context, int) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
Navigator.pop(
context,
parentCategories[int]);
},
child: Column(
children: [
Padding(
padding:
EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.h),
child: Row(
children: [
CircleAvatar(
backgroundColor: Color.fromRGBO(215, 223, 231, 1),
radius: 20.r,
child: Icon(
iconData(parentCategories[int]),
size: 25.sp,
color: red,
)),
SizedBox(
width: 28.w,
),
Expanded(
child: Text(
getTranslated(context, parentCategories[int].text) ??
parentCategories[int].text,
style: TextStyle(fontSize: 22.sp),
overflow: TextOverflow.ellipsis,
),
)
],
),
),
Divider(
thickness: 0.25.h,
indent: 67.w,
color: grey,
)
],
));
},
),
);
}
}
================================================
FILE: lib/project/app_pages/report.dart
================================================
/// Package import
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_swipe_action_cell/core/cell.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:money_assistant_2608/project/classes/alert_dialog.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/classes/custom_toast.dart';
import 'package:money_assistant_2608/project/classes/input_model.dart';
import 'package:money_assistant_2608/project/classes/dropdown_box.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/database_management/sqflite_services.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:money_assistant_2608/project/provider.dart';
import 'package:provider/provider.dart';
import 'dart:io' show Platform;
/// Chart import
import 'package:syncfusion_flutter_charts/charts.dart';
import 'edit.dart';
var year = todayDT.year;
class Report extends StatefulWidget {
final String type;
final String category;
final String selectedDate;
final IconData icon;
const Report({
required this.type,
required this.category,
required this.selectedDate,
required this.icon,
});
@override
_ReportState createState() => _ReportState();
}
class _ReportState extends State {
@override
Widget build(BuildContext context) {
Color color = widget.type == getTranslated(context, 'Income') ? green : red;
return Scaffold(
backgroundColor: blue1,
appBar: BasicAppBar(getTranslated(context, 'Report')!),
body: Column(
children: [
Padding(
padding: EdgeInsets.only(
top: 17.h,
bottom: 15.h,
left: 7.w,
right: 7.w,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(right: 15.w),
child: Icon(this.widget.icon, size: 30.sp, color: color),
),
Flexible(
child: Text(
'${getTranslated(context, widget.category) ?? widget.category} ($year)',
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.bold,
color: color),
overflow: TextOverflow.ellipsis,
),
)
],
),
),
Expanded(
child: ReportBody(widget.type, widget.category, widget.selectedDate,
color, widget.icon),
)
],
),
);
}
}
class ReportBody extends StatefulWidget {
final String type;
final String category;
final String selectedDate;
final Color color;
final IconData icon;
ReportBody(
this.type, this.category, this.selectedDate, this.color, this.icon);
@override
_ReportBodyState createState() => _ReportBodyState();
}
class _ReportBodyState extends State {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => InputModelList(),
builder: (context, child) {
return FutureBuilder>(
initialData: [],
future: Provider.of(context).inputModelList,
builder: (BuildContext context,
AsyncSnapshot> snapshot) {
connectionUI(snapshot);
if (snapshot.data == null ||
snapshot.connectionState == ConnectionState.waiting) {
return SizedBox();
} else {
double yearAmount = 0;
DateTime date(int duration) =>
startOfThisYear.add(Duration(days: duration));
bool isLeapYear(int year) {
return (year % 4 == 0) && (year % 100 != 0) ||
(year % 400 == 0);
}
// widget.transactions.sort((a, b) => a.date!.compareTo(b.date!));
List sortByCategory(
List data, String type) {
return data
.map((data) {
if (data.type == type &&
data.category == widget.category) {
return inputModel(data);
}
})
.where((element) => element != null)
.toList()
.cast();
}
List transactions = widget.type == 'Income'
? sortByCategory(snapshot.data!, 'Income')
: sortByCategory(snapshot.data!, 'Expense');
List transactionsYearly = (transactions
.map((data) {
DateTime dateSelectedDT =
DateFormat('dd/MM/yyyy').parse(data.date!);
if (dateSelectedDT.isAfter(startOfThisYear
.subtract(Duration(days: 1))) &&
dateSelectedDT
.isBefore(DateTime(todayDT.year, 12, 31))) {
return inputModel(data);
}
})
.where((element) => element != null)
.toList())
.cast();
if (transactionsYearly.length > 0) {
for (InputModel? transaction in transactionsYearly) {
yearAmount = yearAmount + transaction!.amount!;
}
}
MonthAmount monthBasedTransaction(
String month, DateTime date, int days) {
double monthAmount = 0;
for (InputModel transaction in transactionsYearly) {
DateTime dateSelectedDT =
DateFormat('dd/MM/yyyy').parse(transaction.date!);
if (dateSelectedDT.isAfter(date) &&
dateSelectedDT.isBefore(
startOfThisYear.add(Duration(days: days)))) {
transaction.amount ??= 0;
monthAmount = monthAmount + transaction.amount!;
}
}
return MonthAmount(month, monthAmount);
}
List? monthBasedTransactionList =
isLeapYear(year)
? []
: [
monthBasedTransaction(
'Jan',
startOfThisYear.subtract(Duration(days: 1)),
30),
monthBasedTransaction('Feb', date(30), 58),
monthBasedTransaction('Mar', date(58), 89),
monthBasedTransaction('Apr', date(89), 119),
monthBasedTransaction('May', date(119), 150),
monthBasedTransaction('Jun', date(150), 180),
monthBasedTransaction('Jul', date(180), 211),
monthBasedTransaction('Aug', date(211), 242),
monthBasedTransaction('Sep', date(242), 272),
monthBasedTransaction('Oct', date(272), 302),
monthBasedTransaction('Nov', date(302), 333),
monthBasedTransaction('Dec', date(333), 364),
];
double maximumMonthAmount =
monthBasedTransactionList[0].amount;
for (int i = 0; i < monthBasedTransactionList.length; i++) {
if (monthBasedTransactionList[i].amount >
maximumMonthAmount) {
maximumMonthAmount = monthBasedTransactionList[i].amount;
}
}
return Column(
children: [
Padding(
padding: EdgeInsets.only(right: 8.0.w),
child: SizedBox(
height: 280.h,
child: SfCartesianChart(
primaryXAxis: CategoryAxis(
// placeLabelsNearAxisLine: true,
// edgeLabelPlacement: EdgeLabelPlacement.none,
// majorTickLines: MajorTickLines(size: 5, width: 1),
axisLine: AxisLine(
width: 3.h,
),
labelPlacement: LabelPlacement.onTicks,
isVisible: true,
labelRotation: -45,
rangePadding: ChartRangePadding.none,
majorGridLines: MajorGridLines(width: 0)),
// tooltipBehavior: _tooltipBehavior,
primaryYAxis: NumericAxis(
majorGridLines: MajorGridLines(width: 0),
minimum: 0,
maximum: maximumMonthAmount,
labelFormat: '{value}',
axisLine: AxisLine(
width: 4.h,
),
majorTickLines: MajorTickLines(size: 5.sp)),
series: _getGradientAreaSeries(
this.widget.type, monthBasedTransactionList),
onMarkerRender: (MarkerRenderArgs args) {
if (this.widget.type == 'Income') {
if (args.pointIndex == 0) {
args.color =
const Color.fromRGBO(9, 110, 16, 1);
} else if (args.pointIndex == 1) {
args.color =
const Color.fromRGBO(19, 134, 13, 1);
} else if (args.pointIndex == 2) {
args.color =
const Color.fromRGBO(55, 171, 49, 1);
} else if (args.pointIndex == 3) {
args.color =
const Color.fromRGBO(77, 213, 70, 1);
} else if (args.pointIndex == 4) {
args.color =
const Color.fromRGBO(134, 213, 70, 1);
} else if (args.pointIndex == 5) {
args.color =
const Color.fromRGBO(156, 222, 103, 1);
} else if (args.pointIndex == 6) {
args.color =
const Color.fromRGBO(153, 249, 172, 1);
} else if (args.pointIndex == 7) {
args.color =
const Color.fromRGBO(189, 235, 120, 1);
} else if (args.pointIndex == 8) {
args.color =
const Color.fromRGBO(177, 249, 191, 1);
} else if (args.pointIndex == 9) {
args.color =
const Color.fromRGBO(217, 241, 179, 1);
} else if (args.pointIndex == 10) {
args.color =
const Color.fromRGBO(235, 246, 199, 1);
} else if (args.pointIndex == 11) {
args.color = Colors.white;
}
} else {
if (args.pointIndex == 0) {
args.color =
const Color.fromRGBO(159, 16, 32, 1);
} else if (args.pointIndex == 1) {
args.color =
const Color.fromRGBO(197, 71, 84, 1);
} else if (args.pointIndex == 2) {
args.color =
const Color.fromRGBO(207, 124, 168, 1);
} else if (args.pointIndex == 3) {
args.color =
const Color.fromRGBO(219, 128, 161, 1);
} else if (args.pointIndex == 4) {
args.color =
const Color.fromRGBO(213, 143, 151, 1);
} else if (args.pointIndex == 5) {
args.color =
const Color.fromRGBO(226, 157, 126, 1);
} else if (args.pointIndex == 6) {
args.color =
const Color.fromRGBO(230, 168, 138, 1);
} else if (args.pointIndex == 7) {
args.color =
const Color.fromRGBO(221, 176, 108, 1);
} else if (args.pointIndex == 8) {
args.color =
const Color.fromRGBO(222, 187, 97, 1);
} else if (args.pointIndex == 9) {
args.color =
const Color.fromRGBO(250, 204, 160, 1);
} else if (args.pointIndex == 10) {
args.color =
const Color.fromRGBO(248, 219, 191, 1);
} else if (args.pointIndex == 11) {
args.color = Colors.white;
}
}
},
),
),
),
Text(
'${getTranslated(context, 'This year')}: ${format(yearAmount.toDouble())} $currency',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.5.sp, fontWeight: FontWeight.bold),
),
ChangeNotifierProvider(
create: (context) => ChangeSelectedDate(),
child: Selector(
selector: (_, changeSelectedDate) =>
changeSelectedDate.selectedReportDate,
builder: (context, selectedAnalysisDate, child) {
selectedAnalysisDate ??= widget.selectedDate;
//selectedTransactions is data sorted by category and selectedDate
List selectedTransactions =
filterData(context, transactions,
selectedAnalysisDate);
double totalAmount = 0;
if (selectedTransactions.length > 0) {
for (InputModel? transaction
in selectedTransactions) {
totalAmount =
totalAmount + transaction!.amount!;
}
}
return Expanded(
child: Column(
children: [
// selectedTransactions = selectedTransactions.reversed.toList();
Padding(
padding: EdgeInsets.only(
left:
totalAmount.toString().length <
16
? 10.w
: 6.w,
right:
totalAmount.toString().length <
15
? 20.h
: 10.h,
top: 25.h),
child: Row(
children: [
DropDownBox(
false, selectedAnalysisDate),
Spacer(),
Text(
'${format(totalAmount.toDouble())} $currency',
style: GoogleFonts.aBeeZee(
fontSize: format(totalAmount
.toDouble())
.toString()
.length >
18
? 14.sp
: format(totalAmount
.toDouble())
.toString()
.length >
14
? 17.sp
: 20.sp,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
color: widget.color),
)
],
),
),
Divider(
thickness: 0.5.h,
height: 25.h,
color: grey,
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount:
selectedTransactions.length,
itemBuilder: (context, int) {
return GestureDetector(
behavior:
HitTestBehavior.translucent,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Edit(
inputModel:
selectedTransactions[
int],
categoryIcon:
widget.icon,
))).then(
(value) => Provider.of<
InputModelList>(
context,
listen: false)
.changeInputModelList());
},
child: SwipeActionCell(
backgroundColor:
Colors.transparent,
key: ObjectKey(
selectedTransactions[
int]),
performsFirstActionWithFullSwipe:
true,
trailingActions: <
SwipeAction>[
SwipeAction(
title: getTranslated(context, 'Delete') ?? 'Delete',
onTap:
(CompletionHandler
handler) async {
Platform.isIOS
? iosDialog(
context,
'Are you sure you want to delete this transaction?',
'Delete',
() async {
DB.delete(
selectedTransactions[
int]
.id!);
await handler(
true);
Provider.of(
context,
listen:
false)
.changeInputModelList();
customToast(
context,
'Transaction has been deleted');
})
: androidDialog(
context,
'Are you sure you want to delete this transaction?',
'Delete',
() async {
DB.delete(
selectedTransactions[
int]
.id!);
await handler(
true);
Provider.of(
context,
listen:
false)
.changeInputModelList();
customToast(
context,
'Transaction has been deleted');
});
},
color: red),
SwipeAction(
title: getTranslated(context, 'Add') ?? 'Add',
onTap:
(CompletionHandler
handler) {
var model =
selectedTransactions[
int];
model.id = null;
DB.insert(model);
Provider.of(
context,
listen: false)
.changeInputModelList();
customToast(context,
'Transaction has been updated');
},
color: Color.fromRGBO(
255, 183, 121, 1)),
],
child: Padding(
padding: EdgeInsets.only(
left: 15.w,
right: 15.w,
top: 7.h),
child: Row(
children: [
Text(
DateFormat(sharedPrefs
.dateFormat)
.format(DateFormat(
'dd/MM/yyyy')
.parse(selectedTransactions[
int]
.date!)),
style: GoogleFonts
.aBeeZee(
fontSize:
17.sp)),
Spacer(),
Text(
'${format(selectedTransactions[int].amount!)} $currency',
style: GoogleFonts
.aBeeZee(
fontSize:
18.5.sp)),
SizedBox(
width: 15.w,
),
Icon(
Icons
.arrow_forward_ios,
size: 17.sp,
)
],
),
),
),
);
}),
)
],
),
);
}))
],
);
}
});
});
}
}
/// Returns the list of spline area series with horizontal gradient.
List> _getGradientAreaSeries(
String type, List monthAmountList) {
// final List color = [];
// color.add(Colors.blue[200]!);
// color.add(Colors.orange[200]!);
// final List stops = [];
// stops.add(0.2);
// stops.add(0.7);
return >[
SplineAreaSeries(
/// To set the gradient colors for border here.
borderGradient: type == 'Income'
? const LinearGradient(colors: [
Color.fromRGBO(56, 135, 5, 1),
Color.fromRGBO(159, 196, 135, 1)
], stops: [
0.2,
0.9
])
: const LinearGradient(colors: [
Color.fromRGBO(212, 126, 166, 1),
Color.fromRGBO(222, 187, 104, 1)
], stops: [
0.2,
0.9
]),
/// To set the gradient colors for series.
gradient: type == 'Income'
? const LinearGradient(colors: [
Color.fromRGBO(101, 181, 60, 1),
Color.fromRGBO(139, 194, 72, 1),
Color.fromRGBO(203, 241, 119, 0.9)
], stops: [
0.2,
0.5,
0.9
])
: const LinearGradient(colors: [
Color.fromRGBO(224, 139, 207, 0.9),
Color.fromRGBO(255, 232, 149, 0.9)
], stops: [
0.2,
0.9
]),
borderWidth: 2.5.w,
markerSettings: MarkerSettings(
isVisible: true,
height: 8.h,
width: 8.h,
borderColor:
type == 'Income' ? Color.fromRGBO(161, 171, 35, 1) : Colors.white,
borderWidth: 2.w,
),
borderDrawMode: BorderDrawMode.all,
dataSource: monthAmountList,
xValueMapper: (MonthAmount monthAmount, _) => monthAmount.month,
yValueMapper: (MonthAmount monthAmount, _) => monthAmount.amount,
)
];
}
class MonthAmount {
final String month;
final double amount;
const MonthAmount(this.month, this.amount);
}
================================================
FILE: lib/project/app_pages/select_date_format.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';
import '../provider.dart';
class FormatDate extends StatelessWidget {
const FormatDate({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
List dateFormats = [
'dd/MM/yyyy',
'MM/dd/yyyy',
'yyyy/MM/dd',
'yMMMd',
'MMMEd',
'MEd',
'MMMMd',
'MMMd',
];
return Scaffold(
appBar: AppBar(
backgroundColor: blue3,
title: Text(
getTranslated(context, 'Select a date format') ??
'Select a date format',
style: TextStyle(fontSize: 21.sp)),
actions: [
Padding(
padding: EdgeInsets.only(right: 5.w),
child: TextButton(
child: Text(
getTranslated(context, 'Save') ?? 'Save',
style: TextStyle(fontSize: 18.5.sp, color: white),
),
onPressed: () => Navigator.pop(context)),
)
]),
body: ChangeNotifierProvider(
create: (context) => OnDateFormatSelected(),
builder: (context, widget) => ListView.builder(
itemCount: dateFormats.length,
itemBuilder: (context, int) => GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
context
.read()
.onDateFormatSelected(dateFormats[int]);
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(
27.h),
child: Row(
children: [
Text(
'${DateFormat(dateFormats[int]).format(now)}',
style: TextStyle(
fontSize: 19.sp,
),
),
Spacer(),
context
.watch()
.dateFormat ==
dateFormats[int]
? Icon(Icons.check_circle,
size: 25.sp, color: blue3)
: SizedBox()
],
),
),
Divider(height: 0, thickness: 0.25, color: grey)
]),
)),
));
}
}
================================================
FILE: lib/project/app_pages/select_icon.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/classes/app_bar.dart';
import 'package:money_assistant_2608/project/classes/category_item.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
class SelectIcon extends StatelessWidget {
final String type;
SelectIcon(this.type);
@override
Widget build(BuildContext context) {
final List icons = createItemList(
forAnalysisPage: false, isIncomeType: false, forSelectIconPage: true);
return Scaffold(
appBar: BasicAppBar(getTranslated(context, 'Icons')!),
body: GridView.count(
crossAxisCount: 4,
childAspectRatio: 0.82,
shrinkWrap: true,
children: List.generate(icons.length, (index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
IconButton(
onPressed: () {
Navigator.pop(context, iconData(icons[index])
);
},
iconSize: 60.sp,
icon: CircleAvatar(
backgroundColor: Color.fromRGBO(215, 223, 231, 1),
radius: 24.r,
child: Icon(
iconData(icons[index]),
size: 30.sp,
color: this.type == 'Income' ? green : red,
)),
),
],
),
);
}),
),
);
}
}
================================================
FILE: lib/project/app_pages/select_language.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/localization/language.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:provider/provider.dart';
import '../provider.dart';
import '../real_main.dart';
class SelectLanguage extends StatefulWidget {
@override
_SelectLanguageState createState() => _SelectLanguageState();
}
class _SelectLanguageState extends State {
@override
Widget build(BuildContext context) {
List languageList = Language.languageList;
return Scaffold(
appBar: AppBar(
backgroundColor: blue3,
title: Text(getTranslated(context, 'Select a language')!,
style: TextStyle(fontSize: 21.sp)),
actions: [
Padding(
padding: EdgeInsets.only(right: 5.w),
child: TextButton(
child: Text(
getTranslated(context, 'Save') ?? 'Save',
style: TextStyle(fontSize: 18.5.sp, color: white),
),
onPressed: () => Navigator.pop(context),
),
)
]
),
body: ChangeNotifierProvider(
create: (context) => OnLanguageSelected(),
builder: (context, widget) => ListView.builder(
itemCount: languageList.length,
itemBuilder: (context, int) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
Locale _locale = sharedPrefs.setLocale(languageList[int].languageCode);
MyApp.setLocale(context, _locale);
context
.read()
.onSelect(languageList[int].languageCode);
},
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(
vertical: 5.h, horizontal: 23.w),
child: Row(
children: [
Text(
languageList[int].flag,
style: TextStyle(fontSize: 45.sp),
),
SizedBox(
width: 35.w,
),
Text(languageList[int].name,
style: TextStyle(
fontSize: 20.sp,
)),
Spacer(),
context.watch().languageCode ==
languageList[int].languageCode
? Icon(Icons.check_circle,
size: 25.sp, color: blue3)
: SizedBox(),
SizedBox(width: 15.w)
],
),
),
Divider(
indent: 90.w,
height: 0,
thickness: 0.25.h,
color: grey,
),
],
),
);
}),
));
}
}
================================================
FILE: lib/project/auth_pages/loading_page.dart
================================================
// import 'package:flutter/material.dart';
// import 'package:flutter_spinkit/flutter_spinkit.dart';
//
// class Loading extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return Container(
// color: Colors.brown[100],
// child: Center(
// child: SpinKitChasingDots(
// color: Colors.brown,
// size: 50.0,
// ),
// ),
// );
// }
// }
================================================
FILE: lib/project/auth_pages/sign_in.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../home.dart';
class SignIn extends StatelessWidget {
Future? _authenticateUsers(LoginData data) {
print('authenticate users');
return Future.delayed(Duration(seconds: 1)).then((_) => null);
}
Future? _onRecoverPassword(String name) {
print('onRecoverPassword');
return Future.delayed(Duration(seconds: 1)).then((_) => null);
}
@override
Widget build(BuildContext context) {
final inputBorder = BorderRadius.vertical(
bottom: Radius.circular(10.0),
top: Radius.circular(20.0),
);
return FlutterLogin(
title: 'MMAS',
logo: 'Hi!',
onSignup: _authenticateUsers,
onLogin: _authenticateUsers,
onRecoverPassword: _onRecoverPassword,
onSubmitAnimationCompleted: () {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => Home()));
},
messages: LoginMessages(
userHint: 'User',
passwordHint: 'Pass',
confirmPasswordHint: 'Confirm',
loginButton: 'LOG IN',
signupButton: 'REGISTER',
forgotPasswordButton: 'Forgot huh?',
recoverPasswordButton: 'HELP ME',
goBackButton: 'GO BACK',
confirmPasswordError: 'Not match!',
recoverPasswordDescription: 'recoverPasswordDescription',
recoverPasswordSuccess: 'Password rescued successfully',
),
loginProviders: [
LoginProvider(
icon: FontAwesomeIcons.google,
label: 'Google',
callback: () async {
print('start google sign in');
await Future.delayed(Duration(seconds: 1));
print('stop google sign in');
return null;
},
),
LoginProvider(
icon: FontAwesomeIcons.facebookF,
label: 'Facebook',
callback: () async {
print('start facebook sign in');
await Future.delayed(Duration(seconds: 1));
print('stop facebook sign in');
return null;
},
),
LoginProvider(
icon: FontAwesomeIcons.linkedinIn,
callback: () async {
print('start linkdin sign in');
await Future.delayed(Duration(seconds: 1));
print('stop linkdin sign in');
return null;
},
),
LoginProvider(
icon: FontAwesomeIcons.githubAlt,
callback: () async {
print('start github sign in');
await Future.delayed(Duration(seconds: 1));
print('stop github sign in');
return null;
},
),
],
theme: LoginTheme(
primaryColor: Colors.teal,
accentColor: Colors.yellow,
errorColor: Colors.deepOrange,
titleStyle: TextStyle(
color: Colors.greenAccent,
fontFamily: 'Quicksand',
letterSpacing: 4,
),
bodyStyle: TextStyle(
fontStyle: FontStyle.italic,
decoration: TextDecoration.underline,
),
textFieldStyle: TextStyle(
color: Colors.orange,
shadows: [Shadow(color: Colors.yellow, blurRadius: 2)],
),
buttonStyle: TextStyle(
fontWeight: FontWeight.w800,
color: Colors.yellow,
),
cardTheme: CardTheme(
color: Colors.yellow.shade100,
elevation: 5,
margin: EdgeInsets.only(top: 15),
shape: ContinuousRectangleBorder(
borderRadius: BorderRadius.circular(100.0)),
),
inputTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.purple.withOpacity(.1),
contentPadding: EdgeInsets.zero,
errorStyle: TextStyle(
backgroundColor: Colors.orange,
color: Colors.white,
),
labelStyle: TextStyle(fontSize: 12),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue.shade700, width: 4),
borderRadius: inputBorder,
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.blue.shade400, width: 5),
borderRadius: inputBorder,
),
errorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.red.shade700, width: 7),
borderRadius: inputBorder,
),
focusedErrorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.red.shade400, width: 8),
borderRadius: inputBorder,
),
disabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey, width: 5),
borderRadius: inputBorder,
),
),
buttonTheme: LoginButtonTheme(
splashColor: Colors.purple,
backgroundColor: Colors.pinkAccent,
highlightColor: Colors.lightGreen,
elevation: 9.0,
highlightElevation: 6.0,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
// shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)),
// shape: CircleBorder(side: BorderSide(color: Colors.green)),
// shape: ContinuousRectangleBorder(borderRadius: BorderRadius.circular(55.0)),
),
),
);
}
}
// import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
//
// import '../../classes/textinput_decoration.dart';
// import 'loading_page.dart';
//
// class SignIn extends StatefulWidget {
// @override
// _SignInState createState() => _SignInState();
// }
//
// class _SignInState extends State {
// final AuthService _auth = AuthService();
// final _formKey = GlobalKey();
// String error = '';
// bool loading = false;
//
// // text field state
// String email = '';
// String password = '';
//
// @override
// Widget build(BuildContext context) {
// return loading
// ? Loading()
// : Scaffold(
// backgroundColor: Colors.brown[100],
// appBar: AppBar(
// backgroundColor: Colors.brown[400],
// elevation: 0.0,
// title: Text('Sign in to Brew Crew'),
// actions: [
// FlatButton.icon(
// icon: Icon(Icons.person),
// label: Text('Register'),
// onPressed: () {},
// ),
// ],
// ),
// body: Container(
// padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
// child: Form(
// key: _formKey,
// child: Column(
// children: [
// SizedBox(height: 20.0),
// TextFormField(
// decoration:
// textInputDecoration.copyWith(hintText: 'email'),
// validator: (val) => val.isEmpty ? 'Enter an email' : null,
// onChanged: (val) {
// setState(() => email = val);
// },
// ),
// SizedBox(height: 20.0),
// TextFormField(
// obscureText: true,
// decoration:
// textInputDecoration.copyWith(hintText: 'password'),
// validator: (val) => val.length < 6
// ? 'Enter a password 6+ chars long'
// : null,
// onChanged: (val) {
// setState(() => password = val);
// },
// ),
// SizedBox(height: 20.0),
// RaisedButton(
// color: Colors.pink[400],
// child: Text(
// 'Sign In',
// style: TextStyle(color: Colors.white),
// ),
// onPressed: () async {
// if (_formKey.currentState.validate()) {
// setState(() => loading = true);
// dynamic result = await _auth
// .signInWithEmailAndPassword(email, password);
// if (result == null) {
// setState(() {
// loading = false;
// error =
// 'Could not sign in with those credentials';
// });
// } else {
// Navigator.pop(context);
// }
// }
// }),
// SizedBox(height: 12.0),
// Text(
// error,
// style: TextStyle(color: Colors.red, fontSize: 14.0),
// ),
// ],
// ),
// ),
// ),
// );
// }
// }
================================================
FILE: lib/project/auth_pages/sign_up.dart
================================================
// import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
//
// import '../../classes/textinput_decoration.dart';
// import 'loading_page.dart';
//
// class SignUp extends StatefulWidget {
// @override
// _SignUpState createState() => _SignUpState();
// }
//
// class _SignUpState extends State {
// final AuthService _auth = AuthService();
// final _formKey = GlobalKey();
// String error = '';
// bool loading = false;
//
// // text field state
// String email = '';
// String password = '';
//
// @override
// Widget build(BuildContext context) {
// return loading
// ? Loading()
// : Scaffold(
// backgroundColor: Colors.brown[100],
// appBar: AppBar(
// backgroundColor: Colors.brown[400],
// elevation: 0.0,
// title: Text('Sign up to Brew Crew'),
// actions: [
// FlatButton.icon(
// icon: Icon(Icons.person),
// label: Text('Sign In'),
// onPressed: () {},
// ),
// ],
// ),
// body: Container(
// padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
// child: Form(
// key: _formKey,
// child: Column(
// children: [
// SizedBox(height: 20.0),
// TextFormField(
// decoration:
// textInputDecoration.copyWith(hintText: 'email'),
// validator: (val) => val.isEmpty ? 'Enter an email' : null,
// onChanged: (val) {
// setState(() => email = val);
// },
// ),
// SizedBox(height: 20.0),
// TextFormField(
// decoration:
// textInputDecoration.copyWith(hintText: 'password'),
// obscureText: true,
// validator: (val) => val.length < 6
// ? 'Enter a password 6+ chars long'
// : null,
// onChanged: (val) {
// setState(() => password = val);
// },
// ),
// SizedBox(height: 20.0),
// RaisedButton(
// color: Colors.pink[400],
// child: Text(
// 'Register',
// style: TextStyle(color: Colors.white),
// ),
// onPressed: () async {
// if (_formKey.currentState.validate()) {
// setState(() => loading = true);
// dynamic result = await _auth
// .registerWithEmailAndPassword(email, password);
//
// if (result == null) {
// setState(() {
// loading = false;
// error = 'Please supply a valid email';
// });
// } else {
// Navigator.pop(context);
// }
// }
// }),
// SizedBox(height: 12.0),
// Text(
// error,
// style: TextStyle(color: Colors.red, fontSize: 14.0),
// )
// ],
// ),
// ),
// ),
// );
// }
// }
================================================
FILE: lib/project/auth_pages/user_account.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
// final AuthService _auth = AuthService();
class UserAccount extends StatefulWidget {
@override
_UserAccountState createState() => _UserAccountState();
}
class _UserAccountState extends State {
List textList = [
"Personal information",
"Account link",
"Change password",
"sign out"
];
List iconList = [
Icons.person,
Icons.link_sharp,
Icons.admin_panel_settings_sharp,
Icons.logout
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(270.h),
child: Container(
height: 270,
child: Padding(
padding: EdgeInsets.only(top: 40.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 20.w),
child: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.pop(context);
}),
),
Center(
child: CircleAvatar(
child: CircleAvatar(
radius: 30.r,
backgroundColor: Color.fromRGBO(210, 234, 251, 1)),
radius: 35.r,
backgroundColor: Colors.grey,
),
),
SizedBox(
height: 10.h,
),
Center(
child: Text(
"User name",
style: TextStyle(fontSize: 25.sp, fontWeight: FontWeight.bold),
)),
SizedBox(
height: 15.h,
),
Center(
child: Container(
width: 100.w,
height: 30.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
border:
Border.all(color: Colors.blueGrey, width: 0.5)),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.crop_free),
SizedBox(
width: 3.w,
),
Text(
"Free",
style: TextStyle(fontSize: 20.sp),
)
],
),
),
),
SizedBox(
height: 20.h,
),
],
),
),
)),
body: Column(
children: [
// Divider(
// height: 0,
// thickness: 0.8,
// color: Colors.grey,
// ),
// Container(
// height: 13,
// color: Color.fromRGBO(210, 234, 251, 1),
// ),
Divider(
height: 0,
thickness: 0.8.w,
color: grey,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: SizedBox(
height: 60.h,
child: Row(
children: [
IconButton(
icon: Icon(
Icons.sports_golf,
size: 30.sp,
),
onPressed: () {},
),
SizedBox(
width: 10.w,
),
Text(
'Explore Premium',
style: TextStyle(fontSize: 25.sp),
)
],
),
),
),
Divider(
height: 0.h,
thickness: 0.8.w,
color: grey,
),
Container(
height: 60.h,
color: Color.fromRGBO(237, 240, 243, 1),
),
Divider(
height: 1.h,
thickness: 0.8.w,
color: grey,
),
Expanded(
child: ListView.builder(
itemCount: iconList.length,
itemBuilder: (context, int) {
return SizedBox(
height: 60.h,
child: Column(children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(
icon: Icon(
iconList[int],
size: 30.sp,
),
onPressed: () async {
// await _auth.signOut();
// Navigator.pop(context);
},
),
SizedBox(
width: 10.w,
),
Text(
textList[int],
style: TextStyle(fontSize: 25.sp),
),
Spacer(),
Icon(
Icons.arrow_forward_ios,
size: 20.sp,
),
],
),
),
Divider(
height: 0,
thickness: 0.8.w,
color: grey,
),
]),
);
}),
)
],
),
);
}
}
================================================
FILE: lib/project/auth_pages/welcome_page.dart
================================================
// import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
//
// import 'sign_in.dart';
// import 'sign_up.dart';
//
// class WelcomePage extends StatefulWidget {
// @override
// _WelcomePageState createState() => _WelcomePageState();
// }
//
// class _WelcomePageState extends State {
// @override
// Widget build(BuildContext context) {
// print('Welcome Page');
// return Scaffold(
// backgroundColor: Color.fromRGBO(139, 205, 254, 1),
// appBar: AppBar(
// backgroundColor: Colors.orange,
// title: Text(
// 'Welcome Page',
// style: TextStyle(fontSize: 20),
// ),
// ),
// body: Center(
// child: Column(
// children: [
// Row(
// children: [
// IconButton(
// icon: Icon(
// Icons.login,
// ),
// onPressed: () => Navigator.push(context,
// MaterialPageRoute(builder: (context) => SignIn())),
// ),
// Text(
// 'Sign in',
// style: TextStyle(fontSize: 20),
// )
// ],
// ),
// Row(
// children: [
// IconButton(
// icon: Icon(
// Icons.app_registration,
// ),
// onPressed: () => Navigator.push(context,
// MaterialPageRoute(builder: (context) => SignUp())),
// ),
// Text(
// 'Sign up',
// style: TextStyle(fontSize: 20),
// )
// ],
// )
// ],
// ),
// ),
// );
// }
// }
================================================
FILE: lib/project/auth_pages/wrapper.dart
================================================
// import 'package:flutter/cupertino.dart';
//
// import '../app_pages/home.dart';
// import 'welcome_page.dart';
//
// class Wrapper extends StatelessWidget {
// final UserUid userUid;
// const Wrapper(this.userUid);
// @override
// Widget build(BuildContext context) {
// print('${userUid.uid}');
// if (userUid.uid == '') {
// return WelcomePage();
// } else {
// return Home();
// }
// }
// }
================================================
FILE: lib/project/auth_services/firebase_authentication.dart
================================================
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:money_assistant_2608/project/classes/custom_toast.dart';
class FirebaseAuthentication {
static Future initializeFireBase() async {
FirebaseApp firebaseApp = await Firebase.initializeApp();
return firebaseApp;
}
static Future googleSignIn({required BuildContext context}) async{
User? user;
FirebaseAuth auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
final GoogleSignInAccount? googleSignInAccount = await googleSignIn.signIn();
if(googleSignInAccount != null){
final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken);
try {
final UserCredential userCredential = await auth.signInWithCredential(credential);
user = userCredential.user;
} on FirebaseException catch (e) {
if (e.code == 'account-exists-with-different-credentia') {
customToast(context,'The account already exists with a different credential.');
} else
if (e.code == 'invalid-credential') {
customToast(context,'Error occurred while accessing credentials. Try again.');
}
}catch(e){
customToast(context,'Error occurred using Google Sign-In. Try again.');
}
}
return user;
}
static Future signOut({required BuildContext context})async{
final GoogleSignIn googleSignIn = GoogleSignIn();
try{
await googleSignIn.signOut();
} catch (e){
customToast(context, 'Error signing out. Try again.');
}
}
}
// final FirebaseAuth _auth = FirebaseAuth.instance;
//
// UserUid _userUid(User user) {
// return user != null ? UserUid(uid: user.uid) : null;
// }
//
// // what is get?
// Stream get user {
// return _auth.authStateChanges().map((User user) => _userUid(user));
// // .map(_userUid);
// }
//
// // sign in anon
// Future signInAnon() async {
// try {
// UserCredential result = await _auth.signInAnonymously();
// User user = result.user;
// return _userUid(user);
// } catch (e) {
// print(e.toString());
// return null;
// }
// }
//
// // sign in with email and password
// Future signInWithEmailAndPassword(String email, String password) async {
// try {
// UserCredential result = await _auth.signInWithEmailAndPassword(
// email: email, password: password);
// User user = result.user;
// return user;
// } catch (error) {
// print(error.toString());
// return null;
// }
// }
//
// // register with email and password
// Future registerWithEmailAndPassword(String email, String password) async {
// try {
// UserCredential result = await _auth.createUserWithEmailAndPassword(
// email: email, password: password);
// User user = result.user;
// return _userUid(user);
// } catch (error) {
// print(error.toString());
// return null;
// }
// }
//
// // sign out
// Future signOut() async {
// try {
// return await _auth.signOut();
// } catch (error) {
// print(error.toString());
// return null;
// }
// }
// }
// class UserUid {
// final String uid;
//
// UserUid({this.uid});
// }
================================================
FILE: lib/project/classes/alert_dialog.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
Future iosDialog(BuildContext context, String content, String action,
Function onAction) =>
showCupertinoDialog(
context: context,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Padding(
padding: EdgeInsets.only(
bottom: 8.h,
),
child: Text(
getTranslated(context, 'Please Confirm') ?? 'Please Confirm',
style: TextStyle(fontSize: 21.sp),
),
),
content: Text(getTranslated(context, content) ?? content,
style: TextStyle(fontSize: 15.5.sp)),
actions: [
CupertinoDialogAction(
onPressed: () {
Navigator.pop(context);
},
child: Padding(
padding: EdgeInsets.symmetric(vertical: 6.h, horizontal: 3.w),
child: Text(getTranslated(context, 'Cancel') ?? 'Cancel',
style: TextStyle(
fontSize: 19.5.sp, fontWeight: FontWeight.w600)),
),
isDefaultAction: false,
isDestructiveAction: false,
),
CupertinoDialogAction(
onPressed: () {
onAction();
Navigator.pop(context);
},
child: Padding(
padding: EdgeInsets.symmetric(vertical: 6.h, horizontal: 3.w),
child: Text(getTranslated(context, action) ?? action,
style: TextStyle(
fontSize: 19.5.sp, fontWeight: FontWeight.w600)),
),
isDefaultAction: true,
isDestructiveAction: true,
)
],
);
});
Future androidDialog(BuildContext context, String content, String action,
Function onAction) =>
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(getTranslated(context, 'Please Confirm')!),
content: Text(
getTranslated(context, content) ?? content),
actions: [
TextButton(
onPressed: () {
onAction();
Navigator.pop(context);
},
child: Text(getTranslated(context, 'Cancel') ?? 'Cancel')),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(getTranslated(context, action) ?? action))
],
);
});
================================================
FILE: lib/project/classes/app_bar.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/app_pages/input.dart';
import 'constants.dart';
class BasicAppBar extends StatelessWidget with PreferredSizeWidget {
final String title;
const BasicAppBar(this.title);
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
@override
Widget build(BuildContext context) {
return AppBar(
backgroundColor: blue3,
title: Text(title, style: TextStyle(fontSize: 21.sp)),
);
}
}
class InExAppBar extends StatelessWidget implements PreferredSizeWidget {
final bool isInputPage;
const InExAppBar(this.isInputPage);
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
@override
Widget build(BuildContext context) {
Tab appBarTab(String title) => Tab(
child: Container(
width: double.maxFinite,
height: double.maxFinite,
decoration: BoxDecoration(),
child: Align(
child: Text(
getTranslated(context, title)!,
style: TextStyle(fontSize: 19.sp),
)),
),
);
return AppBar(
backgroundColor: blue2,
title: TabBar(
unselectedLabelColor: white,
indicatorSize: TabBarIndicatorSize.tab,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(50.r),
color: Color.fromRGBO(82, 179, 252, 1),
),
tabs: [
appBarTab('EXPENSE'),
appBarTab('INCOME')
],
),
actions: isInputPage ? [
IconButton(
icon: Icon(Icons.check),
iconSize: 28,
onPressed: () {
saveInputFunc(context,true);
},
)
] : null,
);
}
}
class CategoryAppBar extends StatelessWidget implements PreferredSizeWidget {
final Widget editCategory;
const CategoryAppBar(this.editCategory);
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
@override
Widget build(BuildContext context) {
return AppBar(
backgroundColor: blue3,
actions: [
Padding(
padding: EdgeInsets.only(
right: 20.w,
),
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => editCategory));
},
child: Row(children: [
Icon(
Icons.edit,
size: 19.sp,
),
SizedBox(width: 3.w),
Text(
getTranslated(context, 'Edit')!,
style: TextStyle(fontSize: 19.sp),
),
]),
),
// child: Icon(Icons.edit),
),
],
title: Text(getTranslated(context, 'Category')!,
style: TextStyle(fontSize: 21.sp)),
);
}
}
class EditCategoryAppBar extends StatelessWidget implements PreferredSizeWidget {
final Widget addCategory;
const EditCategoryAppBar(this.addCategory);
@override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
@override
Widget build(BuildContext context) {
return AppBar(
backgroundColor: blue3,
actions: [
Padding(
padding: EdgeInsets.only(right: 5.w),
child: TextButton(
onPressed: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => addCategory)),
child: Text(
getTranslated(context, 'Add')!,
style: TextStyle(fontSize: 18.5.sp, color: white),
),
),
// child: Icon(Icons.edit),
),
],
title: Text(getTranslated(context, 'Edit Category')!,
style: TextStyle(fontSize: 21.sp)),
);
}
}
================================================
FILE: lib/project/classes/category_item.dart
================================================
class CategoryItem {
int iconCodePoint;
String? iconFontPackage;
String? iconFontFamily;
String text;
String? description;
CategoryItem(this.iconCodePoint, this.iconFontPackage, this.iconFontFamily,
this.text, this.description);
factory CategoryItem.fromJson(Map json) {
return new CategoryItem(json['iconCodePoint'], json['iconFontPackage'],
json['iconFontFamily'], json['text'], json['description']);
}
Map toJson() {
return {
'iconCodePoint': this.iconCodePoint,
'iconFontPackage': this.iconFontPackage,
'iconFontFamily': this.iconFontFamily,
'text': this.text,
'description': this.description
};
}
}
================================================
FILE: lib/project/classes/chart_pie.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'input_model.dart';
class ChartPie extends StatelessWidget {
const ChartPie(this.transactionsSorted);
final List transactionsSorted;
@override
Widget build(BuildContext context) {
bool haveRecords;
String width;
String height;
double animationDuration;
if (this.transactionsSorted[0].category == '') {
haveRecords = false;
width = '67%';
height = '67%';
animationDuration = 0;
} else {
haveRecords = true;
width = '67%';
height = '67%';
animationDuration = 270;
}
return SfCircularChart(
tooltipBehavior: TooltipBehavior(enable: haveRecords),
annotations: [
CircularChartAnnotation(
width: width, height: height, widget: Annotations(haveRecords))
],
series: >[
DoughnutSeries(
startAngle: 90,
endAngle: 90,
animationDuration: animationDuration,
// enableSmartLabels: haveRecords,
sortingOrder: SortingOrder.descending,
sortFieldValueMapper: (InputModel data, _) => data.category,
enableTooltip: haveRecords,
dataSource: this.transactionsSorted,
pointColorMapper: (InputModel data, _) => data.color,
xValueMapper: (InputModel data, _) => getTranslated(context, data.category!) ?? data.category,
yValueMapper: (InputModel data, _) => data.amount,
dataLabelSettings: DataLabelSettings(
showZeroValue: true,
useSeriesColor: true,
labelPosition: ChartDataLabelPosition.outside,
isVisible: haveRecords,
),
innerRadius: '50%',
radius: '67%'),
],
);
}
}
class Annotations extends StatelessWidget {
final bool haveRecords;
const Annotations(this.haveRecords);
@override
Widget build(BuildContext context) {
return PhysicalModel(
child: Container(
child: haveRecords == false
? Center(
child: Text(getTranslated(context, 'There is no data')!,
textAlign: TextAlign.center,
style: TextStyle(
color: Color.fromRGBO(0, 0, 0, 0.5),
fontSize: 23.5.sp,
fontStyle: FontStyle.italic)),
)
: null,
),
shape: BoxShape.circle,
elevation: 10,
shadowColor: Colors.black,
color: const Color.fromRGBO(230, 230, 230, 1));
}
}
================================================
FILE: lib/project/classes/constants.dart
================================================
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'category_item.dart';
import 'input_model.dart';
Color green = Color.fromRGBO(57, 157, 3, 1),
red = Color.fromRGBO(217, 89, 89, 1),
white = Color.fromRGBO(255, 255, 255, 1),
blue1 = Color.fromRGBO(210, 234, 251, 1),
blue2 = Color.fromRGBO(139, 205, 254, 1),
blue3 = Color.fromRGBO(89, 176, 222, 1),
grey = Colors.grey;
List chartPieColors = [
//19
Color.fromRGBO(100, 202, 254, 1),
Color.fromRGBO(80, 157, 253, 1),
Color.fromRGBO(7, 156, 193, 1),
Color.fromRGBO(89, 129, 163, 1),
Color.fromRGBO(79, 94, 120, 1),
Color.fromRGBO(196, 199, 216, 1),
Color.fromRGBO(255, 206, 161, 1),
Color.fromRGBO(255, 183, 121, 1),
Color.fromRGBO(237, 156, 128, 1),
Color.fromRGBO(126, 180, 166, 1),
Color.fromRGBO(212, 216, 140, 1),
Color.fromRGBO(144, 192, 106, 1),
Color.fromRGBO(128, 186, 76, 1),
Color.fromRGBO(224, 217, 255, 1),
Color.fromRGBO(202, 164, 255, 1),
Color.fromRGBO(197, 156, 240, 1),
Color.fromRGBO(241, 197, 211, 1),
Color.fromRGBO(244, 151, 178, 1),
Color.fromRGBO(218, 145, 176, 1),
//20
Color.fromRGBO(141, 190, 255, 1),
Color.fromRGBO(160, 217, 254, 1),
Color.fromRGBO(117, 216, 228, 1),
Color.fromRGBO(120, 217, 192, 1),
Color.fromRGBO(172, 198, 152, 1),
Color.fromRGBO(162, 193, 115, 1),
Color.fromRGBO(112, 164, 112, 1),
Color.fromRGBO(65, 174, 223, 1),
Color.fromRGBO(71, 131, 192, 1),
Color.fromRGBO(32, 225, 188, 1),
Color.fromRGBO(53, 136, 143, 1),
Color.fromRGBO(139, 178, 193, 1),
Color.fromRGBO(125, 150, 191, 1),
Color.fromRGBO(119, 131, 148, 1),
Color.fromRGBO(243, 210, 122, 1),
Color.fromRGBO(254, 203, 94, 1),
Color.fromRGBO(244, 186, 106, 1),
Color.fromRGBO(217, 165, 105, 1),
Color.fromRGBO(214, 142, 96, 1),
Color.fromRGBO(190, 119, 112, 1),
Color.fromRGBO(194, 94, 78, 1),
Color.fromRGBO(192, 72, 42, 1),
Color.fromRGBO(176, 29, 51, 1),
//18
Color.fromRGBO(198, 180, 251, 1),
Color.fromRGBO(155, 128, 217, 1),
Color.fromRGBO(112, 130, 212, 1),
Color.fromRGBO(78, 123, 216, 1),
Color.fromRGBO(139, 205, 254, 1),
Color.fromRGBO(89, 176, 222, 1),
Color.fromRGBO(81, 155, 194, 1),
Color.fromRGBO(4, 135, 192, 1),
Color.fromRGBO(50, 128, 171, 1),
Color.fromRGBO(44, 110, 119, 1),
Color.fromRGBO(41, 147, 134, 1),
Color.fromRGBO(95, 155, 71, 1),
Color.fromRGBO(179, 217, 37, 1),
Color.fromRGBO(237, 178, 135, 1),
Color.fromRGBO(198, 157, 100, 1),
Color.fromRGBO(194, 140, 112, 1),
Color.fromRGBO(214, 138, 88, 1),
Color.fromRGBO(225, 123, 66, 1),
];
String format(double number) =>
NumberFormat("#,###,###,###,###,###.##", "en_US").format(number);
IconData iconData(CategoryItem item) => IconData(item.iconCodePoint,
fontPackage: item.iconFontPackage, fontFamily: item.iconFontFamily);
//should description be '' or null?
CategoryItem categoryItem(IconData icon, String name) =>
CategoryItem(icon.codePoint, icon.fontPackage, icon.fontFamily, name, '');
Widget? connectionUI(AsyncSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.none) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
print('${snapshot.error}');
return Center(
child: CircularProgressIndicator(),
);
}
}
List createItemList({
List? transactions,
required bool forAnalysisPage,
isIncomeType,
forSelectIconPage,
}) {
List itemList = [], items = [], expenseItems = [];
sharedPrefs.getAllExpenseItemsLists().forEach((parentExpenseItem) =>
parentExpenseItem
.forEach((expenseItem) => expenseItems.add(expenseItem)));
if (forAnalysisPage) {
items = isIncomeType ? incomeItems : expenseItems;
} else {
items = [...incomeItems, ...expenseItems];
}
if (forSelectIconPage) {
return items;
} else {
for (InputModel transaction in transactions!) {
for (int i = 0; i < items.length; i++) {
if (transaction.category == items[i].text) {
itemList.add(items[i]);
break;
}
if (i == items.length - 1) {
CategoryItem itemElse = CategoryItem(
Icons.category_outlined.codePoint,
Icons.category_outlined.fontPackage,
Icons.category_outlined.fontFamily,
transaction.category!,
transaction.description);
itemList.add(itemElse);
}
}
}
return itemList;
}
}
//for analysis, report
final DateTime now = DateTime.now(),
todayDT = DateTime(now.year, now.month, now.day),
startOfThisWeek = todayDT.subtract(Duration(days: todayDT.weekday - 1)),
startOfThisMonth = DateTime(todayDT.year, todayDT.month, 1),
startOfThisYear = DateTime(todayDT.year, 1, 1),
startOfThisQuarter = DateTime(todayDT.year, quarterStartMonth, 1);
final int thisQuarter = (todayDT.month + 2) ~/ 3,
quarterStartMonth = 3 * thisQuarter - 2;
final List timeline = [
'Today',
'This week',
'This month',
'This quarter',
'This year',
'All'
];
InputModel inputModel(data) => InputModel(
id: data.id,
type: data.type,
amount: data.amount!,
category: data.category!,
description: data.description!,
date: data.date,
time: data.time);
List filterData(
BuildContext context, List data, String selectedDate) {
// filter data based on user's selected day
return (data
.map((data) {
DateTime dateSelectedDT =
DateFormat('dd/MM/yyyy').parse(data.date!);
if (selectedDate == 'Today') {
if (dateSelectedDT.isAfter(todayDT.subtract(Duration(days: 1))) &&
dateSelectedDT.isBefore(todayDT.add(Duration(days: 1)))) {
return inputModel(data);
}
} else if (selectedDate == 'This week') {
if (dateSelectedDT
.isAfter(startOfThisWeek.subtract(Duration(days: 1))) &&
dateSelectedDT
.isBefore(startOfThisWeek.add(Duration(days: 7)))) {
return inputModel(data);
}
} else if (selectedDate == 'This month') {
if (dateSelectedDT
.isAfter(startOfThisMonth.subtract(Duration(days: 1))) &&
dateSelectedDT
.isBefore(DateTime(todayDT.year, todayDT.month + 1, 1))) {
return inputModel(data);
}
} else if (selectedDate == 'This quarter') {
if (dateSelectedDT.isAfter(
startOfThisQuarter.subtract(Duration(days: 1))) &&
dateSelectedDT.isBefore(DateTime(startOfThisQuarter.year,
startOfThisQuarter.month + 3, 1))) {
return inputModel(data);
}
} else if (selectedDate == 'This year') {
if (dateSelectedDT
.isAfter(startOfThisYear.subtract(Duration(days: 1))) &&
dateSelectedDT.isBefore(DateTime(todayDT.year + 1, 1, 1))) {
return inputModel(data);
}
} else {
return inputModel(data);
}
})
.where((element) => element != null)
.toList())
.cast();
}
// //3
// Color.fromRGBO(97, 162, 195, 1),
// Color.fromRGBO(107, 203, 253, 1),
// //2
// Color.fromRGBO(241, 187, 140, 1),
// Color.fromRGBO(242, 193, 145, 1),
// Color.fromRGBO(241, 197, 161, 1),
// Color.fromRGBO(227, 191, 145, 1),
// Color.fromRGBO(241, 207, 170, 1),
// Color.fromRGBO(151, 209, 255, 1),
// Color.fromRGBO(238, 161, 130, 1),
// Color.fromRGBO(253, 152, 67, 1),
// Color.fromRGBO(11, 153, 178, 1),
// Color.fromRGBO(5, 168, 192, 1),
// //
// Color.fromRGBO( 35, 115, 217, 1),
// Color.fromRGBO(6, 104, 189, 1),
// Color.fromRGBO( 45, 99, 186, 1),
// Color.fromRGBO( 61, 122, 135, 1),
// Color.fromRGBO( 42, 65, 88, 1),
// Color.fromRGBO(36, 85, 120, 1),
// Color.fromRGBO(57, 63, 71, 1),
// Color.fromRGBO(20, 44, 90, 1),
// Color.fromRGBO(27, 114, 181, 1),
// Color.fromRGBO(72, 102, 164, 1),
// Color.fromRGBO( 24, 47, 66, 1),
// Color.fromRGBO(56, 78, 116, 1),
// Color.fromRGBO(101, 70, 163, 1),
// Color.fromRGBO(78, 45, 127, 1),
// Color.fromRGBO(116, 13, 64, 1),
// Color.fromRGBO(83, 92, 112, 1),
// Color.fromRGBO(79, 168, 169, 1),
// Color.fromRGBO(60, 186, 177, 1),
// Color.fromRGBO( 47, 178, 196, 1),
// Color.fromRGBO( 68, 165, 193, 1),
// Color.fromRGBO(50, 101, 114, 1),
// Color.fromRGBO(2, 67, 107, 1),
// Color.fromRGBO(57, 68, 99, 1),
// Color.fromRGBO(65, 96, 142, 1),
// Color.fromRGBO(78, 123, 165, 1),
// Color.fromRGBO(95, 114, 138, 1),
// Color.fromRGBO(218, 60, 81, 1),
// Color.fromRGBO(52, 92, 67, 1),
// Color.fromRGBO(69, 91, 50, 1),
// Color.fromRGBO(71, 90, 44, 1),
// Color.fromRGBO(252, 182, 35, 1),
// Color.fromRGBO(255, 201, 0, 1),
// Color.fromRGBO(254, 190, 48, 1),
// Color.fromRGBO(241, 87, 33, 1),
// Color.fromRGBO(255, 122, 0, 1),
// Color.fromRGBO(215, 122, 2, 1),
// Color.fromRGBO(176, 99, 52, 1),
// Color.fromRGBO(190, 100, 42, 1),
// Color.fromRGBO(166, 120, 73, 1),
// Color.fromRGBO(165, 116, 81, 1),
// Color.fromRGBO(161, 85, 244, 1),
// Color.fromRGBO(162, 74, 125, 1),
// Color.fromRGBO(156, 41, 134, 1),
// Color.fromRGBO(153, 51, 178, 1),
// Color.fromRGBO(233, 114, 228, 1),
// Color.fromRGBO(158, 20, 24, 1),
// Color.fromRGBO(203, 71, 242, 1),
// Color.fromRGBO(218, 91, 160, 1),
// Color.fromRGBO(181, 89, 171, 1),
// Color.fromRGBO(208, 80, 135, 1),
// Color.fromRGBO(2, 157, 193, 1),
// Color.fromRGBO(34, 146, 212, 1),
// Color.fromRGBO(2, 205, 209, 1),
// Color.fromRGBO(85, 16, 9, 1),
// Color.fromRGBO(196, 238, 236, 1),
// Color.fromRGBO(192, 243, 247, 1),
// Color.fromRGBO(164, 255, 242, 1),
// Color.fromRGBO(248, 233, 193, 1),
// Color.fromRGBO(244, 231, 203, 1),
// Color.fromRGBO(212, 228, 244, 1),
// Color.fromRGBO(188, 235, 239, 1),
// Color.fromRGBO(246, 192, 142, 1),
// Color.fromRGBO(233, 242, 235, 1),
// Color.fromRGBO(227, 191, 145, 1),
// Color.fromRGBO(189, 79, 0, 1),
// Color.fromRGBO(214, 89, 43, 1),
// Color.fromRGBO(240, 105, 85, 1),
// Color.fromRGBO(231, 116, 46, 1),
// Color.fromRGBO(156, 89, 98, 1),
// Color.fromRGBO(208, 151, 99, 1),
// Color.fromRGBO(189, 124, 66, 1),
// Color.fromRGBO(181, 116, 67, 1),
// Color.fromRGBO(242, 135, 36, 1),
// Color.fromRGBO(243, 129, 61, 1),
// Color.fromRGBO(240, 131, 36, 1),
// Color.fromRGBO(245, 139, 46, 1),
// Color.fromRGBO(254, 160, 51, 1),
// Color.fromRGBO(239, 156, 71, 1),
// Color.fromRGBO(245, 166, 76, 1),
// Color.fromRGBO(245, 164, 52, 1),
// Color.fromRGBO(243, 170, 48, 1),
// Color.fromRGBO(164, 148, 126, 1),
// Color.fromRGBO(190, 157, 116, 1),
// Color.fromRGBO(191, 164, 131, 1),
// Color.fromRGBO(192, 176, 160, 1),
// Color.fromRGBO(193, 177, 164, 1),
// Color.fromRGBO(214, 193, 165, 1),
// Color.fromRGBO(255, 221, 206, 1),
// Color.fromRGBO(242, 217, 203, 1),
// Color.fromRGBO(245, 236, 224, 1),
// Color.fromRGBO(255, 232, 204, 1),
// Color.fromRGBO(255, 246, 216, 1),
// Color.fromRGBO(157, 245, 255, 1),
// Color.fromRGBO(235, 128, 209, 1),
// Color.fromRGBO(194, 212, 241, 1),
// Color.fromRGBO(196, 222, 235, 1),
// Color.fromRGBO(160, 224, 239, 1),
// Color.fromRGBO(157, 231, 244, 1),
// Color.fromRGBO(127, 191, 239, 1),
// Color.fromRGBO(214, 213, 155, 1),
// Color.fromRGBO(229, 217, 147, 1),
// Color.fromRGBO(208, 185, 240, 1),
// Color.fromRGBO(204, 217, 224, 1),
// Color.fromRGBO(168, 196, 187, 1),
// Color.fromRGBO(198, 219, 207, 1),
// Color.fromRGBO(203, 214, 189, 1),
// Color.fromRGBO(213, 245, 206, 1),
// Color.fromRGBO(186, 183, 168, 1),
// Color.fromRGBO(218, 214, 211, 1),
// Color.fromRGBO(218, 200, 182, 1),
// Color.fromRGBO(182, 168, 165, 1),
// Color.fromRGBO(214, 193, 165, 1),
// Color.fromRGBO(183, 205, 164, 1),
// Color.fromRGBO(159, 198, 127, 1),
// Color.fromRGBO(143, 165, 124, 1),
// Color.fromRGBO(126, 165, 147, 1),
// Color.fromRGBO(124, 188, 49, 1),
// Color.fromRGBO(148, 245, 183, 1),
// Color.fromRGBO(110, 145, 80, 1),
// Color.fromRGBO(115, 137, 78, 1),
// Color.fromRGBO(122, 138, 3, 1),
// Color.fromRGBO(209, 182, 73, 1),
// Color.fromRGBO(90, 138, 11, 1),
// Color.fromRGBO(54, 167, 92, 1),
// Color.fromRGBO(137, 224, 208, 1),
// Color.fromRGBO(150, 166, 191, 1),
// Color.fromRGBO(153, 168, 177, 1),
// Color.fromRGBO(160, 174, 179, 1),
// Color.fromRGBO(25, 200, 255, 1),
// Color.fromRGBO(0, 234, 245, 1),
// Color.fromRGBO(141, 7, 0, 1),
// Color.fromRGBO(104, 15, 28, 1),
// Color.fromRGBO(237, 254, 243, 1),
// Color.fromRGBO(22, 207, 152, 1),
================================================
FILE: lib/project/classes/custom_toast.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class CustomToast extends StatelessWidget {
final String message;
CustomToast(this.message);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 12.h),
decoration: BoxDecoration(
boxShadow: [BoxShadow()],
borderRadius: BorderRadius.circular(25.r),
color: Color.fromRGBO(239, 247, 253, 1),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.check, color: blue3,),
SizedBox(
width: 12.w,
),
Text(getTranslated(context, message) ??
message,
style: TextStyle(fontWeight: FontWeight.w500),
),
],
),
);
}
}
void customToast(BuildContext context, String message){
var fToast = FToast();
fToast.init(context);
fToast.showToast(
child: CustomToast(message),
gravity: ToastGravity.TOP,
toastDuration: Duration(seconds: 2),
);
}
================================================
FILE: lib/project/classes/dropdown_box.dart
================================================
import 'dart:ui';
import 'package:provider/provider.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:money_assistant_2608/project/provider.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'constants.dart';
class DropDownBox extends StatelessWidget {
final bool forAnalysis;
final String selectedDate;
const DropDownBox(this.forAnalysis, this.selectedDate);
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: ShapeDecoration(
shadows: [BoxShadow()],
color: blue2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(15.r))),
),
child: SizedBox(
height: 35.h,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: DropdownButtonHideUnderline(
child: DropdownButton(
dropdownColor: blue2,
value: selectedDate,
elevation: 10,
icon: Icon(
Icons.arrow_drop_down_outlined,
size: 28.sp,
),
onChanged: (value) {
if (this.forAnalysis) {
context
.read()
.changeSelectedAnalysisDate(
newSelectedDate: value.toString());
sharedPrefs.selectedDate = value.toString();
} else {
context
.read()
.changeSelectedReportDate(
newSelectedDate: value.toString());
}
},
items: timeline
.map((time) => DropdownMenuItem(
value: time,
child: Text(
getTranslated(context, time)!,
style: TextStyle(
fontSize: 18.5.sp),
textAlign: TextAlign.center,
),
))
.toList(),
),
),
),
),
);
}
}
================================================
FILE: lib/project/classes/icons.dart
================================================
// //for select_icon
// import 'package:eva_icons_flutter/eva_icons_flutter.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter_boxicons/flutter_boxicons.dart';
// import 'package:font_awesome_flutter/font_awesome_flutter.dart';
// import 'package:icofont_flutter/icofont_flutter.dart';
// import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
// import 'package:outline_material_icons/outline_material_icons.dart';
//
// import 'category_item.dart';
// import 'constants.dart';
//
//
// // [
// // categoryItem(Icons.business_center_rounded, ''),
// // categoryItem(IcoFontIcons.moneyBag, ''),
// // categoryItem(IcoFontIcons.searchJob, ''),
// // categoryItem(IcoFontIcons.gift, ''),
// // categoryItem(MdiIcons.cashPlus, ''),
// // categoryItem(MdiIcons.food, ''),
// // categoryItem(MdiIcons.foodDrumstick, ''),
// // categoryItem(Icons.local_bar, ''),
// // categoryItem(Icons.add_shopping_cart, ''),
// // categoryItem(OMIcons.commute, ''),
// // categoryItem(Icons.local_gas_station, ''),
// // categoryItem(Icons.local_parking, ''),
// // categoryItem(IcoFontIcons.toolsBag, ''),
// // categoryItem(Icons.local_taxi_outlined, ''),
// // categoryItem(IcoFontIcons.businessman, ''),
// // categoryItem(IcoFontIcons.education, ''),
// // categoryItem(Icons.business, ''),
// // categoryItem(IcoFontIcons.bagAlt, ''),
// // categoryItem(IcoFontIcons.shoppingCart, ''),
// // categoryItem(Boxicons.bxs_t_shirt, ''),
// // categoryItem(Boxicons.bxs_binoculars, ''),
// // categoryItem(Boxicons.bxs_devices, ''),
// // categoryItem(Icons.add_photo_alternate_outlined, ''),
// // categoryItem(Icons.movie_filter, ''),
// // categoryItem(IcoFontIcons.gameController, ''),
// // categoryItem(Icons.library_music, ''),
// // categoryItem(Icons.airplanemode_active, ''),
// // categoryItem(MdiIcons.homeHeart, ''),
// // categoryItem(MdiIcons.homeCurrencyUsd, ''),
// // categoryItem(MdiIcons.tableChair, ''),
// // categoryItem(MdiIcons.autoFix, ''),
// // categoryItem(MdiIcons.dogService, ''),
// // categoryItem(Icons.emoji_transportation, ''),
// // categoryItem(IcoFontIcons.lightBulb, ''),
// // categoryItem(IcoFontIcons.globe, ''),
// // categoryItem(IcoFontIcons.stockMobile, ''),
// // categoryItem(IcoFontIcons.waterDrop, ''),
// // categoryItem(FontAwesomeIcons.handHoldingMedical, ''),
// // categoryItem(MdiIcons.soccer, ''),
// // categoryItem(MdiIcons.fileDocumentMultipleOutline, ''),
// // categoryItem(MdiIcons.doctor, ''),
// // categoryItem(MdiIcons.medicalBag, ''),
// // categoryItem(Boxicons.bxs_donate_heart, ''),
// // categoryItem(IcoFontIcons.gift, ''),
// // categoryItem(IcoFontIcons.love, ''),
// // categoryItem(IcoFontIcons.worried, ''),
// // categoryItem(IcoFontIcons.usersSocial, ''),
// // categoryItem(Icons.child_care, ''),
// // categoryItem(MdiIcons.cashCheck, ''),
// // categoryItem(MdiIcons.babyBottle, ''),
// // categoryItem(MdiIcons.humanBabyChangingTable, ''),
// // categoryItem(MdiIcons.bookCheck, ''),
// // categoryItem(EvaIcons.options, ''),
// // ];
================================================
FILE: lib/project/classes/input_model.dart
================================================
import 'dart:ui';
class
InputModel {
int? id;
String? type;
double? amount;
String? category;
String? description;
String? date;
String? time;
Color? color;
InputModel(
{this.id,
this.type,
this.amount,
this.category,
this.description,
this.date,
this.time,
this.color});
Map toMap() {
Map map = {
'id': id,
'type': type,
'amount': amount,
'category': category,
'description': description,
'date': date,
'time': time
};
// use for updating - not necessary?
if (id != null) {
map['id'] = id;
}
return map;
}
static InputModel fromMap(Map map) {
return InputModel(
id: map['id'],
type: map['type'],
amount: map['amount'],
category: map['category'],
description: map['description'],
date: map['date'],
time: map['time'],
);
}
}
================================================
FILE: lib/project/classes/keyboard.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/classes/constants.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:sliding_up_panel/sliding_up_panel.dart';
class CustomKeyboard extends StatelessWidget {
CustomKeyboard(
{required this.panelController,
this.mainFocus,
this.nextFocus,
required this.onTextInput,
required this.onBackspace,
required this.page});
final PanelController panelController;
final FocusNode? mainFocus;
final FocusNode? nextFocus;
final VoidCallback onBackspace;
final ValueSetter onTextInput;
final Widget page;
List generatedKeys() {
List keys = [];
for (String i in [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'.',
'0',
''
]) {
keys.add(
TextKey(
text: i,
onTextInput: this.onTextInput,
onBackspace: this.onBackspace,
),
);
}
return keys;
}
@override
Widget build(BuildContext context) {
return Container(
color: white,
child: Padding(
padding: EdgeInsets.only(bottom: 25.h),
child: Column(children: [
SizedBox(
height: 52.h,
child: Padding(
padding: EdgeInsets.only(left: 5.w, right: 20.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
panelController.close();
FocusScope.of(context).requestFocus(nextFocus);
},
child: SizedBox(
height: 35.h,
width:60.w,
child: Icon(Icons.keyboard_arrow_down,
size: 25.sp, color: Colors.blueGrey),
),
),
// GestureDetector(
// onTap: () {
// panelController.close();
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => this.page,
// ));
// },
// child: Text(
// 'Choose Category',
// style: TextStyle(
// fontSize: 16.sp,
// fontWeight: FontWeight.bold,
// color: Colors.blueGrey),
// ),
// ),
GestureDetector(
onTap: () {
panelController.close();
this.mainFocus!.unfocus();
},
child: Text(
getTranslated(context, "Done")!,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
color: Colors.blue),
),
)
],
),
),
),
Wrap(
children: generatedKeys(),
),
])),
);
}
}
class TextKey extends StatelessWidget {
const TextKey({
required this.text,
this.onBackspace,
this.onTextInput,
});
final VoidCallback? onBackspace;
final String text;
final ValueSetter? onTextInput;
@override
Widget build(BuildContext context) {
return Container(
color: white,
width: 1.sw / 3,
height: 55.h,
child: Material(
child: InkWell(
onTap: () {
this.text.isEmpty
? this.onBackspace?.call()
: this.onTextInput?.call(this.text);
},
child: Center(
child: this.text.isEmpty
? Icon(
Icons.backspace_outlined,
color: red,
)
: Text(
this.text,
style:
TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
)),
),
),
);
}
}
================================================
FILE: lib/project/classes/lockscreen.dart
================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_lock/flutter_app_lock.dart';
import 'package:flutter_screen_lock/configurations/input_button_config.dart';
import 'package:flutter_screen_lock/configurations/screen_lock_config.dart';
import 'package:flutter_screen_lock/configurations/secret_config.dart';
import 'package:flutter_screen_lock/configurations/secrets_config.dart';
import 'package:flutter_screen_lock/input_controller.dart';
import 'package:flutter_screen_lock/screen_lock.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:provider/provider.dart';
import '../provider.dart';
import 'custom_toast.dart';
class MainLockScreen extends StatelessWidget {
const MainLockScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ScreenLock(
correctString: sharedPrefs.passcodeScreenLock,
canCancel: false,
didUnlocked: () => AppLock.of(context)!.didUnlock(),
deleteButton:
const Icon(Icons.close, color: Color.fromRGBO(89, 129, 163, 1)),
title: Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text(
getTranslated(
context,
'Please Enter Passcode',
) ??
'Please Enter Passcode',
style: TextStyle(
color: Color.fromRGBO(71, 131, 192, 1),
fontWeight: FontWeight.w500,
fontSize: 20),
),
),
screenLockConfig: const ScreenLockConfig(
backgroundColor: Color.fromRGBO(210, 234, 251, 1),
),
secretsConfig: SecretsConfig(
secretConfig: SecretConfig(
borderColor: Color.fromRGBO(79, 94, 120, 1),
enabledColor: Color.fromRGBO(89, 129, 163, 1),
)),
inputButtonConfig: InputButtonConfig(
buttonStyle: OutlinedButton.styleFrom(
backgroundColor: Color.fromRGBO(71, 131, 192, 1),
// Color.fromRGBO(89, 129, 163, 1)
),
),
);
}
}
class OtherLockScreen extends StatelessWidget {
final BuildContext providerContext;
const OtherLockScreen({required this.providerContext});
@override
Widget build(BuildContext context) {
final inputController = InputController();
print(getTranslated(
context,
'Please Enter Passcode',
));
return ScreenLock(
correctString: '',
title: Padding(
padding: EdgeInsets.only(bottom: 10.h),
child: Text(
getTranslated(
context,
'Please Enter Passcode',
) ??
'Please Enter Passcode',
style: TextStyle(
color: Color.fromRGBO(71, 131, 192, 1),
fontWeight: FontWeight.w500,
fontSize: 20.sp),
),
),
confirmTitle: Text(
getTranslated(
context,
'Please Re-enter Passcode',
) ??
'Please Re-enter Passcode',
style: TextStyle(
color: Color.fromRGBO(71, 131, 192, 1),
fontWeight: FontWeight.w500,
fontSize: 20.sp),
),
confirmation: true,
inputController: inputController,
deleteButton:
const Icon(Icons.close, color: Color.fromRGBO(71, 131, 192, 1)),
screenLockConfig: const ScreenLockConfig(
backgroundColor: Color.fromRGBO(210, 234, 251, 1),
),
secretsConfig: SecretsConfig(
secretConfig: SecretConfig(
borderColor: Color.fromRGBO(79, 94, 120, 1),
enabledColor: Color.fromRGBO(89, 129, 163, 1),
)),
inputButtonConfig: InputButtonConfig(
buttonStyle: OutlinedButton.styleFrom(
backgroundColor: Color.fromRGBO(71, 131, 192, 1),
// Color.fromRGBO(89, 129, 163, 1)
),
),
didConfirmed: (passCode) {
sharedPrefs.passcodeScreenLock = passCode;
Navigator.pop(context);
customToast(context,'Passcode has been enabled');
},
cancelButton: TextButton(
onPressed: () {
this.providerContext.read().onSwitch();
Navigator.pop(context);
},
child: Text(getTranslated(context, 'Cancel') ?? 'Cancel',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: Color.fromRGBO(71, 131, 192, 1)))),
// footer: TextButton(
// onPressed: () => inputController.unsetConfirmed(),
// child: Padding(
// padding: EdgeInsets.only(top: 20.h),
// child: Text(
// getTranslated(
// context,
// 'Return',
// ) ??
// 'Return',
// style: TextStyle(
// color: Color.fromRGBO(71, 131, 192, 1),
// fontWeight: FontWeight.w500,
// fontSize: 20.sp)),
// ),
// ),
);
}
}
================================================
FILE: lib/project/classes/saveOrSaveAndDeleteButtons.dart
================================================
import 'dart:io' show Platform;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:money_assistant_2608/project/classes/alert_dialog.dart';
import 'package:money_assistant_2608/project/database_management/shared_preferences_services.dart';
import 'package:money_assistant_2608/project/localization/methods.dart';
import 'package:money_assistant_2608/project/app_pages/input.dart';
import 'package:money_assistant_2608/project/provider.dart';
import 'package:provider/provider.dart';
import 'category_item.dart';
import 'constants.dart';
import 'custom_toast.dart';
class SaveButton extends StatefulWidget {
final bool saveInput;
final Function? saveCategoryFunc;
final bool? saveFunction;
const SaveButton(this.saveInput, this.saveCategoryFunc, this.saveFunction);
@override
_SaveButtonState createState() => _SaveButtonState();
}
class _SaveButtonState extends State {
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton.icon(
onPressed: () {
if (widget.saveInput) {
saveInputFunc(context, widget.saveFunction!);
} else {
widget.saveCategoryFunc!();
}
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 20.w),
primary: Color.fromRGBO(236, 158, 66, 1),
onPrimary: white,
onSurface: grey,
elevation: 10,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0.r),
),
),
label: Text(
getTranslated(context, 'Save')!,
style: TextStyle(fontSize: 25.sp),
),
icon: Icon(
Icons.save,
size: 25.sp,
),
),
);
}
}
class SaveAndDeleteButton extends StatelessWidget {
final bool saveAndDeleteInput;
final Function? saveCategory;
final String? parentExpenseItem, categoryName;
final BuildContext? contextEx, contextExEdit, contextIn, contextInEdit;
final GlobalKey? formKey;
const SaveAndDeleteButton({
required this.saveAndDeleteInput,
this.saveCategory,
this.categoryName,
this.parentExpenseItem,
this.contextEx,
this.contextExEdit,
this.contextIn,
this.contextInEdit,
this.formKey,
});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton.icon(
onPressed: () {
if (this.saveAndDeleteInput) {
deleteInputFunction(
context,
);
} else {
deleteCategoryFunction(
context: context,
categoryName: this.categoryName!,
parentExpenseItem: this.parentExpenseItem,
contextEx: this.contextEx,
contextExEdit: this.contextExEdit,
contextIn: this.contextIn,
contextInEdit: this.contextInEdit,
);
}
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 20.w),
primary: white,
onPrimary: red,
onSurface: grey,
side: BorderSide(
color: red,
width: 2.h,
),
elevation: 10,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0.r),
)),
icon: Icon(
Icons.delete,
size: 25.sp,
),
label: Text(
getTranslated(context, 'Delete')!,
style: TextStyle(fontSize: 25.sp),
)),
SaveButton(saveAndDeleteInput, this.saveCategory, false),
],
);
}
}
Future deleteCategoryFunction(
{required BuildContext context,
required String categoryName,
String? parentExpenseItem,
BuildContext? contextEx,
contextExEdit,
contextIn,
contextInEdit}) async {
void onDeletion() {
if (contextInEdit != null) {
List incomeItems = sharedPrefs.getItems('income items');
incomeItems.removeWhere((item) => item.text == categoryName);
sharedPrefs.saveItems('income items', incomeItems);
Provider.of(contextInEdit, listen: false)
.getIncomeItems();
if (contextIn != null) {
Provider.of(contextIn, listen: false)
.getIncomeItems();
}
} else {
if (parentExpenseItem == null) {
sharedPrefs.removeItem(categoryName);
var parentExpenseItemNames = sharedPrefs.parentExpenseItemNames;
parentExpenseItemNames.removeWhere(
(parentExpenseItemName) => categoryName == parentExpenseItemName);
sharedPrefs.parentExpenseItemNames = parentExpenseItemNames;
} else {
List expenseItems =
sharedPrefs.getItems(parentExpenseItem);
expenseItems.removeWhere((item) => item.text == categoryName);
sharedPrefs.saveItems(parentExpenseItem, expenseItems);
}
Provider.of(contextEx!, listen: false)
.getAllExpenseItems();
Provider.of(contextExEdit!, listen: false)
.getAllExpenseItems();
}
Navigator.pop(context);
customToast(context,'Category has been deleted');
}
Platform.isIOS
? await iosDialog(
context,
'Are you sure you want to delete this category?', 'Delete',
onDeletion)
: await androidDialog(context, 'Are you sure you want to delete this category?', 'Delete',onDeletion);
}
================================================
FILE: lib/project/database_management/database.dart
================================================
// import 'package:cloud_firestore/cloud_firestore.dart';
// import 'package:firebase_auth/firebase_auth.dart';
//
// class DatabaseService {
// static final usersCollection = FirebaseFirestore.instance.collection("users");
// static final firebaseUser = FirebaseAuth.instance.currentUser;
//
// static final Future