first commit

This commit is contained in:
developer 2025-08-19 16:03:24 +07:00
commit 7727fa09ed
95 changed files with 4755 additions and 0 deletions

45
.gitignore vendored Normal file
View File

@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

45
.metadata Normal file
View File

@ -0,0 +1,45 @@
# 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: "d8a9f9a52e5af486f80d932e838ee93861ffd863"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: android
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: ios
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: linux
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: macos
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: web
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
- platform: windows
create_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
base_revision: d8a9f9a52e5af486f80d932e838ee93861ffd863
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

16
README.md Normal file
View File

@ -0,0 +1,16 @@
# smartflow
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

28
analysis_options.yaml Normal file
View File

@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

13
android/.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

45
android/app/build.gradle Normal file
View File

@ -0,0 +1,45 @@
plugins {
id "com.android.application"
// START: FlutterFire Configuration
id 'com.google.gms.google-services'
// END: FlutterFire Configuration
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
android {
namespace = "com.example.smartflow"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId = "com.example.smartflow"
minSdk = 23 // ubah langsung ke 23
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
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 = "../.."
}

View File

@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "482063931859",
"project_id": "smartflow-4e1bf",
"storage_bucket": "smartflow-4e1bf.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:482063931859:android:2682986f8d1d4f9e4bf726",
"android_client_info": {
"package_name": "com.example.smartflow"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyCK3SvB94v4sZOGldEXxCtF_iSzvGzym34"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="smartflow"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View File

@ -0,0 +1,5 @@
package com.example.smartflow
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity()

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

18
android/build.gradle Normal file
View File

@ -0,0 +1,18 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip

28
android/settings.gradle Normal file
View File

@ -0,0 +1,28 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.1.0" apply false
// START: FlutterFire Configuration
id "com.google.gms.google-services" version "4.3.15" apply false
// END: FlutterFire Configuration
id "org.jetbrains.kotlin.android" version "1.8.22" apply false
}
include ":app"

1
firebase.json Normal file
View File

@ -0,0 +1 @@
{"flutter":{"platforms":{"android":{"default":{"projectId":"smartflow-4e1bf","appId":"1:482063931859:android:2682986f8d1d4f9e4bf726","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"smartflow-4e1bf","configurations":{"android":"1:482063931859:android:2682986f8d1d4f9e4bf726"}}}}}}

34
ios/.gitignore vendored Normal file
View File

@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
</dict>
</plist>

View File

@ -0,0 +1 @@
#include "Generated.xcconfig"

View File

@ -0,0 +1 @@
#include "Generated.xcconfig"

View File

@ -0,0 +1,616 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
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 = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
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 */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
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 = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartflow;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartflow.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartflow.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartflow.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
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 = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
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 = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartflow;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartflow;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
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 */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,13 @@
import Flutter
import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@ -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.

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

49
ios/Runner/Info.plist Normal file
View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Smartflow</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>smartflow</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"

View File

@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

20
lib/app.dart Normal file
View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'routes/app_routes.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Monitoring Debit Air',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
initialRoute: '/',
routes: AppRoutes.routes,
);
}
}

62
lib/firebase_options.dart Normal file
View File

@ -0,0 +1,62 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for web - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for ios - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.macOS:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for macos - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyCK3SvB94v4sZOGldEXxCtF_iSzvGzym34',
appId: '1:482063931859:android:2682986f8d1d4f9e4bf726',
messagingSenderId: '482063931859',
projectId: 'smartflow-4e1bf',
storageBucket: 'smartflow-4e1bf.firebasestorage.app',
);
}

17
lib/main.dart Normal file
View File

@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:intl/date_symbol_data_local.dart'; // Tambahkan ini
import 'app.dart';
import '/firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
await initializeDateFormatting('id_ID', null); // Tambahkan ini
runApp(const MyApp());
}

View File

View File

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import '../widgets/bottom_bar.dart';
// Import semua halaman yang digunakan
import '../screens/splash_screen.dart';
import '../screens/auth/login_screen.dart';
import '../screens/auth/register_screen.dart';
import '../screens/dashboard/dashboard_screen.dart';
import '../screens/history/history_screen.dart';
import '../screens/billing/billing_screen.dart';
import '../screens/settings/settings_screen.dart';
import '../screens/notifications/notifcations_screen.dart';
import '../screens/dashboard/admin_dashboard_screen.dart';
class AppRoutes {
static Map<String, WidgetBuilder> routes = {
'/': (context) => const SplashScreen(),
'/login': (context) => const LoginScreen(),
'/register': (context) => const RegisterScreen(),
'/dashboard': (context) => const DashboardScreen(),
'/history': (context) => const HistoryScreen(),
'/billing': (context) => const BillingScreen(),
'/settings': (context) => const SettingsScreen(),
'/home': (context) => const CustomBottomNav(),
'/notifications': (context) => const NotificationScreen(),
'/adminDashboard': (context) => const AdminDashboardScreen(),
};
}

View File

@ -0,0 +1,166 @@
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
bool _isLoading = false;
String? _errorMessage;
Future<void> _login() async {
setState(() {
_isLoading = true;
_errorMessage = null;
});
try {
UserCredential userCredential = await FirebaseAuth.instance
.signInWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text.trim());
User? user = userCredential.user;
if (user != null) {
final userDoc = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
if (userDoc.exists) {
final role = userDoc['role'];
if (role == 'admin') {
Navigator.pushReplacementNamed(context, '/adminDashboard');
} else {
Navigator.pushReplacementNamed(context, '/home');
}
} else {
setState(() {
_errorMessage = 'Data pengguna tidak ditemukan.';
});
}
}
} on FirebaseAuthException catch (e) {
setState(() {
_errorMessage = e.message;
});
}
setState(() {
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(255, 107, 139, 255),
Color.fromARGB(255, 140, 163, 247)
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.water_drop_rounded,
size: 80, color: Colors.white),
const SizedBox(height: 16),
const Text(
"Login Monitoring Air",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 32),
Card(
color: Colors.white.withOpacity(0.9),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
elevation: 6,
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
children: [
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email),
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 16),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
),
obscureText: true,
),
const SizedBox(height: 24),
if (_errorMessage != null)
Text(
_errorMessage!,
style: const TextStyle(color: Colors.red),
),
if (_isLoading)
const CircularProgressIndicator()
else
ElevatedButton(
onPressed: _login,
style: ElevatedButton.styleFrom(
backgroundColor:
const Color.fromARGB(255, 107, 139, 255),
minimumSize: const Size.fromHeight(50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text(
"Login",
style:
TextStyle(fontSize: 16, color: Colors.white),
),
),
const SizedBox(height: 12),
TextButton(
onPressed: () {
Navigator.pushNamed(context, '/register');
},
child: const Text("Belum punya akun? Daftar di sini"),
),
],
),
),
),
],
),
),
),
),
);
}
}

View File

@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
class RegisterScreen extends StatelessWidget {
const RegisterScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text("reg")),
);
}
}

View File

@ -0,0 +1,251 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class BillingScreen extends StatefulWidget {
const BillingScreen({super.key});
@override
State<BillingScreen> createState() => _BillingScreenState();
}
class _BillingScreenState extends State<BillingScreen> {
String selectedMonth =
DateFormat('MMMM yyyy', 'en_US').format(DateTime.now());
final user = FirebaseAuth.instance.currentUser;
@override
Widget build(BuildContext context) {
final tagihanRef = FirebaseFirestore.instance
.collection('tagihan')
.where('uid', isEqualTo: user?.uid);
return Scaffold(
backgroundColor: const Color(0xFFF2F6FF),
appBar: AppBar(
title: const Text("Tagihan Air Bulanan"),
backgroundColor: const Color(0xFF6B8BFF),
foregroundColor: Colors.white,
elevation: 0,
),
body: StreamBuilder<QuerySnapshot>(
stream: tagihanRef.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return const Center(child: CircularProgressIndicator());
if (!snapshot.hasData || snapshot.data!.docs.isEmpty)
return const Center(child: Text("Belum ada data tagihan"));
final tagihanList = snapshot.data!.docs;
// Ambil list bulan unik
final uniqueMonths = tagihanList
.map((doc) =>
(doc.data() as Map<String, dynamic>)['bulan'] as String)
.toSet()
.toList()
..sort((a, b) => DateFormat('MMMM yyyy', 'en_US')
.parse(a)
.compareTo(DateFormat('MMMM yyyy', 'en_US').parse(b)));
// Pastikan selectedMonth valid
if (!uniqueMonths.contains(selectedMonth)) {
selectedMonth = uniqueMonths.first;
}
final selectedDoc = tagihanList
.where((e) =>
(e.data() as Map<String, dynamic>)['bulan'] == selectedMonth)
.toList();
Map<String, dynamic>? selectedData = selectedDoc.isNotEmpty
? selectedDoc.first.data() as Map<String, dynamic>
: null;
return Column(
children: [
// Header Total Tagihan
Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
margin: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFF6B8BFF),
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
color: Colors.black12,
blurRadius: 10,
offset: Offset(0, 4))
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Total Tagihan Bulan Ini",
style: TextStyle(color: Colors.white70, fontSize: 14)),
const SizedBox(height: 6),
Text(
selectedData != null
? "Rp ${NumberFormat("#,##0", "id_ID").format(selectedData['tagihan'])}"
: "-",
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
Text(
selectedData != null
? "Pemakaian: ${selectedData['pemakaian']}"
: "",
style:
const TextStyle(color: Colors.white70, fontSize: 14),
),
],
),
),
// Filter Bulan
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: DropdownButtonFormField<String>(
value: selectedMonth,
items: uniqueMonths.map((month) {
// Convert ke Indonesia untuk ditampilkan jika ingin
DateTime parsedMonth =
DateFormat('MMMM yyyy', 'en_US').parse(month);
String displayMonth =
DateFormat('MMMM yyyy', 'id_ID').format(parsedMonth);
return DropdownMenuItem<String>(
value: month,
child: Text(displayMonth),
);
}).toList(),
onChanged: (value) {
setState(() => selectedMonth = value!);
},
decoration: InputDecoration(
labelText: 'Pilih Bulan',
filled: true,
fillColor: Colors.white,
prefixIcon: const Icon(Icons.calendar_month_rounded),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
const SizedBox(height: 16),
// Riwayat Tagihan
Expanded(
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: tagihanList.length,
itemBuilder: (context, index) {
final data =
tagihanList[index].data() as Map<String, dynamic>;
final isPaid = data['status'] == 'Lunas';
// Convert bulan ke Indonesia untuk tampilan
DateTime parsedMonth =
DateFormat('MMMM yyyy', 'en_US').parse(data['bulan']);
String displayMonth =
DateFormat('MMMM yyyy', 'id_ID').format(parsedMonth);
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: isPaid
? Colors.green.withOpacity(0.3)
: Colors.orange.withOpacity(0.3),
width: 1,
),
boxShadow: const [
BoxShadow(
color: Colors.black12,
blurRadius: 5,
offset: Offset(0, 2)),
],
),
child: Row(
children: [
CircleAvatar(
backgroundColor: isPaid
? Colors.green.withOpacity(0.1)
: Colors.orange.withOpacity(0.1),
child: Icon(
isPaid
? Icons.check_circle
: Icons.warning_amber_rounded,
color: isPaid ? Colors.green : Colors.orange,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
displayMonth,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
Text("Pemakaian: ${data['pemakaian']}"),
Text(
"Tagihan: Rp ${NumberFormat("#,##0", "id_ID").format(data['tagihan'])}",
style: const TextStyle(color: Colors.black87),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Chip(
label: Text(
data['status'],
style: const TextStyle(
color: Colors.white, fontSize: 12),
),
backgroundColor:
isPaid ? Colors.green : Colors.orange,
),
IconButton(
icon: const Icon(Icons.download_rounded),
tooltip: "Unduh PDF",
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Unduh tagihan $displayMonth")),
);
},
color: const Color(0xFF6B8BFF),
)
],
)
],
),
);
},
),
),
],
);
},
),
);
}
}

View File

@ -0,0 +1,180 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class AdminDashboardScreen extends StatefulWidget {
const AdminDashboardScreen({super.key});
@override
State<AdminDashboardScreen> createState() => _AdminDashboardScreenState();
}
class _AdminDashboardScreenState extends State<AdminDashboardScreen> {
String _selectedMonth = DateFormat('MMMM yyyy').format(DateTime.now());
@override
Widget build(BuildContext context) {
final tagihanRef = FirebaseFirestore.instance.collection('tagihan');
return Scaffold(
appBar: AppBar(
title: const Text("Dashboard Admin"),
backgroundColor: const Color(0xFF6B8BFF),
foregroundColor: Colors.white,
),
backgroundColor: Colors.blue[50],
body: Padding(
padding: const EdgeInsets.all(16),
child: StreamBuilder<QuerySnapshot>(
stream: tagihanRef.where('bulan', isEqualTo: _selectedMonth).snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) return const Center(child: CircularProgressIndicator());
final data = snapshot.data?.docs ?? [];
final totalUser = data.map((e) => e['uid']).toSet().length;
final totalPemakaian = data.fold<double>(0, (sum, e) => sum + (e['pemakaian'] ?? 0));
final totalTagihan = data.fold<double>(0, (sum, e) => sum + (e['tagihan'] ?? 0));
final lunasCount = data.where((e) => e['status'] == 'Lunas').length;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildMonthFilter(),
const SizedBox(height: 16),
_buildStats(totalUser, totalPemakaian, totalTagihan, lunasCount),
const SizedBox(height: 24),
const Text(
"Daftar Tagihan Bulan Ini",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF6B8BFF)),
),
const SizedBox(height: 12),
Expanded(
child: data.isEmpty
? const Center(child: Text("Tidak ada data tagihan."))
: ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
final doc = data[index];
return Card(
margin: const EdgeInsets.symmetric(vertical: 8),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: ListTile(
leading: const Icon(Icons.person, color: Color(0xFF6B8BFF)),
title: Text(doc['email'] ?? '-'),
subtitle: Text(
'Pemakaian: ${doc['pemakaian'] ?? 0}\nTagihan: Rp${(doc['tagihan'] ?? 0).toStringAsFixed(0)}',
),
trailing: DropdownButton<String>(
value: doc['status'],
onChanged: (value) {
if (value != null) {
doc.reference.update({'status': value});
}
},
items: ['Lunas', 'Belum Lunas']
.map((status) => DropdownMenuItem(
value: status,
child: Text(status),
))
.toList(),
),
),
);
},
),
)
],
);
},
),
),
);
}
Widget _buildMonthFilter() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Filter Bulan:",
style: TextStyle(fontSize: 16, color: Colors.grey[700]),
),
ElevatedButton.icon(
onPressed: () async {
final selected = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2023),
lastDate: DateTime.now(),
helpText: 'Pilih bulan dan tahun',
);
if (selected != null) {
setState(() {
_selectedMonth = DateFormat('MMMM yyyy').format(selected);
});
}
},
icon: const Icon(Icons.calendar_month),
label: Text(_selectedMonth),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6B8BFF),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
),
],
);
}
Widget _buildStats(int totalUser, double totalPemakaian, double totalTagihan, int lunasCount) {
return GridView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisExtent: 100,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
children: [
_buildStatCard("Pengguna", totalUser.toString(), Icons.people, Colors.blue),
_buildStatCard("Pemakaian", "${totalPemakaian.toStringAsFixed(1)}", Icons.water_drop, Colors.indigo),
_buildStatCard("Total Tagihan", "Rp${totalTagihan.toStringAsFixed(0)}", Icons.attach_money, Colors.green),
_buildStatCard("Lunas", lunasCount.toString(), Icons.check_circle, Colors.teal),
],
);
}
Widget _buildStatCard(String title, String value, IconData icon, Color color) {
return Card(
elevation: 4,
color: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
CircleAvatar(
backgroundColor: color.withOpacity(0.15),
child: Icon(icon, color: color),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(title, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500)),
const SizedBox(height: 4),
Text(value, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
],
),
),
],
),
),
);
}
}

View File

@ -0,0 +1,521 @@
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import '../../widgets/costum_header.dart';
class DashboardScreen extends StatefulWidget {
const DashboardScreen({super.key});
@override
State<DashboardScreen> createState() => _DashboardScreenState();
}
class _DashboardScreenState extends State<DashboardScreen> {
DatabaseReference get _ref {
final uid = FirebaseAuth.instance.currentUser?.uid;
return FirebaseDatabase.instance.ref('monitoring/$uid');
}
double _debit = 0.0;
double _volume = 0.0;
bool _isPumpOn = false;
String _selectedRange = 'day';
List<FlSpot> _chartData = [];
@override
void initState() {
super.initState();
_listenToRealtimeData();
_loadChartDataFromFirestore();
}
void _listenToRealtimeData() {
_ref.onValue.listen((event) {
final data = event.snapshot.value as Map?;
if (data != null) {
setState(() {
_debit = (data['debit'] ?? 0).toDouble();
_volume = (data['volume'] ?? 0).toDouble();
final pumpRaw = data['pump'];
if (pumpRaw is bool) {
_isPumpOn = pumpRaw;
} else if (pumpRaw is int) {
_isPumpOn = pumpRaw == 1;
} else {
_isPumpOn = false;
}
});
// Kirim volume saat ini ke fungsi tagihan
generateMonthlyBillIfNeeded(_volume);
}
});
}
Future<void> generateMonthlyBillIfNeeded(double currentVolume) async {
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
final uid = user.uid;
final email = user.email ?? '';
final now = DateTime.now();
final monthName = "${_getMonthName(now.month)} ${now.year}";
final docId = "$uid-$monthName";
final tagihanRef =
FirebaseFirestore.instance.collection('tagihan').doc(docId);
final tagihanSnap = await tagihanRef.get();
if (tagihanSnap.exists) {
print(" Tagihan bulan $monthName sudah ada, tidak dibuat ulang.");
return;
}
// Ambil tarif dari Realtime Database
final tarifRef = FirebaseDatabase.instance.ref("tarif/$uid");
final tarifSnap = await tarifRef.get();
if (!tarifSnap.exists) {
print("❌ Tarif belum diatur untuk user $uid");
return;
}
final rate = int.tryParse(tarifSnap.value.toString()) ?? 0;
// Ambil volume bulan lalu dari Firestore
final lastVolRef =
FirebaseFirestore.instance.collection('last_volume').doc(uid);
final lastVolSnap = await lastVolRef.get();
final lastVolume = (lastVolSnap.data()?['volume'] ?? 0).toDouble();
// Hitung pemakaian dan tagihan
final pemakaianBulanIni = currentVolume - lastVolume;
final tagihan = (pemakaianBulanIni * rate).toInt();
await tagihanRef.set({
'bulan': monthName,
'email': email,
'pemakaian': pemakaianBulanIni,
'status': 'Belum Lunas',
'tagihan': tagihan,
'uid': uid,
'timestamp': FieldValue.serverTimestamp(),
});
await lastVolRef.set({'volume': currentVolume});
print("✅ Tagihan bulan $monthName berhasil dibuat");
}
String _getMonthName(int month) {
const months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
return months[month - 1];
}
Future<void> _loadChartDataFromFirestore() async {
final uid = FirebaseAuth.instance.currentUser?.uid;
if (uid == null) return;
final now = DateTime.now();
late DateTime startDate;
late DateTime endDate;
if (_selectedRange == 'day') {
startDate = DateTime(now.year, now.month, now.day);
endDate = startDate.add(Duration(days: 1));
} else if (_selectedRange == 'week') {
startDate = now.subtract(Duration(days: 6));
endDate = now.add(Duration(days: 1));
} else {
startDate = now.subtract(Duration(days: 29));
endDate = now.add(Duration(days: 1));
}
final querySnapshot = await FirebaseFirestore.instance
.collection('history')
.doc(uid)
.collection('data')
.where('timestamp',
isGreaterThanOrEqualTo: Timestamp.fromDate(startDate))
.where('timestamp', isLessThan: Timestamp.fromDate(endDate))
.orderBy('timestamp')
.get();
final List<FlSpot> spots = [];
for (var doc in querySnapshot.docs) {
final data = doc.data();
final timestampRaw = data['timestamp'];
final debit = (data['debit'] ?? 0).toDouble();
try {
final timestamp = (timestampRaw as Timestamp).toDate();
double x;
if (_selectedRange == 'day') {
x = timestamp.hour.toDouble();
} else if (_selectedRange == 'week') {
x = timestamp.weekday.toDouble(); // 1 = Monday
} else {
x = timestamp.day.toDouble();
}
spots.add(FlSpot(x, debit));
} catch (e) {
continue;
}
}
setState(() {
_chartData = spots;
});
}
void _onRangeChanged(String range) {
setState(() {
_selectedRange = range;
});
_loadChartDataFromFirestore();
}
void _togglePump(bool value) {
setState(() {
_isPumpOn = value;
});
_ref.update({
'pump': value, // langsung boolean
});
}
void _goToBillingPage() {
Navigator.pushNamed(context, '/billing');
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue[50],
appBar: const CustomHeader(
deviceName: 'SmartFlow',
// notificationCount: 3,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// Sensor Cards
Row(
children: [
Expanded(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(Icons.speed,
size: 28,
color: Color.fromARGB(255, 107, 139, 255)),
SizedBox(width: 6),
Text(
"Debit Air",
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: Color.fromARGB(255, 107, 139, 255)),
),
],
),
const SizedBox(height: 12),
_buildSensorCard(
value: _debit,
unit: "L/min",
cardColor: Color.fromARGB(255, 107, 139, 255),
),
],
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(Icons.water,
size: 28,
color: Color.fromARGB(255, 107, 139, 255)),
SizedBox(width: 6),
Text(
"Volume Air",
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: Color.fromARGB(255, 107, 139, 255),
),
),
],
),
const SizedBox(height: 12),
_buildSensorCard(
value: _volume,
unit: "Liter",
cardColor: Color.fromARGB(255, 107, 139, 255),
),
],
),
),
],
),
const SizedBox(height: 24),
// Pompa dan Tombol Tagihan
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Card(
color: Colors.white,
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 12.0),
child: Row(
children: [
Expanded(
child: Row(
children: [
Switch(
value: _isPumpOn,
onChanged: _togglePump,
activeColor:
const Color.fromARGB(255, 107, 139, 255),
),
const SizedBox(width: 8),
const Text(
"Control Pompa",
style: TextStyle(
fontWeight: FontWeight.w600,
color: Color.fromARGB(255, 107, 139, 255),
),
),
],
),
),
ElevatedButton.icon(
onPressed: _goToBillingPage,
icon:
const Icon(Icons.receipt_long, color: Colors.white),
label: const Text("Tagihan"),
style: ElevatedButton.styleFrom(
backgroundColor:
const Color.fromARGB(255, 107, 139, 255),
foregroundColor: Colors.white,
),
),
],
),
),
),
),
// Tombol Range Filter
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildRangeButton('day', 'Per Hari'),
_buildRangeButton('week', 'Per Minggu'),
_buildRangeButton('month', 'Per Bulan'),
],
),
const SizedBox(height: 16),
// Grafik Debit Air
SizedBox(
height: 280,
child: Card(
color: Colors.white,
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Grafik Debit Air",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color.fromARGB(255, 107, 139, 255))),
const SizedBox(height: 12),
Expanded(
child: LineChart(
LineChartData(
minY: 0,
gridData: FlGridData(
show: true,
drawVerticalLine: false,
getDrawingHorizontalLine: (value) => FlLine(
color: Colors.grey[300],
strokeWidth: 1,
),
),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: 20,
reservedSize: 32,
getTitlesWidget: (value, meta) => Text(
value.toInt().toString(),
style: const TextStyle(
fontSize: 12, color: Colors.black54),
),
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: _selectedRange == 'day' ? 4 : 1,
getTitlesWidget: (value, meta) => Text(
_selectedRange == 'day'
? '${value.toInt()}h'
: '${value.toInt() + 1}',
style: const TextStyle(
fontSize: 12, color: Colors.black54),
),
),
),
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false)),
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false)),
),
borderData: FlBorderData(
show: true,
border: const Border(
left: BorderSide(color: Colors.black12),
bottom: BorderSide(color: Colors.black12),
),
),
lineBarsData: [
LineChartBarData(
spots: _chartData,
isCurved: true,
color: const Color.fromARGB(255, 107, 139, 255),
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
colors: [
const Color.fromARGB(255, 107, 139, 255)
.withOpacity(0.4),
const Color.fromARGB(255, 107, 139, 255)
.withOpacity(0.1),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
barWidth: 3,
dotData: FlDotData(show: false),
),
],
),
),
),
],
),
),
),
),
],
),
),
);
}
Widget _buildRangeButton(String key, String label) {
final isSelected = _selectedRange == key;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: OutlinedButton(
onPressed: () => _onRangeChanged(key),
style: OutlinedButton.styleFrom(
backgroundColor: isSelected
? const Color.fromARGB(255, 107, 139, 255)
: Colors.white,
foregroundColor: isSelected
? Colors.white
: const Color.fromARGB(255, 107, 139, 255),
),
child: Text(label),
),
);
}
Widget _buildSensorCard({
required double value,
required String unit,
required Color cardColor,
}) {
return Card(
color: cardColor,
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Container(
width: 120,
height: 120,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: cardColor,
border: Border.all(color: Colors.white, width: 7),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
value.toStringAsFixed(1),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Text(
unit,
style: const TextStyle(
fontSize: 12,
color: Colors.white,
),
),
],
),
),
),
),
),
);
}
}

View File

@ -0,0 +1,298 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
import '../../widgets/costum_header.dart';
class HistoryScreen extends StatefulWidget {
const HistoryScreen({super.key});
@override
State<HistoryScreen> createState() => _HistoryScreenState();
}
class _HistoryScreenState extends State<HistoryScreen> {
DateTime? _startDate;
DateTime? _endDate;
List<String> _selectedDocIds = [];
final user = FirebaseAuth.instance.currentUser;
Future<void> _selectDate({required bool isStart}) async {
final now = DateTime.now();
final initialDate = isStart ? (_startDate ?? now) : (_endDate ?? now);
final picked = await showDatePicker(
context: context,
initialDate: initialDate,
firstDate: DateTime(now.year - 1),
lastDate: now,
);
if (picked != null) {
setState(() {
if (isStart) {
_startDate = picked;
} else {
_endDate = picked;
}
});
}
}
Stream<QuerySnapshot<Map<String, dynamic>>> _getFilteredHistory() {
final uid = user?.uid;
if (uid == null) return const Stream.empty();
final collection =
FirebaseFirestore.instance.collection('history').doc(uid).collection('data');
if (_startDate != null && _endDate != null) {
return collection
.where('timestamp', isGreaterThanOrEqualTo: _startDate)
.where('timestamp',
isLessThanOrEqualTo: _endDate!.add(const Duration(days: 1)))
.orderBy('timestamp', descending: true)
.snapshots();
}
return collection.orderBy('timestamp', descending: true).snapshots();
}
Future<void> _downloadSelected(List<QueryDocumentSnapshot<Map<String, dynamic>>> docs) async {
final pdf = pw.Document();
pdf.addPage(
pw.Page(
build: (pw.Context context) => pw.Column(
children: [
pw.Text("Laporan Riwayat Penggunaan Air",
style: pw.TextStyle(fontSize: 18)),
pw.SizedBox(height: 20),
pw.Table.fromTextArray(
headers: ["Tanggal", "Debit (L/min)", "Volume (L)"],
data: docs.map((doc) {
final data = doc.data();
final rawTimestamp = data['timestamp'];
DateTime? dateTime;
if (rawTimestamp is Timestamp) {
dateTime = rawTimestamp.toDate();
} else if (rawTimestamp is String) {
dateTime = DateTime.tryParse(rawTimestamp);
}
final dateStr = dateTime != null
? DateFormat('dd MMM yyyy, HH:mm').format(dateTime)
: '-';
return [
dateStr,
"${data['debit']?.toStringAsFixed(1) ?? '-'}",
"${data['volume']?.toStringAsFixed(1) ?? '-'}",
];
}).toList(),
),
],
),
),
);
await Printing.layoutPdf(onLayout: (format) async => pdf.save());
}
void _showCustomAlert() {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
title: const Text(
"Pilih Data Dulu",
style:
TextStyle(fontWeight: FontWeight.bold, color: Color(0xFF6B8BFF)),
),
content: const Text(
"Silakan pilih minimal satu data untuk diunduh.",
style: TextStyle(fontSize: 16),
),
actions: [
TextButton(
style: TextButton.styleFrom(
foregroundColor: const Color(0xFF6B8BFF),
),
onPressed: () => Navigator.pop(context),
child: const Text("Tutup"),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue[50],
appBar: const CustomHeader(
deviceName: "SmartFlow",
// notificationCount: 0,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
// Filter tanggal & tombol download
Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () => _selectDate(isStart: true),
icon: const Icon(Icons.calendar_today),
label: Text(
_startDate == null
? "Mulai"
: DateFormat('dd MMM yyyy').format(_startDate!),
),
),
),
const SizedBox(width: 8),
Expanded(
child: OutlinedButton.icon(
onPressed: () => _selectDate(isStart: false),
icon: const Icon(Icons.calendar_today_outlined),
label: Text(
_endDate == null
? "Akhir"
: DateFormat('dd MMM yyyy').format(_endDate!),
),
),
),
const SizedBox(width: 12),
ElevatedButton.icon(
onPressed: () async {
if (_selectedDocIds.isEmpty) {
_showCustomAlert();
return;
}
final uid = user?.uid;
if (uid == null) return;
final snapshot = await FirebaseFirestore.instance
.collection('history')
.doc(uid)
.collection('data')
.where(FieldPath.documentId, whereIn: _selectedDocIds)
.get();
await _downloadSelected(snapshot.docs);
},
icon: const Icon(Icons.download),
label: const Text("Download"),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6B8BFF),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
textStyle: const TextStyle(fontWeight: FontWeight.bold),
),
),
],
),
const SizedBox(height: 16),
// Tabel data
Expanded(
child: StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: _getFilteredHistory(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final docs = snapshot.data!.docs;
if (docs.isEmpty) {
return const Center(child: Text("Tidak ada data."));
}
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
child: DataTable(
columnSpacing: 32,
headingRowColor:
MaterialStateProperty.all(const Color(0xFF6B8BFF)),
headingTextStyle: const TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
columns: const [
DataColumn(
label:
SizedBox(width: 140, child: Text('Tanggal'))),
DataColumn(
label:
SizedBox(width: 50, child: Text('Debit'))),
DataColumn(
label:
SizedBox(width: 50, child: Text('Volume'))),
],
rows: docs.map((doc) {
final data = doc.data();
final id = doc.id;
final rawTimestamp = data['timestamp'];
DateTime? dateTime;
if (rawTimestamp is Timestamp) {
dateTime = rawTimestamp.toDate();
} else if (rawTimestamp is String) {
dateTime = DateTime.tryParse(rawTimestamp);
}
final dateStr = dateTime != null
? DateFormat('dd MMM yyyy, HH:mm')
.format(dateTime)
: '-';
final debit =
"${data['debit']?.toStringAsFixed(1) ?? '-'}";
final volume =
"${data['volume']?.toStringAsFixed(1) ?? '-'}";
final selected = _selectedDocIds.contains(id);
return DataRow(
selected: selected,
onSelectChanged: (_) {
setState(() {
if (selected) {
_selectedDocIds.remove(id);
} else {
_selectedDocIds.add(id);
}
});
},
cells: [
DataCell(
SizedBox(width: 140, child: Text(dateStr))),
DataCell(
SizedBox(width: 50, child: Text(debit))),
DataCell(
SizedBox(width: 50, child: Text(volume))),
],
);
}).toList(),
),
),
);
},
),
),
],
),
),
);
}
}

View File

@ -0,0 +1,189 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class NotificationScreen extends StatelessWidget {
const NotificationScreen({super.key});
IconData getIcon(String level) {
switch (level) {
case 'warning':
return Icons.warning_amber;
case 'info':
return Icons.info_outline;
case 'success':
return Icons.check_circle;
case 'error':
return Icons.error;
default:
return Icons.notifications;
}
}
Color getColor(String level) {
switch (level) {
case 'warning':
return Colors.orange;
case 'info':
return Colors.blue;
case 'success':
return Colors.green;
case 'error':
return Colors.redAccent;
default:
return Colors.grey;
}
}
String formatWaktu(Timestamp? timestamp) {
if (timestamp == null) return "";
final date = timestamp.toDate();
return DateFormat("dd MMMM yyyy • HH:mm", "id_ID").format(date);
}
@override
Widget build(BuildContext context) {
final uid = FirebaseAuth.instance.currentUser?.uid;
if (uid == null) {
return const Scaffold(
body: Center(child: Text("Anda belum login")),
);
}
final messenger = ScaffoldMessenger.of(context);
final notifRef = FirebaseFirestore.instance
.collection('notifikasi')
.doc(uid)
.collection('data')
.orderBy('timestamp', descending: true);
return Scaffold(
backgroundColor: const Color(0xFFF1F5FF),
appBar: AppBar(
title: const Text("Notifikasi"),
backgroundColor: const Color(0xFF6B8BFF),
foregroundColor: Colors.white,
elevation: 0,
),
body: StreamBuilder<QuerySnapshot>(
stream: notifRef.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
final docs = snapshot.data?.docs ?? [];
if (docs.isEmpty) {
return const Center(
child: Text("Tidak ada notifikasi."),
);
}
return ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: docs.length,
itemBuilder: (context, index) {
final doc = docs[index];
final data = doc.data() as Map<String, dynamic>;
final title = data['judul'] ?? 'Notifikasi';
final message = data['pesan'] ?? '';
final level = data['level'] ?? 'info';
final timestamp = data['timestamp'] as Timestamp?;
return Dismissible(
key: Key(doc.id),
background: Container(
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.circular(16),
),
alignment: Alignment.centerRight,
padding: const EdgeInsets.symmetric(horizontal: 24),
child: const Icon(Icons.delete, color: Colors.white),
),
direction: DismissDirection.endToStart,
onDismissed: (_) {
doc.reference.delete().then((_) {
messenger.showSnackBar(
const SnackBar(content: Text('Notifikasi dihapus')),
);
}).catchError((e) {
messenger.showSnackBar(
SnackBar(content: Text('Gagal menghapus: $e')),
);
});
},
child: Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: const [
BoxShadow(
color: Colors.black12,
blurRadius: 6,
offset: Offset(0, 3),
),
],
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: getColor(level).withOpacity(0.12),
shape: BoxShape.circle,
),
child: Icon(
getIcon(level),
color: getColor(level),
size: 28,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF1D2939),
),
),
const SizedBox(height: 4),
Text(
message,
style: const TextStyle(
fontSize: 14,
color: Color(0xFF475467),
),
),
const SizedBox(height: 4),
Text(
formatWaktu(timestamp),
style: const TextStyle(
fontSize: 12,
color: Color(0xFF98A2B3),
),
),
],
),
)
],
),
),
);
},
);
},
),
);
}
}

