commit a8e802ffab3e9955991daa7a5d586303434d9f96 Author: Michael Thomas Date: Wed Aug 31 22:19:20 2022 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64bbc21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +# Custom +/lib/secrets.dart diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..4c7eb5a --- /dev/null +++ b/.metadata @@ -0,0 +1,36 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + - platform: android + create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + - platform: ios + create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + - platform: web + create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md new file mode 100644 index 0000000..5014aef --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# furman_now + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..711ac01 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,71 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "edu.furman.now" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..2498120 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9da0164 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/ic_launcher-playstore.png b/android/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..d6f2b38 Binary files /dev/null and b/android/app/src/main/ic_launcher-playstore.png differ diff --git a/android/app/src/main/kotlin/edu/furman/now/furman_now/MainActivity.kt b/android/app/src/main/kotlin/edu/furman/now/furman_now/MainActivity.kt new file mode 100644 index 0000000..7155078 --- /dev/null +++ b/android/app/src/main/kotlin/edu/furman/now/furman_now/MainActivity.kt @@ -0,0 +1,6 @@ +package edu.furman.now.furman_now + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/ic_launcher_foreground.xml b/android/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..62321ba --- /dev/null +++ b/android/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..0707a41 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..36a27c9 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..9ab5e23 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..a0f04c3 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..57f8b4e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/ic_launcher_background.xml b/android/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..e027b66 --- /dev/null +++ b/android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #53307F + \ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..2498120 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..61a47e7 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.6.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.2.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..94adc3a --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..cc5527d --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/assets/images/bell-tower.svg b/assets/images/bell-tower.svg new file mode 100644 index 0000000..96eae69 --- /dev/null +++ b/assets/images/bell-tower.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/compass.svg b/assets/images/compass.svg new file mode 100644 index 0000000..65273e6 --- /dev/null +++ b/assets/images/compass.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..8d4492f --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..1e8c3c9 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..8ee2123 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - Flutter (1.0.0) + - path_provider_ios (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + path_provider_ios: + :path: ".symlinks/plugins/path_provider_ios/ios" + +SPEC CHECKSUMS: + Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 + +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c + +COCOAPODS: 1.11.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..4e81353 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,549 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 11E039BAA958A82F61CA8CF4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 445C012451AF63F270E13685 /* Pods_Runner.framework */; }; + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3249BE4DF8F2DD8FFDBEF7DC /* 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 = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 445C012451AF63F270E13685 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E0924751F238036F68F3BE44 /* 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 = ""; }; + F1A21E9608592ADA54209C54 /* 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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 11E039BAA958A82F61CA8CF4 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 18B68695A78F6AE81A12B248 /* Pods */ = { + isa = PBXGroup; + children = ( + 3249BE4DF8F2DD8FFDBEF7DC /* Pods-Runner.debug.xcconfig */, + E0924751F238036F68F3BE44 /* Pods-Runner.release.xcconfig */, + F1A21E9608592ADA54209C54 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 18B68695A78F6AE81A12B248 /* Pods */, + F2C3C45531977DF5F83A8199 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + F2C3C45531977DF5F83A8199 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 445C012451AF63F270E13685 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 8CBD88BD92A95B890D828A7D /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + F18EE307CA050517163ABD82 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 8CBD88BD92A95B890D828A7D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + F18EE307CA050517163ABD82 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = edu.furman.now.furmanNow; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = edu.furman.now.furmanNow; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = edu.furman.now.furmanNow; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..c87d15a --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000..dc9ada4 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..28c6bf0 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..f091b6b Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..4cde121 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..d0ef06e Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..dcdc230 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..2ccbfd9 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..c8f9ed8 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..a6d6b86 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..75b2d16 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..c4df70d Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..6a84f41 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0e1f58 Binary files /dev/null and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000..9da19ea Binary files /dev/null and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..c119125 --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Furman Now + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + furman_now + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..4cebb67 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,6 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/app.dart'; + +void main() { + runApp(const App()); +} diff --git a/lib/src/app.dart b/lib/src/app.dart new file mode 100644 index 0000000..6e16d1d --- /dev/null +++ b/lib/src/app.dart @@ -0,0 +1,147 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/screens/events/index.dart'; +import 'package:furman_now/src/screens/home/index.dart'; +import 'package:furman_now/src/screens/info/index.dart'; +import 'package:furman_now/src/screens/map/index.dart'; +import 'package:furman_now/src/screens/student_id/index.dart'; +import 'package:furman_now/src/utils/theme.dart'; +import 'package:navbar_router/navbar_router.dart'; + +class App extends StatelessWidget { + const App({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Furman Now!', + home: const MainPage(), + theme: myFurmanTheme, + ); + } +} + +class MainPage extends StatefulWidget { + const MainPage({Key? key}) : super(key: key); + + @override + State createState() => _MainPageState(); +} + +class _MainPageState extends State { + List items = [ + NavbarItem(Icons.home_outlined, 'Home', backgroundColor: colors[0]), + NavbarItem(Icons.map_outlined, 'Map', backgroundColor: colors[0]), + NavbarItem(Icons.person_outline, 'Meal ID', backgroundColor: colors[0]), + NavbarItem(Icons.calendar_month_outlined, 'Events', backgroundColor: colors[0]), + NavbarItem(Icons.info_outline, 'Info', backgroundColor: colors[0]), + ]; + + final Map> _routes = const { + 0: { + '/': HomeScreen(), + // FeedDetail.route: FeedDetail(), + }, + 1: { + '/': MapScreen(), + // ProductDetail.route: ProductDetail(), + // ProductComments.route: ProductComments(), + }, + 2: { + '/': StudentIdScreen(), + // ProductDetail.route: ProductDetail(), + // ProductComments.route: ProductComments(), + }, + 3: { + '/': EventsScreen(), + // ProfileEdit.route: ProfileEdit(), + }, + 4: { + '/': InfoScreen(), + // ProfileEdit.route: ProfileEdit(), + }, + }; + + void showSnackBar() { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + behavior: SnackBarBehavior.floating, + duration: Duration(milliseconds: 600), + margin: EdgeInsets.only( + bottom: kBottomNavigationBarHeight + 2, right: 2, left: 2), + content: Text('Tap back button again to exit'), + ), + ); + } + + void hideSnackBar() { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + } + + DateTime oldTime = DateTime.now(); + DateTime newTime = DateTime.now(); + + @override + Widget build(BuildContext context) { + final size = MediaQuery.of(context).size; + return Scaffold( + resizeToAvoidBottomInset: false, + body: NavbarRouter( + errorBuilder: (context) { + return const Center(child: Text('Error 404')); + }, + isDesktop: size.width > 600 ? true : false, + onBackButtonPressed: (isExitingApp) { + if (isExitingApp) { + newTime = DateTime.now(); + int difference = newTime.difference(oldTime).inMilliseconds; + oldTime = newTime; + if (difference < 1000) { + hideSnackBar(); + return isExitingApp; + } else { + showSnackBar(); + return false; + } + } else { + return isExitingApp; + } + }, + destinationAnimationCurve: Curves.fastOutSlowIn, + destinationAnimationDuration: 600, + decoration: NavbarDecoration( + selectedLabelTextStyle: const TextStyle(color: Colors.deepPurple), + showUnselectedLabels: true, + unselectedLabelTextStyle: + const TextStyle(color: Colors.black, fontSize: 10), + selectedIconTheme: const IconThemeData(color: Colors.deepPurple), + isExtended: size.width > 800 ? true : false, + navbarType: BottomNavigationBarType.fixed), + // onChanged: (x) { + // debugPrint('index changed $x'); + // }, + backButtonBehavior: BackButtonBehavior.rememberHistory, + destinations: [ + for (int i = 0; i < items.length; i++) + DestinationRouter( + navbarItem: items[i], + destinations: [ + for (int j = 0; j < _routes[i]!.keys.length; j++) + Destination( + route: _routes[i]!.keys.elementAt(j), + widget: _routes[i]!.values.elementAt(j), + ), + ], + initialRoute: _routes[i]!.keys.first, + ), + ], + ), + ); + } +} + +Future navigate(BuildContext context, String route, + {bool isDialog = false, + bool isRootNavigator = true, + Map? arguments}) => + Navigator.of(context, rootNavigator: isRootNavigator) + .pushNamed(route, arguments: arguments); diff --git a/lib/src/layouts/main/index.dart b/lib/src/layouts/main/index.dart new file mode 100644 index 0000000..da17dea --- /dev/null +++ b/lib/src/layouts/main/index.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; + +class MainLayout extends StatelessWidget { + const MainLayout({ + Key? key, + this.body, + }) : super(key: key); + + final Widget? body; + + void _onItemTapped(int index) { + // Navigate to the second screen using a named route. + // Navigator.pushNamed(context, '/second'); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: body, + bottomNavigationBar: BottomNavigationBar( + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.home), + label: 'Home', + ), + BottomNavigationBarItem( + icon: Icon(Icons.map), + label: 'Map', + ), + BottomNavigationBarItem( + icon: Icon(Icons.perm_identity), + label: 'Meal Card', + ), + BottomNavigationBarItem( + icon: Icon(Icons.calendar_month), + label: 'Events', + ), + BottomNavigationBarItem( + icon: Icon(Icons.info_outline), + label: 'Info', + ), + ], + currentIndex: 0, + selectedItemColor: Colors.grey[700], + onTap: _onItemTapped, + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/routes/index.dart b/lib/src/routes/index.dart new file mode 100644 index 0000000..3eff20e --- /dev/null +++ b/lib/src/routes/index.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/screens/home/index.dart'; + +Route routes(RouteSettings settings) { + switch (settings.name) { + case '/': + return MaterialPageRoute(builder: (_) => const HomeScreen()); + // case '/home': + // return MaterialPageRoute(builder: (_) => HomeScreen()); + // case '/auth': + // return MaterialPageRoute(builder: (_) => AuthenticationScreen()); + default: + return MaterialPageRoute(builder: (_) => const HomeScreen()); + } +} diff --git a/lib/src/screens/events/index.dart b/lib/src/screens/events/index.dart new file mode 100644 index 0000000..9c4878b --- /dev/null +++ b/lib/src/screens/events/index.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/utils/date_range.dart'; +import 'package:furman_now/src/utils/theme.dart'; +import 'package:furman_now/src/widgets/header.dart'; +import 'package:furman_now/src/widgets/home/events/events_list.dart'; +import 'package:furman_now/src/widgets/scroll_view_height.dart'; +import 'package:intl/intl.dart'; + +class EventsScreen extends StatelessWidget { + const EventsScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + color: Colors.grey[100], + child: SafeArea( + child: Padding( + padding: const EdgeInsets.only(bottom: kBottomNavigationBarHeight), + child: Stack( + fit: StackFit.loose, + children: [ + SizedBox( + width: double.infinity, + height: double.infinity, + child: Align( + alignment: Alignment.topLeft, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 30), + width: double.infinity, + height: 100, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Icon(Icons.calendar_month_outlined, size: 35, color: Colors.grey[700]), + const SizedBox(width: 12), + Text("Events", style: furmanTextStyle(TextStyle(color: Colors.grey[900], fontSize: 28, fontWeight: FontWeight.w700))), + ], + ), + ], + ), + ), + ), + ), + ScrollViewWithHeight( + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical(top: Radius.circular(30)), + ), + padding: const EdgeInsets.symmetric(vertical: 20), + margin: const EdgeInsets.only(top: 100), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const HeaderWidget(title: "Today"), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: EventsList() + ), + const HeaderWidget(title: "Tomorrow"), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: EventsList(dateRange: constructDateRange( + DateTime.now().add(const Duration(days: 1)), + DateTime.now().add(const Duration(days: 1)), + )), + ), + ...[for(var i=2; i<7; i+=1) i].map((i) { + var date = DateTime.now().add(Duration(days: i)); + var dayName = DateFormat('EEEE').format(date); + return Wrap( + children: [ + HeaderWidget(title: dayName), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: EventsList(dateRange: constructDateRange( + date, + date, + )), + ), + ], + ); + }), + Center(child: + Wrap( + direction: Axis.vertical, + crossAxisAlignment: WrapCrossAlignment.center, + children: const [ + Text("Need more events?"), + Text("Syncdin"), + Text("Athletics"), + Text("CLPs"), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/src/screens/home/index.dart b/lib/src/screens/home/index.dart new file mode 100644 index 0000000..26e1162 --- /dev/null +++ b/lib/src/screens/home/index.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/utils/greeting.dart'; +import 'package:furman_now/src/utils/theme.dart'; +import 'package:furman_now/src/widgets/header.dart'; +import 'package:furman_now/src/widgets/home/events/events_list.dart'; +import 'package:furman_now/src/widgets/home/restaurants/restaurants_list.dart'; +import 'package:furman_now/src/widgets/home/transportation/transportation_card.dart'; +import 'package:furman_now/src/widgets/scroll_view_height.dart'; + +class HomeScreen extends StatelessWidget { + const HomeScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + color: const Color(0xffb7acc9), + child: SafeArea( + child: Container( + color: Colors.grey[100], + padding: const EdgeInsets.only(bottom: kBottomNavigationBarHeight), + child: Stack( + fit: StackFit.loose, + children: [ + Container( + width: double.infinity, + height: double.infinity, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xffb7acc9), + Color(0xffb7acc9), + ], // Gradient from https://learnui.design/tools/gradient-generator.html + tileMode: TileMode.mirror, + ), + ), + child: Align( + alignment: Alignment.topLeft, + child: Container( + padding: const EdgeInsets.all(40), + width: double.infinity, + height: 200, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("${greeting()},\nMichael", style: furmanTextStyle(const TextStyle(color: Color(0xff26183d), fontSize: 36, fontWeight: FontWeight.w800))), + const SizedBox(height: 5), + Text("It's 76º and partly cloudy", style: furmanTextStyle(const TextStyle(color: Color(0xff26183d), fontSize: 16, fontWeight: FontWeight.w500))), + ], + ), + ), + ), + ), + ScrollViewWithHeight( + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical(top: Radius.circular(30)), + ), + padding: const EdgeInsets.symmetric(vertical: 20), + margin: const EdgeInsets.only(top: 200), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const HeaderWidget( + title: "Today's Events", + link: HeaderLink(text: "View more", href: ""), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: EventsList(), + ), + const HeaderWidget(title: "Food & Dining"), + const RestaurantsList(), + const HeaderWidget(title: "Transportation"), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 20), + child: TransportationCard(), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} + diff --git a/lib/src/screens/info/index.dart b/lib/src/screens/info/index.dart new file mode 100644 index 0000000..51842fc --- /dev/null +++ b/lib/src/screens/info/index.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/utils/theme.dart'; +import 'package:furman_now/src/widgets/info/info_card.dart'; +import 'package:furman_now/src/widgets/scroll_view_height.dart'; + +class InfoScreen extends StatelessWidget { + const InfoScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + color: Colors.grey[100], + child: SafeArea( + child: Padding( + padding: const EdgeInsets.only(bottom: kBottomNavigationBarHeight), + child: Stack( + fit: StackFit.loose, + children: [ + SizedBox( + width: double.infinity, + height: double.infinity, + child: Align( + alignment: Alignment.topLeft, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 30), + width: double.infinity, + height: 100, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Icon(Icons.info_outline, size: 35, color: Colors.grey[700]), + const SizedBox(width: 12), + Text("Info", style: furmanTextStyle(TextStyle(color: Colors.grey[900], fontSize: 28, fontWeight: FontWeight.w700))), + ], + ), + ], + ), + ), + ), + ), + ScrollViewWithHeight( + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.vertical(top: Radius.circular(30)), + ), + padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20), + margin: const EdgeInsets.only(top: 100), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InfoCard( + color: Colors.red.shade50, + icon: Icons.local_hospital, + title: "Health and Safety", + description: "Important contact information and links regarding student health and safety.", + ), + const SizedBox(height: 10), + InfoCard( + color: Colors.deepPurple.shade50, + icon: Icons.phone, + title: "Contacts", + description: "Important contact information and links regarding student health and safety.", + ), + const SizedBox(height: 10), + InfoCard( + color: Colors.blue.shade50, + icon: Icons.access_time, + title: "Hours", + description: "Important contact information and links regarding student health and safety.", + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/src/screens/map/index.dart b/lib/src/screens/map/index.dart new file mode 100644 index 0000000..e44c56c --- /dev/null +++ b/lib/src/screens/map/index.dart @@ -0,0 +1,187 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:furman_now/src/utils/theme.dart'; +import 'package:furman_now/src/widgets/map/filter_chip.dart'; +import 'package:furman_now/src/widgets/map/rotate_compass.dart'; +import 'package:latlong2/latlong.dart'; + +class MapScreen extends StatefulWidget { + const MapScreen({Key? key}) : super(key: key); + + @override + State createState() => _MapScreenState(); +} + +class _MapScreenState extends State + with SingleTickerProviderStateMixin { + final MapController _mapController = MapController(); + + late final AnimationController _animationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + + var _rotation = 0.0; + + @override + void initState() { + super.initState(); + _mapController.mapEventStream.listen((event) { + if (event is MapEventRotate) { + setState(() { + _rotation = _mapController.rotation * (2 * pi) / 360; + }); + } + }); + } + + @override + void dispose() { + super.dispose(); + _animationController.dispose(); + } + + void resetRotation() async { + // take the shortest rotation path + var end = _mapController.rotation > 180 ? 360.0 : 0.0; + var animation = Tween( + begin: _mapController.rotation, + end: end, + ).animate(CurvedAnimation( + parent: _animationController, + curve: Curves.easeInOut, + )); + + animationListener() { + _mapController.rotate(animation.value); + } + + animation.addListener(animationListener); + + await _animationController.forward(); + + animation.removeListener(animationListener); + _animationController.reset(); + + _mapController.rotate(0); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + color: const Color(0xffb7acc9), + child: SafeArea( + top: false, + child: Padding( + padding: const EdgeInsets.only(bottom: kBottomNavigationBarHeight), + child: Stack( + children: [ + FlutterMap( + mapController: _mapController, + options: MapOptions( + center: LatLng(34.925926, -82.439397), + enableMultiFingerGestureRace: true, + rotationWinGestures: MultiFingerGesture.all, + pinchZoomThreshold: 0.2, + rotationThreshold: 8, + zoom: 15, + minZoom: 12, + maxZoom: 18, + ), + layers: [ + TileLayerOptions( + urlTemplate: + "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + userAgentPackageName: 'edu.furman.now', + ), + ], + nonRotatedChildren: [ + AttributionWidget( + attributionBuilder: (BuildContext context) { + return const ColoredBox( + color: Color(0xCCFFFFFF), + child: Padding( + padding: EdgeInsets.all(3), + child: Text("©️ OpenStreetMap contributors"), + ), + ); + }, + ), + ], + ), + Positioned( + top: 12, + left: 0, + right: 0, + child: SafeArea( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Container( + width: double.infinity, + height: 50, + padding: const EdgeInsets.only(left: 10, right: 20), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(60)), + boxShadow: [ + BoxShadow( + color: Color(0x33000000), + blurRadius: 8, + ), + ], + ), + child: Stack( + children: [ + Align( + alignment: Alignment.centerLeft, + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + SvgPicture.asset("assets/images/bell-tower.svg", color: Theme.of(context).primaryColor, height: 32), + const SizedBox(width: 10), + Text( + "Search locations", + style: furmanTextStyle(TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Colors.grey.shade500, + )), + ), + ], + ), + ), + ], + ), + ), + ), + // const SizedBox(height: 12), + SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + scrollDirection: Axis.horizontal, + child: Wrap( + spacing: 6, + children: const [ + MapFilterChip(icon: Icons.restaurant, text: "Restaurants"), + MapFilterChip(icon: Icons.train, text: "Transportation"), + MapFilterChip(icon: Icons.school, text: "Campus Buildings"), + ], + ), + ), + MapRotateCompass(rotation: _rotation, resetRotation: resetRotation), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/src/screens/student_id/index.dart b/lib/src/screens/student_id/index.dart new file mode 100644 index 0000000..129b8c9 --- /dev/null +++ b/lib/src/screens/student_id/index.dart @@ -0,0 +1,72 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:barcode_widget/barcode_widget.dart'; +import 'package:furman_now/src/services/get_app/barcode/barcode_service.dart'; +import 'package:furman_now/src/utils/theme.dart'; + +class StudentIdScreen extends StatefulWidget { + const StudentIdScreen({Key? key}) : super(key: key); + + @override + State createState() => _StudentIdScreenState(); +} + +class _StudentIdScreenState extends State { + String barcodeNumber = BarcodeService.generateGetBarcode(); + Timer? timer; + + @override + void initState() { + super.initState(); + + timer = Timer.periodic( + const Duration(seconds: 10), + updateBarcode, + ); + } + + void updateBarcode(Timer timer) { + setState(() { + barcodeNumber = BarcodeService.generateGetBarcode(); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + color: const Color(0xffb7acc9), + child: SafeArea( + child: ListView( + padding: const EdgeInsets.all(40), + children: [ + Text( + "Furman ID", + style: furmanTextStyle(const TextStyle(color: Color(0xff26183d), fontSize: 36, fontWeight: FontWeight.w800)), + ), + const SizedBox(height: 200), + Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(10)), + ), + // hack since the barcode has a weird intrinsic size for some reason + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return BarcodeWidget( + barcode: Barcode.pdf417(moduleHeight: 4), + data: barcodeNumber, + margin: const EdgeInsets.all(10), + height: constraints.maxWidth / 3, + ); + }, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/src/services/events/event.dart b/lib/src/services/events/event.dart new file mode 100644 index 0000000..da7d440 --- /dev/null +++ b/lib/src/services/events/event.dart @@ -0,0 +1,13 @@ +class Event { + Event({ + required this.title, + required this.time, + required this.location, + required this.category + }); + + final String title; + final DateTime time; + final String location; + final String category; +} \ No newline at end of file diff --git a/lib/src/services/events/events_service.dart b/lib/src/services/events/events_service.dart new file mode 100644 index 0000000..44c6586 --- /dev/null +++ b/lib/src/services/events/events_service.dart @@ -0,0 +1,174 @@ +import 'dart:convert'; + +import 'package:furman_now/src/services/events/event.dart'; +import 'package:http/http.dart' as http; + +class EventsService { + static Future> fetchEvents() async { + var athleticsEvents = await fetchAthleticsEvents(); + var clpEvents = await fetchClpEvents(); + List eventsList = List.from(athleticsEvents)..addAll(clpEvents); + eventsList.sort((Event a, Event b) => a.time.compareTo(b.time)); + return eventsList; + } + + static Future> fetchAthleticsEvents() async { + final response = await http + .get(Uri.parse("https://cs.furman.edu/~csdaemon/FUNow/athleticsGet.php")); + + if (response.statusCode == 200) { + // If the server did return a 200 OK response, + // then parse the JSON. + final eventsJson = jsonDecode(response.body); + return (eventsJson["results"] as List).map((event) => + AthleticsEvent.fromJson(event) + ); + } else { + // If the server did not return a 200 OK response, + // then throw an exception. + throw Exception('Failed to load athletics events.'); + } + } + + static Future> fetchClpEvents() async { + final response = await http + .get(Uri.parse("https://cs.furman.edu/~csdaemon/FUNow/clpGet.php")); + + if (response.statusCode == 200) { + // If the server did return a 200 OK response, + // then parse the JSON. + final eventsJson = jsonDecode(response.body); + return (eventsJson["results"] as List).map((event) => + ClpEvent.fromJson(event) + ); + } else { + // If the server did not return a 200 OK response, + // then throw an exception. + throw Exception('Failed to load athletics events.'); + } + } +} + +class AthleticsEvent implements Event { + @override + final String title; + @override + final DateTime time; + @override + final String location; + @override + final String category = "Athletics"; + + final String sportTitle; + final String sportShort; + final AthleticsEventLocation locationIndicator; + final String opponent; + + AthleticsEvent._({ + required this.title, + required this.time, + required this.location, + + required this.sportTitle, + required this.sportShort, + required this.locationIndicator, + required this.opponent, + }); + + factory AthleticsEvent({ + required time, + required location, + required sportTitle, + required sportShort, + required locationIndicator, + required opponent, + }) { + // Determine AthleticsEventLocation from string + AthleticsEventLocation eventLocationIndicator; + switch(locationIndicator) { + case ("H"): + eventLocationIndicator = AthleticsEventLocation.home; + break; + case ("A"): + eventLocationIndicator = AthleticsEventLocation.away; + break; + case ("N"): + default: + eventLocationIndicator = AthleticsEventLocation.neither; + break; + } + + // Generate event title + var eventTitleSeparator = + eventLocationIndicator == AthleticsEventLocation.away + ? "at" + : "vs"; + + return AthleticsEvent._( + title: '$sportTitle $eventTitleSeparator $opponent', + time: time, + location: location, + sportTitle: sportTitle, + sportShort: sportShort, + locationIndicator: eventLocationIndicator, + opponent: opponent, + ); + } + + factory AthleticsEvent.fromJson(Map json) { + return AthleticsEvent( + sportTitle: json["sportTitle"], + sportShort: json["sportShort"], + time: DateTime.parse(json["eventdate"]), + location: json["location"], + locationIndicator: json["location_indicator"], + opponent: json["opponent"], + ); + } +} + +enum AthleticsEventLocation { + home, + away, + neither, +} + +class ClpEvent implements Event { + @override + final String title; + @override + DateTime get time { + return startTime; + } + @override + final String location; + @override + final String category; + + final DateTime startTime; + final DateTime endTime; + final String organization; + final String description; + + ClpEvent({ + required this.title, + required this.startTime, + required this.endTime, + required this.location, + required this.category, + required this.organization, + required this.description, + }); + + factory ClpEvent.fromJson(Map json) { + return ClpEvent( + title: json["title"], + startTime: DateTime.parse(json["date"] + " " + json["start"]), + endTime: DateTime.parse(json["date"] + " " + json["end"]), + location: json["location"], + category: json["eventType"], + organization: json["organization"], + description: json["description"], + ); + } +} \ No newline at end of file diff --git a/lib/src/services/get_app/barcode/barcode_service.dart b/lib/src/services/get_app/barcode/barcode_service.dart new file mode 100644 index 0000000..7a3eb22 --- /dev/null +++ b/lib/src/services/get_app/barcode/barcode_service.dart @@ -0,0 +1,90 @@ +import 'dart:convert'; +import 'package:crypto/crypto.dart'; +import 'dart:typed_data'; +import 'package:convert/convert.dart'; +import 'package:furman_now/secrets.dart'; + +class BarcodeService { + static generateGetBarcode({ int? timestamp }) { + // seconds since epoch + timestamp ??= DateTime.now().millisecondsSinceEpoch ~/ 1000; + + // final timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000; + // const timestamp = 1661567550; + + final timestampBytes = _int64BigEndianBytes(timestamp); + + const cbordKey = Secrets.cbordKey; + const institutionKey = Secrets.institutionKey; + const patronKey = Secrets.testPatronKey; + + final cbordKeyBytes = Uint8List.fromList(hex.decode(utf8.decode(base64Decode(cbordKey)))); + final institutionKeyBytes = Uint8List.fromList(hex.decode(institutionKey)); + final patronKeyBytes = _int32LittleEndianBytes(patronKey); + final sharedKeyBytes = _xorEncrypt(cbordKeyBytes, institutionKeyBytes); + + final checksumDigit = _luhnGenerate(patronKey.toString()); + + // print(hex.encode(_int32BigEndianBytes(timestamp))); + final barcodeOtc = int.parse(_hotp(sharedKeyBytes, timestampBytes, 9, hash: sha256)); + final barcodeOtcBytes = _int32LittleEndianBytes(barcodeOtc); + + final encryptedBytes = _xorEncrypt(patronKeyBytes, barcodeOtcBytes); + final encrypted = ByteData.view(encryptedBytes.buffer).getInt32(0, Endian.little); + + final barcodeData = "${timestamp.toString().padLeft(10, "0")}1${encrypted.toString().padLeft(10, "0")}$checksumDigit"; + + return barcodeData; + } + + static int _luhnGenerate(String code) { + const computed = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]; + var sum = 0, + digit = 0, + i = code.length, + even = false; + + while (i-- > 0) { + digit = int.parse(code[i]); + sum += (even = !even) ? computed[digit] : digit; + } + + return (sum * 9) % 10; + } + + static Uint8List _xorEncrypt(List subject, List key) { + var out = Uint8List(subject.length); + for (int i = 0; i < subject.length; i++) { + out[i] = subject[i] ^ key[i % key.length]; + } + return out; + } + + static _hotp(Uint8List key, Uint8List counter, int digits, { Hash hash = sha256 }) { + final mac = Uint8List.fromList(Hmac(hash, key).convert(counter).bytes); + final offset = mac[mac.length - 1] & 0x0f; + final binary = ByteData.view(mac.buffer).getInt32(offset, Endian.big) & 0x7fffffff; + var binaryString = binary.toString(); + if (binaryString.length - digits > 0) { + binaryString = binaryString.substring(binaryString.length - digits); + } + binaryString = binaryString.padLeft(digits, "0"); + return binaryString; + } + + static Uint8List _int32LittleEndianBytes(int value) => + Uint8List(4)..buffer.asByteData().setInt32(0, value, Endian.little); + + static Uint8List _int64BigEndianBytes(int value) { + var sendValueBytes = ByteData(8); + // setUint64 not implemented on some systems so use setUint32 in + // those cases. Leading zeros to pad to equal 64 bit. + // Epoch as 32-bit good until 2038 Jan 19 @ 03:14:07 + try { + sendValueBytes.setUint64(0, value, Endian.big); + } on UnsupportedError { + sendValueBytes.setUint32(0, value, Endian.big); + } + return sendValueBytes.buffer.asUint8List(); + } +} diff --git a/lib/src/services/get_app/barcode/barcode_test.dart b/lib/src/services/get_app/barcode/barcode_test.dart new file mode 100644 index 0000000..e938b33 --- /dev/null +++ b/lib/src/services/get_app/barcode/barcode_test.dart @@ -0,0 +1,12 @@ +import 'dart:io'; + +import "barcode_service.dart"; + +void main(List args) { + // while(true) { + // print(BarcodeService.generateGetBarcode()); + // sleep(const Duration(seconds:1)); + // } + + print(BarcodeService.generateGetBarcode(timestamp: int.parse(args[0]))); +} diff --git a/lib/src/services/get_app/barcode/results.txt b/lib/src/services/get_app/barcode/results.txt new file mode 100644 index 0000000..1bbe2e3 --- /dev/null +++ b/lib/src/services/get_app/barcode/results.txt @@ -0,0 +1,66 @@ +1661796958104839794376 +1661796959104317426886 +1661796960108255460226 +1661796961106920860666 +1661796962106199730706 +1661796963100729423146 +1661796964100826751576 +1661796965105980662356 +1661796966100792147766 +1661796967103886508706 +1661796968101562397906 +1661796969109347522246 +1661796970104476755206 +1661796971106085404376 +1661796972100553491616 +1661796973101404143146 +1661796974105377734796 +1661796975107065989056 +1661796976105135326536 +1661796977100360805266 +1661796978108364147506 +1661796979106002091456 +1661796980100164117926 +1661796981109290599786 +1661796982107498877976 +1661796983100444791306 +1661796984103380694806 +1661796985105701206076 +1661796986103876556676 +1661796987102846575686 +1661796988109894924956 +1661796989109869297986 +1661796990105289985306 +1661796991107971540556 +1661796992109255791256 +1661796993104515591796 +1661796994105087857776 +1661796995100628463616 +1661796996109645970966 +1661796997102192728376 +1661796998101976381966 +1661796999100836277786 +1661797000107503534346 +1661797001100639859056 +1661797002101528598866 +1661797003105623778966 +1661797004107907863076 +1661797005109315398796 +1661797006100768643546 +1661797007101212204946 +1661797008103273524436 +1661797009101866267696 +1661797010102857038416 +1661797011102685511796 +1661797012100193444876 +1661797013103722518116 +1661797014108411880946 +1661797015101560642386 +1661797016109488069706 +1661797017100723908746 +1661797018103949619536 +1661797019109992965496 +1661797020106962817906 +1661797021108226414706 +1661797022106594105606 +1661797023100233692336 diff --git a/lib/src/services/get_app/user/user_service.dart b/lib/src/services/get_app/user/user_service.dart new file mode 100644 index 0000000..7f26ba3 --- /dev/null +++ b/lib/src/services/get_app/user/user_service.dart @@ -0,0 +1,3 @@ +class UserService { + +} \ No newline at end of file diff --git a/lib/src/services/restaurants/restaurant_service.dart b/lib/src/services/restaurants/restaurant_service.dart new file mode 100644 index 0000000..7998d77 --- /dev/null +++ b/lib/src/services/restaurants/restaurant_service.dart @@ -0,0 +1,227 @@ +import 'dart:convert'; + +import 'package:flutter/painting.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:http/http.dart' as http; + +class RestaurantService { + static Future> fetchRestaurants() async { + var restaurants = (await _fetchRestaurants()).toList(); + await _fetchRestaurantHours(restaurants); + return restaurants; + } + + static Future> _fetchRestaurants() async { + final response = await http + .get(Uri.parse("https://cs.furman.edu/~csdaemon/FUNow/restaurantGet.php")); + + if (response.statusCode == 200) { + // If the server did return a 200 OK response, + // then parse the JSON. + final restaurantsJson = jsonDecode(response.body); + return (restaurantsJson["results"] as List).map((restaurant) => + Restaurant.fromJson(restaurant) + ); + } else { + // If the server did not return a 200 OK response, + // then throw an exception. + throw Exception('Failed to load athletics events.'); + } + } + + static Future _fetchRestaurantHours(List restaurants) async { + final response = await http + .get(Uri.parse("https://cs.furman.edu/~csdaemon/FUNow/restaurantHoursGet.php")); + + if (response.statusCode == 200) { + // If the server did return a 200 OK response, + // then parse the JSON. + final hoursListJson = jsonDecode(response.body); + for (var hoursJson in (hoursListJson["results"] as List)) { + var hours = Hours.fromJson(hoursJson); + restaurants.firstWhere((restaurant) => restaurant.id == hours.id).hoursList.add(hours); + } + } else { + // If the server did not return a 200 OK response, + // then throw an exception. + throw Exception('Failed to load athletics events.'); + } + } +} + +class Restaurant { + int id; + String name; + String? shortName; + String location; + LatLng mapLocation; + int frequency; + int busyness; + String? url; + List hoursList = []; + + Restaurant({ + required this.id, + required this.name, + this.shortName, + required this.location, + required this.mapLocation, + required this.frequency, + required this.busyness, + this.url, + }); + + bool get isOpen { + for(var hours in hoursList) { + var now = DateTime.now(); + var today = Weekday.values[now.weekday - 1]; + if (hours.daysOfWeek.days[today] == true) { + var currentTime = Duration(hours: now.hour, minutes: now.minute, seconds: now.second); + if(hours.startTime != null && hours.endTime != null) { + if (hours.startTime! < currentTime && hours.endTime! > currentTime) { + return true; + } + } + } + } + return false; + } + + // TODO: this is the absolute worst + String get imageUri { + return "https://cs.furman.edu/~csdaemon/FUNow/appIcons/$name Icon.png"; + } + + ImageProvider get image { + return NetworkImage(imageUri); + } + + factory Restaurant.fromJson(Map json) { + return Restaurant( + id: int.parse(json["id"]), + name: json["fullname"], + shortName: json["name"], + location: json["location"], + mapLocation: LatLng(double.parse(json["latitude"]), double.parse(json["longitude"])), + frequency: int.parse(json["frequency"]), + busyness: int.parse(json["busyness"]), + url: (json["url"] != null && (json["url"] as String).isNotEmpty) ? json["url"] : null, + ); + } +} + +class Hours { + int id; + String? meal; + Duration? startTime; + Duration? endTime; + DaysOfWeek daysOfWeek; + int dayOrder; + + Hours({ + required this.id, + this.meal, + this.startTime, + this.endTime, + required this.daysOfWeek, + required this.dayOrder, + }); + + static Duration _getDurationFromString(String s) { + var timeComponents = s.split(":"); + var hours = int.parse(timeComponents[0]); + var minutes = int.parse(timeComponents[1]); + var seconds = int.parse(timeComponents[2]); + + return Duration(hours: hours, minutes: minutes, seconds: seconds); + } + + factory Hours.fromJson(Map json) { + return Hours( + id: int.parse(json["id"]), + meal: json["meal"], + startTime: (json["start"] != null) ? _getDurationFromString(json["start"]) : null, + endTime: (json["end"] != null) ? _getDurationFromString(json["end"]) : null, + daysOfWeek: DaysOfWeek.parse(json["dayOfWeek"]), + dayOrder: int.parse(json["dayOrder"]), + ); + } +} + +class DaysOfWeek { + final Weekday startDay; + final Weekday endDay; + final Map days; + + DaysOfWeek({ + required this.startDay, + required this.endDay, + required this.days, + }); + + static Weekday _getDayFromShortName(String shortName) { + switch(shortName.toLowerCase()) { + case "mon": + return Weekday.monday; + case "tue": + return Weekday.tuesday; + case "wed": + return Weekday.wednesday; + case "thu": + return Weekday.thursday; + case "fri": + return Weekday.friday; + case "sat": + return Weekday.saturday; + case "sun": + return Weekday.sunday; + default: + throw "Invalid date short name."; + } + } + + factory DaysOfWeek.parse(String s) { + final Map days = { for (var e in Weekday.values) e : false }; + + // single day + if (!s.contains("-")) { + var day = _getDayFromShortName(s); + days.update(day, (val) => true); + return DaysOfWeek(startDay: day, endDay: day, days: days); + } + + // date range + var dayNames = s.split("-"); + final startDay = _getDayFromShortName(dayNames[0]); + final endDay = _getDayFromShortName(dayNames[1]); + int i = startDay.index; + + // loop through to add all dates in date range to list + while(i != endDay.index) { + days.update(Weekday.values[i], (val) => true); + + if (i < Weekday.values.length - 1) { + i++; + } else { + i = 0; + } + } + days.update(endDay, (val) => true); + + return DaysOfWeek( + startDay: startDay, + endDay: endDay, + days: days, + ); + } +} + +enum Weekday { + monday, + tuesday, + wednesday, + thursday, + friday, + saturday, + sunday, +} diff --git a/lib/src/services/restaurants/restaurant_test.dart b/lib/src/services/restaurants/restaurant_test.dart new file mode 100644 index 0000000..25fb71e --- /dev/null +++ b/lib/src/services/restaurants/restaurant_test.dart @@ -0,0 +1,29 @@ +import 'dart:convert'; + +import "restaurant_service.dart"; + +void main() async { + var restaurants = await RestaurantService.fetchRestaurants(); + for (var restaurant in restaurants) { + print(restaurant.name + " " + (restaurant.isOpen ? "is open" : "is closed")); + } + // print("Start day: ${restaurants.firstWhere((restaurant) => restaurant.id == 22).hoursList[0].daysOfWeek.startDay}"); + // print("End day: ${restaurants.firstWhere((restaurant) => restaurant.id == 22).hoursList[0].daysOfWeek.endDay}"); + // print("Days of week: ${restaurants.firstWhere((restaurant) => restaurant.id == 22).hoursList[0].daysOfWeek.days}"); + + // var testRestaurant = Restaurant.fromJson(jsonDecode(''' + // { + // "id":"10", + // "name":null, + // "fullname":"Papa John's Pizza", + // "location":"Off Campus: 1507 Poinsett Hwy", + // "latitude":"34.887977","longitude":"-82.405793", + // "frequency":"10", + // "busyness":"0", + // "url":"https:\/\/www.papajohns.com\/order\/stores-near-me" + // } + // ''')); + + // var testDaysOfWeek = DaysOfWeek.parse("Sat-Mon"); + // print(testDaysOfWeek.days); +} diff --git a/lib/src/services/transportation/bus_503.dart b/lib/src/services/transportation/bus_503.dart new file mode 100644 index 0000000..10ac2d9 --- /dev/null +++ b/lib/src/services/transportation/bus_503.dart @@ -0,0 +1,3 @@ +class TransportationBus503Service { + +} \ No newline at end of file diff --git a/lib/src/services/transportation/saferide_shuttle.dart b/lib/src/services/transportation/saferide_shuttle.dart new file mode 100644 index 0000000..01688c1 --- /dev/null +++ b/lib/src/services/transportation/saferide_shuttle.dart @@ -0,0 +1,3 @@ +class TransportationSafeRideShuttleService { + +} \ No newline at end of file diff --git a/lib/src/services/transportation/transportation_service.dart b/lib/src/services/transportation/transportation_service.dart new file mode 100644 index 0000000..df92cdd --- /dev/null +++ b/lib/src/services/transportation/transportation_service.dart @@ -0,0 +1,3 @@ +class TransportationService { + +} \ No newline at end of file diff --git a/lib/src/utils/date_range.dart b/lib/src/utils/date_range.dart new file mode 100644 index 0000000..be3ea29 --- /dev/null +++ b/lib/src/utils/date_range.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +DateTimeRange constructDateRange(DateTime startDateTime, DateTime endDateTime) { + var startDate = DateTime( + startDateTime.year, + startDateTime.month, + startDateTime.day, + ); + var endDate = DateTime( + endDateTime.year, + endDateTime.month, + endDateTime.day, + 23, + 59, + 59, + ); + + return DateTimeRange(start: startDate, end: endDate); +} \ No newline at end of file diff --git a/lib/src/utils/greeting.dart b/lib/src/utils/greeting.dart new file mode 100644 index 0000000..cbdb6e8 --- /dev/null +++ b/lib/src/utils/greeting.dart @@ -0,0 +1,12 @@ +String greeting() { + var currentHour = DateTime.now().hour; + if (currentHour > 4 && currentHour < 11) { + return "Good morning"; + } else if (currentHour >= 11 && currentHour < 17) { + return "Good afternoon"; + } else if (currentHour >= 17 && currentHour < 21) { + return "Good evening"; + } else { + return "Good night"; + } +} \ No newline at end of file diff --git a/lib/src/utils/theme.dart b/lib/src/utils/theme.dart new file mode 100644 index 0000000..c0b8189 --- /dev/null +++ b/lib/src/utils/theme.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +ThemeData _baseTheme = ThemeData( + primarySwatch: Colors.deepPurple, + bottomNavigationBarTheme: BottomNavigationBarThemeData( + backgroundColor: Colors.grey[100], + unselectedItemColor: Colors.grey[500], + ), + textTheme: TextTheme( + subtitle2: TextStyle( + color: Colors.grey[500], + fontWeight: FontWeight.w500, + fontSize: 14, + ), + ), +); + +ThemeData myFurmanTheme = _baseTheme.copyWith( + textTheme: GoogleFonts.interTextTheme(_baseTheme.textTheme), +); + +var furmanTextStyle = (TextStyle baseStyle) => GoogleFonts.inter(textStyle: baseStyle); diff --git a/lib/src/widgets/header.dart b/lib/src/widgets/header.dart new file mode 100644 index 0000000..93229df --- /dev/null +++ b/lib/src/widgets/header.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/utils/theme.dart'; + +class HeaderWidget extends StatelessWidget { + const HeaderWidget({ + required this.title, + this.link, + Key? key + }) : super(key: key); + + final String title; + final HeaderLink? link; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.only(left: 40, right: 40, top: 20, bottom: 15), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + title.toUpperCase(), + style: furmanTextStyle(TextStyle( + color: Colors.grey[800], + fontWeight: FontWeight.w900, + fontSize: 18, + )), + ), + if (link != null) + Text( + link!.text, + style: furmanTextStyle(const TextStyle( + color: Color(0xff755898), + fontSize: 12, + fontWeight: FontWeight.bold, + )), + ), + ], + ), + ); + } +} + +@immutable +class HeaderLink { + final String text; + final String href; + + const HeaderLink({ + required this.text, + required this.href, + }); +} diff --git a/lib/src/widgets/home/events/event_card.dart b/lib/src/widgets/home/events/event_card.dart new file mode 100644 index 0000000..4e57950 --- /dev/null +++ b/lib/src/widgets/home/events/event_card.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/services/events/event.dart'; +import 'package:furman_now/src/utils/theme.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:intl/intl.dart'; + +class EventCard extends StatelessWidget { + const EventCard + ( + this.event, + { + Key? key + } + ) : super(key: key); + + final Event event; + + String get eventHour { + var formatter = DateFormat('hh:mm'); + return formatter.format(event.time); + } + + String get eventAmPm { + var formatter = DateFormat('a'); + return formatter.format(event.time); + } + + @override + Widget build(BuildContext context) { + return Container( + decoration: const BoxDecoration( + color: Color(0xfff9f9fb), + borderRadius: BorderRadius.all(Radius.circular(10)) + ), + child: IntrinsicHeight( + child: Row( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 5), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(eventHour, style: furmanTextStyle(const TextStyle(fontWeight: FontWeight.w700))), + Text(eventAmPm, style: Theme.of(context).textTheme.subtitle2), + ], + ), + ), + VerticalDivider( + width: 2, + thickness: 2, + color: Colors.grey[200], + ), + Flexible( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(event.title, style: furmanTextStyle(const TextStyle(fontWeight: FontWeight.w600))), + const SizedBox(height: 6), + RichText(text: TextSpan( + style: Theme.of(context).textTheme.subtitle2, + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Padding( + padding: const EdgeInsets.only(right: 5.0), + child: Icon(Icons.place_outlined, size: 20, color: Colors.grey[500]) + ), + ), + TextSpan(text: event.location), + ], + )), + const SizedBox(height: 2), + RichText(text: TextSpan( + style: Theme.of(context).textTheme.subtitle2, + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Padding( + padding: const EdgeInsets.only(left: 1.0, right: 6.0), + child: Icon(Icons.sell_outlined, size: 18, color: Colors.grey[500]) + ), + ), + TextSpan(text: event.category), + ], + )), + ], + ), + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/home/events/events_list.dart b/lib/src/widgets/home/events/events_list.dart new file mode 100644 index 0000000..6c40697 --- /dev/null +++ b/lib/src/widgets/home/events/events_list.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/services/events/event.dart'; +import 'package:furman_now/src/services/events/events_service.dart'; +import 'package:furman_now/src/utils/theme.dart'; + +import 'event_card.dart'; + +class EventsList extends StatefulWidget { + final DateTimeRange dateRange; + + const EventsList._({ + required this.dateRange, + Key? key, + }) : super(key: key); + + factory EventsList({ + DateTimeRange? dateRange, + Key? key, + }) { + if (dateRange == null) { + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final tonight = DateTime(now.year, now.month, now.day, 23, 59, 59); + dateRange = DateTimeRange(start: today, end: tonight); + } + return EventsList._( + dateRange: dateRange, + key: key, + ); + } + + @override + State createState() => _EventsListState(); +} + +class _EventsListState extends State { + late Future> futureEventList; + + @override + void initState() { + super.initState(); + futureEventList = EventsService.fetchEvents(); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder>( + future: futureEventList, + builder: (context, snapshot) { + if (snapshot.hasData) { + var events = snapshot.data!.where((event) { + return event.time.isAfter(widget.dateRange.start) && event.time.isBefore(widget.dateRange.end); + }); + if (events.isNotEmpty) { + return Column( + children: events.map((event) { + return Padding( + padding: const EdgeInsets.only(bottom: 15), + child: EventCard(event), + ); + }).toList()); + } else { + return Padding( + padding: const EdgeInsets.only(bottom: 15), + child: SizedBox( + width: double.infinity, + height: 50, + child: Center( + child: Text( + "No events today :(", + style: furmanTextStyle(TextStyle( + color: Colors.grey[600], + fontSize: 16, + fontWeight: FontWeight.w600, + )), + ), + ), + ), + ); + } + } else if (snapshot.hasError) { + return Text('${snapshot.error}', style: const TextStyle(color: Colors.red),); + } + + // By default, show a loading spinner. + return const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 25), + child: CircularProgressIndicator() + ), + ); + }, + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/home/restaurants/restaurant_card.dart b/lib/src/widgets/home/restaurants/restaurant_card.dart new file mode 100644 index 0000000..bdc509f --- /dev/null +++ b/lib/src/widgets/home/restaurants/restaurant_card.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; +import 'package:palette_generator/palette_generator.dart'; +import 'package:furman_now/src/services/restaurants/restaurant_service.dart'; +import 'package:furman_now/src/utils/theme.dart'; + +class RestaurantCard extends StatefulWidget { + const RestaurantCard({ + required this.restaurant, + Key? key, + }) : super(key: key); + + final Restaurant restaurant; + + @override + State createState() => _RestaurantCardState(); +} + +class _RestaurantCardState extends State { + PaletteGenerator? paletteGenerator; + Color? backgroundColor; + + @override + void initState() { + super.initState(); + _updatePaletteGenerator(); + } + + Future _updatePaletteGenerator() async { + paletteGenerator = await PaletteGenerator.fromImageProvider( + widget.restaurant.image, + maximumColorCount: 1, + ); + if (paletteGenerator != null && paletteGenerator!.colors.toList().isNotEmpty) { + var hslColor = HSLColor.fromColor(paletteGenerator!.colors.toList()[0]); + var newColor = HSLColor.fromAHSL(1, hslColor.hue, 40/100, 90/100); + backgroundColor = newColor.toColor(); + } + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return Container( + width: 150, + height: 175, + decoration: BoxDecoration( + // color: Color(0xfff9f9fb), + color: backgroundColor ?? const Color(0xfff9f9fb), + borderRadius: const BorderRadius.all(Radius.circular(10)), + ), + child: Padding( + padding: const EdgeInsets.all(10), + child: Column( + children: [ + Align( + alignment: Alignment.topLeft, + child: RichText( + text: TextSpan( + style: furmanTextStyle(TextStyle( + color: Colors.grey[800], + fontWeight: FontWeight.bold, + fontSize: 12, + )), + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Padding( + padding: const EdgeInsets.only(right: 4), + child: Container( + width: 9, + height: 9, + decoration: BoxDecoration( + color: widget.restaurant.isOpen ? Colors.green : Colors.red.shade700, + shape: BoxShape.circle, + ), + ), + ), + ), + TextSpan(text: widget.restaurant.isOpen ? "Open" : "Closed"), + ], + ), + ), + ), + const SizedBox(height: 10), + ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(5)), + child: Image.network(widget.restaurant.imageUri, height: 90), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + widget.restaurant.name, + textAlign: TextAlign.center, + style: furmanTextStyle(TextStyle( + color: Colors.grey[800], + fontSize: 14, + fontWeight: FontWeight.w600, + )), + ), + ], + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/home/restaurants/restaurants_list.dart b/lib/src/widgets/home/restaurants/restaurants_list.dart new file mode 100644 index 0000000..fd71173 --- /dev/null +++ b/lib/src/widgets/home/restaurants/restaurants_list.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/services/restaurants/restaurant_service.dart'; + +import 'restaurant_card.dart'; + +class RestaurantsList extends StatefulWidget { + const RestaurantsList({Key? key}) : super(key: key); + + @override + State createState() => _RestaurantsListState(); +} + +class _RestaurantsListState extends State { + late Future> futureEventList; + + @override + void initState() { + super.initState(); + futureEventList = RestaurantService.fetchRestaurants(); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder>( + future: futureEventList, + builder: (context, snapshot) { + if (snapshot.hasData) { + var restaurants = snapshot.data!..sort((r1, r2) => (r1.isOpen ? 0 : 1) - (r2.isOpen ? 0 : 1)); + return SizedBox( + height: 175, + child: ListView.builder( + padding: const EdgeInsets.only(left: 20), + itemCount: restaurants.length, + cacheExtent: 10000, + scrollDirection: Axis.horizontal, + prototypeItem: Padding( + padding: const EdgeInsets.only(right: 15), + child: RestaurantCard(restaurant: restaurants.first), + ), + itemBuilder: (context, index) => Padding( + padding: const EdgeInsets.only(right: 15), + child: RestaurantCard(restaurant: restaurants[index]), + ), + ), + ); + } else if (snapshot.hasError) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Text('${snapshot.error}', style: const TextStyle(color: Colors.red)) + ); + } + + // By default, show a loading spinner. + return const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 25), + child: CircularProgressIndicator() + ), + ); + }, + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/home/transportation/transportation_card.dart b/lib/src/widgets/home/transportation/transportation_card.dart new file mode 100644 index 0000000..996f23e --- /dev/null +++ b/lib/src/widgets/home/transportation/transportation_card.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/utils/theme.dart'; + +class TransportationCard extends StatelessWidget { + const TransportationCard({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: const BoxDecoration( + color: Color(0xfff9f9fb), + borderRadius: BorderRadius.all(Radius.circular(10)) + ), + child: IntrinsicHeight( + child: Row( + children: [ + SizedBox( + width: 75, + child: Icon(Icons.directions_bus, size: 50, color: Colors.grey[800]), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RichText( + text: TextSpan( + style: furmanTextStyle(TextStyle( + color: Colors.grey[800], + fontWeight: FontWeight.bold, + fontSize: 12, + )), + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Padding( + padding: const EdgeInsets.only(right: 4), + child: Container( + width: 9, + height: 9, + decoration: const BoxDecoration( + color: Colors.green, + shape: BoxShape.circle, + ), + ), + ), + ), + const TextSpan(text: "Running"), + ], + ), + ), + const SizedBox(height: 2), + Text( + "503 Bus", + style: furmanTextStyle(TextStyle( + fontWeight: FontWeight.bold, + color: Colors.grey[800], + fontSize: 18, + )), + ), + Text( + "Next stop: Pointsett Hwy & Crestwood Dr", + style: furmanTextStyle(TextStyle( + fontWeight: FontWeight.w500, + color: Colors.grey[500], + fontSize: 12, + )), + ), + ], + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/info/info_card.dart b/lib/src/widgets/info/info_card.dart new file mode 100644 index 0000000..f8cb714 --- /dev/null +++ b/lib/src/widgets/info/info_card.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +class InfoCard extends StatelessWidget { + final Color color; + final IconData icon; + final String title; + final String description; + + const InfoCard({ + required this.color, + required this.icon, + required this.title, + required this.description, + Key? key + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(10), + ), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 15, right: 5), + child: Icon(icon, size: 40,) + ), + Flexible( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: Theme.of(context).textTheme.titleLarge,), + Text(description), + ] + ), + ), + ) + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/map/filter_chip.dart b/lib/src/widgets/map/filter_chip.dart new file mode 100644 index 0000000..9cbb62f --- /dev/null +++ b/lib/src/widgets/map/filter_chip.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:furman_now/src/utils/theme.dart'; + +class MapFilterChip extends StatelessWidget { + final IconData icon; + final String text; + + const MapFilterChip({ + required this.icon, + required this.text, + Key? key + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.only( + top: 6, bottom: 6, left: 8, right: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(100), + boxShadow: const [ + BoxShadow( + color: Color(0x33000000), + blurRadius: 4, + ), + ], + ), + child: RichText( + text: TextSpan( + children: [ + WidgetSpan( + child: Icon(icon, + size: 18, + color: Colors.grey.shade800), + alignment: PlaceholderAlignment.middle, + ), + const WidgetSpan(child: SizedBox(width: 6)), + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Text( + text, + style: furmanTextStyle(TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: Colors.grey.shade800, + )), + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/map/rotate_compass.dart b/lib/src/widgets/map/rotate_compass.dart new file mode 100644 index 0000000..1e4e8be --- /dev/null +++ b/lib/src/widgets/map/rotate_compass.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class MapRotateCompass extends StatelessWidget { + final double rotation; + final void Function()? resetRotation; + + @override + const MapRotateCompass({ + required this.rotation, + required this.resetRotation, + Key? key + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AnimatedOpacity( + opacity: rotation != 0.0 ? 1 : 0, + duration: const Duration(milliseconds: 200), + child: Align( + alignment: Alignment.topRight, + child: Padding( + padding: const EdgeInsets.only(top: 4, right: 12), + child: FloatingActionButton( + backgroundColor: Colors.white, + mini: true, + onPressed: resetRotation, + child: Transform.rotate( + angle: rotation, + child: SvgPicture.asset("assets/images/compass.svg", height: 28), + ), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/widgets/scroll_view_height.dart b/lib/src/widgets/scroll_view_height.dart new file mode 100644 index 0000000..6619ee7 --- /dev/null +++ b/lib/src/widgets/scroll_view_height.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class ScrollViewWithHeight extends StatelessWidget { + final Widget child; + + const ScrollViewWithHeight({ + required this.child, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: constraints.copyWith(minHeight: constraints.maxHeight, maxHeight: double.infinity), + child: child, + ), + ); + }); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..f6aab83 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,672 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "46.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.2" + auto_route: + dependency: "direct main" + description: + name: auto_route + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.1" + barcode: + dependency: transitive + description: + name: barcode + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.3" + barcode_widget: + dependency: "direct main" + description: + name: barcode_widget + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.16.0" + convert: + dependency: "direct main" + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + coverage: + dependency: transitive + description: + name: coverage + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2" + crypto: + dependency: "direct main" + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" + english_words: + dependency: "direct main" + description: + name: english_words + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + flutter_map: + dependency: "direct main" + description: + name: flutter_map + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.4" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + http: + dependency: "direct main" + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.13.5" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + intl: + dependency: "direct main" + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.0" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.4" + latlong2: + dependency: "direct main" + description: + name: latlong2 + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.1" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + lists: + dependency: transitive + description: + name: lists + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.4" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + mgrs_dart: + dependency: transitive + description: + name: mgrs_dart + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + navbar_router: + dependency: "direct main" + description: + name: navbar_router + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2" + node_preamble: + dependency: transitive + description: + name: node_preamble + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + palette_generator: + dependency: "direct main" + description: + name: palette_generator + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.3+2" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + path_drawing: + dependency: transitive + description: + name: path_drawing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.19" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + polylabel: + dependency: transitive + description: + name: polylabel + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.1" + positioned_tap_detector_2: + dependency: transitive + description: + name: positioned_tap_detector_2 + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" + proj4dart: + dependency: transitive + description: + name: proj4dart + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + qr: + dependency: transitive + description: + name: qr + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + shelf_static: + dependency: transitive + description: + name: shelf_static + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + source_maps: + dependency: transitive + description: + name: source_maps + url: "https://pub.dartlang.org" + source: hosted + version: "0.10.10" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test: + dependency: "direct main" + description: + name: test + url: "https://pub.dartlang.org" + source: hosted + version: "1.21.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.9" + test_core: + dependency: transitive + description: + name: test_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.13" + tuple: + dependency: transitive + description: + name: tuple + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + unicode: + dependency: transitive + description: + name: unicode + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.1" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" + vm_service: + dependency: transitive + description: + name: vm_service + url: "https://pub.dartlang.org" + source: hosted + version: "8.3.0" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.7.0" + wkt_parser: + dependency: transitive + description: + name: wkt_parser + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+1" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" +sdks: + dart: ">=2.17.6 <3.0.0" + flutter: ">=3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..ee7aef3 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,103 @@ +name: furman_now +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.17.6 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + english_words: ^4.0.0 + navbar_router: ^0.3.2 + intl: ^0.17.0 + google_fonts: ^3.0.1 + http: ^0.13.5 + convert: ^3.0.2 + crypto: ^3.0.2 + test: ^1.21.1 + barcode_widget: ^2.0.3 + flutter_map: ^2.2.0 + latlong2: ^0.8.1 + palette_generator: ^0.3.3+2 + flutter_svg: ^1.1.4 + auto_route: ^5.0.1 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/images/ + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/test/service_tests/get_app/barcode_test.dart b/test/service_tests/get_app/barcode_test.dart new file mode 100644 index 0000000..030f962 --- /dev/null +++ b/test/service_tests/get_app/barcode_test.dart @@ -0,0 +1,7 @@ +import "package:test/test.dart"; + +void main() { + test("Ensure generated barcode is correct", () { + + }); +} \ No newline at end of file diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..65dc18b --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:furman_now/src/app.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const App()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..e60a5b7 --- /dev/null +++ b/web/index.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + furman_now + + + + + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..8344ce0 --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "furman_now", + "short_name": "furman_now", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +}