diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5d32d2a --- /dev/null +++ b/.gitignore @@ -0,0 +1,71 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# Visual Studio Code related +.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.packages +.pub-cache/ +.pub/ +/build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/app/google-services.json +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..033ad2a --- /dev/null +++ b/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b + channel: stable + +project_type: app diff --git a/README.md b/README.md new file mode 100644 index 0000000..3390c2f --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# sandtrackdriver + diff --git a/_.txt b/_.txt new file mode 100644 index 0000000..f87b655 --- /dev/null +++ b/_.txt @@ -0,0 +1,14 @@ +user + number + balance + currency + pin + notifications + date + nature + amount + number + + trransactions + token + diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..12e0d68 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,63 @@ +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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 28 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.sandtrackdriver" + minSdkVersion 18 + targetSdkVersion 28 + multiDexEnabled true + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + 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 { + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' +} +apply plugin: 'com.google.gms.google-services' diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..c6721db --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3aba9d9 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/java/com/example/sandtrackdriver/MainActivity.java b/android/app/src/main/java/com/example/sandtrackdriver/MainActivity.java new file mode 100644 index 0000000..d786298 --- /dev/null +++ b/android/app/src/main/java/com/example/sandtrackdriver/MainActivity.java @@ -0,0 +1,13 @@ +package com.example.sandtrackdriver; + +import android.os.Bundle; +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class MainActivity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + } +} diff --git a/android/app/src/main/res/drawable/icon.png b/android/app/src/main/res/drawable/icon.png new file mode 100644 index 0000000..e55b4f9 Binary files /dev/null and b/android/app/src/main/res/drawable/icon.png differ 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-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png new file mode 100644 index 0000000..d26c0f3 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/launcher_icon.png b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png new file mode 100644 index 0000000..72fbfda Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png new file mode 100644 index 0000000..33278d9 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png new file mode 100644 index 0000000..106872a Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png new file mode 100644 index 0000000..084265a Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png differ diff --git a/android/app/src/main/res/values/developer-config.xml b/android/app/src/main/res/values/developer-config.xml new file mode 100644 index 0000000..734acc6 --- /dev/null +++ b/android/app/src/main/res/values/developer-config.xml @@ -0,0 +1,4 @@ + + + sk.eyJ1IjoibWljaGFlbHRlbmRvIiwiYSI6ImNqeXp1aGg1ejA1N2kzZ3FuengyejVhbTcifQ.wJCJgrPhlraiJZgUz3wvMw + \ 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..00fa441 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..c6721db --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..6ca85f9 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,30 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.google.gms:google-services:4.2.0' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +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..8ce44f6 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableJetifier=true +android.useAndroidX=true + diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2819f02 --- /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-4.10.2-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..5a2f14f --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/assets/images/img_not_available.jpeg b/assets/images/img_not_available.jpeg new file mode 100644 index 0000000..b1384e8 Binary files /dev/null and b/assets/images/img_not_available.jpeg differ diff --git a/assets/images/mimi1.gif b/assets/images/mimi1.gif new file mode 100644 index 0000000..01468a1 Binary files /dev/null and b/assets/images/mimi1.gif differ diff --git a/assets/images/mimi2.gif b/assets/images/mimi2.gif new file mode 100644 index 0000000..8dad0d2 Binary files /dev/null and b/assets/images/mimi2.gif differ diff --git a/assets/images/mimi3.gif b/assets/images/mimi3.gif new file mode 100644 index 0000000..01468a1 Binary files /dev/null and b/assets/images/mimi3.gif differ diff --git a/assets/images/mimi4.gif b/assets/images/mimi4.gif new file mode 100644 index 0000000..cebc61b Binary files /dev/null and b/assets/images/mimi4.gif differ diff --git a/assets/images/mimi5.gif b/assets/images/mimi5.gif new file mode 100644 index 0000000..6103163 Binary files /dev/null and b/assets/images/mimi5.gif differ diff --git a/assets/images/mimi6.gif b/assets/images/mimi6.gif new file mode 100644 index 0000000..3471ba8 Binary files /dev/null and b/assets/images/mimi6.gif differ diff --git a/assets/images/mimi7.gif b/assets/images/mimi7.gif new file mode 100644 index 0000000..789c02c Binary files /dev/null and b/assets/images/mimi7.gif differ diff --git a/assets/images/mimi8.gif b/assets/images/mimi8.gif new file mode 100644 index 0000000..0579b79 Binary files /dev/null and b/assets/images/mimi8.gif differ diff --git a/assets/images/mimi9.gif b/assets/images/mimi9.gif new file mode 100644 index 0000000..7761f9c Binary files /dev/null and b/assets/images/mimi9.gif differ diff --git a/assets/launcher/icon.png b/assets/launcher/icon.png new file mode 100644 index 0000000..c9b8599 Binary files /dev/null and b/assets/launcher/icon.png differ diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..9367d48 --- /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 + 8.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5fb0086 --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,506 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 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 = ( + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, + ); + 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 = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; 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 = ""; }; + 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B80C3931E831B6300D905FE /* App.framework */, + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEBA1CF902C7004384FC /* Flutter.framework */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + 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 = 0910; + ORGANIZATIONNAME = "The Chromium Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + 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 */, + 9740EEB41CF90195004384FC /* Debug.xcconfig 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\" thin"; + }; + 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"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m 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; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + 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_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_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 = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = 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; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = S8QB4VV633; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.sandtrackdriver; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + 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_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_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 = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + 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_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_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 = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.sandtrackdriver; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.sandtrackdriver; + PRODUCT_NAME = "$(TARGET_NAME)"; + 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..1d526a1 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..786d6aa --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner/AppDelegate.h b/ios/Runner/AppDelegate.h new file mode 100644 index 0000000..36e21bb --- /dev/null +++ b/ios/Runner/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/ios/Runner/AppDelegate.m b/ios/Runner/AppDelegate.m new file mode 100644 index 0000000..59a72e9 --- /dev/null +++ b/ios/Runner/AppDelegate.m @@ -0,0 +1,13 @@ +#include "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end 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..3d43d11 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..29b6956 --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + sandtrackdriver + 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 + + + diff --git a/ios/Runner/main.m b/ios/Runner/main.m new file mode 100644 index 0000000..dff6597 --- /dev/null +++ b/ios/Runner/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/lib/about.dart b/lib/about.dart new file mode 100644 index 0000000..bcd9fde --- /dev/null +++ b/lib/about.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class About extends StatefulWidget { + @override + _AboutState createState() => new _AboutState(); +} + +class _AboutState extends State { + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text('About Trackmart Driver'), + actions: [ + IconButton( + icon: Icon( + Icons.language, + color: Colors.white, + ), + onPressed: () async { + if (await canLaunch('http://sandtrackdriverapp.com')) { + await launch('http://sandtrackdriverapp.com'); + } else { + throw 'Could not launch http://www.sandtrackdriverapp.com'; + } + }, + ) + ], + ), + body: new Center( + child: Column(children: [ + Container(height: 60), + Icon(Icons.local_shipping,size:80,color: Theme.of(context).primaryColor,), + Text('Property of:'), + Text('Trackmart Technologies'), + Hyperlink( + 'http://www.sandtrackdriverapp.com', 'sandtrackdriverapp.com'), + Text('Nairobi, Kenya'), + Container(height: 20), + Hyperlink( + 'http://www.sandtrackdriverapp.com/legal', 'Legal documentation'), + Container(height: 20), + Text('Designed and developed by:'), + Text('Michael Tendo Ssemwanga'), + Hyperlink('http://www.tendo.dev', 'tendo.dev'), + ]), + ), + ); + } +} + +class Hyperlink extends StatelessWidget { + final String _url; + final String _text; + + Hyperlink(this._url, this._text); + + _launchURL() async { + if (await canLaunch(_url)) { + await launch(_url); + } else { + throw 'Could not launch $_url'; + } + } + + @override + Widget build(BuildContext context) { + return InkWell( + child: Text( + _text, + style: TextStyle( + decoration: TextDecoration.underline, + color: Theme.of(context).accentColor, + ), + ), + onTap: _launchURL, + ); + } +} diff --git a/lib/chat.dart b/lib/chat.dart new file mode 100644 index 0000000..5db63ce --- /dev/null +++ b/lib/chat.dart @@ -0,0 +1,704 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_storage/firebase_storage.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:intl/intl.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'const.dart'; + +class Chat extends StatelessWidget { + //Firestore store; + final String peerId; + final String peerName; + final String peerAvatar; + + Chat( + {Key key, + @required this.peerId, + this.peerName, + @required this.peerAvatar, + }) + : super(key: key); + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text( + peerName, + //style: TextStyle(color: primaryColor, fontWeight: FontWeight.bold), + ), + centerTitle: true, + ), + body: new ChatScreen( + //store: store??Firestore.instance, + peerId: peerId, + peerName: peerName, + peerAvatar: peerAvatar, + ), + ); + } +} + +class ChatScreen extends StatefulWidget { + final String peerId; + final String peerName; + final String peerAvatar; + + ChatScreen( + {Key key, + this.peerName, + @required this.peerId, + @required this.peerAvatar, + }) + : super(key: key); + + @override + State createState() => new ChatScreenState( + peerId: peerId, + peerName: peerName, + peerAvatar: peerAvatar, + ); +} + +class ChatScreenState extends State { + ChatScreenState( + {Key key, + @required this.peerId, + @required this.peerName, + @required this.peerAvatar, + }); + + //final Firestore firestore; + String peerId; + String peerName; + String peerAvatar; + String id; + + var listMessage; + String groupChatId; + SharedPreferences prefs; + + File imageFile; + bool isLoading; + bool isShowSticker; + String imageUrl; + + final TextEditingController textEditingController = + new TextEditingController(); + final ScrollController listScrollController = new ScrollController(); + final FocusNode focusNode = new FocusNode(); + + @override + void initState() { + super.initState(); + focusNode.addListener(onFocusChange); + + groupChatId = ''; + + isLoading = false; + isShowSticker = false; + imageUrl = ''; + + readLocal(); + } + + void onFocusChange() { + if (focusNode.hasFocus) { + // Hide sticker when keyboard appear + setState(() { + isShowSticker = false; + }); + } + } + + readLocal() async { + prefs = await SharedPreferences.getInstance(); + id = prefs.getString('id') ?? ''; + if (id.hashCode <= peerId.hashCode) { + groupChatId = '$id-$peerId'; + } else { + groupChatId = '$peerId-$id'; + } + + Firestore.instance + .collection('drivers') + .document(id) + .updateData({'chattingWith': peerId}); + + setState(() {}); + } + + Future getImage() async { + imageFile = await ImagePicker.pickImage(source: ImageSource.gallery); + + if (imageFile != null) { + setState(() { + isLoading = true; + }); + uploadFile(); + } + } + + void getSticker() { + // Hide keyboard when sticker appear + focusNode.unfocus(); + setState(() { + isShowSticker = !isShowSticker; + }); + } + + Future uploadFile() async { + String fileName = DateTime.now().millisecondsSinceEpoch.toString(); + StorageReference reference = FirebaseStorage.instance.ref().child(fileName); + StorageUploadTask uploadTask = reference.putFile(imageFile); + StorageTaskSnapshot storageTaskSnapshot = await uploadTask.onComplete; + storageTaskSnapshot.ref.getDownloadURL().then((downloadUrl) { + imageUrl = downloadUrl; + setState(() { + isLoading = false; + onSendMessage(imageUrl, 1); + }); + }, onError: (err) { + setState(() { + isLoading = false; + }); + Fluttertoast.showToast(msg: 'This file is not an image'); + }); + } + + void onSendMessage(String content, int type) { + // type: 0 = text, 1 = image, 2 = sticker + if (content.trim() != '') { + textEditingController.clear(); + + var documentReference = Firestore.instance + .collection('messages') + .document(groupChatId) + .collection(groupChatId) + .document(DateTime.now().millisecondsSinceEpoch.toString()); + + Firestore.instance.runTransaction((transaction) async { + await transaction.set( + documentReference, + { + 'idFrom': id, + 'idTo': peerId, + 'timestamp': DateTime.now().millisecondsSinceEpoch.toString(), + 'content': content, + 'type': type + }, + ); + }); + listScrollController.animateTo(0.0, + duration: Duration(milliseconds: 300), curve: Curves.easeOut); + } else { + Fluttertoast.showToast(msg: 'Nothing to send'); + } + } + + Widget buildItem(int index, DocumentSnapshot document) { + if (document['idFrom'] == id) { + return Row( + children: [ + document['type'] == 0 + // Text + ? Container( + child: Text( + document['content'], + style: TextStyle(color: primaryColor), + ), + padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 10.0), + width: 200.0, + decoration: BoxDecoration( + color: greyColor2, + borderRadius: BorderRadius.circular(8.0)), + margin: EdgeInsets.only( + bottom: isLastMessageRight(index) ? 20.0 : 10.0, + right: 10.0), + ) + : document['type'] == 1 + // Image + ? Container( + child: Material( + child: CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation(themeColor), + ), + width: 200.0, + height: 200.0, + padding: EdgeInsets.all(70.0), + decoration: BoxDecoration( + color: greyColor2, + borderRadius: BorderRadius.all( + Radius.circular(8.0), + ), + ), + ), + errorWidget: (context, url, error) => Material( + child: Image.asset( + 'assets/images/img_not_available.jpeg', + width: 200.0, + height: 200.0, + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.all( + Radius.circular(8.0), + ), + clipBehavior: Clip.hardEdge, + ), + imageUrl: document['content'], + width: 200.0, + height: 200.0, + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.all(Radius.circular(8.0)), + clipBehavior: Clip.hardEdge, + ), + margin: EdgeInsets.only( + bottom: isLastMessageRight(index) ? 20.0 : 10.0, + right: 10.0), + ) + // Sticker + : Container( + child: new Image.asset( + 'assets/images/${document['content']}.gif', + width: 100.0, + height: 100.0, + fit: BoxFit.cover, + ), + margin: EdgeInsets.only( + bottom: isLastMessageRight(index) ? 20.0 : 10.0, + right: 10.0), + ), + ], + mainAxisAlignment: MainAxisAlignment.end, + ); + } else { + // Left (peer message) + return Container( + child: Column( + children: [ + Row( + children: [ + isLastMessageLeft(index) + ? peerAvatar == null + ? CircleAvatar( + child: Text(peerName[0]), + radius: 17.5, + ) + : Material( + child: CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + strokeWidth: 1.0, + valueColor: + AlwaysStoppedAnimation(themeColor), + ), + width: 35.0, + height: 35.0, + padding: EdgeInsets.all(10.0), + ), + imageUrl: peerAvatar, + width: 35.0, + height: 35.0, + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.all( + Radius.circular(18.0), + ), + clipBehavior: Clip.hardEdge, + ) + : Container(width: 35.0), + document['type'] == 0 + ? Container( + child: Text( + document['content'], + style: TextStyle(color: Colors.white), + ), + padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 10.0), + width: 200.0, + decoration: BoxDecoration( + color: primaryColor, + borderRadius: BorderRadius.circular(8.0)), + margin: EdgeInsets.only(left: 10.0), + ) + : document['type'] == 1 + ? Container( + child: Material( + child: CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + themeColor), + ), + width: 200.0, + height: 200.0, + padding: EdgeInsets.all(70.0), + decoration: BoxDecoration( + color: greyColor2, + borderRadius: BorderRadius.all( + Radius.circular(8.0), + ), + ), + ), + errorWidget: (context, url, error) => Material( + child: Image.asset( + 'assets/images/img_not_available.jpeg', + width: 200.0, + height: 200.0, + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.all( + Radius.circular(8.0), + ), + clipBehavior: Clip.hardEdge, + ), + imageUrl: document['content'], + width: 200.0, + height: 200.0, + fit: BoxFit.cover, + ), + borderRadius: + BorderRadius.all(Radius.circular(8.0)), + clipBehavior: Clip.hardEdge, + ), + margin: EdgeInsets.only(left: 10.0), + ) + : Container( + child: new Image.asset( + 'assets/images/${document['content']}.gif', + width: 100.0, + height: 100.0, + fit: BoxFit.cover, + ), + margin: EdgeInsets.only( + bottom: isLastMessageRight(index) ? 20.0 : 10.0, + right: 10.0), + ), + ], + ), + + // Time + isLastMessageLeft(index) + ? Container( + child: Text( + DateFormat('dd MMM kk:mm').format( + DateTime.fromMillisecondsSinceEpoch( + int.parse(document['timestamp']))), + style: TextStyle( + color: greyColor, + fontSize: 12.0, + fontStyle: FontStyle.italic), + ), + margin: EdgeInsets.only(left: 50.0, top: 5.0, bottom: 5.0), + ) + : Container() + ], + crossAxisAlignment: CrossAxisAlignment.start, + ), + margin: EdgeInsets.only(bottom: 10.0), + ); + } + } + + bool isLastMessageLeft(int index) { + if ((index > 0 && + listMessage != null && + listMessage[index - 1]['idFrom'] == id) || + index == 0) { + return true; + } else { + return false; + } + } + + bool isLastMessageRight(int index) { + if ((index > 0 && + listMessage != null && + listMessage[index - 1]['idFrom'] != id) || + index == 0) { + return true; + } else { + return false; + } + } + + Future onBackPress() { + if (isShowSticker) { + setState(() { + isShowSticker = false; + }); + } else { + Firestore.instance + .collection('drivers') + .document(id) + .updateData({'chattingWith': null}); + Navigator.pop(context); + } + + return Future.value(false); + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + child: Stack( + children: [ + Column( + children: [ + // List of messages + buildListMessage(), + + // Sticker + (isShowSticker ? buildSticker() : Container()), + + // Input content + buildInput(), + ], + ), + + // Loading + buildLoading() + ], + ), + onWillPop: onBackPress, + ); + } + + Widget buildSticker() { + return Container( + child: Column( + children: [ + Row( + children: [ + FlatButton( + onPressed: () => onSendMessage('mimi1', 2), + child: new Image.asset( + 'assets/images/mimi1.gif', + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton( + onPressed: () => onSendMessage('mimi2', 2), + child: new Image.asset( + 'assets/images/mimi2.gif', + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton( + onPressed: () => onSendMessage('mimi3', 2), + child: new Image.asset( + 'assets/images/mimi3.gif', + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ) + ], + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + ), + Row( + children: [ + FlatButton( + onPressed: () => onSendMessage('mimi4', 2), + child: new Image.asset( + 'assets/images/mimi4.gif', + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton( + onPressed: () => onSendMessage('mimi5', 2), + child: new Image.asset( + 'assets/images/mimi5.gif', + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton( + onPressed: () => onSendMessage('mimi6', 2), + child: new Image.asset( + 'assets/images/mimi6.gif', + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ) + ], + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + ), + Row( + children: [ + FlatButton( + onPressed: () => onSendMessage('mimi7', 2), + child: new Image.asset( + 'assets/images/mimi7.gif', + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton( + onPressed: () => onSendMessage('mimi8', 2), + child: new Image.asset( + 'assets/images/mimi8.gif', + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ), + FlatButton( + onPressed: () => onSendMessage('mimi9', 2), + child: new Image.asset( + 'assets/images/mimi9.gif', + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ), + ) + ], + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + ) + ], + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + ), + decoration: new BoxDecoration( + border: + new Border(top: new BorderSide(color: greyColor2, width: 0.5)), + color: Colors.white), + padding: EdgeInsets.all(5.0), + height: 180.0, + ); + } + + Widget buildLoading() { + return Positioned( + child: isLoading + ? Container( + child: Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(themeColor)), + ), + color: Colors.white.withOpacity(0.8), + ) + : Container(), + ); + } + + Widget buildInput() { + return Container( + child: Row( + children: [ + // Button send image + Material( + child: new Container( + margin: new EdgeInsets.symmetric(horizontal: 1.0), + child: new IconButton( + icon: new Icon(Icons.image), + onPressed: getImage, + color: primaryColor, + ), + ), + color: Colors.white, + ), + Material( + child: new Container( + margin: new EdgeInsets.symmetric(horizontal: 1.0), + child: new IconButton( + icon: new Icon(Icons.face), + onPressed: getSticker, + color: primaryColor, + ), + ), + color: Colors.white, + ), + + // Edit text + Flexible( + child: Container( + child: TextField( + style: TextStyle(color: primaryColor, fontSize: 15.0), + controller: textEditingController, + decoration: InputDecoration.collapsed( + hintText: 'Type your message...', + hintStyle: TextStyle(color: greyColor), + ), + focusNode: focusNode, + ), + ), + ), + + // Button send message + Material( + child: new Container( + margin: new EdgeInsets.symmetric(horizontal: 8.0), + child: new IconButton( + icon: new Icon(Icons.send), + onPressed: () => onSendMessage(textEditingController.text, 0), + color: primaryColor, + ), + ), + color: Colors.white, + ), + ], + ), + width: double.infinity, + height: 50.0, + decoration: new BoxDecoration( + border: + new Border(top: new BorderSide(color: greyColor2, width: 0.5)), + color: Colors.white), + ); + } + + Widget buildListMessage() { + return Flexible( + child: groupChatId == '' + ? Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(themeColor))) + : StreamBuilder( + stream: Firestore.instance + .collection('messages') + .document(groupChatId) + .collection(groupChatId) + .orderBy('timestamp', descending: true) + .limit(20) + .snapshots(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: CircularProgressIndicator( + valueColor: + AlwaysStoppedAnimation(themeColor))); + } else { + listMessage = snapshot.data.documents; + return ListView.builder( + padding: EdgeInsets.all(10.0), + itemBuilder: (context, index) => + buildItem(index, snapshot.data.documents[index]), + itemCount: snapshot.data.documents.length, + reverse: true, + controller: listScrollController, + ); + } + }, + ), + ); + } +} diff --git a/lib/const.dart b/lib/const.dart new file mode 100644 index 0000000..e70532c --- /dev/null +++ b/lib/const.dart @@ -0,0 +1,6 @@ +import 'dart:ui'; + +final themeColor = Color(0xfff5a623); +final primaryColor = Color(0xff203152); +final greyColor = Color(0xffaeaeae); +final greyColor2 = Color(0xffE8E8E8); diff --git a/lib/contact.dart b/lib/contact.dart new file mode 100644 index 0000000..8b607a7 --- /dev/null +++ b/lib/contact.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class ContactPage extends StatefulWidget { + @override + _ContactPageState createState() => new _ContactPageState(); +} + +class _ContactPageState extends State { + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text('Contact Us'), + actions: [ + IconButton( + icon: Icon( + Icons.phone, + color: Colors.white, + ), + onPressed: () async { + if (await canLaunch('tel:+256700216707')) { + await launch('tel:+256700216707'); + } else { + print('Could not launch tel:+256700216707'); + } + }, + ) + ], + ), + body: new Container( + margin: const EdgeInsets.symmetric(horizontal: 16.0), + child: new Center( + child: Column(children: [ + Container(height: 60), + Icon(Icons.local_shipping,size:80,color: Theme.of(context).primaryColor,), + Text( + 'We suggest that you check out the support page in case of any issues.'), + Text( + 'In case you were not able to solve your issue with the information in the support section,'), + Text('We are here to help, any time of day'), + Text('Call:'), + Hyperlink('tel:+256700216707', '+256700216707'), + Container(height: 20), + Text('Email:'), + Hyperlink('mailto:support@sandtrackdriverapp.com', + 'support@sandtrackdriverapp.com'), + Container(height: 20), + Text('Whatsapp:'), + //Text('Michael Tendo Ssemwanga'), + Hyperlink('http:wa.me/+256700216707', '+256700216707'), + ]), + ), + ), + ); + } +} + +class Hyperlink extends StatelessWidget { + final String _url; + final String _text; + + Hyperlink(this._url, this._text); + + _launchURL() async { + if (await canLaunch(_url)) { + await launch(_url); + } else { + throw 'Could not launch $_url'; + } + } + + @override + Widget build(BuildContext context) { + return InkWell( + child: Text( + _text, + style: TextStyle( + decoration: TextDecoration.underline, + color: Theme.of(context).accentColor, + ), + ), + onTap: _launchURL, + ); + } +} diff --git a/lib/credit_card_bloc.dart b/lib/credit_card_bloc.dart new file mode 100644 index 0000000..40c1141 --- /dev/null +++ b/lib/credit_card_bloc.dart @@ -0,0 +1,64 @@ +import 'dart:async'; + +class CreditCardBloc { + final ccInputController = StreamController(); + final expInputController = StreamController(); + final cvvInputController = StreamController(); + final nameInputController = StreamController(); + + Sink get ccInputSink => ccInputController.sink; + + Sink get expInputSink => expInputController.sink; + + Sink get cvvInputSink => cvvInputController.sink; + + Sink get nameInputSink => nameInputController.sink; + + final ccOutputController = StreamController(); + final expOutputController = StreamController(); + final cvvOutputController = StreamController(); + final nameOutputController = StreamController(); + + Stream get ccOutputStream => ccOutputController.stream; + + Stream get expOutputStream => expOutputController.stream; + + Stream get cvvOutputStream => cvvOutputController.stream; + + Stream get nameOutputStream => nameOutputController.stream; + + CreditCardBloc() { + ccInputController.stream.listen(onCCInput); + expInputController.stream.listen(onExpInput); + cvvInputController.stream.listen(onCvvInput); + nameInputController.stream.listen(onNameInput); + } + + onCCInput(String input) { + ccOutputController.add(input.toString()); + } + + onExpInput(String input) { + expOutputController.add(input); + } + + onCvvInput(String input) { + cvvOutputController.add(input); + } + + onNameInput(String input) { + nameOutputController.add(input); + } + + void ccFormat(String s) { + print(s); + ccInputSink.add(s); + } + + void dispose() { + ccInputController?.close(); + cvvInputController?.close(); + expInputController?.close(); + nameInputController?.close(); + } +} diff --git a/lib/credit_card_page.dart b/lib/credit_card_page.dart new file mode 100644 index 0000000..dda1186 --- /dev/null +++ b/lib/credit_card_page.dart @@ -0,0 +1,243 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_masked_text/flutter_masked_text.dart'; + +import 'credit_card_bloc.dart'; +import 'profile_tile.dart'; + +class CreditCardPage extends StatefulWidget { + final ValueChanged result; + + CreditCardPage({this.result}); + + @override + _CreditCardPageState createState() => new _CreditCardPageState(); +} + +class _CreditCardPageState extends State { + BuildContext _context; + CreditCardBloc cardBloc; + MaskedTextController ccMask = + MaskedTextController(mask: "0000 0000 0000 0000"); + MaskedTextController expMask = MaskedTextController(mask: "00/00"); + TextEditingController cvvControl = new TextEditingController(); + FocusNode _focus2 = new FocusNode(); + bool _isCVV = false; + + void initState() { + super.initState(); + _focus2.addListener(() { + if (_focus2.hasFocus) + setState(() { + _isCVV = true; + }); + else + setState(() { + _isCVV = false; + }); + }); + } + + @override + Widget build(BuildContext context) { + _context = context; + cardBloc = CreditCardBloc(); + + return new Scaffold( + appBar: new AppBar( + leading: IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () { + Navigator.pop(context); + }), + title: new Text('Credit Card'), + actions: [ + IconButton( + icon: Icon( + Icons.help, + color: Colors.white, + ), + onPressed: () {}, + ) + ], + ), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + creditCardWidget(), + fillEntries(), + Container(height: 40) + ], + ), + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () { + widget.result('success'); + Navigator.pop(context); + }, + //backgroundColor: Colors.transparent, + icon: Icon( + Icons.payment, + color: Colors.white, + ), + label: Text( + "Continue", + //style: TextStyle(color: Colors.white), + ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + ); + } + + Widget cardEntries() => Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + StreamBuilder( + stream: cardBloc.ccOutputStream, + initialData: "**** **** **** ****", + builder: (context, snapshot) { + snapshot.data.length > 0 + ? ccMask.updateText(snapshot.data) + : null; + return !_isCVV + ? Text( + snapshot.data.length > 0 + ? snapshot.data + : "**** **** **** ****", + style: TextStyle(color: Colors.white, fontSize: 22.0), + ) + : Container(width: 0, height: 0); + }), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + StreamBuilder( + stream: cardBloc.expOutputStream, + initialData: "MM/YY", + builder: (context, snapshot) { + snapshot.data.length > 0 + ? expMask.updateText(snapshot.data) + : null; + return !_isCVV + ? ProfileTile( + textColor: Colors.white, + title: "Expiry", + subtitle: snapshot.data.length > 0 + ? snapshot.data + : "MM/YY", + ) + : Container(width: 0, height: 0); + }), + SizedBox( + width: 200.0, + ), + StreamBuilder( + stream: cardBloc.cvvOutputStream, + initialData: "***", + builder: (context, snapshot) => _isCVV + ? ProfileTile( + textColor: Colors.white, + title: "CVV", + subtitle: snapshot.data.length > 0 + ? snapshot.data + : "***", + ) + : Container(width: 0, height: 0)), + ], + ), + ], + ), + ); + + Widget fillEntries() => Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: ccMask, + keyboardType: TextInputType.number, + maxLength: 19, + onChanged: (out) => cardBloc.ccInputSink.add(ccMask.text), + decoration: InputDecoration( + labelText: "Card Number", border: OutlineInputBorder()), + ), + TextField( + controller: expMask, + keyboardType: TextInputType.number, + maxLength: 5, + onChanged: (out) => cardBloc.expInputSink.add(expMask.text), + decoration: InputDecoration( + labelStyle: TextStyle(), + labelText: "MM/YY", + border: OutlineInputBorder()), + ), + TextField( + controller: cvvControl, + keyboardType: TextInputType.number, + maxLength: 3, + focusNode: _focus2, + onChanged: (out) => cardBloc.cvvInputSink.add(out), + decoration: InputDecoration( + labelStyle: TextStyle(fontWeight: FontWeight.bold), + labelText: "CVV (on the back of the card)", + border: OutlineInputBorder()), + ), + ], + ), + ); + + Widget creditCardWidget() { + var deviceSize = MediaQuery.of(_context).size; + return Container( + height: deviceSize.height * 0.3, + color: Colors.grey.shade300, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Card( + elevation: 3.0, + child: Stack( + fit: StackFit.expand, + children: [ + Container( + decoration: BoxDecoration( + gradient: LinearGradient(colors: [ + // new Color.fromRGBO(103, 218, 255, 1.0), + // new Color.fromRGBO(3, 169, 244, 1.0), + // new Color.fromRGBO(0, 122, 193, 1.0), + Colors.blueGrey.shade800, + Colors.blue, + ])), + ), + Opacity( + opacity: 0.1, + child: Image.asset( + "assets/images/map.png", + fit: BoxFit.cover, + ), + ), + _isCVV + ? Positioned( + top: 20, + child: SizedBox( + height: 50, + width: MediaQuery.of(_context).size.width, + child: Row(children: [ + Expanded(child: Container(color: Colors.black)) + ]))) + : Container(width: 0, height: 0), + MediaQuery.of(_context).orientation == Orientation.portrait + ? cardEntries() + : FittedBox( + child: cardEntries(), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/home_page.dart b/lib/home_page.dart new file mode 100644 index 0000000..a17a54e --- /dev/null +++ b/lib/home_page.dart @@ -0,0 +1,1836 @@ +import 'dart:async'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:dio/dio.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:intl/intl.dart'; +import 'package:path/path.dart' as path; +import 'package:share/share.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:sqflite/sqflite.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import 'about.dart'; +import 'chat.dart'; +import 'contact.dart'; +import 'settings.dart'; +import 'support.dart'; +import 'map.dart'; + +class HomePage extends StatelessWidget { + final VoidCallback onSignedout; + final String currentUserId; + + HomePage({this.onSignedout, this.currentUserId}); + + @override + Widget build(BuildContext context) { + return new MaterialApp( + theme: new ThemeData( + primaryColor: const Color(0xff004d40), + primaryColorDark: const Color(0xff005B9A), + accentColor: const Color(0xff005B9A), + ), + title: "Trackmart Driver", + home: new TabbedGuy( + onSignedout: this.onSignedout, + currentUserId: this.currentUserId, + ), + ); + } +} + +class TabbedGuy extends StatefulWidget { + const TabbedGuy({this.onSignedout, this.currentUserId}); + + final VoidCallback onSignedout; + final String currentUserId; + + @override + _TabbedGuyState createState() => + new _TabbedGuyState(currentUserId: this.currentUserId); +} + +class _TabbedGuyState extends State + with SingleTickerProviderStateMixin { + bool isLoading = false; + double quantity = 1; + DatabaseReference databaseReference; + FirebaseDatabase database; + Firestore firestore; + + // final formKey = new GlobalKey(); + // final key = new GlobalKey(); + final TextEditingController _filter = new TextEditingController(); + FocusNode myFocusNode; + String _searchText = ""; + Icon _searchIcon = new Icon(Icons.search); + Widget _appBarTitle = new Text('Trackmart Driver'); + List _history = []; + List _filteredHistory = []; + SharedPreferences prefs; + final String currentUserId; + bool _status = false; + Geolocator geolocator = Geolocator(); + StreamSubscription positionStreamSubscription; + String currentUserName; + String currentUserPhoto; + static double currentLat; + static double currentLong; + + //TODO: Don't draw anything before all loading is done, all thens have completed + bool _stillLoading = true; + + _TabbedGuyState({this.currentUserId}); + + static TabController _tabController; + + @override + void initState() { + super.initState(); + _startup().then((value) { + if (mounted) + setState(() { + _stillLoading = !value; + }); + }).catchError((e) { + print(e.toString()); + //sho(e.toString(), context); + if (mounted) + setState(() { + _stillLoading = false; + }); + }); + } + + _updateLocation(Position position) { + if (mounted) + setState(() { + currentLat = position.latitude; + currentLong = position.longitude; + }); + databaseReference + .child('Drivers') + .child(currentUserId) + .child('transit') + .once() + .then((d) { + Map map = d.value?.cast(); + map?.forEach((key, values) { + databaseReference + .child('buyers') + .child(values['userId']) + .child('transit') + .child(key) + //.child('dlat') + //.set( + //position.latitude, + //'dlong':position.longitude + //) + .update({'dlat':position.latitude,'dlong':position.longitude}) + .then((v) { + /*databaseReference + .child('buyers') + .child(values['userId']) + .child('transit') + .child(key) + .child('dlong') + .set( + position.longitude, + //'dlong':position.longitude + ) + .then((v) { + //print('Updated in transit $key'); + });*/ + //print('Updated in transit $key'); + }); + }); + }); + databaseReference + .child('Drivers') + .child(currentUserId) + .update({ + 'lat': position.latitude, + 'long': position.longitude, + }) + .then((v) {}) + .catchError((e) { + print(e.toString()); + }); + } + + Future _startup() async { + database = FirebaseDatabase.instance; + database.setPersistenceEnabled(true); + database.setPersistenceCacheSizeBytes(10000000); + databaseReference = database.reference(); + firestore = Firestore.instance; + prefs = await SharedPreferences.getInstance(); + currentUserName = prefs.getString('displayName'); + currentUserPhoto = prefs.getString('photoUrl'); + await getWorkStatus(); + + _filter.addListener(() { + if (_filter.text.isEmpty) { + if (mounted) + setState(() { + _searchText = ""; + _filteredHistory = _history; + }); + } else { + if (mounted) + setState(() { + _searchText = _filter.text; + }); + } + }); + _getHistory(); + _tabController = TabController(vsync: this, length: 3, initialIndex: 1); + myFocusNode = FocusNode(); + return true; + } + + @override + void dispose() { + myFocusNode.dispose(); + _tabController.dispose(); + positionStreamSubscription?.cancel(); + super.dispose(); + } + + getWorkStatus() async { + bool status = (await databaseReference + .child('drivers') + .child(currentUserId) + .child('status') + .once()) + .value; + setState(() { + _status = status ?? false; + }); + registerLocation(_status); + return _status; + } + + registerLocation(bool value) async { + if (value) { + //geolocator = Geolocator(); + await geolocator + .getCurrentPosition(desiredAccuracy: LocationAccuracy.best) + .then((value) { + Position position = value; + print('Location'); + print(position == null + ? 'Unknown' + : position.latitude.toString() + + ', ' + + position.longitude.toString()); + _updateLocation(position); + //var geolocator = Geolocator(); + var locationOptions = LocationOptions( + accuracy: LocationAccuracy.high, distanceFilter: 10); + positionStreamSubscription = geolocator + .getPositionStream(locationOptions) + .listen((Position position) { + print(position == null + ? 'Unknown' + : position.latitude.toString() + + ', ' + + position.longitude.toString()); + _updateLocation(position); + }); + }); + } else + positionStreamSubscription?.cancel(); + } + + updateWork(bool value) async { + await databaseReference + .child('drivers') + .child(currentUserId) + .child('status') + .set(value); + registerLocation(value); + //TODO:also firestore + } + + Widget build(BuildContext context) { + return _stillLoading + ? SafeArea( + child: Scaffold( + body: Container( + color: Colors.white, + child: Center( + child: + Column(mainAxisSize: MainAxisSize.min, children: [ + Icon( + Icons.local_shipping, + size: 80, + color: Theme.of(context).primaryColor, + ), + Container( + margin: EdgeInsets.only(bottom: 8), + width: 200, + child: LinearProgressIndicator(), + ), + Center( + child: Text('Trackmart Driver', + style: TextStyle( + fontSize: 20, color: Theme.of(context).accentColor)), + ), + Padding( + padding: EdgeInsets.all(8), + child: Center( + child: Text('Drive • Deliver • Earn', + style: TextStyle( + fontSize: 17, color: Theme.of(context).accentColor)), + ), + ), + ])), + ))) + : Scaffold( + drawer: Drawer( + child: ListView( + // Important: Remove any padding from the ListView. + padding: EdgeInsets.zero, + children: [ + DrawerHeader( + child: InkWell( + onTap: () { + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => new SettingsPage( + firestore: firestore, + ))); + }, + child: Center( + child: Column(children: [ + Material( + child: currentUserPhoto != null + ? CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).accentColor), + ), + width: 80.0, + height: 80.0, + //padding: EdgeInsets.all(15.0), + ), + imageUrl: currentUserPhoto, + width: 80.0, + height: 80.0, + fit: BoxFit.cover, + ) + : Icon( + Icons.account_circle, + size: 80.0, + ), + borderRadius: BorderRadius.all(Radius.circular(40.0)), + clipBehavior: Clip.hardEdge, + ), + Padding( + padding: EdgeInsets.all(16), + child: Text(currentUserName ?? '', + style: TextStyle( + fontSize: 17, + color: Theme.of(context).accentColor)), + ), + ])), + ), + decoration: BoxDecoration(), + ), + SwitchListTile( + title: Text( + _status ? 'Available' : 'Not available', + ), + secondary: + Icon(_status ? Icons.work : Icons.not_interested), + value: _status, //TODO: fix this + onChanged: (value) { + updateWork(value).then((v) { + setState(() { + _status = value; + }); + }); + }), + ListTile( + title: Text('Help'), + trailing: new Icon( + Icons.help, + color: Theme.of(context).accentColor, + ), + onTap: () { + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => new SupportPage())); + }, + ), + ListTile( + title: Text('Contact'), + trailing: new Icon( + Icons.phone, + color: Theme.of(context).accentColor, + ), + onTap: () { + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => new ContactPage())); + // Update the state of the app. + // ... + }, + ), + ListTile( + title: Text('About Trackmart Driver'), + trailing: new Icon( + Icons.local_shipping, + color: Theme.of(context).accentColor, + ), + onTap: () { + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => new About())); + }, + ), + ListTile( + title: Text('Invite'), + trailing: new Icon( + Icons.share, + color: Theme.of(context).accentColor, + ), + onTap: () { + Share.share( + 'You can make money and get more customers deliverying Sand to buyers using Trackmart Driver Driver app. Download it at https://play.google.com/sandtrackdriverapp?uid=${currentUserId}'); + print( + 'You can make money and get more customers deliverying Sand to buyers using Trackmart Driver Driver app. Download it at https://play.google.com/sandtrackdriverapp?uid=${currentUserId}'); + }, + ), + ListTile( + title: Text('Log out'), + trailing: new Icon( + Icons.exit_to_app, + color: Theme.of(context).accentColor, + ), + onTap: () { + widget.onSignedout(); + }, + ), + ], + ), + ), + appBar: _buildBar(context), + body: new InkWell( + onTapDown: (t) { + FocusScope.of(context).requestFocus(new FocusNode()); + }, + child: TabBarView( + controller: _tabController, + children: [ + new Column( + //modified + children: [ + //new + new Flexible( + child: _buildContacts(), //new + ), + //new + ], //new + ), + Container( + margin: const EdgeInsets.symmetric(horizontal: 8.0), + child: _buildHome()), + new Column( + //modified + children: [ + //new + new Flexible( + //new + child: _buildHistory(), //new + ), //new + ], //new + ), + ], + ), + ), + ); + } + + Widget _buildBar(BuildContext context) { + return new AppBar( + actions: [ + IconButton( + icon: _searchIcon, + onPressed: _searchPressed, + ), + IconButton( + icon: Icon(Icons.map), + onPressed: () { + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => new MapPage2( + driverId: widget.currentUserId, + dlat: _TabbedGuyState.currentLat, + dlong: _TabbedGuyState.currentLong, + selectOrder: confirmOrder, + ))); + }, + ), + ], + bottom: TabBar( + controller: _tabController, + tabs: [ + Tab(text: ('Chats')), + Tab(text: ('Delivery')), + Tab(text: ('Earnings')), + ], + ), + title: _appBarTitle, + ); + } + + confirmOrder(String orderKey) {} + static showModal(text, sent_context) { + showDialog( + context: sent_context, + barrierDismissible: false, + builder: (BuildContext context) { + return new AlertDialog( + title: Text(text), + content: LinearProgressIndicator(), + contentPadding: EdgeInsets.all(10.0), + ); + }); + } + + Widget _buildContacts() { + return Stack( + children: [ + // List + Container( + child: StreamBuilder( + stream: firestore.collection('buyers').snapshots(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).accentColor), + ), + ); + } else { + return ListView.separated( + separatorBuilder: (context, index) => Divider(), + itemBuilder: (context, index) => + buildItem(context, snapshot.data.documents[index]), + itemCount: snapshot.data.documents.length, + ); + } + }, + ), + ), + Positioned( + child: isLoading + ? Container( + child: Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).accentColor)), + ), + color: Colors.white.withOpacity(0.8), + ) + : Container(), + ) + ], + ); + } + + Widget buildItem(BuildContext context, DocumentSnapshot document) { + return document['displayName'] + .toLowerCase() + .contains(_searchText.toLowerCase()) + ? Container( + child: FlatButton( + child: Row( + children: [ + Material( + child: document['photoUrl'] != null + ? CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).accentColor), + ), + width: 50.0, + height: 50.0, + //padding: EdgeInsets.all(15.0), + ), + imageUrl: document['photoUrl'], + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ) + : Icon( + Icons.account_circle, + size: 50.0, + //color: greyColor, + ), + borderRadius: BorderRadius.all(Radius.circular(25.0)), + clipBehavior: Clip.hardEdge, + ), + Flexible( + child: Container( + child: Column( + children: [ + Container( + child: Text( + '${document['displayName']}', + style: TextStyle( + color: Theme.of(context).primaryColor), + ), + alignment: Alignment.centerLeft, + margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 5.0), + ), + ], + ), + margin: EdgeInsets.only(left: 5.0), + ), + ), + ], + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Chat( + peerName: document['displayName'], + //store: firestore, + peerId: document.documentID, + peerAvatar: document['photoUrl'], + ))); + }, + padding: EdgeInsets.fromLTRB(5.0, 5.0, 5.0, 5.0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0)), + ), + margin: EdgeInsets.only(left: 5.0, right: 5.0), + ) + : Container( + width: 0, + height: 0, + ); + } + + _buildInTransit() { + return StreamBuilder( + stream: databaseReference + .child('Drivers') + .child(currentUserId) + .child('transit') + .onValue, + //TODO: on value changes, chill streambuilder, listen and setstate on names. + builder: (context, snap) { + if (snap.hasData && + !snap.hasError && + snap.data.snapshot.value != null) { +//taking the data snapshot. + //DataSnapshot snapshot = snap.data.snapshot; + List items = []; +//it gives all the documents in this list. + //List> _list=; +//Now we're just checking if document is not null then add it to another list called "item". +//I faced this problem it works fine without null check until you remove a document and then your stream reads data including the removed one with a null value(if you have some better approach let me know). + Map map = + snap.data.snapshot.value.cast(); + map.forEach((key, values) { + if (values != null) { + items.add(Order( + getHistory: _getHistory, + key: key, + type: Order.TRANSIT, + userId: values['userId'], + userName: values['userName'], + driverId: values['driverId'], + driverName: values['driverName'], + driverPhone: values['driverPhone'], + quantity: values['quantity'].toDouble(), + payment: values['payment'], + price: values['price'].toDouble(), + unit: values['unit'], + timestamp: values['timestamp'], + destlat: values['destlat'], + destlong: values['destlong'], + ).toHistoryItem()); + } + }); + return items.isNotEmpty + ? Column(children: [ + InkWell( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('In transit', style: TextStyle(fontSize: 16)), + IconButton( + icon: Icon(transit + ? Icons.arrow_drop_up + : Icons.arrow_drop_down), + onPressed: () { + if (mounted) + setState(() { + transit = !transit; + }); + }) + ]), + onTap: () { + if (mounted) + setState(() { + transit = !transit; + }); + }), + transit + ? Column( + mainAxisSize: MainAxisSize.min, + children: items, + ) + : Container(width: 0, height: 0), + ]) + : Container(width: 0, height: 0); + } else { + return Container(width: 0, height: 0); + } + }, + ); + } + + bool requested = true; + bool transit = true; + + Widget _buildHome() { + return SingleChildScrollView( + child: Column(children: [ + _buildInTransit(), + StreamBuilder( + stream: databaseReference + .child('Drivers') + .child(currentUserId) + .child('requests') + .orderByChild('timestamp') + .onValue, + //TODO: on value changes, chill streambuilder, listen and setstate on names. + builder: (context, snap) { + if (snap.hasData && + !snap.hasError && + snap.data.snapshot.value != null) { +//taking the data snapshot. + //DataSnapshot snapshot = snap.data.snapshot; + List items = []; +//it gives all the documents in this list. + //List> _list=; +//Now we're just checking if document is not null then add it to another list called "item". +//I faced this problem it works fine without null check until you remove a document and then your stream reads data including the removed one with a null value(if you have some better approach let me know). + Map map = + snap.data.snapshot.value.cast(); + map.forEach((key, values) { + if (values != null) { + items.add(Order( + type: Order.REQUESTED, + key: key, + userId: values['userId'], + userName: values['userName'], + driverId: values['driverId'], + driverName: values['driverName'], + driverPhone: values['driverPhone'], + quantity: values['quantity'].toDouble(), + payment: values['payment'], + price: values['price'].toDouble(), + unit: values['unit'], + timestamp: values['timestamp'], + destlat: values['destlat'], + destlong: values['destlong'], + ).toHistoryItem()); + } + }); + return items.isNotEmpty + ? Column(children: [ + InkWell( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Requested', style: TextStyle(fontSize: 16)), + IconButton( + icon: Icon(requested + ? Icons.arrow_drop_up + : Icons.arrow_drop_down), + onPressed: () { + if (mounted) + setState(() { + requested = !requested; + }); + }) + ]), + onTap: () { + if (mounted) + setState(() { + requested = !requested; + }); + }), + requested + ? Column( + mainAxisSize: MainAxisSize.min, + children: items, + ) + : Container(width: 0, height: 0), + //Divider(), + ]) + : Container(width: 0, height: 0); + } else { + return Padding( + padding: EdgeInsets.all(16), + child: Text('No requested deliveries')); + } + }, + ), + ])); + } + + Widget _buildHistory() { + if (_searchText.isNotEmpty) { + List tempList = new List(); + for (int i = 0; i < _history.length; i++) { + if (_history[i] + .user + .toLowerCase() + .contains(_searchText.toLowerCase()) || + _history[i] + .date + .toLowerCase() + .contains(_searchText.toLowerCase())) { + tempList.add(_history[i]); + } + } + _filteredHistory = tempList; + } + return SingleChildScrollView( + child: Column(mainAxisSize: MainAxisSize.min, children: [ + Container( + height: 10, + ), + SingleChildScrollView( + child: Column(children: [ + _filteredHistory.length > 0 + ? Column( + mainAxisSize: MainAxisSize.min, + children: _filteredHistory, + ) + : Padding( + padding: EdgeInsets.all(8), + child: Text( + 'No delivered orders to display', + textAlign: TextAlign.center, + )) + ])) + ])); + } + + void _searchPressed() { + if (mounted) + setState(() { + if (this._searchIcon.icon == Icons.search) { + if (_tabController.index == 1) _tabController.animateTo(0); + this._searchIcon = new Icon(Icons.close); + this._appBarTitle = new TextField( + focusNode: myFocusNode, + controller: _filter, + decoration: new InputDecoration( + //prefixIcon: new Icon(Icons.search), + hintText: + 'Search ${_tabController.index == 0 ? 'chats' : _tabController.index == 2 ? 'orders' : '...'}'), + ); + } else { + this._searchIcon = new Icon(Icons.search); + this._appBarTitle = new Text('Trackmart'); + //filteredDrivers = names; + _filteredHistory = _history; + _filter.clear(); + } + }); + } + + void _getHistory() async { + Future> transactions() async { + final Future database = openDatabase( + path.join(await getDatabasesPath(), 'history.db'), + onCreate: (db, version) { + return db.execute( + "CREATE TABLE history(timestamp DATETIME DEFAULT CURRENT_TIMESTAMP PRIMARY KEY, user TEXT, amount INTEGER, userId TEXT, quantity TEXT, payment TEXT, date TEXT, unit TEXT)"); + }, + version: 1, + ); + final Database db = await database; + final List> maps = + await db.query('history', orderBy: 'timestamp DESC'); + return List.generate(maps.length, (i) { + return HistoryItem( + type: Order.DELIVERED, + userId: maps[i]['userId'], + user: maps[i]['user'], + quantity: double.parse(maps[i]['quantity']), + payment: maps[i]['payment'], + unit: maps[i]['unit'], + date: maps[i]['date'], + amount: maps[i]['amount'].toString(), + ); + }); + } + + List tempList = await transactions(); + if (mounted) + setState(() { + _history = tempList; + _filteredHistory = _history; + }); + } +} + +class HistoryItem extends StatefulWidget { + HistoryItem( + {this.getHistory, + this.type, + this.user, + this.orderKey, + this.userId, + this.userPhone, + this.quantity, + this.payment, + this.amount, + this.unit, + this.date, + this.destlat, + this.destlong, + this.driverId}); + + final int type; + final String user; + final double destlat; + final double destlong; + final String userId; + final String driverId; + final String userPhone; + final String amount; + final String payment; + final double quantity; + final String unit; + final String date; + final String orderKey; + final VoidCallback getHistory; + Map toMap() { + return { + 'userId': userId, + 'user': user, + 'quantity': quantity, + 'payment': payment, + 'date': date, + 'unit': unit, + 'amount': int.parse(amount) + }; + } + + Future insertHistory() async { + final Future database = openDatabase( + path.join(await getDatabasesPath(), 'history.db'), + onCreate: (db, version) { + return db.execute( + "CREATE TABLE history(timestamp DATETIME DEFAULT CURRENT_TIMESTAMP PRIMARY KEY, user TEXT, amount INTEGER, userId TEXT, quantity TEXT, payment TEXT, date TEXT, unit TEXT)"); + }, + version: 1, + ); + final Database db = await database; + await db.insert( + 'history', + toMap(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + getHistory(); + //_TabbedGuy. + } + + @override + State createState() { + // TODO: implement createState + return HistoryItemState(); + } +} + +class HistoryItemState extends State { + int distance; + String avatar; + HistoryItemState(); + + @override + void initState() { + // TODO: implement initState + super.initState(); + Firestore.instance + .collection('buyers') + .document(widget.userId) + .get() + .then((d) { + if (mounted) + setState(() { + avatar = d['photoUrl']; + }); + }); + if (widget.type != Order.DELIVERED) + Geolocator() + .getPositionStream(LocationOptions(accuracy: LocationAccuracy.high, distanceFilter: 10)) + .listen((Position position) { + Geolocator() + .distanceBetween(position.latitude, position.longitude, + widget.destlat, widget.destlong) + .then((value) { + if (mounted) + setState(() { + distance = value.toInt(); + }); + }); + }); + } + + info(name, avatar, distance, phone, driverId) { + showDialog( + context: context, + builder: (context) { + return Dialog( + child: SizedBox( + height: 150, + child: User( + destlong: widget.destlong, + destlat: widget.destlat, + name: name, + avatar: avatar, + distance: distance, + phone: phone, + id: widget.userId, + selected: true, + ), + ), + ); + }); + } + + @override + Widget build(BuildContext context) { + return widget.type == Order.DELIVERED + ? Column( + children: [ + Container( + margin: EdgeInsets.only(left: 8, right: 8), + child: InkWell( + onTap: () { + print('hi'); + info( + widget.user, + avatar, + '${distance != null ? ((distance) / 1000).toStringAsFixed(2) + ' km' : ''}', + widget.userPhone, + widget.userId); + }, + child: new Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + new Container( + margin: const EdgeInsets.only(right: 8), + child: InkWell( + onTap: () { + info( + widget.user, + avatar, + '${distance != null ? ((distance) / 1000).toStringAsFixed(2) + ' km' : ''}', + widget.userPhone, + widget.userId); + }, + child: Material( + child: avatar != null + ? CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + strokeWidth: 1.0, + valueColor: + AlwaysStoppedAnimation( + Theme.of(context).accentColor), + ), + width: 50.0, + height: 50.0, + //padding: EdgeInsets.all(15.0), + ), + imageUrl: avatar, + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ) + : new CircleAvatar( + child: widget.user == null + ? Icon( + Icons.account_circle, + size: 50.0, + //color: greyColor, + ) + : new Text(widget.user[0], + style: TextStyle(fontSize: 30)), + radius: 25), + borderRadius: + BorderRadius.all(Radius.circular(25.0)), + clipBehavior: Clip.hardEdge, + ), + ), + ), + Expanded( + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text(widget.user?.split(' ')[0] ?? 'Buyer', + style: Theme.of(context).textTheme.subhead), + new Container( + margin: const EdgeInsets.only(top: 5.0), + child: new Text(widget.quantity == null + ? '' + : '${widget.quantity} ${widget.unit}${widget.quantity > 1 ? 's' : ''}'), + ), + ], + ), + ), + new Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + new Container( + margin: const EdgeInsets.only(bottom: 5.0), + child: new Text(widget.amount ?? ''), + ), + new Text(widget.date ?? ''), + ], + ), + /*IconButton( + icon: Icon( + Icons.info, + color: Theme.of(context).accentColor, + ), + onPressed: null),*/ + ], + ), + ), + ), + Divider() + ], + ) + : Card( + child: Padding( + padding: EdgeInsets.only(top: 8, left: 8, right: 8), + child: Column( + children: [ + new InkWell( + onTap: () { + info( + widget.user, + avatar, + '${distance != null ? ((distance) / 1000).toStringAsFixed(2) + ' km' : ''}', + widget.userPhone, + widget.userId); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + new Container( + margin: const EdgeInsets.only(right: 8), + child: InkWell( + onTap: () { + info( + widget.user, + avatar, + '${distance != null ? ((distance) / 1000).toStringAsFixed(2) + ' km' : ''}', + widget.userPhone, + widget.userId); + }, + child: Material( + child: avatar != null + ? CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + strokeWidth: 1.0, + valueColor: AlwaysStoppedAnimation< + Color>( + Theme.of(context).accentColor), + ), + width: 50.0, + height: 50.0, + //padding: EdgeInsets.all(15.0), + ), + imageUrl: avatar, + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ) + : new CircleAvatar( + child: widget.user == null + ? Icon( + Icons.account_circle, + size: 50.0, + //color: greyColor, + ) + : new Text(widget.user[0], + style: TextStyle(fontSize: 30)), + radius: 25), + borderRadius: + BorderRadius.all(Radius.circular(25.0)), + clipBehavior: Clip.hardEdge, + ), + ), + ), + /*new Container( + margin: const EdgeInsets.only(right: 8), + child: InkWell( + onTap: () { + info( + widget.driver, + avatar, + '${distance != null ? ((distance) / 1000).toStringAsFixed(2) : '_'} km', + widget.driverPhone, + widget.driverId); + }, + child: new CircleAvatar( + child: Text(widget.driver[0]), + ), + ), + ),*/ + Expanded( + child: new Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text( + '${widget.user?.split(' ')[0] ?? ''} (${distance != null ? ((distance) / 1000).toStringAsFixed(2) + ' km' : ''})', + style: Theme.of(context).textTheme.subhead), + new Container( + margin: const EdgeInsets.only(top: 5.0), + child: new Text(widget.quantity == null + ? '' + : '${widget.quantity} ${widget.unit}${widget.quantity > 1 ? 's' : ''}'), + ), + ], + ), + ), + new Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text(widget.date), + new Container( + margin: const EdgeInsets.only(top: 5.0), + child: new Text(widget.amount ?? ''), + ), + ], + ), + //Text(date), + ], + ), + ), + new Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + /*FlatButton.icon( + label: Text('Info'), + icon: Icon( + Icons.info, + color: Theme.of(context).accentColor, + ), + onPressed: null),*/ + FlatButton.icon( + label: Text('Cancel'), + icon: Icon( + Icons.cancel, + color: Theme.of(context).accentColor, + ), + onPressed: () => widget.type == Order.REQUESTED + ? _cancelOrder(widget.orderKey, context) + : _cancelTransitOrder(widget.orderKey, + context), /*_infoOn(orderKey,driverId)*/ + ), + widget.type == Order.REQUESTED + ? FlatButton.icon( + label: Text('Confirm'), + icon: Icon( + Icons.check, + color: Theme.of(context).accentColor, + ), + onPressed: () => _confirmOrder(widget.orderKey, + context), /*_infoOn(orderKey,driverId)*/ + ) + : widget.type == Order.TRANSIT + ? FlatButton.icon( + label: Text('Drive'), + icon: Icon( + Icons.directions, + color: Theme.of(context).accentColor, + ), + onPressed: () { + print('destlat${widget.destlat}'); + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => new MapPage( + //driverId: widget.driverId, + userName: widget.user, + ulat: widget.destlat, + ulong: widget.destlong, + dlat: + _TabbedGuyState.currentLat, + dlong: _TabbedGuyState + .currentLong))); + }, /*_infoOn(orderKey,driverId)*/ + ) + /*: FlatButton.icon( + label: Text('Complete'), + icon: Icon( + Icons.check_circle_outline, + color: Theme.of(context).accentColor, + ), + onPressed: () { + _finishOrder(widget.orderKey, context); + }, */ /*_infoOn(orderKey,driverId)*/ /* + )*/ + : Container(width: 0, height: 0), + widget.type == Order.TRANSIT && (distance ?? 101) < 100 + ? FlatButton.icon( + label: Text('Complete'), + icon: Icon( + Icons.check_circle_outline, + color: Theme.of(context).accentColor, + ), + onPressed: () { + _finishOrder(widget.orderKey, context); + }, /*_infoOn(orderKey,driverId)*/ + ) + : Container(width: 0, height: 0), + ], + ), + ], + ), + ), + ); + //Divider() + /*], + );*/ + } + + _cancelTransitOrder(String orderKey, BuildContext context) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Confirm'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Cancel order\n$orderKey?'), + Text('Buyer: ${widget.user}') + ], + ), + actions: [ + FlatButton.icon( + onPressed: () => Navigator.of(context).pop(), + icon: Icon(Icons.arrow_back), + label: Text('Back'), + ), + //TODO:Replace all FlatButton with FlatButton.icon + FlatButton.icon( + onPressed: () { + _deleteTransitOrder(orderKey, context).then((v) { + //Fluttertoast.showToast(msg: 'Delete successful'); + Navigator.of(context).pop(); + }); + }, + icon: Icon(Icons.cancel), + label: Text('Cancel'), + ), + ], + ); + }, + ); + } + + _cancelOrder(String orderKey, BuildContext context) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Confirm'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Cancel order\n$orderKey?'), + Text('Driver: ${widget.user}') + ], + ), + actions: [ + FlatButton.icon( + onPressed: () => Navigator.of(context).pop(), + icon: Icon(Icons.arrow_back), + label: Text('Back'), + ), + //TODO:Replace all FlatButton with FlatButton.icon + FlatButton.icon( + onPressed: () { + _deleteOrder(orderKey, context).then((v) { + //Fluttertoast.showToast(msg: 'Delete successful'); + Navigator.of(context).pop(); + }); + }, + icon: Icon(Icons.cancel), + label: Text('Cancel'), + ), + ], + ); + }, + ); + } + + _confirmOrder(String orderKey, BuildContext context) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Confirm'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Cofirm order\n$orderKey\nand start delivery?'), + Text('Buyer: ${widget.user}') + ], + ), + actions: [ + FlatButton.icon( + onPressed: () => Navigator.of(context).pop(), + icon: Icon(Icons.arrow_back), + label: Text('Back'), + ), + //TODO:Replace all FlatButton with FlatButton.icon + FlatButton.icon( + onPressed: () { + _TabbedGuyState.showModal('Confirming...', context); + _startOrder(orderKey).then((v) { + //Fluttertoast.showToast(msg: 'Delete successful'); + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }); + }, + icon: Icon(Icons.check), + label: Text('Confirm'), + ), + ], + ); + }, + ); + } + + _startOrder(String orderKey) async { + FirebaseDatabase database = new FirebaseDatabase(); + DatabaseReference dataRef = database.reference(); + var order = (await dataRef + .child('Drivers') + .child(widget.driverId) + .child('requests') + .child(orderKey) + .once()) + .value; + await dataRef + .child('buyers') + .child(widget.userId) + .child('transit') + .child(orderKey) + .set(order); + await dataRef + .child('Drivers') + .child(widget.driverId) + .child('transit') + .child(orderKey) + .set(order); + await dataRef + .child('Drivers') + .child(widget.driverId) + .child('requests') + .child(orderKey) + .remove(); + await dataRef + .child('buyers') + .child(widget.userId) + .child('requests') + .child(orderKey) + .remove(); + } + + _finishOrder(String orderKey, BuildContext context) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Confirm'), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Complete order\n$orderKey?'), + Text('Driver: ${widget.user}'), + ], + ), + actions: [ + FlatButton.icon( + onPressed: () => Navigator.of(context).pop(), + icon: Icon(Icons.arrow_back), + label: Text('Back'), + ), + //TODO:Replace all FlatButton with FlatButton.icon + FlatButton.icon( + onPressed: () async { + _TabbedGuyState.showModal('Finishing...', context); + await FirebaseDatabase.instance + .reference() + .child('Drivers') + .child(widget.driverId) + .child('transit') + .child(orderKey) + .remove() + .then((v) { + Navigator.of(context).pop(); + widget.insertHistory().then((v) { + Navigator.of(context).pop(); + _TabbedGuyState._tabController.animateTo(2); + }); + }); + }, + icon: Icon(Icons.check_circle_outline), + label: Text('Complete'), + ), + ], + ); + }, + ); + } + + _deleteTransitOrder(String orderKey, context) async { + print('delete'); + _TabbedGuyState.showModal('Cancelling...', context); + FirebaseDatabase database = new FirebaseDatabase(); + DatabaseReference dataRef = database.reference(); + await dataRef + .child('Drivers') + .child(widget.driverId) + .child('transit') + .child(orderKey) + .remove(); + await dataRef + .child('buyers') + .child(widget.userId) + .child('transit') + .child(orderKey) + .remove(); + Navigator.of(context).pop(); + } + + _deleteOrder(String orderKey, context) async { + _TabbedGuyState.showModal('Cancelling...', context); + FirebaseDatabase database = new FirebaseDatabase(); + DatabaseReference dataRef = database.reference(); + await dataRef + .child('Drivers') + .child(widget.driverId) + .child('requests') + .child(orderKey) + .remove(); + await dataRef + .child('buyers') + .child(widget.userId) + .child('requests') + .child(orderKey) + .remove(); + Navigator.of(context).pop(); + } +} + +class Request extends StatelessWidget { + final Order order; + + Request(this.order); + + @override + Widget build(BuildContext context) {} +} + +class User extends StatefulWidget { + User( + {this.distance, + this.avatar, + this.id, + this.name, + this.destlat, + this.destlong, + this.phone, + this.selected = true}); + + final String name; + String avatar; + final String phone; + final String id; + double destlat; + double destlong; + String distance; + bool selected; + + @override + State createState() { + // TODO: implement createState + return UserState(distance: distance, avatar: avatar); + } +} + +class UserState extends State { + String distance; + String avatar; + + UserState({this.distance, this.avatar}); + + @override + void initState() { + // TODO: implement initState + super.initState(); + Firestore.instance.collection('buyers').document(widget.id).get().then((d) { + if (mounted) + setState(() { + avatar = d['photoUrl']; + }); + widget.avatar = d['photoUrl']; + }); + if(widget.destlat!=null) + Geolocator() + .distanceBetween(_TabbedGuyState.currentLat, + _TabbedGuyState.currentLong, widget.destlat, widget.destlong) + .then((value) { + if (mounted) + setState(() { + distance = (value / 1000).toStringAsFixed(2) + ' km'; + }); + }); + } + + _launchURL() async { + if (await canLaunch('tel:${widget.phone}')) { + await launch('tel:${widget.phone}'); + } else { + throw 'Could not launch tel:${widget.phone}'; + } + } + + @override + Widget build(BuildContext context) { + if (widget.selected) + return Card( + shape: new RoundedRectangleBorder( + side: new BorderSide( + color: Theme.of(context).accentColor, width: 2.0), + borderRadius: BorderRadius.circular(4.0)), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ListTile( + leading: Material( + child: avatar != null + ? CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + strokeWidth: 1.0, + valueColor: AlwaysStoppedAnimation( + Theme.of(context).accentColor), + ), + width: 50.0, + height: 50.0, + //padding: EdgeInsets.all(15.0), + ), + imageUrl: avatar, + width: 50.0, + height: 50.0, + fit: BoxFit.cover, + ) + : /* Icon( + Icons.account_circle, + size: 50.0, + //color: greyColor, + ),*/ + new CircleAvatar( + child: widget.name == null + ? Icon( + Icons.account_circle, + size: 60.0, + //color: greyColor, + ) + : new Text(widget.name[0], + style: TextStyle(fontSize: 30)), + radius: 30), + borderRadius: BorderRadius.all(Radius.circular(25.0)), + clipBehavior: Clip.hardEdge, + ), + title: Text(widget.name ?? ''), + subtitle: Text(distance ?? ''), + trailing: IconButton( + icon: Icon(Icons.close), + onPressed: () { + if (mounted) + setState(() { + Navigator.of(context).pop(); + }); + }), + selected: true, + ), + new Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + (widget.destlat!=null)? + FlatButton.icon( + label: Text('Route'), + icon: Icon( + Icons.pin_drop, + color: Theme.of(context).accentColor, + ), + onPressed: () { + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => new MapPage( + //driverId: widget.id, + userName: widget.name, + ulat: widget.destlat, + ulong: widget.destlong, + dlat: _TabbedGuyState.currentLat, + dlong: _TabbedGuyState.currentLong, + ))); + }):Container(width: 0,height: 0,), //), + widget.id!=null?FlatButton.icon( + label: Text('Text'), + icon: Icon( + Icons.textsms, + color: Theme.of(context).accentColor, + ), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Chat( + peerName: widget.name, + peerId: widget.id, + peerAvatar: avatar, + ))); + /*_infoOn(orderKey,driverId)*/ + }):Container(width: 0,height: 0,), + widget.phone!=null? + FlatButton.icon( + label: Text('Call'), + icon: Icon( + Icons.phone, + color: Theme.of(context).accentColor, + ), + onPressed: _launchURL):Container(width: 0,height: 0,), + //), + ], + ), + ], + ), + ); + } +} + +class Order { + final String userName; + final String userId; + final String driverName; + final String driverPhone; + final String userPhone; + final String userAvatar; + final String driverId; + final double price; + final String payment; + final double quantity; + final String unit; + final String key; + final VoidCallback getHistory; + var timestamp; + final double destlat; + final double destlong; + final int type; + static final int REQUESTED = 1; + static final int TRANSIT = 2; + static final int DELIVERED = 3; + + Order( + {this.getHistory, + this.destlat, + this.destlong, + this.type, + this.key, + this.userId, + this.userName, + this.driverId, + this.driverName, + this.userAvatar, + this.driverPhone, + this.userPhone, + this.quantity, + this.payment, + this.price, + this.unit, + this.timestamp}); + + HistoryItem toHistoryItem() { + return new HistoryItem( + userPhone: userPhone, + getHistory: getHistory, + destlat: destlat, + destlong: destlong, + type: type, + driverId: driverId, + userId: userId, + orderKey: key, + user: userName, + quantity: quantity, + payment: payment, + date: DateFormat('dd MMM kk:mm') + .format(DateTime.fromMillisecondsSinceEpoch((timestamp))), + amount: (price * quantity).toStringAsFixed(0), + unit: unit); + } + + Map toMap(String uid) { + return { + 'userId': userId, + 'userName': userName, + 'driverId': driverId, + 'driverName': driverName, + 'driverPhone': driverPhone, + 'userPhone': userPhone, + 'userAvatar': userAvatar, + 'quantity': quantity, + 'payment': payment, + 'price': price, + 'unit': unit, + 'timestamp': timestamp, + 'userId': uid, + }; + } +} diff --git a/lib/login_page.dart b/lib/login_page.dart new file mode 100644 index 0000000..8a5c846 --- /dev/null +++ b/lib/login_page.dart @@ -0,0 +1,632 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:country_code_picker/country_code_picker.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class LoginPage extends StatefulWidget { + LoginPage({this.onSignedIn, this.login}); + + final ValueChanged onSignedIn; + final bool login; + + @override + State createState() { + // TODO: implement createState + return new LoginPageState(login: this.login); + } +} + +enum FormType { login, register } + +class LoginPageState extends State { + final FirebaseAuth fAuth = FirebaseAuth.instance; + final formKey = new GlobalKey(); + bool _obscureText = true; + String _countryCode = '+254'; + String phoneNo; + String email; + String _pin; + String _name; + String smsCode; + String verificationId; + bool useEmail = false; + bool login; + bool _agreed = false; + + LoginPageState({this.login}); + + void _toggle() { + setState(() { + _obscureText = !_obscureText; + }); + } + + void _toggleEmail() { + setState(() { + useEmail = !useEmail; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: new AppBar( + actions: [ + IconButton( + icon: Icon( + Icons.help, + color: Colors.white, + ), + onPressed: () {}, + ), + ], + title: Text('Welcome'), + centerTitle: true, + ), + body: new GestureDetector( + onTap: () { + FocusScope.of(context).requestFocus(new FocusNode()); + }, + child: SingleChildScrollView( + child: Center( + child: new Container( + padding: EdgeInsets.all(16.0), + child: new Form( + key: formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: EdgeInsets.all(8), + child: Icon(Icons.local_shipping,size:120,color: Theme.of(context).primaryColor,), + ), + Center( + child: Text('Trackmart Driver', + style: TextStyle( + fontSize: 20, + color: Theme.of(context).accentColor)), + ), + Padding( + padding: EdgeInsets.all(8), + child: Center( + child: Text('Drive • Deliver • Earn', + style: TextStyle( + fontSize: 17, + color: Theme.of(context).accentColor)), + ), + ), + useEmail + ? new TextField( + decoration: InputDecoration( + labelText: "Enter email address"), + + //onSaved: (value) => phoneNo = value, + onChanged: (value) { + this.email = value; + }, + ) + : Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + CountryCodePicker( + onChanged: (value) { + _countryCode = value.toString(); + }, + // Initial selection and favorite can be one of code ('IT') OR dial_code('+39') + initialSelection: 'KE', + //favorite: ['+256', 'UG'], + // optional. Shows only country name and flag + showCountryOnly: false, + // optional. Shows only country name and flag when popup is closed. + //showOnlyCountryCodeWhenClosed: false, + // optional. aligns the flag and the Text left + alignLeft: false, + ), + Expanded( + child: new TextField( + keyboardType: TextInputType.phone, + decoration: InputDecoration( + labelText: "Enter phone number"), + onChanged: (value) { + this.phoneNo = value; + }, + ), + ) + ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: new TextField( + decoration: InputDecoration( + //icon: IconButton(icon: new Icon(Icons.remove_red_eye)), + labelText: + '${login ? "Enter" : "Choose a"} password', + ), + obscureText: _obscureText, + + //onSaved: (value) => phoneNo = value, + onChanged: (value) { + this._pin = value; + }, + ), + ), + new FlatButton( + onPressed: _toggle, + child: + new Text(_obscureText ? "Show" : "Hide")) + ], + ), + login + ? useEmail + ? Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: new EdgeInsets.all(8.0), + child: InkWell( + onTap: () async { + try { + await FirebaseAuth + .instance + .sendPasswordResetEmail( + email: email); + showDialog( + context: context, + builder: (BuildContext + context) { + return AlertDialog( + title: Text( + 'Password reset'), + content: Text( + 'A password reset link has been sent to $email. Check $email for further instructions on resetting your password'), + actions: [ + new FlatButton( + onPressed: () { + Navigator.of( + context) + .pop(); + }, + child: + Text('OK'), + ), + ], + ); + }, + ); + } catch (e) { + showMessageDialog( + e.message); + } + }, + child: new Text( + 'Forgot password?', + style: TextStyle( + color: Theme.of( + context) + .accentColor)))) + ]) + : Container( + width: 0, + height: 0, + ) + : Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + decoration: InputDecoration( + labelText: 'Name', + ), + onChanged: (value) { + this._name = value; + }, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Checkbox( + value: _agreed, + onChanged: (value) { + setState(() { + _agreed = value; + }); + }), + Text('I agree to Trackmart Driver '), + InkWell( + child: Text('Terms of use', + style: TextStyle( + color: Theme.of(context) + .accentColor)), + onTap: () async { + if (await canLaunch( + 'http://sandtrackdriverapp.com/legal')) { + await launch( + 'http://sandtrackdriverapp.com/legal'); + } else { + print( + 'Could not launch tel:+256700216707'); + } + }, + ) + ], + ), + ], + ), + Padding( + padding: EdgeInsets.all(8), + child: OutlineButton( + borderSide: BorderSide( + color: Theme.of(context).accentColor), + //textColor: Theme.of(context).accentColor, + onPressed: (login || (!login && _agreed)) + ? !useEmail ? verifyPhone : verifyEmail + : () { + showMessageDialog( + 'Please agree to terms and conditions'); + }, + color: Theme.of(context).accentColor, + child: Text('${login ? "Login " : " Sign up"}', + style: TextStyle( + color: Theme.of(context).accentColor)), + ), + ), + FlatButton( + onPressed: _toggleEmail, + child: new Text( + 'Use ${useEmail ? "Phone" : "Email"} instead')), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '${login ? "New to Trackmart Driver?" : "Already have an account?"}'), + InkWell( + child: Text('${login ? " Sign up" : " Log in"}', + style: TextStyle( + color: Theme.of(context).accentColor)), + onTap: () { + setState(() { + login = !login; + }); + }, + ) + ], + ), + ]), + ), + ), + ), + ))); + } + + saveUserDetails(FirebaseUser user) async { + DatabaseReference dRef = FirebaseDatabase.instance.reference(); + Firestore fRef = Firestore.instance; + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('id', user.uid); + await prefs.setString('displayName', _name); + await prefs.setString('phoneNo', phoneNo!=null?(_countryCode+phoneNo):null); + await prefs.setString('photoUrl', user.photoUrl); + login = !(fRef.collection('drivers').document(user.uid)==null); + if(login){ + print('old'); + Map map = + (await fRef.collection('drivers').document(user.uid)?.get()).data; + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('id', user.uid); + await prefs.setString('displayName', map!=null?map['displayName']:_name); + await prefs.setString('photoUrl', map!=null?map['photoUrl']:null); + await prefs.setString('phoneNo', map!=null?map['phoneNo']:(phoneNo!=null?_countryCode+phoneNo:null)); + } + else { + print('new'); + await dRef.child('drivers').child(user.uid).set({ + 'id': user.uid, + 'photoUrl': user.photoUrl, + 'displayName': _name, + 'phoneNo': (phoneNo!=null?_countryCode+phoneNo:null) + }); + await fRef.collection('drivers').document(user.uid).setData({ + 'id': user.uid, + 'displayName': _name, + 'photoUrl': user.photoUrl, + 'phoneNo': (phoneNo!=null?_countryCode+phoneNo:null) + }); + await fRef.collection('users').document(user.uid).updateData({ + 'displayName': _name, + //'photoUrl': user.photoUrl, + 'phoneNo': (phoneNo!=null?_countryCode+phoneNo:null) + }); + + } + } + + showMessageDialog(String message) { + print(message); + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Note'), + content: Text(message??'Try again'), + actions: [ + new FlatButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('OK'), + ), + ], + ); + }, + ); + } + + showErrorDialog(PlatformException e) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Error'), + content: Text(e.message), + actions: [ + new FlatButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('OK'), + ), + ], + ); + }, + ); + } + + showSimple(title, content) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return new AlertDialog( + title: Text(title), + content: SingleChildScrollView( + child: ListBody(children: [ + Container( + child: LinearProgressIndicator(), + ), + Text(content) + ]), + ), + contentPadding: EdgeInsets.all(10.0), + ); + }); + } + + Future verifyEmail() async { + if (login) { + showSimple('Log in', 'Verifying user credentials...'); + try { + FirebaseUser user = await fAuth.signInWithEmailAndPassword( + email: email, password: _pin); + if (user.isEmailVerified) { + await saveUserDetails(user); + Navigator.of(context).pop(); + widget.onSignedIn(user); + } else { + await user.sendEmailVerification(); + Navigator.of(context).pop(); + showMessageDialog( + 'Email is not verified, check $email for verification link'); + } + } catch (e) { + Navigator.of(context).pop(); + print(e.toString()); + showMessageDialog(e.message); + } + } else { + showSimple('A moment', 'Creating your account...'); + try { + FirebaseUser user = await fAuth.createUserWithEmailAndPassword( + email: email, password: this._pin); + await user.sendEmailVerification(); + Navigator.of(context).pop(); + } catch (e) { + Navigator.of(context).pop(); + print(e.toString()); + //Navigator.of(context).pop(); + showMessageDialog(e.message); + return; + } + + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return new StreamBuilder( + stream: fAuth.onAuthStateChanged, + builder: (BuildContext context, snapshot) { + if (snapshot.hasData) { + return snapshot.data.isEmailVerified + ? AlertDialog( + title: Text('Verification'), + content: Text('$email is verified'), + contentPadding: EdgeInsets.all(10.0), + actions: [ + new FlatButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('Change email'), + ), + new FlatButton( + onPressed: () { + setState(() { + login = true; + }); + Navigator.of(context).pop(); + }, + child: Text('Log in'), + ), + ], + ) //MainPage() + : AlertDialog( + title: Text('Verification'), + content: Text('Check $email for verification link.'), + contentPadding: EdgeInsets.all(10.0), + actions: [ + new FlatButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('Change email'), + ), + new FlatButton( + onPressed: () { + setState(() { + login = true; + }); + Navigator.of(context).pop(); + }, + child: Text('Log in'), + ), + ], + ); // VerifyEmailPage(user: snapshot.data); + } else { + return AlertDialog( + title: Text('Waiting'), + content: CircularProgressIndicator(), + contentPadding: EdgeInsets.all(10.0), + actions: [ + new FlatButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('Change email'), + ), + new FlatButton( + onPressed: () { + //Navigator.of(context).pop(); + }, + child: Text('OK'), + ), + ], + ); + } + }, + ); + }); + } + } + + Future verifyPhone() async { + if (login) { + showSimple('Log in', 'Verifying user credentials...'); + try { + FirebaseUser user = await fAuth.signInWithEmailAndPassword( + email: _countryCode + phoneNo + '@sandtrackdriverapp.com', + password: _pin); + await saveUserDetails(user); + Navigator.of(context).pop(); + widget.onSignedIn(user); + } catch (e) { + Navigator.of(context).pop(); + print(e.toString()); + showMessageDialog(e.message); + } + } else { + final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verId) { + print('Autoretrieval tmed out'); + this.verificationId = verId; + }; + final PhoneVerificationCompleted verifiedSuccess = + (AuthCredential credential) async { + showSimple('Success', + 'Retrieved SMS code... Saving user credentials...'); + try { + FirebaseUser user = await fAuth.createUserWithEmailAndPassword( + email: _countryCode + phoneNo + '@sandtrackdriverapp.com', + password: _pin); + await saveUserDetails(user); + Navigator.of(context).pop(); + Navigator.of(context).pop(); + widget.onSignedIn(user); + } catch (e) { + try { + FirebaseUser user = await fAuth.signInWithEmailAndPassword( + email: _countryCode + phoneNo + '@sandtrackdriverapp.com', + password: _pin); + login=true; + await saveUserDetails(user); + Navigator.of(context).pop(); + Navigator.of(context).pop(); + widget.onSignedIn(user); + } catch (e) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + showMessageDialog(e.message); + } + } + }; + final PhoneCodeSent smsCodeSent = (String verId, [int forceCodeResend]) { + this.verificationId = verId; + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return new AlertDialog( + title: Text( + 'SMS Code has been sent to ${_countryCode + this.phoneNo}'), + content: SingleChildScrollView( + child: ListBody(children: [ + TextField( + onChanged: (value) { + this.smsCode = value; + }, + decoration: InputDecoration( + labelText: 'Enter code', + )), + ]), + ), + contentPadding: EdgeInsets.all(10.0), + actions: [ + new FlatButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text('Change number'), + ), + new FlatButton( + child: Text('Done'), + onPressed: () { + final AuthCredential credential = + PhoneAuthProvider.getCredential( + verificationId: verificationId, + smsCode: smsCode, + ); + verifiedSuccess(credential); + }, + ), + ], + ); + }); + }; + + final PhoneVerificationFailed veriFailed = (AuthException exception) { + print('${exception.message}'); + showMessageDialog(exception.message); + }; + await fAuth.verifyPhoneNumber( + phoneNumber: this._countryCode + this.phoneNo, + codeAutoRetrievalTimeout: autoRetrieve, + codeSent: smsCodeSent, + timeout: const Duration(seconds: 15), + verificationCompleted: verifiedSuccess, + verificationFailed: veriFailed); + } + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..54e31e3 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,22 @@ +//import 'dart:convert'; +//import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:sandtrackdriver/root_page.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: new ThemeData( + primaryColor: const Color(0xff004d40), + primaryColorDark: const Color(0xff003e33), + accentColor: const Color(0xff005B9A), + ), + home: RootPage(), + debugShowCheckedModeBanner: false, + //theme: ThemeData(primarySwatch: Colors.blue), + ); + } +} diff --git a/lib/map.dart b/lib/map.dart new file mode 100644 index 0000000..6443d1f --- /dev/null +++ b/lib/map.dart @@ -0,0 +1,611 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:http/http.dart' as http; +import 'package:firebase_database/firebase_database.dart'; +import 'package:latlong/latlong.dart'; +import 'package:geolocator/geolocator.dart'; +import 'dart:convert'; +import 'package:intl/intl.dart'; +import 'package:flutter_polyline_points/flutter_polyline_points.dart'; + +class MapPage extends StatefulWidget { + MapPage({this.userName,this.ulat, this.ulong, this.dlat, this.dlong}); + double ulat; + double dlat; + double ulong; + double dlong; + String userName = 'Buyer'; + @override + _MapState createState() => new _MapState(dlat:dlat,dlong:dlong); +} + +class _MapState extends State { + String distance; + String duration; + double dlat; + double dlong; + List points = []; + LatLng _center; + MapController _mapController = MapController(); + Geolocator geolocator = Geolocator(); + StreamSubscription positionStreamSubscription; + var steps = []; + String summary = 'Steps'; + String instruction; + _MapState({this.dlong,this.dlat}); + @override + void initState() { +_updateLocation(Position(longitude: dlong,latitude: dlat)); + /*points = [ + new LatLng(widget.lat, widget.long), + new LatLng(widget.lat+0.001, widget.long), + new LatLng(widget.lat, widget.long+0.001), + new LatLng(widget.lat+0.0005, widget.long+0.0005), + new LatLng(widget.lat, widget.long), + ];*/ + var locationOptions = LocationOptions( + accuracy: LocationAccuracy.high, distanceFilter: 10); + positionStreamSubscription = geolocator + .getPositionStream(locationOptions) + .listen((Position position) { + _updateLocation(position); + }); + super.initState(); + } + _updateLocation(Position position) { + setState(() { + dlat = position.latitude; + dlong = position.longitude; + }); + var url = + 'https://api.mapbox.com/directions/v5/mapbox/driving/${dlong},${dlat};${widget.ulong},${widget.ulat}?steps=true&access_token=pk.eyJ1IjoibWljaGFlbHRlbmRvIiwiYSI6ImNqeWQ0aXp4eTBvb3IzZW52MW1yNjE5a2EifQ.6z7-FOwn2hZuYFRvrIcsFQ'; + http.get(url).then((response) { + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + var route = json.decode(response.body)['routes'][0]; + print('route$route'); + steps = route['legs'][0]['steps']; + summary = route['legs'][0]['summary']; + var k =PolylinePoints().decodePolyline(route['geometry']); + setState(() { + points=List.generate(k.length, (i){ + return LatLng(k[i].latitude, k[i].longitude); + }); + distance = '${(route['distance'] / 1000).toStringAsFixed(2)}km'; + duration = '${(route['duration'] / 60).toStringAsFixed(0)}min'; + }); + }).catchError((e){ + print(e.toString()); + }); + } +@override +void dispose(){ + positionStreamSubscription?.cancel(); + super.dispose(); +} + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text('Directions to ${widget.userName}'), + actions: [ + IconButton( + icon: Icon( + Icons.help, + color: Colors.white, + ), + onPressed: instructions, + ) + ], + ), + body: + /*MapboxOverlay( + controller: new MapboxOverlayController(), + options: new MapboxMapOptions( + style: Style.dark, + camera: new CameraPosition( + target: new LatLng(lat: widget.lat, lng: widget.long), + zoom: 15.0, + bearing: 0.0, + tilt: 0.0), + ), + )*/ + Stack( + children: [ + FlutterMap( + options: new MapOptions( + onTap: (l) { + print(l); + }, + center: _center ?? new LatLng(widget.dlat, widget.dlong), + zoom: 15.0, + ), + mapController: _mapController, + layers: [ + new TileLayerOptions( + urlTemplate: "https://api.tiles.mapbox.com/v4/" + "{id}/{z}/{x}/{y}@2x.png?access_token={accessToken}", + additionalOptions: { + 'accessToken': + 'sk.eyJ1IjoibWljaGFlbHRlbmRvIiwiYSI6ImNqeWQ0bm50aDA3MjQzaW10cWY5NjQ2bjkifQ.D2FbpBl62dQgiNU9qHRdxw', + 'id': 'mapbox.streets', + }, + ),PolylineLayerOptions(polylines:[new Polyline( + points: points, + strokeWidth: 5.0, + color: Theme.of(context).accentColor, + )]), + new MarkerLayerOptions( + markers: [ + new Marker( + width: 60.0, + height: 60.0, + point: new LatLng(dlat, dlong), + builder: (ctx) => new Container( + child: GestureDetector( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('You', + style: + TextStyle(backgroundColor: Colors.white)), + /*IconButton( + icon: */ + Icon( + Icons.local_shipping, + color: Theme.of(context).accentColor, + size: 30, + ), /* + onPressed: null),*/ + ], + ), + onTap: () { + print('hi'); + }, + ), + ), + ), + new Marker( + width: 60.0, + height: 60.0, + point: new LatLng(widget.ulat, widget.ulong), + builder: (ctx) => new Container( + child: GestureDetector( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(widget.userName,maxLines: 1, + style: + TextStyle(backgroundColor: Colors.white)), + Icon( + Icons.person_pin_circle, + color: Theme.of(context).accentColor, + size: 30, + ), + ], + ), + onTap: () { + /*showDialog( + context: context, + builder: (context) { + return Dialog( + child: SizedBox( + height: 150, + child: User( + destlong: destlong, + destlat: destlat, + name: name, + avatar: avatar, + distance: distance, + phone: phone, + id: userId, + selected: true, + ), + ), + ); + });*/ + }), + ), + ), + ], + ), + ], + ), + distance != null + ? Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: EdgeInsets.only(bottom: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + /*FlatButton.icon( + color: Colors.white, + onPressed: () { + instructions(); + }, + icon: Icon( + Icons.info, + color: Theme.of(context).accentColor, + ), + label: Text(instruction??'Steps'), + ),*/ + /*Text(instruction,style: + TextStyle(backgroundColor: Colors.white)),*/ + Text(steps[0]['maneuver']['instruction'], + style: + TextStyle(backgroundColor: Colors.white)), + FlatButton.icon( + color: Colors.white, + onPressed: () { + instructions(); + }, + icon: Icon( + Icons.directions, + color: Theme.of(context).accentColor, + ), + label: Text('${distance ?? ''} (${duration ?? ''})'), + ), + ], + ), + ), + ) + : Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: EdgeInsets.only(bottom: 8), + child: CircularProgressIndicator())), + + Align(alignment: Alignment.bottomLeft, + child:Padding( + padding: EdgeInsets.only(bottom: 8,right: 8), + child: IconButton(iconSize:40,icon: Icon(Icons.my_location,color: Theme.of(context).accentColor), + onPressed: (){ + _mapController.move(LatLng(widget.dlat, + widget.dlong), _mapController.zoom); + }),), + ), + Align(alignment: Alignment.bottomRight, + child:Padding( + padding: EdgeInsets.only(bottom: 8,right: 8), + child: IconButton(iconSize:40,icon: Icon(Icons.person_pin_circle,color: Theme.of(context).accentColor), + onPressed: (){ + _mapController.move(LatLng(widget.ulat, + widget.ulong), _mapController.zoom); + }),), + ), + ], + ), + ); + } + + void instructions() { + showDialog( + context: context, + builder: (context) { + return SimpleDialog( + title: Text(summary), + children: List.generate(steps.length, (i) { + return ListTile( + onTap: () { + Navigator.of(context).pop(); + setState(() { + _mapController.move( + new LatLng(steps[i]['maneuver']['location'][1], + steps[i]['maneuver']['location'][0]), + _mapController.zoom); + }); + }, + title: Text(steps[i]['maneuver']['instruction']), + subtitle: Text('${steps[i]['distance']} m'), + ); + }), + ); + }); + } +} + +class MapPage2 extends StatefulWidget { + MapPage2({@required this.driverId,this.selectOrder, this.dlat, this.dlong}); + final ValueChanged selectOrder; + final double dlat; + final double dlong; +final String driverId; + @override + _MapState2 createState() => new _MapState2(); +} + +class _MapState2 extends State { + BuildContext context; + List names; + List distances; + List durations; + List points=[]; + List keys; + LatLng _center; + MapController _mapController = MapController(); + @override + void initState() { + print(widget.driverId); + FirebaseDatabase.instance + .reference() + .child('Drivers') + .child(widget.driverId) + .child('requests') + .onValue + .listen((e) { + Map map = e.snapshot.value?.cast(); + if (map != null) { + names=[]; + distances=[]; + durations=[]; + points=[]; + keys=[]; + map.forEach((key, values) { + var url = + 'https://api.mapbox.com/directions/v5/mapbox/driving/${values['destlong']},${values['destlat']};${widget.dlong},${widget.dlat}?access_token=pk.eyJ1IjoibWljaGFlbHRlbmRvIiwiYSI6ImNqeWQ0aXp4eTBvb3IzZW52MW1yNjE5a2EifQ.6z7-FOwn2hZuYFRvrIcsFQ'; + http.get(url).then((response) { + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + var route = json.decode(response.body)['routes'][0]; + if(mounted)setState(() { + distances.add('${(route['distance'] / 1000).toStringAsFixed(2)}km'); + durations.add((route['duration'] / 60).toInt()); + if (values != null) { + keys.add(key); + names.add(values['displayName']); + points.add(new LatLng(values['lat'], values['long'])); + } + }); + }); + }); + } + else showDialog(context: context, + builder: (context){ + return AlertDialog( + title: Text('No requests!'), + ); + } + ); + }); + //_updateLocation(Position(longitude: map['dlong'].toDouble(), latitude: map['dlat'].toDouble())); + super.initState(); + } + @override + void dispose() { + super.dispose(); + } + @override + Widget build(BuildContext context) { + this.context=context; + return new Scaffold( + appBar: new AppBar( + title: new Text('Delivery requests'), + actions: [ + IconButton( + icon: Icon( + Icons.help, + color: Colors.white, + ), + onPressed: () {}, //_eta//(){}//instructions, + ) + ], + ), + body: + /*MapboxOverlay( + controller: new MapboxOverlayController(), + options: new MapboxMapOptions( + style: Style.dark, + camera: new CameraPosition( + target: new LatLng(lat: widget.lat, lng: widget.long), + zoom: 15.0, + bearing: 0.0, + tilt: 0.0), + ), + )*/ + Stack( + children: [ + FlutterMap( + options: new MapOptions( + center: _center ?? new LatLng(widget.dlat, widget.dlong), + zoom: 15.0, + ), + mapController: _mapController, + layers: [ + new TileLayerOptions( + urlTemplate: "https://api.tiles.mapbox.com/v4/" + "{id}/{z}/{x}/{y}@2x.png?access_token={accessToken}", + additionalOptions: { + 'accessToken': + 'sk.eyJ1IjoibWljaGFlbHRlbmRvIiwiYSI6ImNqeWQ0bm50aDA3MjQzaW10cWY5NjQ2bjkifQ.D2FbpBl62dQgiNU9qHRdxw', + 'id': 'mapbox.streets', + }, + ), + new MarkerLayerOptions( + markers: [ + Marker( + width: 60.0, + height: 60.0, + point: new LatLng(widget.dlat, widget.dlong), + builder: (ctx) => new Container( + child: GestureDetector( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('You', + style: TextStyle(backgroundColor: Colors.white)), + Icon( + Icons.person_pin_circle, + color: Theme.of(context).accentColor, + size: 30, + ), + ], + ), + onTap: () { + print('hi'); + }), + ), + ), + ]+List.generate(points.length, (i){ + return Marker( + width: 120, + height: 100, + point: points[i], + builder: (ctx) => new Container( + child: GestureDetector( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(names[i], + style: TextStyle(backgroundColor: Colors.white),maxLines: 1,), + Text('${distances[i]} (${durations[i]})', + style: TextStyle(backgroundColor: Colors.white)), + Icon( + Icons.local_shipping, + color: Theme.of(context).accentColor, + size: 30, + ), + ], + ), + onTap: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('ETA'), + content: Text( + '${DateFormat('h:mm a').format(DateTime.now().add(Duration(minutes: durations[i])))}', + style: TextStyle( + color: Theme.of(context).accentColor, + fontWeight: FontWeight.bold, + fontSize: 24), + ), + actions: [ + FlatButton.icon( + onPressed: () { + widget.selectOrder(keys[i]); + Navigator.of(context).pop(); + Navigator.of(this.context??context).pop(); + }, + icon: Icon(Icons.check), + label: Text('Confirm')) + ], + ); + }, + ); + }, + ), + ), + ); + }), + ), + ], + ), + points.isNotEmpty + ? Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: EdgeInsets.only(bottom: 8), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + /*FlatButton.icon( + color: Colors.white, + onPressed: () { + instructions(); + }, + icon: Icon( + Icons.info, + color: Theme.of(context).accentColor, + ), + label: Text(instruction??'Steps'), + ),*/ + /*Text(instruction,style: + TextStyle(backgroundColor: Colors.white)),*/ /* + Text(steps[0]['maneuver']['instruction'], + style: TextStyle(backgroundColor: Colors.white)),*/ + FlatButton.icon( + color: Colors.white, + onPressed: list, + icon: Icon( + Icons.person_pin_circle, + color: Theme.of(context).accentColor, + ), + label: Text('Requests'), + ), + ], + ), + ), + ) + : Container(width: 0,height: 0,)/*Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: EdgeInsets.only(bottom: 8), + child: CircularProgressIndicator()))*/, + Align( + alignment: Alignment.bottomLeft, + child: Padding( + padding: EdgeInsets.only(bottom: 8, right: 8), + child: IconButton( + iconSize: 40, + icon: Icon(Icons.my_location, + color: Theme.of(context).accentColor), + onPressed: () { + _mapController.move( + LatLng(widget.dlat, widget.dlong), _mapController.zoom); + }), + ), + ), + /*Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.only(bottom: 8, right: 8), + child: IconButton( + iconSize: 40, + icon: Icon(Icons.local_shipping, + color: Theme.of(context).accentColor), + onPressed: () { + _mapController.move( + LatLng(dlat, dlong), _mapController.zoom); + }), + ), + ),*/ + ], + ), + ); + } + void list() { + showDialog( + context: context, + builder: (context) { + return SimpleDialog( + title: Text('Requests'), + children: List.generate(points.length, (i) { + return ListTile( + onTap: () { + Navigator.of(context).pop(); + setState(() { + _mapController.move( + points[i], + _mapController.zoom); + }); + }, + trailing:Text(distances[i]), + title: Text(names[i]), + subtitle: Text('${durations[i]} min'), + ); + }), + ); + }); + } + +/* + void _eta() { + + } +*/ +} diff --git a/lib/profile_tile.dart b/lib/profile_tile.dart new file mode 100644 index 0000000..0eedb2a --- /dev/null +++ b/lib/profile_tile.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +class ProfileTile extends StatelessWidget { + final title; + final subtitle; + final textColor; + + ProfileTile({this.title, this.subtitle, this.textColor = Colors.black}); + + @override + Widget build(BuildContext context) { + return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + title, + style: TextStyle( + fontSize: 20.0, fontWeight: FontWeight.w700, color: textColor), + ), + SizedBox( + height: 5.0, + ), + Text( + subtitle, + style: TextStyle( + fontSize: 15.0, fontWeight: FontWeight.normal, color: textColor), + ), + ], + ); + } +} diff --git a/lib/root_page.dart b/lib/root_page.dart new file mode 100644 index 0000000..f88abc1 --- /dev/null +++ b/lib/root_page.dart @@ -0,0 +1,218 @@ +import 'dart:convert'; + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:sandtrackdriver/home_page.dart'; +import 'login_page.dart'; +//import 'chat.dart'; +class RootPage extends StatefulWidget { + //RootPage(); + @override + State createState() => RootPageState(); +} + +enum AuthStatus { notSignedIn, signedIn, loading } + +class RootPageState extends State { + final FirebaseMessaging firebaseMessaging = FirebaseMessaging(); + final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + new FlutterLocalNotificationsPlugin(); + AuthStatus authStatus = AuthStatus.loading; + String currentUserId; + bool login = false; +Firestore firestore; + @override + void initState() { + //await Firestore.instance.settings(persistenceEnabled: true); + firestore=Firestore.instance; + super.initState(); + firebaseMessaging.configure(onMessage: (Map message) { + print('onMessage: $message'); + showNotification(message['notification']); + //_handleMessage(message); + return; + }, onResume: (Map message) { + print('onResume: $message'); + //_handleMessage(message); + return; + }, onLaunch: (Map message) { + print('onLaunch: $message'); + //_handleMessage(message); + return; + }); + configLocalNotification(); + FirebaseAuth.instance.currentUser().then((user) { + setState(() { + authStatus = + user == null ? AuthStatus.notSignedIn : AuthStatus.signedIn; + if (user != null) { + currentUserId = user.uid; + if (user.email[0] != '+'&&!user.isEmailVerified) { + login=true; + authStatus = AuthStatus.notSignedIn; + } + } + }); + }); + } +/*void _handleMessage(Map message){ + var data = message['data'] ?? message; + String type = data['type']; + if(type=='request') + requestDialog(message); + if(type=='message') + { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Chat( + peerName: data['peerName'], + store: firestore, + peerId: data['peerId'], + peerAvatar: data['peerAvatar'], + ))); + } + }*/ + void showNotification(message) async { + var androidPlatformChannelSpecifics = new AndroidNotificationDetails( + 'com.example.sandtrackdriver', + 'Trackmart Driver', + 'Communication', + playSound: true, + enableVibration: true, + importance: Importance.Max, + priority: Priority.High, + ); + var iOSPlatformChannelSpecifics = new IOSNotificationDetails(); + var platformChannelSpecifics = new NotificationDetails( + androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics); + flutterLocalNotificationsPlugin.show(0, message['title'].toString(), + message['body'].toString(), platformChannelSpecifics, + payload: json.encode(message)); + } + + void configLocalNotification() { + var initializationSettingsAndroid = + new AndroidInitializationSettings('icon'); + var initializationSettingsIOS = new IOSInitializationSettings(); + var initializationSettings = new InitializationSettings( + initializationSettingsAndroid, initializationSettingsIOS); + flutterLocalNotificationsPlugin.initialize(initializationSettings); + } + + requestDialog(message) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + message['notification']['title'], + style: TextStyle(color: Theme.of(context).accentColor), + ), + content: SingleChildScrollView( + child: ListBody( + children: [ + Text(message['notification']['body']), + ], + ), + ), + actions: [ + FlatButton.icon( + icon:Icon(Icons.cancel), + label: Text('Cancel'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + FlatButton.icon( + icon:Icon(Icons.check), + label: Text('Accept'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + + void _signedIn(FirebaseUser user) async { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return new AlertDialog( + title: Text('A moment'), + content: SingleChildScrollView( + child: ListBody(children: [ + Container( + child: LinearProgressIndicator(), + ), + Text( + 'Setting things up...') + ]), + ), + contentPadding: EdgeInsets.all(10.0), + ); + }); + String token = await firebaseMessaging.getToken(); //.then((token){ + await FirebaseDatabase.instance + .reference() + .child('users') + .child(user.uid) + .update({ + 'deviceToken': token, + }); + await firestore.collection('users').document(user.uid).updateData({ + 'pushToken': token, + }); + Navigator.of(context).pop(); + setState(() { + currentUserId = user.uid; + authStatus = AuthStatus.signedIn; + }); + } + + void _signedOut() async { + try { + await FirebaseAuth.instance.signOut(); + } catch (e) { + print(e.toString); + } + setState(() { + authStatus = AuthStatus.notSignedIn; + }); + } + + @override + Widget build(BuildContext context) { + switch (authStatus) { + case AuthStatus.notSignedIn: + return new LoginPage( + onSignedIn: _signedIn, + login: this.login, + ); + case AuthStatus.signedIn: + return new HomePage( + //auth: widget.auth, + onSignedout: _signedOut, + currentUserId: this.currentUserId, + ); + case AuthStatus.loading: + return new Scaffold( + body: Center( + child: + //CircularProgressIndicator() + Icon(Icons.local_shipping,size:120,color: Theme.of(context).primaryColor,), + ), + ); + } + + return new LoginPage(); + } +} diff --git a/lib/settings.dart b/lib/settings.dart new file mode 100644 index 0000000..c6c963c --- /dev/null +++ b/lib/settings.dart @@ -0,0 +1,387 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:firebase_database/firebase_database.dart'; +import 'package:firebase_storage/firebase_storage.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'const.dart'; + +class SettingsPage extends StatefulWidget { + final Firestore firestore; + final DatabaseReference database; + + SettingsPage({this.firestore, this.database}); + + @override + _SettingsPageState createState() => + new _SettingsPageState(firestore: firestore, database: database); +} + +class _SettingsPageState extends State { + /*bool _monitor = true; + bool _lights = false; + bool _kitchen = false; + bool _bedroom = false;*/ + final Firestore firestore; + final DatabaseReference database; + + _SettingsPageState({this.firestore, this.database}); + + bool _changed = false; + String _unit = 'Tonne'; + List _units = ['Tonne', 'Truck']; + List _quality = ['Fine', 'Coarse']; + String _qualty = 'Fine'; + SharedPreferences prefs; + TextEditingController controllerNickname; + TextEditingController controllerAboutMe; + String id; + String displayName; + String aboutMe; + String photoUrl; + + bool isLoading = false; + File avatarImageFile; + + final FocusNode focusNodeNickname = new FocusNode(); + final FocusNode focusNodeAboutMe = new FocusNode(); + + void _submit() {} + + @override + initState() { + super.initState(); + readLocal(); + } + + void readLocal() async { + prefs = await SharedPreferences.getInstance(); + setState(() { + _unit = (prefs.getString('unit') ?? 'Tonne'); + _qualty = (prefs.getString('quality') ?? 'Fine'); + }); + id = prefs.getString('id') ?? ''; + displayName = prefs.getString('displayName') ?? ''; + aboutMe = prefs.getString('aboutMe') ?? ''; + photoUrl = prefs.getString('photoUrl') ?? ''; + + controllerNickname = new TextEditingController(text: displayName); + controllerAboutMe = new TextEditingController(text: aboutMe); + + // Force refresh input + setState(() {}); + } + + Future getImage() async { + File image = await ImagePicker.pickImage(source: ImageSource.gallery); + + if (image != null) { + setState(() { + avatarImageFile = image; + isLoading = true; + }); + } + uploadFile(); + } + + Future uploadFile() async { + String fileName = id; + StorageReference reference = FirebaseStorage.instance.ref().child(fileName); + StorageUploadTask uploadTask = reference.putFile(avatarImageFile); + StorageTaskSnapshot storageTaskSnapshot; + uploadTask.onComplete.then((value) { + if (value.error == null) { + storageTaskSnapshot = value; + storageTaskSnapshot.ref.getDownloadURL().then((downloadUrl) { + photoUrl = downloadUrl; + firestore.collection('users').document(id).updateData({ + 'displayName': displayName, + 'aboutMe': aboutMe, + 'photoUrl': photoUrl + }); + firestore.collection('drivers').document(id).updateData({ + 'displayName': displayName, + 'aboutMe': aboutMe, + 'photoUrl': photoUrl + }).then((data) async { + await prefs.setString('photoUrl', photoUrl); + setState(() { + isLoading = false; + }); + Fluttertoast.showToast(msg: "Upload success"); + }).catchError((err) { + setState(() { + isLoading = false; + }); + Fluttertoast.showToast(msg: err.toString()); + }); + }, onError: (err) { + setState(() { + isLoading = false; + }); + Fluttertoast.showToast(msg: 'This file is not an image'); + }); + } else { + setState(() { + isLoading = false; + }); + Fluttertoast.showToast(msg: 'This file is not an image'); + } + }, onError: (err) { + setState(() { + isLoading = false; + }); + Fluttertoast.showToast(msg: err.toString()); + }); + } + + void handleUpdateData() { + focusNodeNickname.unfocus(); + focusNodeAboutMe.unfocus(); + + setState(() { + isLoading = true; + }); + firestore.collection('users').document(id).updateData({ + 'displayName': displayName, + 'aboutMe': aboutMe, + 'photoUrl': photoUrl + }); + firestore.collection('drivers').document(id).updateData({ + 'displayName': displayName, + 'aboutMe': aboutMe, + 'photoUrl': photoUrl + }).then((data) async { + await prefs.setString('displayName', displayName); + await prefs.setString('aboutMe', aboutMe); + await prefs.setString('photoUrl', photoUrl); + setState(() { + isLoading = false; + }); + Fluttertoast.showToast(msg: "Update success"); + Navigator.of(context).pop(); + }).catchError((err) { + setState(() { + isLoading = false; + }); + + Fluttertoast.showToast(msg: err.toString()); + }); + } + + List _buildForm(BuildContext context) { + Form form = new Form( + child: SingleChildScrollView( + child: Column( + children: [ + // Avatar + Container( + child: Center( + child: Stack( + children: [ + (avatarImageFile == null) + ? (photoUrl != '' + ? Material( + child: CachedNetworkImage( + placeholder: (context, url) => Container( + child: CircularProgressIndicator( + strokeWidth: 2.0, + valueColor: AlwaysStoppedAnimation( + themeColor), + ), + width: 90.0, + height: 90.0, + padding: EdgeInsets.all(20.0), + ), + imageUrl: photoUrl, + width: 90.0, + height: 90.0, + fit: BoxFit.cover, + ), + borderRadius: + BorderRadius.all(Radius.circular(45.0)), + clipBehavior: Clip.hardEdge, + ) + : Icon( + Icons.account_circle, + size: 90.0, + color: greyColor, + )) + : Material( + child: Image.file( + avatarImageFile, + width: 90.0, + height: 90.0, + fit: BoxFit.cover, + ), + borderRadius: + BorderRadius.all(Radius.circular(45.0)), + clipBehavior: Clip.hardEdge, + ), + IconButton( + icon: Icon( + Icons.camera_alt, + //color: primaryColor.withOpacity(0.5), + ), + onPressed: getImage, + padding: EdgeInsets.all(30.0), + //splashColor: Colors.transparent, + //highlightColor: greyColor, + iconSize: 30.0, + ), + ], + ), + ), + width: double.infinity, + margin: EdgeInsets.all(20.0), + ), + Container( + child: Text( + 'Name', + //style: TextStyle(fontStyle: FontStyle.italic, fontWeight: FontWeight.bold, color: primaryColor), + ), + margin: EdgeInsets.only(left: 10.0, bottom: 5.0, top: 10.0), + ), + Container( + child: Theme( + data: Theme.of(context).copyWith(primaryColor: primaryColor), + child: TextField( + decoration: InputDecoration( + //hintText: 'First name', + contentPadding: new EdgeInsets.all(5.0), + //hintStyle: TextStyle(color: greyColor), + ), + controller: controllerNickname, + onChanged: (value) { + setState(() { + _changed = true; + }); + displayName = value; + }, + focusNode: focusNodeNickname, + ), + ), + margin: EdgeInsets.only(left: 30.0, right: 30.0), + ), + + // About me + Container( + child: Text( + 'About me', + //style: TextStyle(fontStyle: FontStyle.italic, fontWeight: FontWeight.bold, color: primaryColor), + ), + margin: EdgeInsets.only(left: 10.0, top: 30.0, bottom: 5.0), + ), + Container( + child: Theme( + data: Theme.of(context).copyWith(primaryColor: primaryColor), + child: TextField( + decoration: InputDecoration( + //hintText: 'Fun, like travel and play PES...', + contentPadding: EdgeInsets.all(5.0), + //hintStyle: TextStyle(color: greyColor), + ), + controller: controllerAboutMe, + onChanged: (value) { + setState(() { + _changed = true; + }); + aboutMe = value; + }, + focusNode: focusNodeAboutMe, + ), + ), + margin: EdgeInsets.only(left: 30.0, right: 30.0), + ), + + Container( + height: 8, + ), + _changed + ? new ListTile( + leading: Icon(Icons.warning), + title: const Text('Tap disc icon and restart app to save changes'), + ) + : isLoading + ? Center( + child: CircularProgressIndicator(), + ) + : Container( + width: 0, + height: 0, + ), + ], + ), + padding: EdgeInsets.only(left: 15.0, right: 15.0), + ), + ); + + var l = new List(); + l.add(form); + return l; + } + + _leave() { + Navigator.of(context).pop(); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + leading: IconButton( + icon: IconButton( + icon: Icon(Icons.arrow_back), + onPressed: () { + if (_changed) + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Unsaved changes will be lost'), + actions: [ + FlatButton( + child: Text('Lose'), + onPressed: () { + Navigator.of(context).pop(); + _leave(); + //Navigator.of(context).pop(); + }, + ), + FlatButton( + child: Text('Save'), + onPressed: () { + Navigator.of(context).pop(); + handleUpdateData(); + }), + ]); + }); + else + Navigator.of(context).pop(); + }), + onPressed: () { + //Navigator.of(context).pop(); + }), + title: new Text('Profile'), + actions: [ + IconButton( + icon: Icon( + Icons.save, + color: Colors.white, + ), + onPressed: handleUpdateData, + ) + ], + ), + body: new Stack( + children: _buildForm(context), + ), + ); + } +} diff --git a/lib/support.dart b/lib/support.dart new file mode 100644 index 0000000..3bf0d65 --- /dev/null +++ b/lib/support.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import 'contact.dart'; + +class SupportPage extends StatefulWidget { + @override + _SupportPageState createState() => new _SupportPageState(); +} + +class _SupportPageState extends State { + bool _q1 = false; + bool _q2 = false; + bool _q3 = false; + bool _q4 = false; + bool _q5 = false; + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text('Help'), + actions: [ + IconButton( + icon: Icon( + Icons.language, + color: Colors.white, + ), + onPressed: () async { + if (await canLaunch('http://sandtrackdriverapp.com/faq')) { + await launch('http://sandtrackdriverapp.com/faq'); + } else { + throw 'Could not launch http://www.sandtrackdriverapp.com/faq'; + } + }, + ) + ], + ), + body: new Container( + margin: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column(children: [ + //Container(height:20), + + InkWell( + child: Row(children: [ + Text('What does Trackmart Driver do?', + style: TextStyle(fontSize: 16)), + IconButton( + icon: + Icon(_q1 ? Icons.arrow_drop_up : Icons.arrow_drop_down), + onPressed: () { + setState(() { + _q1 = !_q1; + }); + }) + ]), + onTap: () { + setState(() { + _q1 = !_q1; + }); + }), + _q1 + ? Text( + 'We mediate between truck drivers and consumers to facilitate order requests for construction materials in the comfort of your preffered location, along with tracking to enable you concentrate on building your future') + : Container(width: 0, height: 0), + Divider(), + Text('If this didn\'t solve your problem,'), + RaisedButton( + color: Theme.of(context).accentColor, + onPressed: () { + Navigator.of(context).pop(); + Navigator.push( + context, + new MaterialPageRoute( + builder: (context) => new ContactPage())); + //_showDialog(); + }, + child: Text('Contact Us', style: TextStyle(color: Colors.white)), + ), + ]), + ), + ); + } +} + +class Hyperlink extends StatelessWidget { + final String _url; + final String _text; + + Hyperlink(this._url, this._text); + + _launchURL() async { + if (await canLaunch(_url)) { + await launch(_url); + } else { + throw 'Could not launch $_url'; + } + } + + @override + Widget build(BuildContext context) { + return InkWell( + child: Text( + _text, + style: TextStyle( + decoration: TextDecoration.underline, + color: Theme.of(context).accentColor, + ), + ), + onTap: _launchURL, + ); + } +} diff --git a/lib/uidata.dart b/lib/uidata.dart new file mode 100644 index 0000000..3c653de --- /dev/null +++ b/lib/uidata.dart @@ -0,0 +1,89 @@ +import 'dart:math'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +class UIData { + //routes + static const String homeRoute = "/home"; + static const String profileOneRoute = "/View Profile"; + static const String profileTwoRoute = "/Profile 2"; + static const String notFoundRoute = "/No Search Result"; + static const String timelineOneRoute = "/Feed"; + static const String timelineTwoRoute = "/Tweets"; + static const String settingsOneRoute = "/Device Settings"; + static const String shoppingOneRoute = "/Shopping List"; + static const String shoppingTwoRoute = "/Shopping Details"; + static const String shoppingThreeRoute = "/Product Details"; + static const String paymentOneRoute = "/Credit Card"; + static const String paymentTwoRoute = "/Payment Success"; + static const String loginOneRoute = "/Login With OTP"; + static const String loginTwoRoute = "/Login 2"; + static const String dashboardOneRoute = "/Dashboard 1"; + static const String dashboardTwoRoute = "/Dashboard 2"; + + //strings + static const String appName = "Flutter UIKit"; + + //fonts + static const String quickFont = "Quicksand"; + static const String ralewayFont = "Raleway"; + static const String quickBoldFont = "Quicksand_Bold.otf"; + static const String quickNormalFont = "Quicksand_Book.otf"; + static const String quickLightFont = "Quicksand_Light.otf"; + + //images + static const String imageDir = "assets/images"; + static const String pkImage = "$imageDir/pk.jpg"; + static const String profileImage = "$imageDir/profile.jpg"; + static const String blankImage = "$imageDir/blank.jpg"; + static const String dashboardImage = "$imageDir/dashboard.jpg"; + static const String loginImage = "$imageDir/login.jpg"; + static const String paymentImage = "$imageDir/payment.jpg"; + static const String settingsImage = "$imageDir/setting.jpeg"; + static const String shoppingImage = "$imageDir/shopping.jpeg"; + static const String timelineImage = "$imageDir/timeline.jpeg"; + static const String verifyImage = "$imageDir/verification.jpg"; + + //login + static const String enter_code_label = "Phone Number"; + static const String enter_code_hint = "10 Digit Phone Number"; + static const String enter_otp_label = "OTP"; + static const String enter_otp_hint = "4 Digit OTP"; + static const String get_otp = "Get OTP"; + static const String resend_otp = "Resend OTP"; + static const String login = "Login"; + static const String enter_valid_number = "Enter 10 digit phone number"; + static const String enter_valid_otp = "Enter 4 digit otp"; + + //gneric + static const String error = "Error"; + static const String success = "Success"; + static const String ok = "OK"; + static const String forgot_password = "Forgot Password?"; + static const String something_went_wrong = "Something went wrong"; + static const String coming_soon = "Coming Soon"; + + static const MaterialColor ui_kit_color = Colors.grey; + +//colors + static List kitGradients = [ + // new Color.fromRGBO(103, 218, 255, 1.0), + // new Color.fromRGBO(3, 169, 244, 1.0), + // new Color.fromRGBO(0, 122, 193, 1.0), + Colors.blueGrey.shade800, + Colors.black87, + ]; + static List kitGradients2 = [ + Colors.cyan.shade600, + Colors.blue.shade900 + ]; + + //randomcolor + static final Random _random = new Random(); + + /// Returns a random color. + static Color next() { + return new Color(0xFF000000 + _random.nextInt(0x00FFFFFF)); + } +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..3b3bcfa --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,490 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + ansicolor: + dependency: transitive + description: + name: ansicolor + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.10" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.2" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.7+1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.14.11" + console_log_handler: + dependency: transitive + description: + name: console_log_handler + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + cookie_jar: + dependency: transitive + description: + name: cookie_jar + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + country_code_picker: + dependency: "direct main" + description: + name: country_code_picker + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.7" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + dio: + dependency: "direct main" + description: + name: dio + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.13" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.1+11" + firebase_core: + dependency: transitive + description: + name: firebase_core + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.0+7" + firebase_database: + dependency: "direct main" + description: + name: firebase_database + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.5" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.2" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.4" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.1" + flutter_image: + dependency: transitive + description: + name: flutter_image + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.2+1" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.0" + flutter_map: + dependency: "direct main" + description: + name: flutter_map + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.0+2" + flutter_masked_text: + dependency: "direct main" + description: + name: flutter_masked_text + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.0" + flutter_polyline_points: + dependency: "direct main" + description: + name: flutter_polyline_points + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + geolocator: + dependency: "direct main" + description: + name: geolocator + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.1+1" + google_api_availability: + dependency: transitive + description: + name: google_api_availability + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + http: + dependency: transitive + description: + name: http + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.0+2" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.3" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + image_picker: + dependency: "direct main" + description: + name: image_picker + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.0+17" + intl: + dependency: "direct main" + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.8" + latlong: + dependency: transitive + description: + name: latlong + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.1" + location_permissions: + dependency: transitive + description: + name: location_permissions + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "0.11.3+2" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.5" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" + path: + dependency: "direct main" + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.2" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + pedantic: + dependency: transitive + description: + name: pedantic + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "2.4.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + positioned_tap_detector: + dependency: transitive + description: + name: positioned_tap_detector + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + share: + dependency: "direct main" + description: + name: share + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.2+1" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.3+4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.5" + sqflite: + dependency: "direct main" + description: + name: sqflite + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6+1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.9.3" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0+1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.5" + transparent_image: + dependency: transitive + description: + name: transparent_image + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + tuple: + dependency: transitive + description: + name: tuple + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.0+3" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" + validate: + dependency: transitive + description: + name: validate + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "3.5.0" + yaml: + dependency: transitive + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.16" +sdks: + dart: ">=2.4.0 <3.0.0" + flutter: ">=1.6.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..633982c --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,99 @@ +name: sandtrackdriver +description: A new Flutter project. + +# 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.2.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + dio: 2.1.13 + permission_handler: '^3.1.0' + sqflite: + path: + flutter_masked_text: ^0.8.0 + url_launcher: ^4.0.3 + firebase_auth: ^0.11.1+7 + country_code_picker: 1.1.7 + firebase_database: 3.0.5 + share: 0.6.2+1 + shared_preferences: 0.5.3+4 + firebase_messaging: 5.1.2 + flutter_local_notifications: 0.8.0 + cloud_firestore: 0.12.7+1 + cached_network_image: 1.1.1 + firebase_storage: 3.0.4 + fluttertoast: 3.1.0 + image_picker: 0.6.0+17 + intl: 0.15.8 + geolocator: 5.1.1+1 + flutter_polyline_points: 0.1.0 + flutter_map: 0.7.0+2 # or the latest version on Pub + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_launcher_icons: "^0.7.2" + +flutter_icons: + ios: false + android: "launcher_icon" + image_path: "assets/launcher/icon.png" + +# For information on the generic Dart part of this file, see the +# following page: https://www.dartlang.org/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + assets: + - assets/images/ + - assets/launcher/ + + + # 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: + # - images/a_dot_burr.jpeg + # - 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/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..c347dc6 --- /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 that Flutter provides. 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:sandtrackdriver/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // 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); + }); +}