View File

@ -0,0 +1,336 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import '../../widgets/costum_header.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
final DatabaseReference _dbKalibrasi =
FirebaseDatabase.instance.ref('kalibrasi');
double? _calibrationValue;
int? _tarifValue;
String? uid;
@override
void initState() {
super.initState();
uid = FirebaseAuth.instance.currentUser?.uid;
_fetchCalibration();
_fetchTarif();
}
Future<void> _fetchCalibration() async {
final snapshot = await _dbKalibrasi.get();
if (snapshot.exists) {
setState(() {
_calibrationValue = double.tryParse(snapshot.value.toString());
});
}
}
Future<void> _fetchTarif() async {
if (uid == null) return;
final snapshot =
await FirebaseDatabase.instance.ref('tarif/$uid').get();
if (snapshot.exists) {
setState(() {
_tarifValue = int.tryParse(snapshot.value.toString());
});
}
}
Future<void> _showCalibrationDialog() async {
final controller = TextEditingController(
text: _calibrationValue?.toString() ?? '',
);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text(
"Edit Nilai Kalibrasi",
style:
TextStyle(fontWeight: FontWeight.bold, color: Color(0xFF6B8BFF)),
),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 8),
TextField(
controller: controller,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(
labelText: "Masukkan nilai kalibrasi (misal: 4.5)",
border: OutlineInputBorder(),
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Batal", style: TextStyle(color: Colors.grey)),
),
ElevatedButton(
onPressed: () async {
final value = double.tryParse(controller.text);
if (value == null) {
Navigator.pop(context);
_showMessage("Input tidak valid!", isError: true);
return;
}
setState(() => _calibrationValue = value);
await _dbKalibrasi.set(value);
Navigator.pop(context);
_showMessage("Kalibrasi berhasil disimpan!", isError: false);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6B8BFF),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
),
child: const Text("Simpan"),
),
],
),
);
}
Future<void> _showTarifDialog() async {
final controller = TextEditingController(
text: _tarifValue?.toString() ?? '',
);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text(
"Edit Tarif Air (Rp/liter)",
style:
TextStyle(fontWeight: FontWeight.bold, color: Color(0xFF6B8BFF)),
),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 8),
TextField(
controller: controller,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: "Masukkan tarif air (misal: 5)",
border: OutlineInputBorder(),
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Batal", style: TextStyle(color: Colors.grey)),
),
ElevatedButton(
onPressed: () async {
final value = int.tryParse(controller.text);
if (value == null || uid == null) {
Navigator.pop(context);
_showMessage("Input tidak valid!", isError: true);
return;
}
setState(() => _tarifValue = value);
await FirebaseDatabase.instance.ref('tarif/$uid').set(value);
Navigator.pop(context);
_showMessage("Tarif berhasil disimpan!", isError: false);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6B8BFF),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
),
child: const Text("Simpan"),
),
],
),
);
}
void _logout() async {
await FirebaseAuth.instance.signOut();
if (mounted) {
Navigator.pushReplacementNamed(context, '/login');
}
}
void _showMessage(String message, {required bool isError}) {
showDialog(
context: context,
builder: (_) => AlertDialog(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
contentPadding: const EdgeInsets.all(24),
titlePadding: const EdgeInsets.only(top: 24),
title: Column(
children: [
Icon(
isError ? Icons.error_outline : Icons.check_circle_outline,
color: isError ? Colors.redAccent : const Color(0xFF6B8BFF),
size: 48,
),
const SizedBox(height: 16),
Text(
isError ? "Gagal" : "Berhasil",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: isError ? Colors.redAccent : const Color(0xFF6B8BFF),
),
textAlign: TextAlign.center,
),
],
),
content: Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16),
),
actionsAlignment: MainAxisAlignment.center,
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text(
"OK",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF6B8BFF),
),
),
)
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue[50],
appBar: const CustomHeader(
deviceName: "SmartFlow",
// notificationCount: 8,
),
body: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
children: [
// Kalibrasi
Card(
color: Colors.white,
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: ListTile(
contentPadding: const EdgeInsets.all(16),
title: const Text(
"Kalibrasi Sensor",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Color(0xFF6B8BFF),
),
),
subtitle: Text(
_calibrationValue != null
? "${_calibrationValue!.toStringAsFixed(2)} (L/pulse)"
: "Belum ada data",
style: const TextStyle(fontSize: 14),
),
trailing: ElevatedButton.icon(
onPressed: _showCalibrationDialog,
icon: const Icon(Icons.edit),
label: const Text("Edit"),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6B8BFF),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 10),
),
),
),
),
const SizedBox(height: 16),
// Tarif
Card(
color: Colors.white,
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: ListTile(
contentPadding: const EdgeInsets.all(16),
title: const Text(
"Tarif Air",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Color(0xFF6B8BFF),
),
),
subtitle: Text(
_tarifValue != null
? "Rp$_tarifValue / liter"
: "Belum ada data",
style: const TextStyle(fontSize: 14),
),
trailing: ElevatedButton.icon(
onPressed: _showTarifDialog,
icon: const Icon(Icons.edit),
label: const Text("Edit"),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF6B8BFF),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 10),
),
),
),
),
const SizedBox(height: 32),
// Logout
ElevatedButton.icon(
onPressed: _logout,
icon: const Icon(Icons.logout),
label: const Text("Logout"),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.redAccent,
foregroundColor: Colors.white,
minimumSize: const Size.fromHeight(50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
)
],
),
),
);
}
}

View File

