├── .gitignore
├── .metadata
├── README.md
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── stonks
│ │ │ │ └── mood
│ │ │ │ └── mood
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launcher_icon.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launcher_icon.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launcher_icon.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launcher_icon.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launcher_icon.png
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── assets
└── images
│ ├── drive.svg
│ ├── icon.png
│ └── yoga.svg
├── examples
└── screenshots
│ └── main.png
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-App-1024x1024@1x.png
│ │ ├── Icon-App-20x20@1x.png
│ │ ├── Icon-App-20x20@2x.png
│ │ ├── Icon-App-20x20@3x.png
│ │ ├── Icon-App-29x29@1x.png
│ │ ├── Icon-App-29x29@2x.png
│ │ ├── Icon-App-29x29@3x.png
│ │ ├── Icon-App-40x40@1x.png
│ │ ├── Icon-App-40x40@2x.png
│ │ ├── Icon-App-40x40@3x.png
│ │ ├── Icon-App-50x50@1x.png
│ │ ├── Icon-App-50x50@2x.png
│ │ ├── Icon-App-57x57@1x.png
│ │ ├── Icon-App-57x57@2x.png
│ │ ├── Icon-App-60x60@2x.png
│ │ ├── Icon-App-60x60@3x.png
│ │ ├── Icon-App-72x72@1x.png
│ │ ├── Icon-App-72x72@2x.png
│ │ ├── Icon-App-76x76@1x.png
│ │ ├── Icon-App-76x76@2x.png
│ │ └── Icon-App-83.5x83.5@2x.png
│ └── LaunchImage.imageset
│ │ ├── Contents.json
│ │ ├── LaunchImage.png
│ │ ├── LaunchImage@2x.png
│ │ ├── LaunchImage@3x.png
│ │ └── README.md
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
├── lib
├── constants.dart
├── database
│ └── database.dart
├── main.dart
├── models
│ ├── activity.dart
│ ├── challenge.dart
│ └── reward.dart
├── providers
│ ├── activities.dart
│ ├── challenges.dart
│ └── rewards.dart
├── screens
│ ├── challenge_screen.dart
│ ├── check_back_screen.dart
│ ├── home_screen.dart
│ ├── review_screen.dart
│ ├── reward_screen.dart
│ ├── schedule_screen.dart
│ └── triage_screen.dart
└── widgets
│ ├── action_panel.dart
│ ├── custom_safe_area.dart
│ ├── emoji_button.dart
│ ├── knob_slider.dart
│ ├── loading_screen.dart
│ └── slider_question.dart
├── pubspec.lock
└── pubspec.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 |
33 | # Web related
34 | lib/generated_plugin_registrant.dart
35 |
36 | # Symbolication related
37 | app.*.symbols
38 |
39 | # Obfuscation related
40 | app.*.map.json
41 |
42 | # Exceptions to above rules.
43 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
44 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 7c9f95ac0aca4f43efae9b4377137f3f47f2fb06
8 | channel: master
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Helm
2 | Helm is an app that gamifies stress/anxiety/depression management in an actionable manner to provide relief.
3 |
4 | To try it out, [setup flutter on your machine](https://flutter.dev/docs/get-started/install) and clone the project source.
5 | ```bash
6 | # Clone project
7 | git clone https://github.com/chuabingquan/helm.git
8 |
9 | # For iOS deployment
10 | cd helm/
11 | rm -rf ios android/
12 | flutter create .
13 | flutter pub run flutter_launcher_icons:main
14 | flutter run
15 |
16 | # For Android Deployment
17 | cd helm/
18 | flutter run
19 | ```
20 |
21 | ## Context
22 | As part of [NUS DSC's Hack For Good 2020](https://dsc.comp.nus.edu.sg/hackforgood2020), we developed Helm to address the problem statement of using technology to aid issues centered around stress/anxiety/depression. More info can be found in our [pitch deck](https://docs.google.com/presentation/d/1hH11FbyhlhNWxPFe0hKWraFyy2q5O0zYZZu8inYZYG8/edit?usp=sharing).
23 |
24 | ## How Does it Work?
25 | - Helm quantifies the difference in a user's actual and ideal stress/anxiety/depression level as credits to be spent on a prescribed set of activities before the day ends. By doing so, users inadvertently take actionable steps to deal with their condition.
26 |
27 | - Users then decide on the activities to spend their credits on, the reward they get for completing them (to positively reinforce the habit of taking actionable steps to deal with their condition), and a time for Helm to notify them to check back in.
28 |
29 | - When users check back in, Helm prompts them to reward themselves, then proceeds to re-evaluate their stress/anxiety/depression levels.
30 |
31 | - Should their stress/anxiety/depression levels be not up to mark, users get to re-attempt the challenge with their re-evaluated levels, restarting the whole process.
32 |
33 | ## Screenshots
34 | 
35 |
36 | ## Credits
37 | - [Mary Heng](https://github.com/maryheng)
38 | - [Bing Quan](https://github.com/chuabingquan)
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 28
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "com.stonks.helm"
42 | minSdkVersion 16
43 | targetSdkVersion 28
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | }
47 |
48 | buildTypes {
49 | release {
50 | // TODO: Add your own signing config for the release build.
51 | // Signing with the debug keys for now, so `flutter run --release` works.
52 | signingConfig signingConfigs.debug
53 | }
54 | }
55 | }
56 |
57 | flutter {
58 | source '../..'
59 | }
60 |
61 | dependencies {
62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
63 | }
64 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
9 |
10 |
11 |
15 |
22 |
26 |
30 |
35 |
39 |
40 |
41 |
42 |
43 |
44 |
46 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/stonks/mood/mood/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.stonks.helm
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/android/app/src/main/res/mipmap-hdpi/launcher_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/android/app/src/main/res/mipmap-mdpi/launcher_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/assets/images/drive.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/assets/images/icon.png
--------------------------------------------------------------------------------
/assets/images/yoga.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/screenshots/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/examples/screenshots/main.png
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def parse_KV_file(file, separator='=')
14 | file_abs_path = File.expand_path(file)
15 | if !File.exists? file_abs_path
16 | return [];
17 | end
18 | generated_key_values = {}
19 | skip_line_start_symbols = ["#", "/"]
20 | File.foreach(file_abs_path) do |line|
21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
22 | plugin = line.split(pattern=separator)
23 | if plugin.length == 2
24 | podname = plugin[0].strip()
25 | path = plugin[1].strip()
26 | podpath = File.expand_path("#{path}", file_abs_path)
27 | generated_key_values[podname] = podpath
28 | else
29 | puts "Invalid plugin specification: #{line}"
30 | end
31 | end
32 | generated_key_values
33 | end
34 |
35 | target 'Runner' do
36 | use_frameworks!
37 | use_modular_headers!
38 |
39 | # Flutter Pod
40 |
41 | copied_flutter_dir = File.join(__dir__, 'Flutter')
42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
48 |
49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
50 | unless File.exist?(generated_xcode_build_settings_path)
51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
52 | end
53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
55 |
56 | unless File.exist?(copied_framework_path)
57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
58 | end
59 | unless File.exist?(copied_podspec_path)
60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
61 | end
62 | end
63 |
64 | # Keep pod path relative so it can be checked into Podfile.lock.
65 | pod 'Flutter', :path => 'Flutter'
66 |
67 | # Plugin Pods
68 |
69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
70 | # referring to absolute paths on developers' machines.
71 | system('rm -rf .symlinks')
72 | system('mkdir -p .symlinks/plugins')
73 | plugin_pods = parse_KV_file('../.flutter-plugins')
74 | plugin_pods.each do |name, path|
75 | symlink = File.join('.symlinks', 'plugins', name)
76 | File.symlink(path, symlink)
77 | pod name, :path => File.join(symlink, 'ios')
78 | end
79 | end
80 |
81 | post_install do |installer|
82 | installer.pods_project.targets.each do |target|
83 | target.build_configurations.each do |config|
84 | config.build_settings['ENABLE_BITCODE'] = 'NO'
85 | end
86 | end
87 | end
88 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - flutter_local_notifications (0.0.1):
4 | - Flutter
5 | - FMDB (2.7.5):
6 | - FMDB/standard (= 2.7.5)
7 | - FMDB/standard (2.7.5)
8 | - path_provider (0.0.1):
9 | - Flutter
10 | - path_provider_macos (0.0.1):
11 | - Flutter
12 | - sqflite (0.0.1):
13 | - Flutter
14 | - FMDB (~> 2.7.2)
15 |
16 | DEPENDENCIES:
17 | - Flutter (from `Flutter`)
18 | - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
19 | - path_provider (from `.symlinks/plugins/path_provider/ios`)
20 | - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`)
21 | - sqflite (from `.symlinks/plugins/sqflite/ios`)
22 |
23 | SPEC REPOS:
24 | trunk:
25 | - FMDB
26 |
27 | EXTERNAL SOURCES:
28 | Flutter:
29 | :path: Flutter
30 | flutter_local_notifications:
31 | :path: ".symlinks/plugins/flutter_local_notifications/ios"
32 | path_provider:
33 | :path: ".symlinks/plugins/path_provider/ios"
34 | path_provider_macos:
35 | :path: ".symlinks/plugins/path_provider_macos/ios"
36 | sqflite:
37 | :path: ".symlinks/plugins/sqflite/ios"
38 |
39 | SPEC CHECKSUMS:
40 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
41 | flutter_local_notifications: 9e4738ce2471c5af910d961a6b7eadcf57c50186
42 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
43 | path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d
44 | path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0
45 | sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0
46 |
47 | PODFILE CHECKSUM: c34e2287a9ccaa606aeceab922830efb9a6ff69a
48 |
49 | COCOAPODS: 1.8.3
50 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 1E0064D01192A5D855C7CC7C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4F79109C17624C191B3A315 /* Pods_Runner.framework */; };
12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXCopyFilesBuildPhase section */
20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
21 | isa = PBXCopyFilesBuildPhase;
22 | buildActionMask = 2147483647;
23 | dstPath = "";
24 | dstSubfolderSpec = 10;
25 | files = (
26 | );
27 | name = "Embed Frameworks";
28 | runOnlyForDeploymentPostprocessing = 0;
29 | };
30 | /* End PBXCopyFilesBuildPhase section */
31 |
32 | /* Begin PBXFileReference section */
33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
35 | 1F682888619725305B9656E0 /* 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 = ""; };
36 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
37 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
38 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
39 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
40 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
41 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
42 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
43 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
44 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
45 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
46 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
47 | B4F79109C17624C191B3A315 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
48 | CC144868051890330377EB2F /* 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 = ""; };
49 | F936A1EE97F52DDCDB5EE553 /* 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 = ""; };
50 | /* End PBXFileReference section */
51 |
52 | /* Begin PBXFrameworksBuildPhase section */
53 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
54 | isa = PBXFrameworksBuildPhase;
55 | buildActionMask = 2147483647;
56 | files = (
57 | 1E0064D01192A5D855C7CC7C /* Pods_Runner.framework in Frameworks */,
58 | );
59 | runOnlyForDeploymentPostprocessing = 0;
60 | };
61 | /* End PBXFrameworksBuildPhase section */
62 |
63 | /* Begin PBXGroup section */
64 | 5075D4304C2E22A4F564641F /* Pods */ = {
65 | isa = PBXGroup;
66 | children = (
67 | 1F682888619725305B9656E0 /* Pods-Runner.debug.xcconfig */,
68 | F936A1EE97F52DDCDB5EE553 /* Pods-Runner.release.xcconfig */,
69 | CC144868051890330377EB2F /* Pods-Runner.profile.xcconfig */,
70 | );
71 | path = Pods;
72 | sourceTree = "";
73 | };
74 | 6FB6BB698445B5D512DA866D /* Frameworks */ = {
75 | isa = PBXGroup;
76 | children = (
77 | B4F79109C17624C191B3A315 /* Pods_Runner.framework */,
78 | );
79 | name = Frameworks;
80 | sourceTree = "";
81 | };
82 | 9740EEB11CF90186004384FC /* Flutter */ = {
83 | isa = PBXGroup;
84 | children = (
85 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
86 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
87 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
88 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
89 | );
90 | name = Flutter;
91 | sourceTree = "";
92 | };
93 | 97C146E51CF9000F007C117D = {
94 | isa = PBXGroup;
95 | children = (
96 | 9740EEB11CF90186004384FC /* Flutter */,
97 | 97C146F01CF9000F007C117D /* Runner */,
98 | 97C146EF1CF9000F007C117D /* Products */,
99 | 5075D4304C2E22A4F564641F /* Pods */,
100 | 6FB6BB698445B5D512DA866D /* Frameworks */,
101 | );
102 | sourceTree = "";
103 | };
104 | 97C146EF1CF9000F007C117D /* Products */ = {
105 | isa = PBXGroup;
106 | children = (
107 | 97C146EE1CF9000F007C117D /* Runner.app */,
108 | );
109 | name = Products;
110 | sourceTree = "";
111 | };
112 | 97C146F01CF9000F007C117D /* Runner */ = {
113 | isa = PBXGroup;
114 | children = (
115 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
116 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
117 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
118 | 97C147021CF9000F007C117D /* Info.plist */,
119 | 97C146F11CF9000F007C117D /* Supporting Files */,
120 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
121 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
122 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
123 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
124 | );
125 | path = Runner;
126 | sourceTree = "";
127 | };
128 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
129 | isa = PBXGroup;
130 | children = (
131 | );
132 | name = "Supporting Files";
133 | sourceTree = "";
134 | };
135 | /* End PBXGroup section */
136 |
137 | /* Begin PBXNativeTarget section */
138 | 97C146ED1CF9000F007C117D /* Runner */ = {
139 | isa = PBXNativeTarget;
140 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
141 | buildPhases = (
142 | 81876A32DC5F3D6A8967E401 /* [CP] Check Pods Manifest.lock */,
143 | 9740EEB61CF901F6004384FC /* Run Script */,
144 | 97C146EA1CF9000F007C117D /* Sources */,
145 | 97C146EB1CF9000F007C117D /* Frameworks */,
146 | 97C146EC1CF9000F007C117D /* Resources */,
147 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
148 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
149 | 360774CD843DF6A9C5D262C1 /* [CP] Embed Pods Frameworks */,
150 | );
151 | buildRules = (
152 | );
153 | dependencies = (
154 | );
155 | name = Runner;
156 | productName = Runner;
157 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
158 | productType = "com.apple.product-type.application";
159 | };
160 | /* End PBXNativeTarget section */
161 |
162 | /* Begin PBXProject section */
163 | 97C146E61CF9000F007C117D /* Project object */ = {
164 | isa = PBXProject;
165 | attributes = {
166 | LastUpgradeCheck = 1020;
167 | ORGANIZATIONNAME = "";
168 | TargetAttributes = {
169 | 97C146ED1CF9000F007C117D = {
170 | CreatedOnToolsVersion = 7.3.1;
171 | LastSwiftMigration = 1100;
172 | };
173 | };
174 | };
175 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
176 | compatibilityVersion = "Xcode 9.3";
177 | developmentRegion = en;
178 | hasScannedForEncodings = 0;
179 | knownRegions = (
180 | en,
181 | Base,
182 | );
183 | mainGroup = 97C146E51CF9000F007C117D;
184 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
185 | projectDirPath = "";
186 | projectRoot = "";
187 | targets = (
188 | 97C146ED1CF9000F007C117D /* Runner */,
189 | );
190 | };
191 | /* End PBXProject section */
192 |
193 | /* Begin PBXResourcesBuildPhase section */
194 | 97C146EC1CF9000F007C117D /* Resources */ = {
195 | isa = PBXResourcesBuildPhase;
196 | buildActionMask = 2147483647;
197 | files = (
198 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
199 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
200 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
201 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
202 | );
203 | runOnlyForDeploymentPostprocessing = 0;
204 | };
205 | /* End PBXResourcesBuildPhase section */
206 |
207 | /* Begin PBXShellScriptBuildPhase section */
208 | 360774CD843DF6A9C5D262C1 /* [CP] Embed Pods Frameworks */ = {
209 | isa = PBXShellScriptBuildPhase;
210 | buildActionMask = 2147483647;
211 | files = (
212 | );
213 | inputFileListPaths = (
214 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
215 | );
216 | name = "[CP] Embed Pods Frameworks";
217 | outputFileListPaths = (
218 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
219 | );
220 | runOnlyForDeploymentPostprocessing = 0;
221 | shellPath = /bin/sh;
222 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
223 | showEnvVarsInLog = 0;
224 | };
225 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
226 | isa = PBXShellScriptBuildPhase;
227 | buildActionMask = 2147483647;
228 | files = (
229 | );
230 | inputPaths = (
231 | );
232 | name = "Thin Binary";
233 | outputPaths = (
234 | );
235 | runOnlyForDeploymentPostprocessing = 0;
236 | shellPath = /bin/sh;
237 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
238 | };
239 | 81876A32DC5F3D6A8967E401 /* [CP] Check Pods Manifest.lock */ = {
240 | isa = PBXShellScriptBuildPhase;
241 | buildActionMask = 2147483647;
242 | files = (
243 | );
244 | inputFileListPaths = (
245 | );
246 | inputPaths = (
247 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
248 | "${PODS_ROOT}/Manifest.lock",
249 | );
250 | name = "[CP] Check Pods Manifest.lock";
251 | outputFileListPaths = (
252 | );
253 | outputPaths = (
254 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
255 | );
256 | runOnlyForDeploymentPostprocessing = 0;
257 | shellPath = /bin/sh;
258 | 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";
259 | showEnvVarsInLog = 0;
260 | };
261 | 9740EEB61CF901F6004384FC /* Run Script */ = {
262 | isa = PBXShellScriptBuildPhase;
263 | buildActionMask = 2147483647;
264 | files = (
265 | );
266 | inputPaths = (
267 | );
268 | name = "Run Script";
269 | outputPaths = (
270 | );
271 | runOnlyForDeploymentPostprocessing = 0;
272 | shellPath = /bin/sh;
273 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
274 | };
275 | /* End PBXShellScriptBuildPhase section */
276 |
277 | /* Begin PBXSourcesBuildPhase section */
278 | 97C146EA1CF9000F007C117D /* Sources */ = {
279 | isa = PBXSourcesBuildPhase;
280 | buildActionMask = 2147483647;
281 | files = (
282 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
283 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
284 | );
285 | runOnlyForDeploymentPostprocessing = 0;
286 | };
287 | /* End PBXSourcesBuildPhase section */
288 |
289 | /* Begin PBXVariantGroup section */
290 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
291 | isa = PBXVariantGroup;
292 | children = (
293 | 97C146FB1CF9000F007C117D /* Base */,
294 | );
295 | name = Main.storyboard;
296 | sourceTree = "";
297 | };
298 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
299 | isa = PBXVariantGroup;
300 | children = (
301 | 97C147001CF9000F007C117D /* Base */,
302 | );
303 | name = LaunchScreen.storyboard;
304 | sourceTree = "";
305 | };
306 | /* End PBXVariantGroup section */
307 |
308 | /* Begin XCBuildConfiguration section */
309 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
310 | isa = XCBuildConfiguration;
311 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
312 | buildSettings = {
313 | ALWAYS_SEARCH_USER_PATHS = NO;
314 | CLANG_ANALYZER_NONNULL = YES;
315 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
316 | CLANG_CXX_LIBRARY = "libc++";
317 | CLANG_ENABLE_MODULES = YES;
318 | CLANG_ENABLE_OBJC_ARC = YES;
319 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
320 | CLANG_WARN_BOOL_CONVERSION = YES;
321 | CLANG_WARN_COMMA = YES;
322 | CLANG_WARN_CONSTANT_CONVERSION = YES;
323 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
324 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
325 | CLANG_WARN_EMPTY_BODY = YES;
326 | CLANG_WARN_ENUM_CONVERSION = YES;
327 | CLANG_WARN_INFINITE_RECURSION = YES;
328 | CLANG_WARN_INT_CONVERSION = YES;
329 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
330 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
331 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
332 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
333 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
334 | CLANG_WARN_STRICT_PROTOTYPES = YES;
335 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
336 | CLANG_WARN_UNREACHABLE_CODE = YES;
337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
339 | COPY_PHASE_STRIP = NO;
340 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
341 | ENABLE_NS_ASSERTIONS = NO;
342 | ENABLE_STRICT_OBJC_MSGSEND = YES;
343 | GCC_C_LANGUAGE_STANDARD = gnu99;
344 | GCC_NO_COMMON_BLOCKS = YES;
345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
347 | GCC_WARN_UNDECLARED_SELECTOR = YES;
348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
349 | GCC_WARN_UNUSED_FUNCTION = YES;
350 | GCC_WARN_UNUSED_VARIABLE = YES;
351 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
352 | MTL_ENABLE_DEBUG_INFO = NO;
353 | SDKROOT = iphoneos;
354 | SUPPORTED_PLATFORMS = iphoneos;
355 | TARGETED_DEVICE_FAMILY = "1,2";
356 | VALIDATE_PRODUCT = YES;
357 | };
358 | name = Profile;
359 | };
360 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
361 | isa = XCBuildConfiguration;
362 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
363 | buildSettings = {
364 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
365 | CLANG_ENABLE_MODULES = YES;
366 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
367 | DEVELOPMENT_TEAM = 589NVB575D;
368 | ENABLE_BITCODE = NO;
369 | FRAMEWORK_SEARCH_PATHS = (
370 | "$(inherited)",
371 | "$(PROJECT_DIR)/Flutter",
372 | );
373 | INFOPLIST_FILE = Runner/Info.plist;
374 | LD_RUNPATH_SEARCH_PATHS = (
375 | "$(inherited)",
376 | "@executable_path/Frameworks",
377 | );
378 | LIBRARY_SEARCH_PATHS = (
379 | "$(inherited)",
380 | "$(PROJECT_DIR)/Flutter",
381 | );
382 | PRODUCT_BUNDLE_IDENTIFIER = com.stonks.helm;
383 | PRODUCT_NAME = "$(TARGET_NAME)";
384 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
385 | SWIFT_VERSION = 5.0;
386 | VERSIONING_SYSTEM = "apple-generic";
387 | };
388 | name = Profile;
389 | };
390 | 97C147031CF9000F007C117D /* Debug */ = {
391 | isa = XCBuildConfiguration;
392 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
393 | buildSettings = {
394 | ALWAYS_SEARCH_USER_PATHS = NO;
395 | CLANG_ANALYZER_NONNULL = YES;
396 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
397 | CLANG_CXX_LIBRARY = "libc++";
398 | CLANG_ENABLE_MODULES = YES;
399 | CLANG_ENABLE_OBJC_ARC = YES;
400 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
401 | CLANG_WARN_BOOL_CONVERSION = YES;
402 | CLANG_WARN_COMMA = YES;
403 | CLANG_WARN_CONSTANT_CONVERSION = YES;
404 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
405 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
406 | CLANG_WARN_EMPTY_BODY = YES;
407 | CLANG_WARN_ENUM_CONVERSION = YES;
408 | CLANG_WARN_INFINITE_RECURSION = YES;
409 | CLANG_WARN_INT_CONVERSION = YES;
410 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
411 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
412 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
413 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
414 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
415 | CLANG_WARN_STRICT_PROTOTYPES = YES;
416 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
417 | CLANG_WARN_UNREACHABLE_CODE = YES;
418 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
419 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
420 | COPY_PHASE_STRIP = NO;
421 | DEBUG_INFORMATION_FORMAT = dwarf;
422 | ENABLE_STRICT_OBJC_MSGSEND = YES;
423 | ENABLE_TESTABILITY = YES;
424 | GCC_C_LANGUAGE_STANDARD = gnu99;
425 | GCC_DYNAMIC_NO_PIC = NO;
426 | GCC_NO_COMMON_BLOCKS = YES;
427 | GCC_OPTIMIZATION_LEVEL = 0;
428 | GCC_PREPROCESSOR_DEFINITIONS = (
429 | "DEBUG=1",
430 | "$(inherited)",
431 | );
432 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
433 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
434 | GCC_WARN_UNDECLARED_SELECTOR = YES;
435 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
436 | GCC_WARN_UNUSED_FUNCTION = YES;
437 | GCC_WARN_UNUSED_VARIABLE = YES;
438 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
439 | MTL_ENABLE_DEBUG_INFO = YES;
440 | ONLY_ACTIVE_ARCH = YES;
441 | SDKROOT = iphoneos;
442 | TARGETED_DEVICE_FAMILY = "1,2";
443 | };
444 | name = Debug;
445 | };
446 | 97C147041CF9000F007C117D /* Release */ = {
447 | isa = XCBuildConfiguration;
448 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
449 | buildSettings = {
450 | ALWAYS_SEARCH_USER_PATHS = NO;
451 | CLANG_ANALYZER_NONNULL = YES;
452 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
453 | CLANG_CXX_LIBRARY = "libc++";
454 | CLANG_ENABLE_MODULES = YES;
455 | CLANG_ENABLE_OBJC_ARC = YES;
456 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
457 | CLANG_WARN_BOOL_CONVERSION = YES;
458 | CLANG_WARN_COMMA = YES;
459 | CLANG_WARN_CONSTANT_CONVERSION = YES;
460 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
461 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
462 | CLANG_WARN_EMPTY_BODY = YES;
463 | CLANG_WARN_ENUM_CONVERSION = YES;
464 | CLANG_WARN_INFINITE_RECURSION = YES;
465 | CLANG_WARN_INT_CONVERSION = YES;
466 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
467 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
468 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
469 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
470 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
471 | CLANG_WARN_STRICT_PROTOTYPES = YES;
472 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
473 | CLANG_WARN_UNREACHABLE_CODE = YES;
474 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
475 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
476 | COPY_PHASE_STRIP = NO;
477 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
478 | ENABLE_NS_ASSERTIONS = NO;
479 | ENABLE_STRICT_OBJC_MSGSEND = YES;
480 | GCC_C_LANGUAGE_STANDARD = gnu99;
481 | GCC_NO_COMMON_BLOCKS = YES;
482 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
483 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
484 | GCC_WARN_UNDECLARED_SELECTOR = YES;
485 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
486 | GCC_WARN_UNUSED_FUNCTION = YES;
487 | GCC_WARN_UNUSED_VARIABLE = YES;
488 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
489 | MTL_ENABLE_DEBUG_INFO = NO;
490 | SDKROOT = iphoneos;
491 | SUPPORTED_PLATFORMS = iphoneos;
492 | SWIFT_COMPILATION_MODE = wholemodule;
493 | SWIFT_OPTIMIZATION_LEVEL = "-O";
494 | TARGETED_DEVICE_FAMILY = "1,2";
495 | VALIDATE_PRODUCT = YES;
496 | };
497 | name = Release;
498 | };
499 | 97C147061CF9000F007C117D /* Debug */ = {
500 | isa = XCBuildConfiguration;
501 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
502 | buildSettings = {
503 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
504 | CLANG_ENABLE_MODULES = YES;
505 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
506 | DEVELOPMENT_TEAM = 589NVB575D;
507 | ENABLE_BITCODE = NO;
508 | FRAMEWORK_SEARCH_PATHS = (
509 | "$(inherited)",
510 | "$(PROJECT_DIR)/Flutter",
511 | );
512 | INFOPLIST_FILE = Runner/Info.plist;
513 | LD_RUNPATH_SEARCH_PATHS = (
514 | "$(inherited)",
515 | "@executable_path/Frameworks",
516 | );
517 | LIBRARY_SEARCH_PATHS = (
518 | "$(inherited)",
519 | "$(PROJECT_DIR)/Flutter",
520 | );
521 | PRODUCT_BUNDLE_IDENTIFIER = com.stonks.helm;
522 | PRODUCT_NAME = "$(TARGET_NAME)";
523 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
524 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
525 | SWIFT_VERSION = 5.0;
526 | VERSIONING_SYSTEM = "apple-generic";
527 | };
528 | name = Debug;
529 | };
530 | 97C147071CF9000F007C117D /* Release */ = {
531 | isa = XCBuildConfiguration;
532 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
533 | buildSettings = {
534 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
535 | CLANG_ENABLE_MODULES = YES;
536 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
537 | DEVELOPMENT_TEAM = 589NVB575D;
538 | ENABLE_BITCODE = NO;
539 | FRAMEWORK_SEARCH_PATHS = (
540 | "$(inherited)",
541 | "$(PROJECT_DIR)/Flutter",
542 | );
543 | INFOPLIST_FILE = Runner/Info.plist;
544 | LD_RUNPATH_SEARCH_PATHS = (
545 | "$(inherited)",
546 | "@executable_path/Frameworks",
547 | );
548 | LIBRARY_SEARCH_PATHS = (
549 | "$(inherited)",
550 | "$(PROJECT_DIR)/Flutter",
551 | );
552 | PRODUCT_BUNDLE_IDENTIFIER = com.stonks.helm;
553 | PRODUCT_NAME = "$(TARGET_NAME)";
554 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
555 | SWIFT_VERSION = 5.0;
556 | VERSIONING_SYSTEM = "apple-generic";
557 | };
558 | name = Release;
559 | };
560 | /* End XCBuildConfiguration section */
561 |
562 | /* Begin XCConfigurationList section */
563 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
564 | isa = XCConfigurationList;
565 | buildConfigurations = (
566 | 97C147031CF9000F007C117D /* Debug */,
567 | 97C147041CF9000F007C117D /* Release */,
568 | 249021D3217E4FDB00AE95B9 /* Profile */,
569 | );
570 | defaultConfigurationIsVisible = 0;
571 | defaultConfigurationName = Release;
572 | };
573 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
574 | isa = XCConfigurationList;
575 | buildConfigurations = (
576 | 97C147061CF9000F007C117D /* Debug */,
577 | 97C147071CF9000F007C117D /* Release */,
578 | 249021D4217E4FDB00AE95B9 /* Profile */,
579 | );
580 | defaultConfigurationIsVisible = 0;
581 | defaultConfigurationName = Release;
582 | };
583 | /* End XCConfigurationList section */
584 | };
585 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
586 | }
587 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | if #available(iOS 10.0, *) {
11 | UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
12 | }
13 | GeneratedPluginRegistrant.register(with: self)
14 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chuabingquan/helm/6c00ea1408de15f59f5bd81233ecc132ece174ba/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | Helm
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/lib/constants.dart:
--------------------------------------------------------------------------------
1 | library constants;
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | const PRIMARY_COLOR = MaterialColor(0xffffd98e, {});
6 | const ACCENT_COLOR = MaterialColor(0xff314675, {});
7 |
8 | const DATABASE_NAME = 'helm';
--------------------------------------------------------------------------------
/lib/database/database.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:path/path.dart';
4 | import 'package:sqflite/sqflite.dart';
5 | import '../models/activity.dart';
6 | import '../models/reward.dart';
7 |
8 | final _defaultRewards = [
9 | Reward(
10 | id: 1,
11 | name: 'Ice Cream',
12 | emoji: '🍦',
13 | ),
14 | Reward(
15 | id: 2,
16 | name: 'Volunteer',
17 | emoji: '🙋',
18 | ),
19 | Reward(
20 | id: 3,
21 | name: 'Personal Praise',
22 | emoji: '🙌',
23 | ),
24 | Reward(
25 | id: 4,
26 | name: 'Mobile Game',
27 | emoji: '🎮',
28 | ),
29 | Reward(
30 | id: 5,
31 | name: 'Buy something nice',
32 | emoji: '🛒',
33 | ),
34 | Reward(
35 | id: 6,
36 | name: 'Nap',
37 | emoji: '😴',
38 | ),
39 | ];
40 |
41 | final _defaultActivities = [
42 | Activity(
43 | id: 1,
44 | name: 'Meditate',
45 | description: 'Do 10 sets of breathing exercises',
46 | emoji: '🧘',
47 | credits: 1,
48 | ),
49 | Activity(
50 | id: 2,
51 | name: 'Watch a TED Talk',
52 | description: 'Keep yourself motivated!',
53 | emoji: '📺',
54 | credits: 1,
55 | ),
56 | Activity(
57 | id: 3,
58 | name: 'Jog',
59 | description: 'Go for a 1/2 hour jog',
60 | emoji: '🏃',
61 | credits: 2,
62 | ),
63 | Activity(
64 | id: 4,
65 | name: 'Read',
66 | description: 'Keep your mind focused on some other literature',
67 | emoji: '📚',
68 | credits: 2,
69 | ),
70 | Activity(
71 | id: 5,
72 | name: 'Stroll',
73 | description: 'Take a short break and enjoy the scenery',
74 | emoji: '🚶🏻♀️',
75 | credits: 2,
76 | ),
77 | Activity(
78 | id: 6,
79 | name: 'Write',
80 | description: 'Vent your anxiety through writing',
81 | emoji: '📝',
82 | credits: 3,
83 | ),
84 | Activity(
85 | id: 7,
86 | name: 'Bake',
87 | description: 'Bake a cake',
88 | emoji: '🎂',
89 | credits: 3,
90 | ),
91 | Activity(
92 | id: 8,
93 | name: 'Talk to a friend',
94 | description: 'A listening ear helps!',
95 | emoji: '🗣',
96 | credits: 4,
97 | ),
98 | Activity(
99 | id: 9,
100 | name: 'Do someone a favour',
101 | description: 'Helping others helps you to feel better',
102 | emoji: '💪',
103 | credits: 4,
104 | ),
105 | ];
106 |
107 | Future connectToDB(String dbName) async {
108 | try {
109 | return await openDatabase(
110 | join(await getDatabasesPath(), '$dbName.db'),
111 | onCreate: (db, version) async {
112 | try {
113 | final batch = db.batch();
114 |
115 | // Create and insert default rewards into database.
116 | batch.execute(
117 | 'CREATE TABLE IF NOT EXISTS rewards(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, emoji TEXT NOT NULL);');
118 | _defaultRewards.forEach((reward) {
119 | batch.insert('rewards', reward.toMap());
120 | });
121 |
122 | // Create and insert default activities into database.
123 | batch.execute(
124 | 'CREATE TABLE IF NOT EXISTS activities(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, description TEXT NOT NULL, emoji TEXT NOT NULL, credits INTEGER NOT NULL);');
125 | _defaultActivities.forEach((activity) {
126 | batch.insert('activities', activity.toMap());
127 | });
128 |
129 | // Create challenges table.
130 | batch.execute(
131 | "CREATE TABLE IF NOT EXISTS challenges(id INTEGER PRIMARY KEY AUTOINCREMENT, initial_level INTEGER NOT NULL, ideal_level INTEGER NOT NULL, updated_level INTEGER, reward_id INTEGER NOT NULL, created_at INTEGER NOT NULL, remind_at INTEGER NOT NULL, condition TEXT NOT NULL, FOREIGN KEY(reward_id) REFERENCES rewards(id));");
132 |
133 | // Create challenges_activities table.
134 | batch.execute(
135 | 'CREATE TABLE IF NOT EXISTS challenges_activities(id INTEGER PRIMARY KEY AUTOINCREMENT, challenge_id INTEGER NOT NULL, activity_id INTEGER NOT NULL)');
136 |
137 | await batch.commit();
138 | } catch (err) {
139 | throw err;
140 | }
141 | },
142 | version: 2,
143 | );
144 | } catch (err) {
145 | throw err;
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:google_fonts/google_fonts.dart';
4 | import 'package:provider/provider.dart';
5 | import 'package:sqflite/sqflite.dart';
6 | import 'package:flutter_local_notifications/flutter_local_notifications.dart';
7 |
8 | import './constants.dart' as Constants;
9 | import './screens/home_screen.dart';
10 | import './screens/triage_screen.dart';
11 | import './screens/challenge_screen.dart';
12 | import './screens/reward_screen.dart';
13 | import './screens/schedule_screen.dart';
14 | import './screens/check_back_screen.dart';
15 | import './screens/review_screen.dart';
16 | import './providers/activities.dart';
17 | import './providers/rewards.dart';
18 | import './providers/challenges.dart';
19 | import './database/database.dart';
20 | import './widgets/loading_screen.dart';
21 |
22 | final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
23 | FlutterLocalNotificationsPlugin();
24 | NotificationAppLaunchDetails notificationAppLaunchDetails;
25 |
26 | void main() async {
27 | WidgetsFlutterBinding.ensureInitialized();
28 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
29 |
30 | try {
31 | // Enforce portrait orientation.
32 | await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
33 |
34 | // Setup local notifications.
35 | notificationAppLaunchDetails =
36 | await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
37 | final initializationSettingsAndroid =
38 | AndroidInitializationSettings('@mipmap/ic_launcher');
39 | final initializationSettingsIOS = IOSInitializationSettings();
40 | final initializationSettings = InitializationSettings(
41 | initializationSettingsAndroid, initializationSettingsIOS);
42 | await flutterLocalNotificationsPlugin.initialize(
43 | initializationSettings,
44 | );
45 |
46 | runApp(App());
47 | } catch (err) {
48 | throw err;
49 | }
50 | }
51 |
52 | class App extends StatefulWidget {
53 | @override
54 | _AppState createState() => _AppState();
55 | }
56 |
57 | class _AppState extends State {
58 | var _isInit = true;
59 | Database _database;
60 |
61 | @override
62 | void didChangeDependencies() {
63 | super.didChangeDependencies();
64 | if (_isInit) {
65 | connectToDB(Constants.DATABASE_NAME).then((database) {
66 | _database = database;
67 | }).catchError((err) {
68 | print(err);
69 | }).whenComplete(() {
70 | setState(() {
71 | _isInit = false;
72 | });
73 | });
74 | }
75 | }
76 |
77 | @override
78 | Widget build(BuildContext context) {
79 | return !_isInit
80 | ? MultiProvider(
81 | providers: [
82 | ChangeNotifierProvider(
83 | create: (_) => Activities(_database),
84 | ),
85 | ChangeNotifierProvider(
86 | create: (_) => Rewards(_database),
87 | ),
88 | ChangeNotifierProvider(
89 | create: (_) => Challenges(_database),
90 | ),
91 | ],
92 | child: Consumer(
93 | builder: (context, challenges, _) {
94 | return MaterialApp(
95 | title: 'Helm',
96 | debugShowCheckedModeBanner: false,
97 | theme: ThemeData(
98 | primaryColor: Constants.PRIMARY_COLOR,
99 | accentColor: Constants.ACCENT_COLOR,
100 | textTheme: GoogleFonts.karlaTextTheme(
101 | Theme.of(context).textTheme,
102 | ),
103 | ),
104 | home: challenges.latestChallenge == null
105 | ? HomeScreen()
106 | : CheckBackScreen(
107 | challenge: challenges.latestChallenge,
108 | ),
109 | routes: {
110 | TriageScreen.routeName: (ctx) => TriageScreen(),
111 | ChallengeScreen.routeName: (ctx) => ChallengeScreen(),
112 | RewardScreen.routeName: (ctx) => RewardScreen(),
113 | ScheduleScreen.routeName: (ctx) =>
114 | ScheduleScreen(_scheduleNotification),
115 | ReviewScreen.routeName: (ctx) => ReviewScreen(),
116 | },
117 | );
118 | },
119 | ),
120 | )
121 | : MaterialApp(
122 | debugShowCheckedModeBanner: false,
123 | home: Container(
124 | color: Constants.PRIMARY_COLOR,
125 | child: LoadingScreen(),
126 | ),
127 | );
128 | }
129 | }
130 |
131 | Future _scheduleNotification(
132 | DateTime scheduledDateTime, String title, String description) async {
133 | final androidPlatformChannelSpecifics = AndroidNotificationDetails(
134 | 'helm_id',
135 | 'helm',
136 | 'Remind people to check in after spending their credits',
137 | );
138 | final iOSPlatformChannelSpecifics =
139 | IOSNotificationDetails(sound: 'slow_spring_board.aiff');
140 | final platformChannelSpecifics = NotificationDetails(
141 | androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
142 | await flutterLocalNotificationsPlugin.schedule(
143 | 0,
144 | title,
145 | description,
146 | scheduledDateTime,
147 | platformChannelSpecifics,
148 | );
149 | }
150 |
--------------------------------------------------------------------------------
/lib/models/activity.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 |
3 | enum Problem { Anxiety, Stress, Depression }
4 |
5 | class ProblemDetails {
6 | final Problem type;
7 | final String noun;
8 | final String adjective;
9 |
10 | ProblemDetails(
11 | this.type,
12 | this.noun,
13 | this.adjective,
14 | );
15 | }
16 |
17 | ProblemDetails getProblemDetails(Problem type) {
18 | switch (type) {
19 | case Problem.Anxiety:
20 | return ProblemDetails(Problem.Anxiety, 'anxiety', 'anxious');
21 | case Problem.Stress:
22 | return ProblemDetails(Problem.Stress, 'stress', 'stressed');
23 | case Problem.Depression:
24 | return ProblemDetails(Problem.Depression, 'depression', 'depressed');
25 | default:
26 | throw Exception('Problem type is not recognised.');
27 | }
28 | }
29 |
30 | ProblemDetails getProblemDetailsByNoun(String noun) {
31 | switch (noun) {
32 | case 'anxiety':
33 | return getProblemDetails(Problem.Anxiety);
34 | case 'stress':
35 | return getProblemDetails(Problem.Stress);
36 | case 'depression':
37 | return getProblemDetails(Problem.Depression);
38 | default:
39 | throw Exception('Problem noun is not recognised.');
40 | }
41 | }
42 |
43 | class Activity {
44 | final int id;
45 | final String name;
46 | final String description;
47 | final String emoji;
48 | final int credits;
49 |
50 | Activity({
51 | @required this.id,
52 | @required this.name,
53 | @required this.description,
54 | @required this.emoji,
55 | @required this.credits,
56 | });
57 |
58 | Map toMap() => {
59 | 'id': id,
60 | 'name': name,
61 | 'description': description,
62 | 'emoji': emoji,
63 | 'credits': credits,
64 | };
65 |
66 | factory Activity.fromMap(Map map) => Activity(
67 | id: map['id'] as int,
68 | name: map['name'] as String,
69 | description: map['description'] as String,
70 | emoji: map['emoji'] as String,
71 | credits: map['credits'] as int,
72 | );
73 | }
74 |
--------------------------------------------------------------------------------
/lib/models/challenge.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 |
3 | import './activity.dart';
4 | import './reward.dart';
5 |
6 | class Challenge {
7 | final int id;
8 | final int initialLevel;
9 | final int idealLevel;
10 | final int updatedLevel;
11 | final Set activities;
12 | final Reward reward;
13 | final DateTime remindAt;
14 | final ProblemDetails problem;
15 |
16 | Challenge({
17 | this.id,
18 | @required this.initialLevel,
19 | @required this.idealLevel,
20 | this.updatedLevel,
21 | @required this.activities,
22 | @required this.reward,
23 | @required this.remindAt,
24 | @required this.problem,
25 | });
26 |
27 | Map toMap() => {
28 | 'initial_level': initialLevel,
29 | 'ideal_level': idealLevel,
30 | 'updated_level': updatedLevel,
31 | 'reward_id': reward.id,
32 | 'remind_at': remindAt.millisecondsSinceEpoch,
33 | 'condition': problem.noun,
34 | };
35 |
36 | factory Challenge.fromMap(Map map) => Challenge(
37 | id: map['id'],
38 | initialLevel: map['initial_level'],
39 | idealLevel: map['ideal_level'],
40 | updatedLevel: map['updated_level'],
41 | activities:
42 | (map['activities'] as List).map((a) => Activity.fromMap(a)).toSet(),
43 | reward: Reward.fromMap(map['reward']),
44 | remindAt: DateTime.fromMillisecondsSinceEpoch(map['remind_at']),
45 | problem: getProblemDetailsByNoun(map['condition']),
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/lib/models/reward.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 |
3 | class Reward {
4 | final int id;
5 | final String name;
6 | final String emoji;
7 |
8 | Reward({
9 | @required this.id,
10 | @required this.name,
11 | @required this.emoji,
12 | });
13 |
14 | Map toMap() => {
15 | 'id': id,
16 | 'name': name,
17 | 'emoji': emoji,
18 | };
19 |
20 | factory Reward.fromMap(Map map) => Reward(
21 | id: map['id'] as int,
22 | name: map['name'] as String,
23 | emoji: map['emoji'] as String,
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/lib/providers/activities.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:sqflite/sqflite.dart';
3 |
4 | import '../models/activity.dart';
5 |
6 | class Activities with ChangeNotifier {
7 | final Database _database;
8 | List _activities = [];
9 |
10 | Activities(this._database);
11 |
12 | List get activities {
13 | return _activities.map((a) => a).toList();
14 | }
15 |
16 | Future fetchAndSetActivities() async {
17 | try {
18 | final results = await _database.query('activities');
19 | _activities = results.map((a) => Activity.fromMap(a)).toList();
20 | notifyListeners();
21 | } catch (err) {
22 | throw err;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/providers/challenges.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:sqflite/sqflite.dart';
3 |
4 | import '../models/challenge.dart';
5 |
6 | class Challenges with ChangeNotifier {
7 | final Database _database;
8 | Challenge _latestChallengeToday;
9 |
10 | Challenges(this._database) {
11 | fetchAndSetLatestChallenge();
12 | }
13 |
14 | Challenge get latestChallenge {
15 | return _latestChallengeToday;
16 | }
17 |
18 | Future fetchAndSetLatestChallenge() async {
19 | try {
20 | final challengeResults = await _database.query(
21 | 'challenges',
22 | where:
23 | "DATE(created_at/1000, 'unixepoch', 'localtime')=Date('now', 'localtime') AND updated_level IS NULL",
24 | orderBy: 'created_at DESC',
25 | limit: 1,
26 | );
27 |
28 | if (challengeResults.length < 1) {
29 | _latestChallengeToday = null;
30 | return;
31 | }
32 |
33 | final challengeId = challengeResults[0]['id'] as int;
34 | final activityResults = await _database.query(
35 | 'activities',
36 | where:
37 | 'id IN (SELECT activity_id FROM challenges_activities WHERE challenge_id=?)',
38 | whereArgs: [challengeId],
39 | );
40 |
41 | final rewardResults = await _database.query(
42 | 'rewards',
43 | where: 'id=?',
44 | whereArgs: [challengeResults[0]['reward_id'] as int],
45 | );
46 |
47 | _latestChallengeToday = Challenge.fromMap({
48 | ...challengeResults[0],
49 | 'activities': activityResults,
50 | 'reward': rewardResults[0],
51 | });
52 |
53 | notifyListeners();
54 | } catch (err) {
55 | throw err;
56 | }
57 | }
58 |
59 | Future create(Challenge challenge) async {
60 | try {
61 | await _database.transaction((tx) async {
62 | var newChallengeId = 1;
63 | final mostRecentChallengeList =
64 | await tx.query('challenges', orderBy: 'id DESC', limit: 1);
65 |
66 | if (mostRecentChallengeList.length > 0) {
67 | newChallengeId = (mostRecentChallengeList[0]['id'] as int) + 1;
68 | }
69 |
70 | final batch = tx.batch();
71 |
72 | final challengeToInsert = challenge.toMap();
73 | challengeToInsert['id'] = newChallengeId;
74 | challengeToInsert['created_at'] = DateTime.now().millisecondsSinceEpoch;
75 | batch.insert('challenges', challengeToInsert);
76 |
77 | challenge.activities.forEach((a) {
78 | batch.insert('challenges_activities', {
79 | 'challenge_id': newChallengeId,
80 | 'activity_id': a.id,
81 | });
82 | });
83 |
84 | await batch.commit();
85 | });
86 | await fetchAndSetLatestChallenge();
87 | notifyListeners();
88 | } catch (err) {
89 | throw err;
90 | }
91 | }
92 |
93 | Future endChallenge(int challengeId, int updatedLevel) async {
94 | try {
95 | await _database.update(
96 | 'challenges',
97 | {'updated_level': updatedLevel},
98 | where: 'id=?',
99 | whereArgs: [challengeId],
100 | );
101 | await fetchAndSetLatestChallenge();
102 | notifyListeners();
103 | } catch (err) {
104 | throw err;
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/lib/providers/rewards.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:sqflite/sqflite.dart';
3 |
4 | import '../models/reward.dart';
5 |
6 | class Rewards with ChangeNotifier {
7 | final Database _database;
8 | List _rewards = [];
9 |
10 | Rewards(this._database);
11 |
12 | List get rewards {
13 | return _rewards.map((r) => r).toList();
14 | }
15 |
16 | Future fetchAndSetRewards() async {
17 | try {
18 | final results = await _database.query('rewards');
19 | _rewards = results.map((r) => Reward.fromMap(r)).toList();
20 | notifyListeners();
21 | } catch (err) {
22 | print(err);
23 | throw err;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/screens/challenge_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:google_fonts/google_fonts.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | import '../widgets/action_panel.dart';
6 | import '../widgets/emoji_button.dart';
7 | import '../widgets/custom_safe_area.dart';
8 | import './reward_screen.dart';
9 | import '../models/activity.dart';
10 | import '../providers/activities.dart';
11 |
12 | class ChallengeScreen extends StatefulWidget {
13 | static const routeName = '/challenge';
14 |
15 | @override
16 | _ChallengeScreenState createState() => _ChallengeScreenState();
17 | }
18 |
19 | class _ChallengeScreenState extends State {
20 | var _isInit = true;
21 | ProblemDetails _problem;
22 | int _actualLevel;
23 | int _idealLevel;
24 | Set _selectedActivities = {};
25 |
26 | @override
27 | void didChangeDependencies() {
28 | super.didChangeDependencies();
29 | if (_isInit) {
30 | final params =
31 | ModalRoute.of(context).settings.arguments as Map;
32 | _problem = params['problem'] as ProblemDetails;
33 | _actualLevel = params['actualLevel'] as int;
34 | _idealLevel = params['idealLevel'] as int;
35 |
36 | _isInit = false;
37 | }
38 | }
39 |
40 | // Use only for non-empty strings.
41 | String _capitaliseFirstChar(String value) {
42 | return value[0].toUpperCase() + value.substring(1);
43 | }
44 |
45 | @override
46 | Widget build(BuildContext context) {
47 | final theme = Theme.of(context);
48 | final equationProblemWord = _capitaliseFirstChar(_problem.noun);
49 | final activities =
50 | Provider.of(context, listen: false).activities;
51 |
52 | return CustomSafeArea(
53 | child: Scaffold(
54 | appBar: AppBar(
55 | backgroundColor: theme.primaryColor,
56 | elevation: 0.0,
57 | leading: IconButton(
58 | icon: Icon(Icons.arrow_back),
59 | onPressed: () => Navigator.of(context).pop(),
60 | ),
61 | centerTitle: false,
62 | // title: Text('Challenge'),
63 | ),
64 | body: Stack(
65 | children: [
66 | LayoutBuilder(
67 | builder: (ctx, constraints) => Container(
68 | width: double.infinity,
69 | height: constraints.maxHeight - 67.0,
70 | padding: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0),
71 | child: Column(
72 | mainAxisSize: MainAxisSize.min,
73 | crossAxisAlignment: CrossAxisAlignment.start,
74 | children: [
75 | Column(
76 | mainAxisSize: MainAxisSize.min,
77 | crossAxisAlignment: CrossAxisAlignment.start,
78 | children: [
79 | Flexible(
80 | child: FittedBox(
81 | child: Text(
82 | 'The Challenge',
83 | style: const TextStyle(
84 | fontWeight: FontWeight.bold,
85 | fontSize: 20.0,
86 | letterSpacing: 1.2,
87 | ),
88 | ),
89 | ),
90 | ),
91 | SizedBox(
92 | height: 12.0,
93 | ),
94 | Flexible(
95 | child: Text(
96 | 'Your extra ${_problem.noun} is converted into credits.',
97 | style: const TextStyle(
98 | fontSize: 14,
99 | height: 1.2,
100 | ),
101 | ),
102 | ),
103 | SizedBox(
104 | height: 14.0,
105 | ),
106 | Flexible(
107 | child: Container(
108 | width: double.infinity,
109 | padding: const EdgeInsets.symmetric(
110 | vertical: 12.0,
111 | ),
112 | decoration: BoxDecoration(
113 | color: Colors.grey.shade200,
114 | borderRadius: BorderRadius.circular(5.0),
115 | ),
116 | child: Text(
117 | 'Current $equationProblemWord - Ideal $equationProblemWord \n= $equationProblemWord Credits',
118 | textAlign: TextAlign.center,
119 | style: GoogleFonts.robotoMono().copyWith(
120 | fontSize: 13.0,
121 | ),
122 | ),
123 | ),
124 | ),
125 | SizedBox(
126 | height: 14.0,
127 | ),
128 | Flexible(
129 | child: Text(
130 | 'Your objective is to spend all of it to bring your current ${_problem.noun} level down to an ideal level before the day ends.',
131 | style: const TextStyle(
132 | fontSize: 14.0,
133 | height: 1.2,
134 | ),
135 | ),
136 | ),
137 | ],
138 | ),
139 | SizedBox(
140 | height: 30.0,
141 | ),
142 | Expanded(
143 | child: Column(
144 | mainAxisSize: MainAxisSize.min,
145 | children: [
146 | Row(
147 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
148 | children: [
149 | Flexible(
150 | child: FittedBox(
151 | child: Text(
152 | 'Spend Credits On',
153 | style: const TextStyle(
154 | fontWeight: FontWeight.bold,
155 | letterSpacing: 1.1,
156 | fontSize: 16.0,
157 | ),
158 | ),
159 | ),
160 | ),
161 | Flexible(
162 | child: FittedBox(
163 | child: Text(
164 | '${_actualLevel - _idealLevel} $equationProblemWord Credits',
165 | style: const TextStyle(
166 | fontSize: 14.0,
167 | ),
168 | ),
169 | ),
170 | ),
171 | ],
172 | ),
173 | SizedBox(
174 | height: 15.0,
175 | ),
176 | Flexible(
177 | child: ListView.builder(
178 | itemCount: activities.length,
179 | itemBuilder: (ctx, index) {
180 | final activity = activities[index];
181 | final isSelected = _selectedActivities
182 | .where((a) => a.id == activity.id)
183 | .length >
184 | 0;
185 |
186 | return Padding(
187 | padding: const EdgeInsets.symmetric(
188 | vertical: 4.0,
189 | ),
190 | child: EmojiButton(
191 | emoji: activity.emoji,
192 | title: activity.name,
193 | enableSubtitle: true,
194 | backgroundColor: isSelected
195 | ? Colors.greenAccent.shade100
196 | : null,
197 | outlineColor: isSelected
198 | ? Colors.greenAccent.shade200
199 | : null,
200 | subtitle:
201 | '${activity.credits} $equationProblemWord Credits',
202 | onPressed: () {
203 | if (isSelected) {
204 | setState(() {
205 | _selectedActivities.removeWhere(
206 | (a) => a.id == activity.id,
207 | );
208 | });
209 | } else {
210 | var selectedCredits = 0;
211 |
212 | if (_selectedActivities
213 | .isNotEmpty) {
214 | selectedCredits =
215 | _selectedActivities.fold(
216 | 0,
217 | (acc, a) => acc + a.credits,
218 | );
219 | }
220 |
221 | if ((selectedCredits +
222 | activity.credits) <=
223 | (_actualLevel - _idealLevel)) {
224 | setState(() {
225 | _selectedActivities
226 | .add(activity);
227 | });
228 | } else {
229 | showDialog(
230 | context: context,
231 | child: AlertDialog(
232 | title: Text('Oops!'),
233 | content: Text(
234 | 'You do not have sufficient ${_problem.noun} credits left.',
235 | ),
236 | actions: [
237 | FlatButton(
238 | textColor:
239 | theme.accentColor,
240 | child: Text('Okay'),
241 | onPressed: () =>
242 | Navigator.of(context)
243 | .pop(),
244 | ),
245 | ],
246 | ),
247 | );
248 | }
249 | }
250 | }),
251 | );
252 | }),
253 | ),
254 | ],
255 | ),
256 | ),
257 | ],
258 | ),
259 | ),
260 | ),
261 | Align(
262 | alignment: Alignment.bottomCenter,
263 | child: ActionPanel(
264 | title: 'Next',
265 | onPressed: () {
266 | var isValid = !(_selectedActivities.isEmpty ||
267 | _selectedActivities.fold(
268 | 0, (acc, a) => acc + a.credits) !=
269 | _actualLevel - _idealLevel);
270 |
271 | if (isValid) {
272 | Navigator.of(context).pushNamed(
273 | RewardScreen.routeName,
274 | arguments: {
275 | 'problem': _problem,
276 | 'actualLevel': _actualLevel,
277 | 'idealLevel': _idealLevel,
278 | 'activities': _selectedActivities,
279 | },
280 | );
281 | } else {
282 | showDialog(
283 | context: context,
284 | child: AlertDialog(
285 | title: Text('Oops!'),
286 | content: Text(
287 | 'It appears that you have not utilised all your ${_problem.noun} credits yet.',
288 | ),
289 | actions: [
290 | FlatButton(
291 | child: Text('Okay'),
292 | textColor: theme.accentColor,
293 | onPressed: () => Navigator.of(context).pop(),
294 | )
295 | ],
296 | ),
297 | );
298 | }
299 | },
300 | ),
301 | ),
302 | ],
303 | ),
304 | ),
305 | );
306 | }
307 | }
308 |
--------------------------------------------------------------------------------
/lib/screens/check_back_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_svg/flutter_svg.dart';
3 |
4 | import '../widgets/emoji_button.dart';
5 | import '../widgets/custom_safe_area.dart';
6 | import '../widgets/action_panel.dart';
7 | import '../models/challenge.dart';
8 | import './review_screen.dart';
9 |
10 | class CheckBackScreen extends StatelessWidget {
11 | static const routeName = '/check-back';
12 | final Challenge challenge;
13 |
14 | CheckBackScreen({
15 | @required this.challenge,
16 | });
17 |
18 | // Use only for non-empty strings.
19 | String _capitaliseFirstChar(String value) {
20 | return value[0].toUpperCase() + value.substring(1);
21 | }
22 |
23 | @override
24 | Widget build(BuildContext context) {
25 | final theme = Theme.of(context);
26 | final creditsProblemWord = _capitaliseFirstChar(challenge.problem.noun);
27 |
28 | return CustomSafeArea(
29 | child: Scaffold(
30 | appBar: AppBar(
31 | backgroundColor: Colors.transparent,
32 | automaticallyImplyLeading: false,
33 | elevation: 0.0,
34 | ),
35 | body: Stack(
36 | children: [
37 | LayoutBuilder(
38 | builder: (ctx, constraints) => Container(
39 | width: double.infinity,
40 | padding: const EdgeInsets.fromLTRB(80.0, 0.0, 0.0, 30.0),
41 | height: constraints.maxHeight - 67.0,
42 | child: Column(
43 | mainAxisSize: MainAxisSize.min,
44 | children: [
45 | Flexible(
46 | child: SvgPicture.asset(
47 | 'assets/images/drive.svg',
48 | alignment: Alignment.bottomCenter,
49 | ),
50 | ),
51 | ],
52 | ),
53 | )),
54 | LayoutBuilder(
55 | builder: (ctx, constraints) => Container(
56 | width: double.infinity,
57 | height: constraints.maxHeight - 67.0,
58 | padding: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0),
59 | // color: theme.primaryColor,
60 | child: Column(
61 | mainAxisSize: MainAxisSize.min,
62 | crossAxisAlignment: CrossAxisAlignment.start,
63 | children: [
64 | Column(
65 | mainAxisSize: MainAxisSize.min,
66 | crossAxisAlignment: CrossAxisAlignment.start,
67 | children: [
68 | Flexible(
69 | child: FittedBox(
70 | child: Text(
71 | 'Objective',
72 | style: TextStyle(
73 | fontWeight: FontWeight.bold,
74 | fontSize: 25.0,
75 | letterSpacing: 1.2,
76 | ),
77 | ),
78 | ),
79 | ),
80 | SizedBox(
81 | height: 10.0,
82 | ),
83 | Flexible(
84 | child: Text(
85 | 'Spend ${challenge.initialLevel - challenge.idealLevel} ${challenge.problem.noun} credits on',
86 | style: const TextStyle(
87 | fontSize: 14,
88 | height: 1.2,
89 | ),
90 | ),
91 | ),
92 | ],
93 | ),
94 | SizedBox(
95 | height: 15.0,
96 | ),
97 | Expanded(
98 | child: ListView.builder(
99 | itemCount: challenge.activities.length,
100 | itemBuilder: (ctx, index) {
101 | final activity =
102 | challenge.activities.toList()[index];
103 | return Padding(
104 | padding: const EdgeInsets.symmetric(
105 | vertical: 4.0,
106 | ),
107 | child: EmojiButton(
108 | emoji: activity.emoji,
109 | title: activity.name,
110 | enableSubtitle: true,
111 | subtitle:
112 | '${activity.credits} $creditsProblemWord Credits',
113 | backgroundColor: Colors.white,
114 | outlineColor: Colors.white,
115 | onPressed: () {},
116 | ),
117 | );
118 | }),
119 | ),
120 | ],
121 | ),
122 | ),
123 | ),
124 | Align(
125 | alignment: Alignment.bottomCenter,
126 | child: ActionPanel(
127 | title: 'I\'m Done!',
128 | onPressed: () {
129 | showDialog(
130 | context: context,
131 | barrierDismissible: false,
132 | child: AlertDialog(
133 | title: Text('Great Job ${challenge.reward.emoji}'),
134 | content: Text(
135 | 'Reward yourself now with a/an ${challenge.reward.name.toLowerCase()} for the hard work you\'ve put in!',
136 | ),
137 | actions: [
138 | FlatButton(
139 | child: Text('Okay'),
140 | textColor: theme.accentColor,
141 | onPressed: () {
142 | Navigator.of(context).pop();
143 | Navigator.of(context).pushNamed(
144 | ReviewScreen.routeName,
145 | arguments: challenge,
146 | );
147 | }),
148 | ],
149 | ),
150 | );
151 | },
152 | ),
153 | ),
154 | ],
155 | ),
156 | ),
157 | );
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/lib/screens/home_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_svg/flutter_svg.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | import '../widgets/emoji_button.dart';
6 | import '../screens/triage_screen.dart';
7 | import '../widgets/custom_safe_area.dart';
8 | import '../models/activity.dart';
9 | import '../providers/activities.dart';
10 | import '../providers/rewards.dart';
11 |
12 | class HomeScreen extends StatefulWidget {
13 | @override
14 | _HomeScreenState createState() => _HomeScreenState();
15 | }
16 |
17 | class _HomeScreenState extends State {
18 | var _isInit = true;
19 |
20 | @override
21 | void didChangeDependencies() {
22 | super.didChangeDependencies();
23 | if (_isInit) {
24 | Provider.of(context, listen: false)
25 | .fetchAndSetActivities()
26 | .then((_) {
27 | return Provider.of(context, listen: false)
28 | .fetchAndSetRewards();
29 | })
30 | .catchError((err) => showDialog(
31 | context: context,
32 | child: AlertDialog(
33 | title: Text('Oops!'),
34 | content: Text(
35 | 'An unexpected error occurred when initialising data.',
36 | ),
37 | actions: [
38 | FlatButton(
39 | textColor: Theme.of(context).accentColor,
40 | child: Text(
41 | 'Okay',
42 | ),
43 | onPressed: () => Navigator.of(context).pop(),
44 | ),
45 | ],
46 | ),
47 | ))
48 | .whenComplete(() {
49 | _isInit = false;
50 | });
51 | }
52 | }
53 |
54 | @override
55 | Widget build(BuildContext context) {
56 | return CustomSafeArea(
57 | child: Scaffold(
58 | body: Container(
59 | width: double.infinity,
60 | color: Theme.of(context).primaryColor,
61 | child: Column(
62 | children: [
63 | Expanded(
64 | flex: 2,
65 | child: Padding(
66 | padding: const EdgeInsets.symmetric(
67 | horizontal: 20.0,
68 | ),
69 | child: Column(
70 | mainAxisAlignment: MainAxisAlignment.end,
71 | crossAxisAlignment: CrossAxisAlignment.start,
72 | children: [
73 | Flexible(
74 | child: Padding(
75 | padding: const EdgeInsets.symmetric(
76 | horizontal: 4.0,
77 | ),
78 | child: SvgPicture.asset(
79 | 'assets/images/yoga.svg',
80 | alignment: Alignment.bottomCenter,
81 | ),
82 | ),
83 | ),
84 | Flexible(
85 | child: Container(
86 | margin: const EdgeInsets.symmetric(
87 | vertical: 20.0,
88 | ),
89 | child: FittedBox(
90 | child: Text(
91 | 'I\'m feeling . . .',
92 | style: TextStyle(
93 | fontWeight: FontWeight.bold,
94 | letterSpacing: 1.2,
95 | fontSize: 24.0,
96 | ),
97 | ),
98 | ),
99 | ),
100 | ),
101 | ],
102 | ),
103 | ),
104 | ),
105 | Flexible(
106 | flex: 1,
107 | child: Container(
108 | width: double.infinity,
109 | padding: const EdgeInsets.symmetric(
110 | horizontal: 10.0,
111 | vertical: 2.0,
112 | ),
113 | color: Colors.white,
114 | child: Column(
115 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
116 | children: [
117 | EmojiButton(
118 | height: 50.0,
119 | emoji: '😰',
120 | title: 'Anxious',
121 | onPressed: () => Navigator.of(context).pushNamed(
122 | TriageScreen.routeName,
123 | arguments: {
124 | 'problem': Problem.Anxiety
125 | },
126 | ),
127 | ),
128 | EmojiButton(
129 | height: 50.0,
130 | emoji: '😫',
131 | title: 'Stressed',
132 | onPressed: () => Navigator.of(context).pushNamed(
133 | TriageScreen.routeName,
134 | arguments: {
135 | 'problem': Problem.Stress,
136 | },
137 | ),
138 | ),
139 | EmojiButton(
140 | height: 50.0,
141 | emoji: '😞',
142 | title: 'Depressed',
143 | onPressed: () => Navigator.of(context).pushNamed(
144 | TriageScreen.routeName,
145 | arguments: {
146 | 'problem': Problem.Depression,
147 | },
148 | ),
149 | ),
150 | ],
151 | ),
152 | ),
153 | ),
154 | ],
155 | ),
156 | ),
157 | ),
158 | );
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/lib/screens/review_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 |
4 | import '../widgets/custom_safe_area.dart';
5 | import '../widgets/action_panel.dart';
6 | import '../widgets/knob_slider.dart';
7 | import '../models/challenge.dart';
8 | import '../providers/challenges.dart';
9 | import './triage_screen.dart';
10 |
11 | class ReviewScreen extends StatefulWidget {
12 | static const routeName = '/review';
13 |
14 | @override
15 | _ReviewScreenState createState() => _ReviewScreenState();
16 | }
17 |
18 | class _ReviewScreenState extends State {
19 | var _isInit = true;
20 | Challenge _challenge;
21 | var _updatedLevel = 0;
22 |
23 | @override
24 | void didChangeDependencies() {
25 | super.didChangeDependencies();
26 | if (_isInit) {
27 | _challenge = ModalRoute.of(context).settings.arguments as Challenge;
28 | _isInit = false;
29 | }
30 | }
31 |
32 | @override
33 | Widget build(BuildContext context) {
34 | final theme = Theme.of(context);
35 |
36 | return CustomSafeArea(
37 | child: Scaffold(
38 | appBar: AppBar(
39 | backgroundColor: theme.primaryColor,
40 | elevation: 0.0,
41 | leading: IconButton(
42 | icon: Icon(Icons.arrow_back),
43 | onPressed: () => Navigator.of(context).pop(),
44 | ),
45 | ),
46 | body: Stack(
47 | children: [
48 | LayoutBuilder(
49 | builder: (ctx, constraints) => Container(
50 | width: double.infinity,
51 | height: constraints.maxHeight - 67.0,
52 | padding: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0),
53 | child: Column(
54 | mainAxisSize: MainAxisSize.min,
55 | crossAxisAlignment: CrossAxisAlignment.start,
56 | children: [
57 | Column(
58 | mainAxisSize: MainAxisSize.min,
59 | crossAxisAlignment: CrossAxisAlignment.start,
60 | children: [
61 | Flexible(
62 | child: FittedBox(
63 | child: Text(
64 | 'Review',
65 | style: const TextStyle(
66 | fontWeight: FontWeight.bold,
67 | fontSize: 20.0,
68 | letterSpacing: 1.2,
69 | ),
70 | ),
71 | ),
72 | ),
73 | SizedBox(
74 | height: 12.0,
75 | ),
76 | Flexible(
77 | child: Text(
78 | 'Please rate your ${_challenge.problem.noun} levels after having spent your ${_challenge.problem.noun} credits.',
79 | style: const TextStyle(
80 | fontSize: 14,
81 | height: 1.2,
82 | ),
83 | ),
84 | ),
85 | ],
86 | ),
87 | SizedBox(
88 | height: 15.0,
89 | ),
90 | Flexible(
91 | fit: FlexFit.loose,
92 | child: Column(
93 | mainAxisSize: MainAxisSize.min,
94 | children: [
95 | KnobSlider(
96 | min: 0,
97 | max: 10,
98 | initialValue: 0,
99 | onChanged: (value) {
100 | _updatedLevel = value;
101 | return true;
102 | },
103 | ),
104 | ],
105 | ),
106 | ),
107 | ],
108 | ),
109 | ),
110 | ),
111 | Align(
112 | alignment: Alignment.bottomCenter,
113 | child: ActionPanel(
114 | title: 'Next',
115 | onPressed: () async {
116 | try {
117 | await Provider.of(context, listen: false)
118 | .endChallenge(_challenge.id, _updatedLevel);
119 |
120 | var dialogMessage = '';
121 | var dialogActions = [];
122 | final okayButton = FlatButton(
123 | child: Text('Okay'),
124 | textColor: theme.accentColor,
125 | onPressed: () =>
126 | Navigator.of(context).pushNamedAndRemoveUntil(
127 | '/',
128 | (route) => false,
129 | ),
130 | );
131 | final tryAgainButton = FlatButton(
132 | child: Text('Try Again'),
133 | textColor: theme.accentColor,
134 | onPressed: () {
135 | Navigator.of(context).pop();
136 | Navigator.of(context).pushReplacementNamed(
137 | TriageScreen.routeName,
138 | arguments: {
139 | 'problem': _challenge.problem.type,
140 | 'initialLevel': _updatedLevel,
141 | },
142 | );
143 | },
144 | );
145 | final takeABreakButton = FlatButton(
146 | child: Text('Take a Break'),
147 | textColor: theme.accentColor,
148 | onPressed: () =>
149 | Navigator.of(context).pushNamedAndRemoveUntil(
150 | '/',
151 | (route) => false,
152 | ),
153 | );
154 |
155 | if (_updatedLevel < 1) {
156 | dialogMessage =
157 | 'Congratulations on getting your ${_challenge.problem.noun} levels down to zero! You rock 🤘';
158 | dialogActions.add(okayButton);
159 | } else if (_updatedLevel < _challenge.idealLevel) {
160 | dialogMessage =
161 | 'Your ${_challenge.problem.noun} level seems better than ideal, keep up the good work!';
162 | dialogActions.addAll([takeABreakButton, tryAgainButton]);
163 | } else if (_updatedLevel == _challenge.idealLevel) {
164 | dialogMessage =
165 | 'Good job spending all your ${_challenge.problem.noun} credits, you\'re getting better!';
166 | dialogActions.addAll([takeABreakButton, tryAgainButton]);
167 | } else if (_updatedLevel > _challenge.idealLevel &&
168 | _updatedLevel < _challenge.initialLevel) {
169 | dialogMessage =
170 | 'Don\'t give up, you made great effort spending your ${_challenge.problem.noun} credits!';
171 | dialogActions.addAll([takeABreakButton, tryAgainButton]);
172 | } else if (_updatedLevel < 7) {
173 | dialogMessage =
174 | 'We take some steps forward, some steps backwards, but most importantly, we\'re progressing!';
175 | dialogActions.addAll([takeABreakButton, tryAgainButton]);
176 | } else {
177 | dialogMessage =
178 | 'Progress takes time, you\'re putting in effort and that\'s all that matters! Do talk to someone if your ${_challenge.problem.noun} persists 💪';
179 | dialogActions.addAll([takeABreakButton, tryAgainButton]);
180 | }
181 |
182 | showDialog(
183 | context: context,
184 | barrierDismissible: false,
185 | builder: (ctx) => AlertDialog(
186 | title: Text('Review'),
187 | content: Text(
188 | dialogMessage,
189 | style: TextStyle(
190 | height: 1.3,
191 | ),
192 | ),
193 | actions: dialogActions,
194 | ),
195 | );
196 | } catch (err) {
197 | showDialog(
198 | context: context,
199 | child: AlertDialog(
200 | title: Text('Oops!'),
201 | content: Text('An unexpected error occurred.'),
202 | actions: [
203 | FlatButton(
204 | child: Text('Okay'),
205 | textColor: theme.accentColor,
206 | onPressed: () => Navigator.of(context).pop(),
207 | )
208 | ],
209 | ),
210 | );
211 | }
212 | },
213 | ),
214 | ),
215 | ],
216 | ),
217 | ),
218 | );
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/lib/screens/reward_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 |
4 | import '../widgets/action_panel.dart';
5 | import '../widgets/custom_safe_area.dart';
6 | import '../widgets/emoji_button.dart';
7 | import '../screens/schedule_screen.dart';
8 | import '../models/activity.dart';
9 | import '../models/reward.dart';
10 | import '../providers/rewards.dart';
11 |
12 | class RewardScreen extends StatefulWidget {
13 | static const routeName = '/reward';
14 |
15 | @override
16 | _RewardScreenState createState() => _RewardScreenState();
17 | }
18 |
19 | class _RewardScreenState extends State {
20 | var _isInit = true;
21 | ProblemDetails _problem;
22 | int _actualLevel;
23 | int _idealLevel;
24 | Set _selectedActivities = {};
25 | Reward _selectedReward;
26 |
27 | @override
28 | void didChangeDependencies() {
29 | super.didChangeDependencies();
30 | if (_isInit) {
31 | final params =
32 | ModalRoute.of(context).settings.arguments as Map;
33 | _problem = params['problem'] as ProblemDetails;
34 | _actualLevel = params['actualLevel'] as int;
35 | _idealLevel = params['idealLevel'] as int;
36 | _selectedActivities = params['activities'] as Set;
37 |
38 | _isInit = false;
39 | }
40 | }
41 |
42 | @override
43 | Widget build(BuildContext context) {
44 | final theme = Theme.of(context);
45 | final rewards = Provider.of(context, listen: false).rewards;
46 |
47 | return CustomSafeArea(
48 | child: Scaffold(
49 | appBar: AppBar(
50 | backgroundColor: theme.primaryColor,
51 | elevation: 0.0,
52 | leading: IconButton(
53 | icon: Icon(Icons.arrow_back),
54 | onPressed: () => Navigator.of(context).pop(),
55 | ),
56 | ),
57 | body: Stack(
58 | children: [
59 | LayoutBuilder(
60 | builder: (ctx, constraints) => Container(
61 | width: double.infinity,
62 | height: constraints.maxHeight - 67.0,
63 | padding: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0),
64 | child: Column(
65 | mainAxisSize: MainAxisSize.min,
66 | crossAxisAlignment: CrossAxisAlignment.start,
67 | children: [
68 | Column(
69 | mainAxisSize: MainAxisSize.min,
70 | crossAxisAlignment: CrossAxisAlignment.start,
71 | children: [
72 | Flexible(
73 | child: FittedBox(
74 | child: Text(
75 | 'Reward',
76 | style: const TextStyle(
77 | fontWeight: FontWeight.bold,
78 | fontSize: 20.0,
79 | letterSpacing: 1.2,
80 | ),
81 | ),
82 | ),
83 | ),
84 | SizedBox(
85 | height: 12.0,
86 | ),
87 | Flexible(
88 | child: Text(
89 | 'We encourage you to pick a reward to award yourself with after you’ve spent your ${_problem.noun} credits. This would reinforce the positive habit of dealing with ${_problem.noun} in an actionable manner.',
90 | style: const TextStyle(
91 | fontSize: 14,
92 | height: 1.2,
93 | ),
94 | ),
95 | ),
96 | ],
97 | ),
98 | SizedBox(
99 | height: 30.0,
100 | ),
101 | Expanded(
102 | child: Column(
103 | mainAxisSize: MainAxisSize.min,
104 | children: [
105 | Row(
106 | children: [
107 | Flexible(
108 | fit: FlexFit.loose,
109 | child: FittedBox(
110 | child: Text(
111 | 'Pick a Reward',
112 | style: const TextStyle(
113 | fontWeight: FontWeight.bold,
114 | letterSpacing: 1.1,
115 | fontSize: 16.0,
116 | ),
117 | ),
118 | ),
119 | ),
120 | ],
121 | ),
122 | SizedBox(
123 | height: 15.0,
124 | ),
125 | Flexible(
126 | child: ListView.builder(
127 | itemCount: rewards.length,
128 | itemBuilder: (ctx, index) {
129 | final reward = rewards[index];
130 | final isSelected = _selectedReward != null &&
131 | reward.id == _selectedReward.id;
132 |
133 | return Padding(
134 | padding: const EdgeInsets.symmetric(
135 | vertical: 4.0,
136 | ),
137 | child: EmojiButton(
138 | emoji: reward.emoji,
139 | title: reward.name,
140 | backgroundColor: isSelected
141 | ? Colors.greenAccent.shade100
142 | : null,
143 | outlineColor: isSelected
144 | ? Colors.greenAccent.shade200
145 | : null,
146 | onPressed: () {
147 | setState(() {
148 | _selectedReward = reward;
149 | });
150 | },
151 | ),
152 | );
153 | }),
154 | ),
155 | ],
156 | ),
157 | ),
158 | ],
159 | ),
160 | ),
161 | ),
162 | Align(
163 | alignment: Alignment.bottomCenter,
164 | child: ActionPanel(
165 | title: 'Next',
166 | onPressed: () {
167 | if (_selectedReward != null) {
168 | Navigator.of(context).pushNamed(
169 | ScheduleScreen.routeName,
170 | arguments: {
171 | 'problem': _problem,
172 | 'actualLevel': _actualLevel,
173 | 'idealLevel': _idealLevel,
174 | 'activities': _selectedActivities,
175 | 'reward': _selectedReward,
176 | },
177 | );
178 | } else {
179 | showDialog(
180 | context: context,
181 | child: AlertDialog(
182 | title: Text('Oops!'),
183 | content: Text(
184 | 'Positive reinforcement is important for making a good habit of dealing with ${_problem.noun} in an actionable manner. Please pick a reward!',
185 | ),
186 | actions: [
187 | FlatButton(
188 | child: Text('Okay'),
189 | textColor: theme.accentColor,
190 | onPressed: () => Navigator.of(context).pop(),
191 | ),
192 | ],
193 | ),
194 | );
195 | }
196 | }),
197 | ),
198 | ],
199 | ),
200 | ),
201 | );
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/lib/screens/schedule_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:intl/intl.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | import '../widgets/custom_safe_area.dart';
6 | import '../widgets/action_panel.dart';
7 | import '../widgets/emoji_button.dart';
8 | import '../models/activity.dart';
9 | import '../models/reward.dart';
10 | import '../models/challenge.dart';
11 | import '../providers/challenges.dart';
12 |
13 | class ScheduleScreen extends StatefulWidget {
14 | static const routeName = '/schedule';
15 | final Future Function(DateTime, String, String) scheduleNotification;
16 |
17 | ScheduleScreen(this.scheduleNotification);
18 |
19 | @override
20 | _ScheduleScreenState createState() => _ScheduleScreenState();
21 | }
22 |
23 | class _ScheduleScreenState extends State {
24 | var _isInit = true;
25 | ProblemDetails _problem;
26 | int _actualLevel;
27 | int _idealLevel;
28 | Set _selectedActivities = {};
29 | Reward _selectedReward;
30 |
31 | TimeOfDay _scheduledTime = TimeOfDay(
32 | hour: TimeOfDay.now().hour + 1,
33 | minute: TimeOfDay.now().minute,
34 | );
35 |
36 | @override
37 | void didChangeDependencies() {
38 | super.didChangeDependencies();
39 | if (_isInit) {
40 | final params =
41 | ModalRoute.of(context).settings.arguments as Map;
42 | _problem = params['problem'] as ProblemDetails;
43 | _actualLevel = params['actualLevel'] as int;
44 | _idealLevel = params['idealLevel'] as int;
45 | _selectedActivities = params['activities'] as Set;
46 | _selectedReward = params['reward'] as Reward;
47 |
48 | _isInit = false;
49 | }
50 | }
51 |
52 | String _formatTimeOfDay(TimeOfDay time) {
53 | final today = DateTime.now();
54 | final dateTime =
55 | DateTime(today.year, today.month, today.day, time.hour, time.minute);
56 | return DateFormat.jm().format(dateTime);
57 | }
58 |
59 | @override
60 | Widget build(BuildContext context) {
61 | final theme = Theme.of(context);
62 | final _timePickerThemeData = ThemeData(
63 | primaryColor: theme.primaryColor,
64 | accentColor: theme.accentColor,
65 | colorScheme: ColorScheme(
66 | background: Colors.white,
67 | brightness: Brightness.light,
68 | error: Colors.red,
69 | onBackground: Colors.black87,
70 | onError: Colors.red,
71 | onSurface: Colors.black87,
72 | onSecondary: Colors.black87,
73 | onPrimary: Colors.black,
74 | primary: theme.accentColor,
75 | primaryVariant: theme.accentColor,
76 | secondary: Colors.black,
77 | secondaryVariant: Colors.black,
78 | surface: Colors.white,
79 | ),
80 | );
81 |
82 | return CustomSafeArea(
83 | child: Scaffold(
84 | appBar: AppBar(
85 | backgroundColor: theme.primaryColor,
86 | elevation: 0.0,
87 | leading: IconButton(
88 | icon: Icon(Icons.arrow_back),
89 | onPressed: () => Navigator.of(context).pop(),
90 | ),
91 | ),
92 | body: Stack(
93 | children: [
94 | LayoutBuilder(
95 | builder: (ctx, constraints) => Container(
96 | width: double.infinity,
97 | height: constraints.maxHeight - 67.0,
98 | padding: const EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 0.0),
99 | child: Column(
100 | mainAxisSize: MainAxisSize.min,
101 | crossAxisAlignment: CrossAxisAlignment.start,
102 | children: [
103 | Column(
104 | mainAxisSize: MainAxisSize.min,
105 | crossAxisAlignment: CrossAxisAlignment.start,
106 | children: [
107 | Flexible(
108 | child: FittedBox(
109 | child: Text(
110 | 'Check Back With Us',
111 | style: const TextStyle(
112 | fontWeight: FontWeight.bold,
113 | fontSize: 20.0,
114 | letterSpacing: 1.2,
115 | ),
116 | ),
117 | ),
118 | ),
119 | SizedBox(
120 | height: 12.0,
121 | ),
122 | Flexible(
123 | child: Text(
124 | 'Set a time today to spend your ${_problem.noun} credits by and check back in with us! We’ll help to re-evaluate your ${_problem.noun} levels again.',
125 | style: const TextStyle(
126 | fontSize: 14,
127 | height: 1.2,
128 | ),
129 | ),
130 | ),
131 | ],
132 | ),
133 | SizedBox(
134 | height: 30.0,
135 | ),
136 | Column(
137 | mainAxisSize: MainAxisSize.min,
138 | crossAxisAlignment: CrossAxisAlignment.start,
139 | children: [
140 | Row(
141 | children: [
142 | Flexible(
143 | fit: FlexFit.loose,
144 | child: FittedBox(
145 | child: Text(
146 | 'Set a Time Today',
147 | style: const TextStyle(
148 | fontWeight: FontWeight.bold,
149 | letterSpacing: 1.1,
150 | fontSize: 16.0,
151 | ),
152 | ),
153 | ),
154 | ),
155 | ],
156 | ),
157 | SizedBox(
158 | height: 15.0,
159 | ),
160 | EmojiButton(
161 | emoji: '📅',
162 | title: _formatTimeOfDay(_scheduledTime),
163 | subtitle: 'Select to change time',
164 | enableSubtitle: true,
165 | onPressed: () async {
166 | try {
167 | final selectedTime = await showTimePicker(
168 | context: context,
169 | builder: (ctx, child) => Theme(
170 | data: _timePickerThemeData,
171 | child: child,
172 | ),
173 | initialTime: _scheduledTime,
174 | );
175 |
176 | if (selectedTime == null) return;
177 |
178 | if (selectedTime.hour < TimeOfDay.now().hour ||
179 | (selectedTime.hour == TimeOfDay.now().hour &&
180 | selectedTime.minute <=
181 | TimeOfDay.now().minute + 5)) {
182 | final minimumTimeOfDay = _formatTimeOfDay(
183 | TimeOfDay(
184 | hour: TimeOfDay.now().hour,
185 | minute: TimeOfDay.now().minute + 5));
186 | showDialog(
187 | context: context,
188 | child: AlertDialog(
189 | title: Text('Oops!'),
190 | content: Text(
191 | 'Please schedule for a time past $minimumTimeOfDay today.'),
192 | actions: [
193 | FlatButton(
194 | textColor: theme.accentColor,
195 | child: Text('Okay'),
196 | onPressed: () =>
197 | Navigator.of(context).pop(),
198 | )
199 | ],
200 | ),
201 | );
202 | } else {
203 | setState(() {
204 | _scheduledTime = selectedTime;
205 | });
206 | }
207 | } catch (err) {
208 | showDialog(
209 | context: context,
210 | child: AlertDialog(
211 | title: Text(
212 | 'Oops!',
213 | ),
214 | content:
215 | Text('An unexpected error occurred!'),
216 | actions: [
217 | FlatButton(
218 | textColor: theme.accentColor,
219 | child: Text(
220 | 'Okay',
221 | ),
222 | onPressed: () =>
223 | Navigator.of(context).pop(),
224 | ),
225 | ],
226 | ),
227 | );
228 | }
229 | },
230 | ),
231 | ],
232 | )
233 | ],
234 | ),
235 | ),
236 | ),
237 | Align(
238 | alignment: Alignment.bottomCenter,
239 | child: ActionPanel(
240 | title: 'Start Challenge',
241 | onPressed: () {
242 | final timeNow = TimeOfDay.now();
243 | final isValid = !(_scheduledTime.hour < timeNow.hour ||
244 | (_scheduledTime.hour == timeNow.hour &&
245 | _scheduledTime.minute <= timeNow.minute));
246 | if (isValid) {
247 | final dateTimeNow = DateTime.now();
248 | final reminderDateTime = DateTime(
249 | dateTimeNow.year,
250 | dateTimeNow.month,
251 | dateTimeNow.day,
252 | _scheduledTime.hour,
253 | _scheduledTime.minute,
254 | );
255 |
256 | final newChallenge = Challenge(
257 | initialLevel: _actualLevel,
258 | idealLevel: _idealLevel,
259 | activities: _selectedActivities,
260 | reward: _selectedReward,
261 | remindAt: reminderDateTime,
262 | problem: _problem,
263 | );
264 |
265 | Provider.of(context, listen: false)
266 | .create(newChallenge)
267 | .then((_) {
268 | return widget.scheduleNotification(
269 | reminderDateTime, 'Check In', 'Let\'s re-evaluate your ${_problem.noun} levels again!');
270 | }).then((_) {
271 | Navigator.of(context).pushNamedAndRemoveUntil(
272 | '/',
273 | (route) => false,
274 | );
275 | }).catchError((err) {
276 | print(err);
277 | showDialog(
278 | context: context,
279 | child: AlertDialog(
280 | title: Text('Oops!'),
281 | content: Text('An unexpected error occurred!'),
282 | actions: [
283 | FlatButton(
284 | child: Text('Okay'),
285 | textColor: theme.accentColor,
286 | onPressed: () => Navigator.of(context).pop(),
287 | ),
288 | ],
289 | ),
290 | );
291 | });
292 | } else {
293 | showDialog(
294 | context: context,
295 | child: AlertDialog(
296 | title: Text('Oops!'),
297 | content: Text(
298 | 'It appears that you\'ve picked a time that is over, please pick another time to check back in with us.',
299 | ),
300 | actions: [
301 | FlatButton(
302 | child: Text('Okay'),
303 | textColor: theme.accentColor,
304 | onPressed: () => Navigator.of(context).pop(),
305 | ),
306 | ],
307 | ),
308 | );
309 | }
310 | },
311 | ),
312 | ),
313 | ],
314 | ),
315 | ),
316 | );
317 | }
318 | }
319 |
--------------------------------------------------------------------------------
/lib/screens/triage_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../widgets/slider_question.dart';
4 | import '../widgets/action_panel.dart';
5 | import '../widgets/custom_safe_area.dart';
6 | import '../screens/challenge_screen.dart';
7 | import '../models/activity.dart';
8 |
9 | class TriageScreen extends StatefulWidget {
10 | static const routeName = '/triage';
11 |
12 | @override
13 | _TriageScreenState createState() => _TriageScreenState();
14 | }
15 |
16 | class _TriageScreenState extends State {
17 | var _selectedActualLevel = 1;
18 | var _selectedIdealLevel = 0;
19 | var _initialLevel = 1;
20 | ProblemDetails _problem;
21 | var _isInit = true;
22 |
23 | @override
24 | void didChangeDependencies() {
25 | super.didChangeDependencies();
26 | if (_isInit) {
27 | final params =
28 | ModalRoute.of(context).settings.arguments as Map;
29 | _problem = getProblemDetails(params['problem'] as Problem);
30 | if (params.containsKey('initialLevel')) {
31 | _initialLevel = params['initialLevel'] as int;
32 | setState(() {
33 | _selectedActualLevel = _initialLevel;
34 | });
35 | }
36 | _isInit = false;
37 | }
38 | }
39 |
40 | @override
41 | Widget build(BuildContext context) {
42 | final theme = Theme.of(context);
43 |
44 | return CustomSafeArea(
45 | child: Scaffold(
46 | appBar: AppBar(
47 | backgroundColor: theme.primaryColor,
48 | elevation: 0.0,
49 | leading: IconButton(
50 | icon: Icon(Icons.arrow_back),
51 | onPressed: () => Navigator.of(context).pop(),
52 | ),
53 | ),
54 | body: Stack(
55 | children: [
56 | Container(
57 | width: double.infinity,
58 | padding: const EdgeInsets.symmetric(
59 | vertical: 20.0,
60 | horizontal: 20.0,
61 | ),
62 | child: Column(
63 | mainAxisSize: MainAxisSize.min,
64 | children: [
65 | Flexible(
66 | child: SliderQuestion(
67 | question: 'How ${_problem.adjective} are you?',
68 | min: 1,
69 | max: 10,
70 | initialValue: _initialLevel,
71 | onChanged: (value) {
72 | if (_selectedIdealLevel > value) {
73 | showDialog(
74 | context: context,
75 | builder: (ctx) => AlertDialog(
76 | title: Text('Oops!'),
77 | content: Text(
78 | 'Your ideal ${_problem.noun} level cannot be more than your actual ${_problem.noun} level.'),
79 | actions: [
80 | FlatButton(
81 | textColor: theme.accentColor,
82 | child: Text(
83 | 'Okay',
84 | style: TextStyle(
85 | fontWeight: FontWeight.bold,
86 | ),
87 | ),
88 | onPressed: () => Navigator.of(context).pop(),
89 | )
90 | ],
91 | ),
92 | );
93 | return false;
94 | } else {
95 | setState(() {
96 | _selectedActualLevel = value;
97 | });
98 | return true;
99 | }
100 | },
101 | ),
102 | ),
103 | SizedBox(
104 | height: 40.0,
105 | ),
106 | Flexible(
107 | child: SliderQuestion(
108 | question: 'Pick an ideal ${_problem.noun} level',
109 | min: 0,
110 | max: _selectedActualLevel,
111 | initialValue: 0,
112 | onChanged: (value) {
113 | setState(() {
114 | _selectedIdealLevel = value;
115 | });
116 | return true;
117 | }),
118 | ),
119 | ],
120 | ),
121 | ),
122 | Align(
123 | alignment: Alignment.bottomCenter,
124 | child: ActionPanel(
125 | title: 'Next',
126 | onPressed: () {
127 | if (_selectedIdealLevel >= _selectedActualLevel) {
128 | showDialog(
129 | barrierDismissible: false,
130 | context: context,
131 | builder: (ctx) => AlertDialog(
132 | title: Text('Oops!'),
133 | content: Text(
134 | 'Please pick an ideal ${_problem.noun} level that is realistically lower than your actual ${_problem.noun} level.',
135 | ),
136 | actions: [
137 | FlatButton(
138 | child: Text('Okay'),
139 | textColor: theme.accentColor,
140 | onPressed: () => Navigator.of(context).pop(),
141 | ),
142 | ],
143 | ),
144 | );
145 | } else {
146 | Navigator.of(context).pushNamed(
147 | ChallengeScreen.routeName,
148 | arguments: {
149 | 'problem': _problem,
150 | 'actualLevel': _selectedActualLevel,
151 | 'idealLevel': _selectedIdealLevel,
152 | },
153 | );
154 | }
155 | }),
156 | ),
157 | ],
158 | ),
159 | ),
160 | );
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/lib/widgets/action_panel.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ActionPanel extends StatelessWidget {
4 | final String title;
5 | final Function onPressed;
6 | final Color panelColor;
7 | final bool enableDivider;
8 |
9 | ActionPanel({
10 | @required this.title,
11 | @required this.onPressed,
12 | this.panelColor = Colors.white,
13 | this.enableDivider = true,
14 | });
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | final theme = Theme.of(context);
19 |
20 | return Container(
21 | padding: const EdgeInsets.symmetric(
22 | vertical: 11.0,
23 | horizontal: 20.0,
24 | ),
25 | decoration: BoxDecoration(
26 | color: panelColor,
27 | boxShadow: enableDivider
28 | ? [
29 | BoxShadow(
30 | blurRadius: 3.0,
31 | offset: Offset(0.0, -4.0),
32 | color: Colors.grey.shade100,
33 | ),
34 | ]
35 | : null,
36 | ),
37 | width: double.infinity,
38 | child: ButtonTheme(
39 | height: 45,
40 | child: FlatButton(
41 | onPressed: onPressed,
42 | shape: RoundedRectangleBorder(
43 | borderRadius: BorderRadius.circular(5.5),
44 | ),
45 | color: theme.accentColor,
46 | textColor: Colors.white,
47 | child: Text(
48 | title,
49 | style: const TextStyle(
50 | fontWeight: FontWeight.bold,
51 | letterSpacing: 1.2,
52 | ),
53 | ),
54 | ),
55 | ),
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lib/widgets/custom_safe_area.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class CustomSafeArea extends StatelessWidget {
4 | final Widget child;
5 | final Color color;
6 |
7 | CustomSafeArea({
8 | @required this.child,
9 | this.color = Colors.white,
10 | });
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return Container(
15 | color: color,
16 | width: double.infinity,
17 | child: SafeArea(
18 | top: false,
19 | bottom: true,
20 | child: child,
21 | ),
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/widgets/emoji_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class EmojiButton extends StatelessWidget {
4 | final String emoji;
5 | final String title;
6 | final String subtitle;
7 | final double height;
8 | final bool enableSubtitle;
9 | final Function onPressed;
10 | final Color backgroundColor;
11 | final Color outlineColor;
12 |
13 | EmojiButton({
14 | @required this.emoji,
15 | @required this.title,
16 | this.subtitle = '',
17 | this.height = 55.0,
18 | this.enableSubtitle = false,
19 | @required this.onPressed,
20 | this.backgroundColor,
21 | this.outlineColor,
22 | });
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | return DecoratedBox(
27 | decoration: BoxDecoration(
28 | borderRadius: BorderRadius.circular(
29 | 8.0,
30 | ),
31 | color: backgroundColor,
32 | ),
33 | child: ButtonTheme(
34 | height: height,
35 | child: OutlineButton(
36 | shape: RoundedRectangleBorder(
37 | borderRadius: BorderRadius.circular(
38 | 8.0,
39 | ),
40 | ),
41 | borderSide: BorderSide(
42 | color: outlineColor == null ? Colors.grey.shade100 : outlineColor,
43 | ),
44 | highlightColor: Colors.grey.shade100,
45 | highlightedBorderColor: Colors.grey.shade100,
46 | child: Row(
47 | children: [
48 | Text(
49 | emoji,
50 | style: const TextStyle(
51 | fontSize: 18.0,
52 | ),
53 | ),
54 | SizedBox(
55 | width: 20.0,
56 | ),
57 | enableSubtitle
58 | ? Column(
59 | crossAxisAlignment: CrossAxisAlignment.start,
60 | children: [
61 | Text(
62 | title,
63 | style: const TextStyle(
64 | fontSize: 16.0,
65 | ),
66 | ),
67 | SizedBox(
68 | height: 2,
69 | ),
70 | Text(
71 | subtitle,
72 | style: const TextStyle(
73 | fontSize: 12.0,
74 | ),
75 | )
76 | ],
77 | )
78 | : Text(
79 | title,
80 | style: const TextStyle(
81 | fontSize: 16.0,
82 | ),
83 | ),
84 | ],
85 | ),
86 | onPressed: onPressed,
87 | ),
88 | ),
89 | );
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/lib/widgets/knob_slider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class KnobSlider extends StatefulWidget {
4 | final int min;
5 | final int max;
6 | final int initialValue;
7 | final bool Function(int) onChanged;
8 |
9 | KnobSlider({
10 | @required this.min,
11 | @required this.max,
12 | this.initialValue = 0,
13 | @required this.onChanged,
14 | });
15 |
16 | @override
17 | _KnobSliderState createState() => _KnobSliderState();
18 | }
19 |
20 | class _KnobSliderState extends State {
21 | var _isInit = true;
22 | double _sliderValue = 0.0;
23 |
24 | @override
25 | void didChangeDependencies() {
26 | super.didChangeDependencies();
27 | if (_isInit) {
28 | _sliderValue = widget.initialValue.toDouble();
29 | _isInit = false;
30 | }
31 | }
32 |
33 | @override
34 | Widget build(BuildContext context) {
35 | final theme = Theme.of(context);
36 |
37 | return Row(
38 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
39 | children: [
40 | Text(
41 | widget.min.toInt().toString(),
42 | style: TextStyle(
43 | fontWeight: FontWeight.bold,
44 | fontSize: 18.0,
45 | letterSpacing: 1.2,
46 | ),
47 | ),
48 | Expanded(
49 | child: SliderTheme(
50 | data: SliderTheme.of(context).copyWith(
51 | activeTrackColor: Colors.grey.shade200,
52 | inactiveTrackColor: Colors.grey.shade100,
53 | trackHeight: 9.0,
54 | thumbColor: theme.accentColor,
55 | thumbShape: const RoundSliderThumbShape(
56 | enabledThumbRadius: 15.0,
57 | disabledThumbRadius: 15.0,
58 | ),
59 | overlayColor: theme.accentColor.withAlpha(32),
60 | inactiveTickMarkColor: Colors.grey.shade200,
61 | valueIndicatorColor: theme.accentColor.withOpacity(0.95),
62 | ),
63 | child: Slider(
64 | min: widget.min.toDouble(),
65 | max: widget.max.toDouble(),
66 | value: _sliderValue,
67 | divisions: widget.max,
68 | label: _sliderValue.floor().toString(),
69 | onChanged: (value) {
70 | if (widget.onChanged(value.toInt())) {
71 | setState(() {
72 | _sliderValue = value;
73 | });
74 | }
75 | },
76 | ),
77 | ),
78 | ),
79 | Text(
80 | widget.max.toInt().toString(),
81 | style: TextStyle(
82 | fontWeight: FontWeight.bold,
83 | fontSize: 18.0,
84 | letterSpacing: 1.2,
85 | ),
86 | ),
87 | ],
88 | );
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/lib/widgets/loading_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class LoadingScreen extends StatelessWidget {
4 | @override
5 | Widget build(BuildContext context) {
6 | return Container(
7 | width: double.infinity,
8 | child: Center(
9 | child: CircularProgressIndicator(
10 | valueColor: AlwaysStoppedAnimation(Colors.grey.shade400),
11 | ),
12 | ),
13 | );
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/widgets/slider_question.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import './knob_slider.dart';
4 |
5 | class SliderQuestion extends StatelessWidget {
6 | final String question;
7 | final int min;
8 | final int max;
9 | final int initialValue;
10 | final bool Function(int) onChanged;
11 |
12 | SliderQuestion({
13 | @required this.question,
14 | @required this.min,
15 | @required this.max,
16 | @required this.initialValue,
17 | @required this.onChanged,
18 | });
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return Column(
23 | mainAxisSize: MainAxisSize.min,
24 | crossAxisAlignment: CrossAxisAlignment.start,
25 | children: [
26 | Flexible(
27 | child: Text(
28 | question,
29 | style: const TextStyle(
30 | fontWeight: FontWeight.bold,
31 | fontSize: 20.0,
32 | letterSpacing: 1.2,
33 | ),
34 | ),
35 | ),
36 | KnobSlider(
37 | min: min,
38 | initialValue: initialValue,
39 | max: max,
40 | onChanged: onChanged,
41 | ),
42 | ],
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | archive:
5 | dependency: transitive
6 | description:
7 | name: archive
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.0.11"
11 | args:
12 | dependency: transitive
13 | description:
14 | name: args
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "1.5.2"
18 | async:
19 | dependency: transitive
20 | description:
21 | name: async
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "2.4.0"
25 | boolean_selector:
26 | dependency: transitive
27 | description:
28 | name: boolean_selector
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.0.5"
32 | charcode:
33 | dependency: transitive
34 | description:
35 | name: charcode
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.2"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.14.11"
46 | convert:
47 | dependency: transitive
48 | description:
49 | name: convert
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "2.1.1"
53 | crypto:
54 | dependency: transitive
55 | description:
56 | name: crypto
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "2.1.3"
60 | cupertino_icons:
61 | dependency: "direct main"
62 | description:
63 | name: cupertino_icons
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "0.1.3"
67 | flutter:
68 | dependency: "direct main"
69 | description: flutter
70 | source: sdk
71 | version: "0.0.0"
72 | flutter_launcher_icons:
73 | dependency: "direct dev"
74 | description:
75 | name: flutter_launcher_icons
76 | url: "https://pub.dartlang.org"
77 | source: hosted
78 | version: "0.7.4"
79 | flutter_local_notifications:
80 | dependency: "direct main"
81 | description:
82 | name: flutter_local_notifications
83 | url: "https://pub.dartlang.org"
84 | source: hosted
85 | version: "1.2.2"
86 | flutter_local_notifications_platform_interface:
87 | dependency: transitive
88 | description:
89 | name: flutter_local_notifications_platform_interface
90 | url: "https://pub.dartlang.org"
91 | source: hosted
92 | version: "1.0.1"
93 | flutter_svg:
94 | dependency: "direct main"
95 | description:
96 | name: flutter_svg
97 | url: "https://pub.dartlang.org"
98 | source: hosted
99 | version: "0.17.3+1"
100 | flutter_test:
101 | dependency: "direct dev"
102 | description: flutter
103 | source: sdk
104 | version: "0.0.0"
105 | google_fonts:
106 | dependency: "direct main"
107 | description:
108 | name: google_fonts
109 | url: "https://pub.dartlang.org"
110 | source: hosted
111 | version: "0.3.9"
112 | http:
113 | dependency: transitive
114 | description:
115 | name: http
116 | url: "https://pub.dartlang.org"
117 | source: hosted
118 | version: "0.12.0+4"
119 | http_parser:
120 | dependency: transitive
121 | description:
122 | name: http_parser
123 | url: "https://pub.dartlang.org"
124 | source: hosted
125 | version: "3.1.3"
126 | image:
127 | dependency: transitive
128 | description:
129 | name: image
130 | url: "https://pub.dartlang.org"
131 | source: hosted
132 | version: "2.1.4"
133 | intl:
134 | dependency: "direct main"
135 | description:
136 | name: intl
137 | url: "https://pub.dartlang.org"
138 | source: hosted
139 | version: "0.16.1"
140 | matcher:
141 | dependency: transitive
142 | description:
143 | name: matcher
144 | url: "https://pub.dartlang.org"
145 | source: hosted
146 | version: "0.12.6"
147 | meta:
148 | dependency: transitive
149 | description:
150 | name: meta
151 | url: "https://pub.dartlang.org"
152 | source: hosted
153 | version: "1.1.8"
154 | nested:
155 | dependency: transitive
156 | description:
157 | name: nested
158 | url: "https://pub.dartlang.org"
159 | source: hosted
160 | version: "0.0.4"
161 | path:
162 | dependency: transitive
163 | description:
164 | name: path
165 | url: "https://pub.dartlang.org"
166 | source: hosted
167 | version: "1.6.4"
168 | path_drawing:
169 | dependency: transitive
170 | description:
171 | name: path_drawing
172 | url: "https://pub.dartlang.org"
173 | source: hosted
174 | version: "0.4.1"
175 | path_parsing:
176 | dependency: transitive
177 | description:
178 | name: path_parsing
179 | url: "https://pub.dartlang.org"
180 | source: hosted
181 | version: "0.1.4"
182 | path_provider:
183 | dependency: transitive
184 | description:
185 | name: path_provider
186 | url: "https://pub.dartlang.org"
187 | source: hosted
188 | version: "1.6.5"
189 | path_provider_macos:
190 | dependency: transitive
191 | description:
192 | name: path_provider_macos
193 | url: "https://pub.dartlang.org"
194 | source: hosted
195 | version: "0.0.4"
196 | path_provider_platform_interface:
197 | dependency: transitive
198 | description:
199 | name: path_provider_platform_interface
200 | url: "https://pub.dartlang.org"
201 | source: hosted
202 | version: "1.0.1"
203 | pedantic:
204 | dependency: transitive
205 | description:
206 | name: pedantic
207 | url: "https://pub.dartlang.org"
208 | source: hosted
209 | version: "1.9.0"
210 | petitparser:
211 | dependency: transitive
212 | description:
213 | name: petitparser
214 | url: "https://pub.dartlang.org"
215 | source: hosted
216 | version: "2.4.0"
217 | platform:
218 | dependency: transitive
219 | description:
220 | name: platform
221 | url: "https://pub.dartlang.org"
222 | source: hosted
223 | version: "2.2.1"
224 | plugin_platform_interface:
225 | dependency: transitive
226 | description:
227 | name: plugin_platform_interface
228 | url: "https://pub.dartlang.org"
229 | source: hosted
230 | version: "1.0.2"
231 | provider:
232 | dependency: "direct main"
233 | description:
234 | name: provider
235 | url: "https://pub.dartlang.org"
236 | source: hosted
237 | version: "4.0.4"
238 | quiver:
239 | dependency: transitive
240 | description:
241 | name: quiver
242 | url: "https://pub.dartlang.org"
243 | source: hosted
244 | version: "2.0.5"
245 | sky_engine:
246 | dependency: transitive
247 | description: flutter
248 | source: sdk
249 | version: "0.0.99"
250 | source_span:
251 | dependency: transitive
252 | description:
253 | name: source_span
254 | url: "https://pub.dartlang.org"
255 | source: hosted
256 | version: "1.5.5"
257 | sqflite:
258 | dependency: "direct main"
259 | description:
260 | name: sqflite
261 | url: "https://pub.dartlang.org"
262 | source: hosted
263 | version: "1.3.0"
264 | sqflite_common:
265 | dependency: transitive
266 | description:
267 | name: sqflite_common
268 | url: "https://pub.dartlang.org"
269 | source: hosted
270 | version: "1.0.0+1"
271 | stack_trace:
272 | dependency: transitive
273 | description:
274 | name: stack_trace
275 | url: "https://pub.dartlang.org"
276 | source: hosted
277 | version: "1.9.3"
278 | stream_channel:
279 | dependency: transitive
280 | description:
281 | name: stream_channel
282 | url: "https://pub.dartlang.org"
283 | source: hosted
284 | version: "2.0.0"
285 | string_scanner:
286 | dependency: transitive
287 | description:
288 | name: string_scanner
289 | url: "https://pub.dartlang.org"
290 | source: hosted
291 | version: "1.0.5"
292 | synchronized:
293 | dependency: transitive
294 | description:
295 | name: synchronized
296 | url: "https://pub.dartlang.org"
297 | source: hosted
298 | version: "2.2.0"
299 | term_glyph:
300 | dependency: transitive
301 | description:
302 | name: term_glyph
303 | url: "https://pub.dartlang.org"
304 | source: hosted
305 | version: "1.1.0"
306 | test_api:
307 | dependency: transitive
308 | description:
309 | name: test_api
310 | url: "https://pub.dartlang.org"
311 | source: hosted
312 | version: "0.2.15"
313 | typed_data:
314 | dependency: transitive
315 | description:
316 | name: typed_data
317 | url: "https://pub.dartlang.org"
318 | source: hosted
319 | version: "1.1.6"
320 | vector_math:
321 | dependency: transitive
322 | description:
323 | name: vector_math
324 | url: "https://pub.dartlang.org"
325 | source: hosted
326 | version: "2.0.8"
327 | xml:
328 | dependency: transitive
329 | description:
330 | name: xml
331 | url: "https://pub.dartlang.org"
332 | source: hosted
333 | version: "3.5.0"
334 | yaml:
335 | dependency: transitive
336 | description:
337 | name: yaml
338 | url: "https://pub.dartlang.org"
339 | source: hosted
340 | version: "2.2.0"
341 | sdks:
342 | dart: ">=2.7.0 <3.0.0"
343 | flutter: ">=1.12.13+hotfix.5 <2.0.0"
344 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: Helm
2 | description: A new Flutter project.
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # In Android, build-name is used as versionName while build-number used as versionCode.
10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
12 | # Read more about iOS versioning at
13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
14 | version: 1.0.0+1
15 |
16 | environment:
17 | sdk: ">=2.2.2 <3.0.0"
18 |
19 | dependencies:
20 | flutter:
21 | sdk: flutter
22 | flutter_svg: ^0.17.3+1
23 | google_fonts: ^0.3.9
24 | intl: ^0.16.1
25 | provider: ^4.0.4
26 | sqflite: ^1.3.0
27 | flutter_local_notifications: ^1.2.2
28 |
29 | # The following adds the Cupertino Icons font to your application.
30 | # Use with the CupertinoIcons class for iOS style icons.
31 | cupertino_icons: ^0.1.3
32 |
33 | dev_dependencies:
34 | flutter_test:
35 | sdk: flutter
36 | flutter_launcher_icons: ^0.7.4
37 |
38 | flutter_icons:
39 | android: "launcher_icon"
40 | ios: true
41 | image_path: "assets/images/icon.png"
42 |
43 | # For information on the generic Dart part of this file, see the
44 | # following page: https://dart.dev/tools/pub/pubspec
45 |
46 | # The following section is specific to Flutter.
47 | flutter:
48 |
49 | # The following line ensures that the Material Icons font is
50 | # included with your application, so that you can use the icons in
51 | # the material Icons class.
52 | uses-material-design: true
53 |
54 | # To add assets to your application, add an assets section, like this:
55 | assets:
56 | - assets/images/yoga.svg
57 | - assets/images/drive.svg
58 | # - images/a_dot_burr.jpeg
59 | # - images/a_dot_ham.jpeg
60 |
61 | # An image asset can refer to one or more resolution-specific "variants", see
62 | # https://flutter.dev/assets-and-images/#resolution-aware.
63 |
64 | # For details regarding adding assets from package dependencies, see
65 | # https://flutter.dev/assets-and-images/#from-packages
66 |
67 | # To add custom fonts to your application, add a fonts section here,
68 | # in this "flutter" section. Each entry in this list should have a
69 | # "family" key with the font family name, and a "fonts" key with a
70 | # list giving the asset and other descriptors for the font. For
71 | # example:
72 | # fonts:
73 | # - family: Schyler
74 | # fonts:
75 | # - asset: fonts/Schyler-Regular.ttf
76 | # - asset: fonts/Schyler-Italic.ttf
77 | # style: italic
78 | # - family: Trajan Pro
79 | # fonts:
80 | # - asset: fonts/TrajanPro.ttf
81 | # - asset: fonts/TrajanPro_Bold.ttf
82 | # weight: 700
83 | #
84 | # For details regarding fonts from package dependencies,
85 | # see https://flutter.dev/custom-fonts/#from-packages
86 |
--------------------------------------------------------------------------------