@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
super.initState();
_checkAuthStatus();
}
Future<void> _checkAuthStatus() async {
await Future.delayed(const Duration(seconds: 2));
final user = FirebaseAuth.instance.currentUser;
try {
if (user != null) {
// Paksa refresh untuk validasi user masih ada di server
await user.reload();
final refreshedUser = FirebaseAuth.instance.currentUser;
if (refreshedUser == null) {
// User sudah dihapus dari server
Navigator.pushReplacementNamed(context, '/login');
} else {
Navigator.pushReplacementNamed(context, '/home');
}
} else {
Navigator.pushReplacementNamed(context, '/login');
}
} catch (e) {
// Jika reload gagal (misalnya user sudah tidak ada)
await FirebaseAuth.instance.signOut();
Navigator.pushReplacementNamed(context, '/login');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
const Color.fromARGB(255, 107, 139, 255),
Color.fromARGB(255, 139, 164, 255)
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
width: double.infinity,
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.water_drop_rounded, size: 100, color: Colors.white),
SizedBox(height: 24),
Text(
'Monitoring Debit Air',
style: TextStyle(
fontSize: 26,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 16),
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
],
),
),
);
}
}

View File

View File

View File

View File

112
lib/widgets/bottom_bar.dart Normal file
View File

@ -0,0 +1,112 @@
import 'package:flutter/material.dart';
import 'package:smartflow/screens/settings/settings_screen.dart';
import '../../screens/dashboard/dashboard_screen.dart';
import '../../screens/history/history_screen.dart';
class CustomBottomNav extends StatefulWidget {
const CustomBottomNav({super.key});
@override
State<CustomBottomNav> createState() => _CustomBottomNavState();
}
class _CustomBottomNavState extends State<CustomBottomNav> {
int _currentIndex = 1; // Dashboard di tengah, index = 1
final List<Widget> _screens = const [
HistoryScreen(), // index 0 (kiri)
DashboardScreen(), // index 1 (tengah - FAB)
SettingsScreen(), // index 2 (kanan)
];
void _onTap(int index) {
setState(() {
_currentIndex = index;
});
}
void _onMiddleTap() {
_onTap(1); // Dashboard
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
extendBody: true,
resizeToAvoidBottomInset:
false, // <--- Fix FAB ikut naik saat keyboard muncul
body: _screens[_currentIndex],
floatingActionButton: FloatingActionButton(
onPressed: _onMiddleTap,
backgroundColor: const Color.fromARGB(255, 107, 139, 255),
elevation: 8,
shape: const CircleBorder(),
child: Icon(
_currentIndex == 1 ? Icons.dashboard : Icons.dashboard_outlined,
size: 30,
color: Colors.white,
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: BottomAppBar(
shape: const CircularNotchedRectangle(),
notchMargin: 10,
elevation: 10,
color: Colors.white,
child: SizedBox(
height: 70,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildNavItem(
Icons.history_outlined,
Icons.history,
"History",
0,
),
const SizedBox(width: 48), // Spacer untuk FAB
_buildNavItem(
Icons.settings_outlined,
Icons.settings,
"Setting",
2,
),
],
),
),
),
);
}
Widget _buildNavItem(IconData icon, IconData activeIcon, String label, int index) {
final isSelected = _currentIndex == index;
return GestureDetector(
onTap: () => _onTap(index),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
isSelected ? activeIcon : icon,
color: isSelected
? const Color.fromARGB(255, 107, 139, 255)
: Colors.grey,
),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 12,
color: isSelected
? const Color.fromARGB(255, 107, 139, 255)
: Colors.grey,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
],
),
);
}
}

View File

@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import '../../screens/dashboard/dashboard_screen.dart';
import '../../screens/history/history_screen.dart';
import '../../screens/billing/billing_screen.dart';
class CustomBottomNav extends StatefulWidget {
const CustomBottomNav({super.key});
@override
State<CustomBottomNav> createState() => _CustomBottomNavState();
}
class _CustomBottomNavState extends State<CustomBottomNav> {
int _currentIndex = 0;
final List<Widget> _screens = const [
DashboardScreen(),
HistoryScreen(),
BillingScreen(),
];
void _onTap(int index) {
setState(() {
_currentIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: _screens[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: _onTap,
selectedItemColor: Colors.blueAccent,
unselectedItemColor: Colors.grey,
backgroundColor: Colors.white,
type: BottomNavigationBarType.fixed,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.dashboard),
label: 'Dashboard',
),
BottomNavigationBarItem(
icon: Icon(Icons.history),
label: 'Riwayat',
),
BottomNavigationBarItem(
icon: Icon(Icons.receipt_long),
label: 'Tagihan',
),
],
),
);
}
}

View File

@ -0,0 +1,116 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class CustomHeader extends StatelessWidget implements PreferredSizeWidget {
final String deviceName;
const CustomHeader({
super.key,
required this.deviceName,
});
@override
Widget build(BuildContext context) {
final String today =
DateFormat("d MMMM yyyy", 'id_ID').format(DateTime.now());
final user = FirebaseAuth.instance.currentUser;
final uid = user?.uid;
return AppBar(
backgroundColor: const Color(0xFF6B8BFF),
elevation: 0,
automaticallyImplyLeading: false,
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
deviceName,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 2),
Text(
today,
style: const TextStyle(
fontSize: 14,
color: Colors.white70,
),
),
],
),
actions: [
if (uid != null)
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("notifikasi")
.doc(uid)
.collection("data")
.snapshots(),
builder: (context, snapshot) {
final notifCount = snapshot.data?.docs.length ?? 0;
return Padding(
padding: const EdgeInsets.only(right: 16),
child: GestureDetector(
onTap: () {
Navigator.pushNamed(context, '/notifications');
},
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
width: 44,
height: 44,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white24,
),
child: const Icon(
Icons.notifications,
color: Colors.white,
size: 26,
),
),
if (notifCount > 0)
Positioned(
right: -2,
top: -2,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
constraints:
const BoxConstraints(minWidth: 20, minHeight: 20),
child: Center(
child: Text(
notifCount > 99 ? '99+' : '$notifCount',
style: const TextStyle(
color: Colors.white,
fontSize: 11,
fontWeight: FontWeight.bold,
),
),
),
),
),
],
),
),
);
},
),
],
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight + 4);
}

View File

626
pubspec.lock Normal file
View File

@ -0,0 +1,626 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_flutterfire_internals:
dependency: transitive
description:
name: _flutterfire_internals
sha256: "214e6f07e2a44f45972e0365c7b537eaeaddb4598db0778dd4ac64b4acd3f5b1"
url: "https://pub.dev"
source: hosted
version: "1.3.55"
archive:
dependency: transitive
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev"
source: hosted
version: "4.0.7"
async:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.11.0"
barcode:
dependency: transitive
description:
name: barcode
sha256: "7b6729c37e3b7f34233e2318d866e8c48ddb46c1f7ad01ff7bb2a8de1da2b9f4"
url: "https://pub.dev"
source: hosted
version: "2.2.9"
bidi:
dependency: transitive
description:
name: bidi
sha256: "77f475165e94b261745cf1032c751e2032b8ed92ccb2bf5716036db79320637d"
url: "https://pub.dev"
source: hosted
version: "2.0.13"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
characters:
dependency: transitive
description:
name: characters
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
clock:
dependency: transitive
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
cloud_firestore:
dependency: "direct main"
description:
name: cloud_firestore
sha256: d25c956be5261c14bc9a69c9662de8addb308376b4b53a64469aade52e7b02f8
url: "https://pub.dev"
source: hosted
version: "5.6.8"
cloud_firestore_platform_interface:
dependency: transitive
description:
name: cloud_firestore_platform_interface
sha256: ee2b8f8c602ede36073afd3741e99cfea9dd982b4a44833daf665134d151c32a
url: "https://pub.dev"
source: hosted
version: "6.6.8"
cloud_firestore_web:
dependency: transitive
description:
name: cloud_firestore_web
sha256: b99bc4f1f70787f694b73bc6fce238d4d6cc822c9b31ba8ef1578b180b6f77bc
url: "https://pub.dev"
source: hosted
version: "4.4.8"
collection:
dependency: transitive
description:
name: collection
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
url: "https://pub.dev"
source: hosted
version: "1.19.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev"
source: hosted
version: "1.0.8"
equatable:
dependency: transitive
description:
name: equatable
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
url: "https://pub.dev"
source: hosted
version: "2.0.7"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
firebase_auth:
dependency: "direct main"
description:
name: firebase_auth
sha256: "10cd3f00a247f33b0a5c77574011a87379432bf3fec77a500b55f2bcc30ddd8b"
url: "https://pub.dev"
source: hosted
version: "5.5.4"
firebase_auth_platform_interface:
dependency: transitive
description:
name: firebase_auth_platform_interface
sha256: "2d15872a8899b0459fab6b4c148fd142e135acfc8a303d383d80b455e4dba7bd"
url: "https://pub.dev"
source: hosted
version: "7.6.3"
firebase_auth_web:
dependency: transitive
description:
name: firebase_auth_web
sha256: efba45393050ca03d992eae1d305d5fc8c0c9f5980624053512e935c23767c4f
url: "https://pub.dev"
source: hosted
version: "5.14.3"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
sha256: "8cfe3c900512399ce8d50fcc817e5758ff8615eeb6fa5c846a4cc47bbf6353b6"
url: "https://pub.dev"
source: hosted
version: "3.13.1"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: d7253d255ff10f85cfd2adaba9ac17bae878fa3ba577462451163bd9f1d1f0bf
url: "https://pub.dev"
source: hosted
version: "5.4.0"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: ddd72baa6f727e5b23f32d9af23d7d453d67946f380bd9c21daf474ee0f7326e
url: "https://pub.dev"
source: hosted
version: "2.23.0"
firebase_database:
dependency: "direct main"
description:
name: firebase_database
sha256: e98c32b9462779f4c45eb036dcaf22cd6a17204e80f77c292bd1d05b08c8ef81
url: "https://pub.dev"
source: hosted
version: "11.3.6"
firebase_database_platform_interface:
dependency: transitive
description:
name: firebase_database_platform_interface
sha256: "3e81c5e2152ad996309458f8338977989d81a44a692e27180db9380f6faffafb"
url: "https://pub.dev"
source: hosted
version: "0.2.6+6"
firebase_database_web:
dependency: transitive
description:
name: firebase_database_web
sha256: "0289c1c7832b3ca79520517c54bf42a40b9bfefa8198906145ab17e75989d80f"
url: "https://pub.dev"
source: hosted
version: "0.2.6+12"
fl_chart:
dependency: "direct main"
description:
name: fl_chart
sha256: "577aeac8ca414c25333334d7c4bb246775234c0e44b38b10a82b559dd4d764e7"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: transitive
description:
name: http
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
image:
dependency: transitive
description:
name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
url: "https://pub.dev"
source: hosted
version: "4.5.4"
intl:
dependency: "direct main"
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
version: "0.20.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
url: "https://pub.dev"
source: hosted
version: "10.0.7"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
url: "https://pub.dev"
source: hosted
version: "3.0.8"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
url: "https://pub.dev"
source: hosted
version: "5.1.1"
matcher:
dependency: transitive
description:
name: matcher
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev"
source: hosted
version: "1.15.0"
path:
dependency: transitive
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
path_parsing:
dependency: transitive
description:
name: path_parsing
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
url: "https://pub.dev"
source: hosted
version: "2.2.17"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.3.0"
pdf:
dependency: "direct main"
description:
name: pdf
sha256: "28eacad99bffcce2e05bba24e50153890ad0255294f4dd78a17075a2ba5c8416"
url: "https://pub.dev"
source: hosted
version: "3.11.3"
pdf_widget_wrapper:
dependency: transitive
description:
name: pdf_widget_wrapper
sha256: c930860d987213a3d58c7ec3b7ecf8085c3897f773e8dc23da9cae60a5d6d0f5
url: "https://pub.dev"
source: hosted
version: "1.0.4"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted
version: "6.0.2"
platform:
dependency: transitive
description:
name: platform
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
posix:
dependency: transitive
description:
name: posix
sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62
url: "https://pub.dev"
source: hosted
version: "6.0.2"
printing:
dependency: "direct main"
description:
name: printing
sha256: "482cd5a5196008f984bb43ed0e47cbfdca7373490b62f3b27b3299275bf22a93"
url: "https://pub.dev"
source: hosted
version: "5.14.2"
qr:
dependency: transitive
description:
name: qr
sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
url: "https://pub.dev"
source: hosted
version: "2.5.3"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac"
url: "https://pub.dev"
source: hosted
version: "2.4.10"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
url: "https://pub.dev"
source: hosted
version: "2.4.3"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_span:
dependency: transitive
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
url: "https://pub.dev"
source: hosted
version: "1.12.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.2"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
url: "https://pub.dev"
source: hosted
version: "0.7.3"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b
url: "https://pub.dev"
source: hosted
version: "14.3.0"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted
version: "6.5.0"
sdks:
dart: ">=3.6.2 <4.0.0"
flutter: ">=3.27.4"

99
pubspec.yaml Normal file
View File

@ -0,0 +1,99 @@
name: smartflow
description: "A new Flutter project."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: ^3.6.2
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
firebase_core: ^3.13.1
firebase_auth: ^5.5.4
firebase_database: ^11.3.6
fl_chart: ^1.0.0
intl: ^0.20.2
cloud_firestore: ^5.6.8
pdf: ^3.11.3
printing: ^5.14.2
path_provider: ^2.1.5
shared_preferences: ^2.5.3
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - 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/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# 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/to/font-from-package

BIN
web/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

BIN
web/icons/Icon-192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
web/icons/Icon-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

38
web/index.html Normal file
View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="smartflow">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>smartflow</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<script src="flutter_bootstrap.js" async></script>
</body>
</html>

35
web/manifest.json Normal file
View File

@ -0,0 +1,35 @@
{
"name": "smartflow",
"short_name": "smartflow",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}