first commit

This commit is contained in:
Andreas Malvino
2025-06-02 22:39:03 +07:00
commit e7090af3da
245 changed files with 49210 additions and 0 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
SUPABASE_URL=https://jyrmkggbszzloxyibcwf.supabase.co
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imp5cm1rZ2dic3p6bG94eWliY3dmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDIzNjQzOTUsImV4cCI6MjA1Nzk0MDM5NX0.bAG51o1cou2HTtNt-lHpl722mBQ5-qv4uZbx6mJgfOw

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: "c23637390482d4cf9598c3ce3f2be31aa7332daf"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: android
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: ios
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: linux
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: macos
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: web
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
- platform: windows
create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf
# 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 @@
# bumrent_app
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

14
android/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,44 @@
plugins {
id("com.android.application")
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.bumrent_app"
compileSdk = flutter.compileSdkVersion
ndkVersion = "27.0.12077973"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.bumrent_app"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
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.getByName("debug")
}
}
}
flutter {
source = "../.."
}

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="bumrent_app"
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.bumrent_app
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>

21
android/build.gradle.kts Normal file
View File

@ -0,0 +1,21 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View File

@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -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.10.2-all.zip

View File

@ -0,0 +1,25 @@
pluginManagement {
val flutterSdkPath = run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
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.7.0" apply false
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
}
include(":app")

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

BIN
assets/images/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
assets/images/kerusakan.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
assets/images/logo_lama.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
assets/images/profil.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

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.bumrentApp;
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.bumrentApp.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.bumrentApp.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.bumrentApp.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.bumrentApp;
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.bumrentApp;
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,99 @@
<?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"
enableGPUValidationMode = "1"
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>Bumrent App</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>bumrent_app</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.
}
}

View File

@ -0,0 +1,25 @@
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import '../data/providers/auth_provider.dart';
import '../modules/auth/controllers/auth_controller.dart';
class AuthBinding extends Bindings {
@override
void dependencies() {
debugPrint('Initializing AuthBinding dependencies');
// Pastikan AuthProvider dibuat sekali dan bersifat permanen
if (!Get.isRegistered<AuthProvider>()) {
debugPrint('Registering AuthProvider in AuthBinding');
Get.put<AuthProvider>(AuthProvider(), permanent: true);
} else {
debugPrint('AuthProvider already registered');
}
// Buat AuthController
debugPrint('Creating AuthController');
Get.lazyPut<AuthController>(() => AuthController());
debugPrint('AuthBinding dependencies initialized');
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,30 @@
import 'package:get/get.dart';
import '../data/providers/auth_provider.dart';
import '../modules/petugas_bumdes/controllers/petugas_bumdes_dashboard_controller.dart';
class PetugasBumdesBinding extends Bindings {
@override
void dependencies() {
// Pastikan AuthProvider teregistrasi
if (!Get.isRegistered<AuthProvider>()) {
Get.put(AuthProvider());
}
// Hapus terlebih dahulu untuk memastikan clean state
try {
if (Get.isRegistered<PetugasBumdesDashboardController>()) {
Get.delete<PetugasBumdesDashboardController>(force: true);
}
} catch (e) {
print('Error removing controller: $e');
}
// Gunakan put untuk memastikan controller selalu tersedia dan permanent
Get.put<PetugasBumdesDashboardController>(
PetugasBumdesDashboardController(),
permanent: true,
);
print('✅ PetugasBumdesDashboardController registered successfully');
}
}

View File

@ -0,0 +1,13 @@
import 'package:get/get.dart';
import '../data/providers/auth_provider.dart';
import '../modules/petugas_mitra/controllers/petugas_mitra_dashboard_controller.dart';
class PetugasMitraBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<AuthProvider>(() => AuthProvider());
Get.lazyPut<PetugasMitraDashboardController>(
() => PetugasMitraDashboardController(),
);
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,25 @@
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import '../data/providers/auth_provider.dart';
import '../modules/splash/controllers/splash_controller.dart';
class SplashBinding extends Bindings {
@override
void dependencies() {
debugPrint('Initializing SplashBinding dependencies');
// Pastikan AuthProvider dibuat sekali dan bersifat permanen
if (!Get.isRegistered<AuthProvider>()) {
debugPrint('Registering AuthProvider in SplashBinding');
Get.put<AuthProvider>(AuthProvider(), permanent: true);
} else {
debugPrint('AuthProvider already registered');
}
// Buat SplashController
debugPrint('Creating SplashController');
Get.put<SplashController>(SplashController());
debugPrint('SplashBinding dependencies initialized');
}
}

View File

@ -0,0 +1,11 @@
import 'package:get/get.dart';
import '../data/providers/auth_provider.dart';
import '../modules/warga/controllers/warga_dashboard_controller.dart';
class WargaBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<AuthProvider>(() => AuthProvider());
Get.lazyPut<WargaDashboardController>(() => WargaDashboardController());
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,84 @@
import 'package:get/get.dart';
class AsetModel {
final String id;
final String nama;
final String deskripsi;
final String kategori;
final int harga;
final int? denda;
final String status;
final DateTime? createdAt;
final DateTime? updatedAt;
final int? kuantitas;
final int? kuantitasTerpakai;
final String? satuanUkur;
// Untuk menampung URL gambar pertama dari tabel foto_aset
String? imageUrl;
// Menggunakan RxList untuk membuatnya mutable dan reaktif
RxList<Map<String, dynamic>> satuanWaktuSewa = <Map<String, dynamic>>[].obs;
AsetModel({
required this.id,
required this.nama,
required this.deskripsi,
required this.kategori,
required this.harga,
this.denda,
required this.status,
this.createdAt,
this.updatedAt,
this.kuantitas,
this.kuantitasTerpakai,
this.satuanUkur,
this.imageUrl,
List<Map<String, dynamic>>? initialSatuanWaktuSewa,
}) {
// Inisialisasi satuanWaktuSewa jika ada data awal
if (initialSatuanWaktuSewa != null) {
satuanWaktuSewa.addAll(initialSatuanWaktuSewa);
}
}
factory AsetModel.fromJson(Map<String, dynamic> json) {
return AsetModel(
id: json['id'] ?? '',
nama: json['nama'] ?? '',
deskripsi: json['deskripsi'] ?? '',
kategori: json['kategori'] ?? '',
harga: json['harga'] ?? 0,
denda: json['denda'],
status: json['status'] ?? '',
createdAt:
json['created_at'] != null
? DateTime.parse(json['created_at'])
: null,
updatedAt:
json['updated_at'] != null
? DateTime.parse(json['updated_at'])
: null,
kuantitas: json['kuantitas'],
kuantitasTerpakai: json['kuantitas_terpakai'],
satuanUkur: json['satuan_ukur'],
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'nama': nama,
'deskripsi': deskripsi,
'kategori': kategori,
'harga': harga,
'denda': denda,
'status': status,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
'kuantitas': kuantitas,
'kuantitas_terpakai': kuantitasTerpakai,
'satuan_ukur': satuanUkur,
};
}
}

View File

@ -0,0 +1,41 @@
class FotoAsetModel {
final String id;
final String fotoAset; // URL foto
final DateTime? createdAt;
final DateTime? updatedAt;
final String idAset;
FotoAsetModel({
required this.id,
required this.fotoAset,
this.createdAt,
this.updatedAt,
required this.idAset,
});
factory FotoAsetModel.fromJson(Map<String, dynamic> json) {
return FotoAsetModel(
id: json['id'] ?? '',
fotoAset: json['foto_aset'] ?? '',
createdAt:
json['created_at'] != null
? DateTime.parse(json['created_at'])
: null,
updatedAt:
json['updated_at'] != null
? DateTime.parse(json['updated_at'])
: null,
idAset: json['id_aset'] ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'foto_aset': fotoAset,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
'id_aset': idAset,
};
}
}

View File

@ -0,0 +1,54 @@
import 'dart:convert';
class PaketModel {
final String? id;
final String? nama;
final String? deskripsi;
final int? harga;
final int? kuantitas;
final String? foto_paket;
final List<dynamic>? satuanWaktuSewa;
PaketModel({
this.id,
this.nama,
this.deskripsi,
this.harga,
this.kuantitas,
this.foto_paket,
this.satuanWaktuSewa,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'nama': nama,
'deskripsi': deskripsi,
'harga': harga,
'kuantitas': kuantitas,
'foto_paket': foto_paket,
'satuanWaktuSewa': satuanWaktuSewa,
};
}
factory PaketModel.fromMap(Map<String, dynamic> map) {
return PaketModel(
id: map['id'],
nama: map['nama'],
deskripsi: map['deskripsi'],
harga: map['harga']?.toInt(),
kuantitas: map['kuantitas']?.toInt(),
foto_paket: map['foto_paket'],
satuanWaktuSewa: map['satuanWaktuSewa'],
);
}
String toJson() => json.encode(toMap());
factory PaketModel.fromJson(String source) => PaketModel.fromMap(json.decode(source));
@override
String toString() {
return 'PaketModel(id: $id, nama: $nama, deskripsi: $deskripsi, harga: $harga, kuantitas: $kuantitas, foto_paket: $foto_paket, satuanWaktuSewa: $satuanWaktuSewa)';
}
}

View File

@ -0,0 +1,79 @@
class PesananModel {
final String id;
final String asetId;
final String satuanWaktuId;
final String userId;
final String status;
final DateTime tanggalPemesanan;
final String jamPemesanan;
final int durasi;
final int totalHarga;
final DateTime? createdAt;
final DateTime? updatedAt;
// Optional fields for joined data from other tables
String? namaSatuanWaktu;
String? namaAset;
String? namaUser;
PesananModel({
required this.id,
required this.asetId,
required this.satuanWaktuId,
required this.userId,
required this.status,
required this.tanggalPemesanan,
required this.jamPemesanan,
required this.durasi,
required this.totalHarga,
this.createdAt,
this.updatedAt,
this.namaSatuanWaktu,
this.namaAset,
this.namaUser,
});
factory PesananModel.fromJson(Map<String, dynamic> json) {
return PesananModel(
id: json['id'] ?? '',
asetId: json['aset_id'] ?? '',
satuanWaktuId: json['satuan_waktu_id'] ?? '',
userId: json['user_id'] ?? '',
status: json['status'] ?? 'pending',
tanggalPemesanan:
json['tanggal_pemesanan'] != null
? DateTime.parse(json['tanggal_pemesanan'])
: DateTime.now(),
jamPemesanan: json['jam_pemesanan'] ?? '00:00',
durasi: json['durasi'] ?? 1,
totalHarga: json['total_harga'] ?? 0,
createdAt:
json['created_at'] != null
? DateTime.parse(json['created_at'])
: null,
updatedAt:
json['updated_at'] != null
? DateTime.parse(json['updated_at'])
: null,
namaSatuanWaktu: json['nama_satuan_waktu'],
namaAset: json['nama_aset'],
namaUser: json['nama_user'],
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'aset_id': asetId,
'satuan_waktu_id': satuanWaktuId,
'user_id': userId,
'status': status,
'tanggal_pemesanan': tanggalPemesanan.toIso8601String().split('T')[0],
'jam_pemesanan': jamPemesanan,
'durasi': durasi,
'total_harga': totalHarga,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
};
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,102 @@
import 'dart:convert';
class RentalItem {
final String id;
final String title;
final String description;
final double pricePerDay;
final String? imageUrl;
final String ownerId;
final String category;
final List<String>? features;
final bool isAvailable;
final String? location;
final DateTime createdAt;
final DateTime? updatedAt;
RentalItem({
required this.id,
required this.title,
required this.description,
required this.pricePerDay,
this.imageUrl,
required this.ownerId,
required this.category,
this.features,
required this.isAvailable,
this.location,
required this.createdAt,
this.updatedAt,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'title': title,
'description': description,
'price_per_day': pricePerDay,
'image_url': imageUrl,
'owner_id': ownerId,
'category': category,
'features': features,
'is_available': isAvailable,
'location': location,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
};
}
factory RentalItem.fromMap(Map<String, dynamic> map) {
return RentalItem(
id: map['id'] ?? '',
title: map['title'] ?? '',
description: map['description'] ?? '',
pricePerDay: map['price_per_day']?.toDouble() ?? 0.0,
imageUrl: map['image_url'],
ownerId: map['owner_id'] ?? '',
category: map['category'] ?? '',
features:
map['features'] != null ? List<String>.from(map['features']) : null,
isAvailable: map['is_available'] ?? true,
location: map['location'],
createdAt: DateTime.parse(map['created_at']),
updatedAt:
map['updated_at'] != null ? DateTime.parse(map['updated_at']) : null,
);
}
String toJson() => json.encode(toMap());
factory RentalItem.fromJson(String source) =>
RentalItem.fromMap(json.decode(source));
RentalItem copyWith({
String? id,
String? title,
String? description,
double? pricePerDay,
String? imageUrl,
String? ownerId,
String? category,
List<String>? features,
bool? isAvailable,
String? location,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return RentalItem(
id: id ?? this.id,
title: title ?? this.title,
description: description ?? this.description,
pricePerDay: pricePerDay ?? this.pricePerDay,
imageUrl: imageUrl ?? this.imageUrl,
ownerId: ownerId ?? this.ownerId,
category: category ?? this.category,
features: features ?? this.features,
isAvailable: isAvailable ?? this.isAvailable,
location: location ?? this.location,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}

View File

@ -0,0 +1,37 @@
class SatuanWaktuModel {
final String id;
final String namaSatuanWaktu;
final DateTime? createdAt;
final DateTime? updatedAt;
SatuanWaktuModel({
required this.id,
required this.namaSatuanWaktu,
this.createdAt,
this.updatedAt,
});
factory SatuanWaktuModel.fromJson(Map<String, dynamic> json) {
return SatuanWaktuModel(
id: json['id'] ?? '',
namaSatuanWaktu: json['nama_satuan_waktu'] ?? '',
createdAt:
json['created_at'] != null
? DateTime.parse(json['created_at'])
: null,
updatedAt:
json['updated_at'] != null
? DateTime.parse(json['updated_at'])
: null,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'nama_satuan_waktu': namaSatuanWaktu,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
};
}
}

View File

@ -0,0 +1,53 @@
class SatuanWaktuSewaModel {
final String id;
final String asetId;
final String satuanWaktuId;
final int harga;
final int? denda;
final DateTime? createdAt;
final DateTime? updatedAt;
// Untuk menyimpan nama satuan waktu (jam/hari) dari tabel satuan_waktu
String? namaSatuanWaktu;
SatuanWaktuSewaModel({
required this.id,
required this.asetId,
required this.satuanWaktuId,
required this.harga,
this.denda,
this.createdAt,
this.updatedAt,
this.namaSatuanWaktu,
});
factory SatuanWaktuSewaModel.fromJson(Map<String, dynamic> json) {
return SatuanWaktuSewaModel(
id: json['id'] ?? '',
asetId: json['aset_id'] ?? '',
satuanWaktuId: json['satuan_waktu_id'] ?? '',
harga: json['harga'] ?? 0,
denda: json['denda'],
createdAt:
json['created_at'] != null
? DateTime.parse(json['created_at'])
: null,
updatedAt:
json['updated_at'] != null
? DateTime.parse(json['updated_at'])
: null,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'aset_id': asetId,
'satuan_waktu_id': satuanWaktuId,
'harga': harga,
'denda': denda,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
};
}
}

View File

@ -0,0 +1,71 @@
import 'dart:convert';
class User {
final String id;
final String email;
final String? name;
final String? avatarUrl;
final String? phoneNumber;
final DateTime? createdAt;
final DateTime? updatedAt;
User({
required this.id,
required this.email,
this.name,
this.avatarUrl,
this.phoneNumber,
this.createdAt,
this.updatedAt,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'email': email,
'name': name,
'avatar_url': avatarUrl,
'phone_number': phoneNumber,
'created_at': createdAt?.toIso8601String(),
'updated_at': updatedAt?.toIso8601String(),
};
}
factory User.fromMap(Map<String, dynamic> map) {
return User(
id: map['id'] ?? '',
email: map['email'] ?? '',
name: map['name'],
avatarUrl: map['avatar_url'],
phoneNumber: map['phone_number'],
createdAt:
map['created_at'] != null ? DateTime.parse(map['created_at']) : null,
updatedAt:
map['updated_at'] != null ? DateTime.parse(map['updated_at']) : null,
);
}
String toJson() => json.encode(toMap());
factory User.fromJson(String source) => User.fromMap(json.decode(source));
User copyWith({
String? id,
String? email,
String? name,
String? avatarUrl,
String? phoneNumber,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return User(
id: id ?? this.id,
email: email ?? this.email,
name: name ?? this.name,
avatarUrl: avatarUrl ?? this.avatarUrl,
phoneNumber: phoneNumber ?? this.phoneNumber,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,535 @@
import 'package:flutter/foundation.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:get/get.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
class AuthProvider extends GetxService {
late final SupabaseClient client;
bool _isInitialized = false;
Future<AuthProvider> init() async {
// Cek jika sudah diinisialisasi sebelumnya
if (_isInitialized) {
debugPrint('Supabase already initialized');
return this;
}
try {
// Cek jika dotenv sudah dimuat
if (dotenv.env['SUPABASE_URL'] == null ||
dotenv.env['SUPABASE_ANON_KEY'] == null) {
await dotenv.load();
}
final supabaseUrl = dotenv.env['SUPABASE_URL'];
final supabaseKey = dotenv.env['SUPABASE_ANON_KEY'];
if (supabaseUrl == null || supabaseKey == null) {
throw Exception('Supabase credentials not found in .env file');
}
debugPrint(
'Initializing Supabase with URL: ${supabaseUrl.substring(0, 15)}...',
);
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
debug: true, // Aktifkan debugging untuk membantu troubleshooting
);
client = Supabase.instance.client;
_isInitialized = true;
debugPrint('Supabase initialized successfully');
return this;
} catch (e) {
debugPrint('Error initializing Supabase: $e');
rethrow;
}
}
// Authentication methods
Future<AuthResponse> signUp({
required String email,
required String password,
Map<String, dynamic>? data,
}) async {
return await client.auth.signUp(
email: email,
password: password,
data: data,
);
}
Future<AuthResponse> signIn({
required String email,
required String password,
}) async {
return await client.auth.signInWithPassword(
email: email,
password: password,
);
}
Future<void> signOut() async {
await client.auth.signOut();
}
User? get currentUser => client.auth.currentUser;
Stream<AuthState> get authChanges => client.auth.onAuthStateChange;
String? getCurrentUserId() {
try {
final session = Supabase.instance.client.auth.currentSession;
return session?.user.id;
} catch (e) {
print('Error getting current user ID: $e');
return null;
}
}
// Metode untuk mendapatkan role_id dari raw_user_meta_data
Future<String?> getUserRoleId() async {
final user = currentUser;
if (user == null) {
debugPrint('No current user found when getting role');
return null;
}
try {
debugPrint('Fetching role_id from user metadata for user ID: ${user.id}');
// Cek user metadata untuk role_id
final userMetadata = user.userMetadata;
debugPrint('User metadata: $userMetadata');
// Cek beberapa kemungkinan nama field untuk role_id
if (userMetadata != null) {
if (userMetadata.containsKey('role_id')) {
final roleId = userMetadata['role_id'].toString();
debugPrint('Found role_id in metadata: $roleId');
return roleId;
}
if (userMetadata.containsKey('role')) {
final role = userMetadata['role'].toString();
debugPrint('Found role in metadata: $role');
// Coba konversi nama role ke UUID (from hardcoded data)
if (role.toUpperCase() == 'WARGA') {
return 'bb5360d5-8fd0-404e-8f6f-71ec4d8ad0ae';
}
if (role.toUpperCase() == 'PETUGAS_BUMDES') {
return '38a8a23c-1873-4033-b977-3293247903b';
}
if (role.toUpperCase() == 'PETUGAS_MITRA') {
return '8b1af754-0866-4e12-a9d8-da8ed31bec15';
}
}
}
// Jika tidak ada di metadata, coba cari di tabel roles dengan user_id
debugPrint('Checking roles table for user ID: ${user.id}');
try {
// Mencoba mengambil roles berdasarkan id user di auth
final roleData =
await client
.from('roles')
.select('id')
.eq('user_id', user.id)
.maybeSingle();
debugPrint('Role data by user_id: $roleData');
if (roleData != null && roleData.containsKey('id')) {
final roleId = roleData['id'].toString();
debugPrint('Found role ID in roles table: $roleId');
return roleId;
}
} catch (e) {
debugPrint('Error querying roles by user_id: $e');
}
// Jika tidak ditemukan dengan user_id, coba lihat seluruh tabel roles
// untuk debugging
debugPrint('Getting all roles to debug matching issues');
final allRoles = await client.from('roles').select('*').limit(10);
debugPrint('All roles in table: $allRoles');
// Fallback - tampaknya user belum di-assign role
// Berikan hardcoded role berdasarkan email pattern
final email = user.email?.toLowerCase();
if (email != null) {
if (email.contains('bumdes')) {
return '38a8a23c-1873-4033-b977-3293247903b'; // PETUGAS_BUMDES
} else if (email.contains('mitra')) {
return '8b1af754-0866-4e12-a9d8-da8ed31bec15'; // PETUGAS_MITRA
}
}
// Default ke WARGA
return 'bb5360d5-8fd0-404e-8f6f-71ec4d8ad0ae'; // WARGA
} catch (e) {
debugPrint('Error fetching user role_id: $e');
// Default ke WARGA sebagai fallback
return 'bb5360d5-8fd0-404e-8f6f-71ec4d8ad0ae';
}
}
// Metode untuk mendapatkan nama role dari tabel roles berdasarkan role_id
Future<String?> getRoleName(String roleId) async {
try {
debugPrint('Fetching role name for role_id: $roleId');
// Ambil nama role dari tabel roles
// ID di tabel roles adalah tipe UUID, pastikan format roleId sesuai
final roleData =
await client
.from('roles')
.select('nama_role, id')
.eq('id', roleId)
.maybeSingle();
debugPrint('Query result for roles table: $roleData');
if (roleData != null) {
// Cek berbagai kemungkinan nama kolom
String? roleName;
if (roleData.containsKey('nama_role')) {
roleName = roleData['nama_role'].toString();
} else if (roleData.containsKey('nama_role')) {
roleName = roleData['nama_role'].toString();
} else if (roleData.containsKey('role_name')) {
roleName = roleData['role_name'].toString();
}
if (roleName != null) {
debugPrint('Found role name in roles table: $roleName');
return roleName;
}
// Jika tidak ada nama kolom yang cocok, tampilkan kolom yang tersedia
debugPrint(
'Available columns in roles table: ${roleData.keys.join(', ')}',
);
}
// Lihat data lengkap tabel untuk troubleshooting
debugPrint('Getting all roles data for troubleshooting');
final allRoles = await client.from('roles').select('*').limit(5);
debugPrint('All roles table data (up to 5 rows): $allRoles');
// Hardcoded fallback berdasarkan UUID roleId yang dilihat dari data
debugPrint('Using hardcoded fallback for role_id: $roleId');
if (roleId == 'bb5360d5-8fd0-404e-8f6f-71ec4d8ad0ae') return 'WARGA';
if (roleId == '38a8a23c-1873-4033-b977-3293247903b') {
return 'PETUGAS_BUMDES';
}
if (roleId == '8b1af754-0866-4e12-a9d8-da8ed31bec15') {
return 'PETUGAS_MITRA';
}
// Default fallback jika role_id tidak dikenali
debugPrint('Unrecognized role_id: $roleId, defaulting to WARGA');
return 'WARGA';
} catch (e) {
debugPrint('Error fetching role name: $e');
return 'WARGA'; // Default fallback
}
}
// Metode untuk mendapatkan nama lengkap dari tabel warga_desa berdasarkan user_id
Future<String?> getUserFullName() async {
final user = currentUser;
if (user == null) {
debugPrint('No current user found when getting full name');
return null;
}
try {
debugPrint('Fetching nama_lengkap for user_id: ${user.id}');
// Coba ambil nama lengkap dari tabel warga_desa
final userData =
await client
.from('warga_desa')
.select('nama_lengkap')
.eq('user_id', user.id)
.maybeSingle();
debugPrint('User data from warga_desa table: $userData');
// Jika berhasil mendapatkan data
if (userData != null && userData.containsKey('nama_lengkap')) {
final namaLengkap = userData['nama_lengkap']?.toString();
if (namaLengkap != null && namaLengkap.isNotEmpty) {
debugPrint('Found nama_lengkap: $namaLengkap');
return namaLengkap;
}
}
// Jika tidak ada data di warga_desa, coba cek struktur tabel untuk troubleshooting
debugPrint('Checking warga_desa table structure');
final tableData =
await client.from('warga_desa').select('*').limit(1).maybeSingle();
if (tableData != null) {
debugPrint(
'Available columns in warga_desa table: ${tableData.keys.join(', ')}',
);
} else {
debugPrint('No data found in warga_desa table');
}
// Fallback ke data dari Supabase Auth
final userMetadata = user.userMetadata;
if (userMetadata != null) {
if (userMetadata.containsKey('full_name')) {
return userMetadata['full_name']?.toString();
}
if (userMetadata.containsKey('name')) {
return userMetadata['name']?.toString();
}
}
// Gunakan email jika nama tidak ditemukan
return user.email?.split('@').first ?? 'Pengguna Warga';
} catch (e) {
debugPrint('Error fetching user full name: $e');
return 'Pengguna Warga'; // Default fallback
}
}
// Metode untuk mendapatkan avatar dari tabel warga_desa berdasarkan user_id
Future<String?> getUserAvatar() async {
final user = currentUser;
if (user == null) {
debugPrint('No current user found when getting avatar');
return null;
}
try {
debugPrint('Fetching avatar for user_id: ${user.id}');
// Coba ambil avatar dari tabel warga_desa
final userData =
await client
.from('warga_desa')
.select('avatar')
.eq('user_id', user.id)
.maybeSingle();
debugPrint('Avatar data from warga_desa table: $userData');
// Jika berhasil mendapatkan data
if (userData != null && userData.containsKey('avatar')) {
final avatarUrl = userData['avatar']?.toString();
if (avatarUrl != null && avatarUrl.isNotEmpty) {
debugPrint('Found avatar URL: $avatarUrl');
return avatarUrl;
}
}
// Fallback ke data dari Supabase Auth
final userMetadata = user.userMetadata;
if (userMetadata != null && userMetadata.containsKey('avatar_url')) {
return userMetadata['avatar_url']?.toString();
}
return null; // No avatar found
} catch (e) {
debugPrint('Error fetching user avatar: $e');
return null;
}
}
// Metode untuk mendapatkan email pengguna
Future<String?> getUserEmail() async {
final user = currentUser;
if (user == null) {
debugPrint('No current user found when getting email');
return null;
}
// Email ada di data user Supabase Auth
return user.email;
}
// Metode untuk mendapatkan NIK dari tabel warga_desa berdasarkan user_id
Future<String?> getUserNIK() async {
final user = currentUser;
if (user == null) {
debugPrint('No current user found when getting NIK');
return null;
}
try {
debugPrint('Fetching NIK for user_id: ${user.id}');
// Coba ambil NIK dari tabel warga_desa
final userData =
await client
.from('warga_desa')
.select('nik')
.eq('user_id', user.id)
.maybeSingle();
// Jika berhasil mendapatkan data
if (userData != null && userData.containsKey('nik')) {
final nik = userData['nik']?.toString();
if (nik != null && nik.isNotEmpty) {
debugPrint('Found NIK: $nik');
return nik;
}
}
// Fallback ke data dari metadata
final userMetadata = user.userMetadata;
if (userMetadata != null && userMetadata.containsKey('nik')) {
return userMetadata['nik']?.toString();
}
return null;
} catch (e) {
debugPrint('Error fetching user NIK: $e');
return null;
}
}
// Metode untuk mendapatkan nomor telepon dari tabel warga_desa berdasarkan user_id
Future<String?> getUserPhone() async {
final user = currentUser;
if (user == null) {
debugPrint('No current user found when getting phone');
return null;
}
try {
debugPrint('Fetching phone for user_id: ${user.id}');
// Coba ambil nomor telepon dari tabel warga_desa
final userData =
await client
.from('warga_desa')
.select('nomor_telepon, no_telepon, phone')
.eq('user_id', user.id)
.maybeSingle();
// Jika berhasil mendapatkan data, cek beberapa kemungkinan nama kolom
if (userData != null) {
if (userData.containsKey('nomor_telepon')) {
final phone = userData['nomor_telepon']?.toString();
if (phone != null && phone.isNotEmpty) return phone;
}
if (userData.containsKey('no_telepon')) {
final phone = userData['no_telepon']?.toString();
if (phone != null && phone.isNotEmpty) return phone;
}
if (userData.containsKey('phone')) {
final phone = userData['phone']?.toString();
if (phone != null && phone.isNotEmpty) return phone;
}
}
// Fallback ke data dari Supabase Auth
final userMetadata = user.userMetadata;
if (userMetadata != null) {
if (userMetadata.containsKey('phone')) {
return userMetadata['phone']?.toString();
}
if (userMetadata.containsKey('phone_number')) {
return userMetadata['phone_number']?.toString();
}
}
return null;
} catch (e) {
debugPrint('Error fetching user phone: $e');
return null;
}
}
// Metode untuk mendapatkan alamat dari tabel warga_desa berdasarkan user_id
Future<String?> getUserAddress() async {
final user = currentUser;
if (user == null) {
debugPrint('No current user found when getting address');
return null;
}
try {
debugPrint('Fetching address for user_id: ${user.id}');
// Coba ambil alamat dari tabel warga_desa
final userData =
await client
.from('warga_desa')
.select('alamat')
.eq('user_id', user.id)
.maybeSingle();
// Jika berhasil mendapatkan data
if (userData != null && userData.containsKey('alamat')) {
final address = userData['alamat']?.toString();
if (address != null && address.isNotEmpty) {
debugPrint('Found address: $address');
return address;
}
}
// Fallback ke data dari Supabase Auth
final userMetadata = user.userMetadata;
if (userMetadata != null && userMetadata.containsKey('address')) {
return userMetadata['address']?.toString();
}
return null;
} catch (e) {
debugPrint('Error fetching user address: $e');
return null;
}
}
// Mendapatkan data sewa_aset berdasarkan status (misal: MENUNGGU PEMBAYARAN, PEMBAYARANAN DENDA)
Future<List<Map<String, dynamic>>> getSewaAsetByStatus(
List<String> statuses,
) async {
final user = currentUser;
if (user == null) {
debugPrint('No current user found when getting sewa_aset by status');
return [];
}
try {
debugPrint(
'Fetching sewa_aset for user_id: \\${user.id} with statuses: \\${statuses.join(', ')}',
);
// Supabase expects the IN filter as a comma-separated string in parentheses
final statusString = '(${statuses.map((s) => '"$s"').join(',')})';
final response = await client
.from('sewa_aset')
.select('*')
.eq('user_id', user.id)
.filter('status', 'in', statusString);
debugPrint('Fetched sewa_aset count: \\${response.length}');
// Pastikan response adalah List
if (response is List) {
return response
.map<Map<String, dynamic>>(
(item) => Map<String, dynamic>.from(item),
)
.toList();
} else {
return [];
}
} catch (e) {
debugPrint('Error fetching sewa_aset by status: \\${e.toString()}');
return [];
}
}
}

View File

@ -0,0 +1,187 @@
import 'package:bumrent_app/app/data/models/aset_model.dart';
import 'package:bumrent_app/app/data/models/pesanan_model.dart';
import 'package:bumrent_app/app/data/models/satuan_waktu_model.dart';
import 'package:bumrent_app/app/data/providers/auth_provider.dart';
import 'package:get/get.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
class PesananProvider {
final SupabaseClient _supabase = Supabase.instance.client;
final _tableName = 'pesanan';
Future<List<PesananModel>> getPesananByUserId(String userId) async {
try {
final response = await _supabase
.from(_tableName)
.select('*, aset(nama)')
.eq('user_id', userId)
.order('created_at', ascending: false);
final List<PesananModel> pesananList = [];
for (final item in response) {
final pesanan = PesananModel.fromJson(item);
// Attach the asset name
if (item['aset'] != null) {
pesanan.namaAset = item['aset']['nama'];
}
// Get and attach satuan waktu name
final satuanWaktu = await getSatuanWaktuById(pesanan.satuanWaktuId);
if (satuanWaktu != null) {
pesanan.namaSatuanWaktu = satuanWaktu.namaSatuanWaktu;
}
pesananList.add(pesanan);
}
return pesananList;
} catch (e) {
print('Error getting pesanan by user ID: $e');
return [];
}
}
Future<List<PesananModel>> getAllPesanan() async {
try {
final response = await _supabase
.from(_tableName)
.select('*, aset(nama), auth_users(full_name)')
.order('created_at', ascending: false);
final List<PesananModel> pesananList = [];
for (final item in response) {
final pesanan = PesananModel.fromJson(item);
// Attach the asset name
if (item['aset'] != null) {
pesanan.namaAset = item['aset']['nama'];
}
// Attach the user name
if (item['auth_users'] != null) {
pesanan.namaUser = item['auth_users']['full_name'];
}
// Get and attach satuan waktu name
final satuanWaktu = await getSatuanWaktuById(pesanan.satuanWaktuId);
if (satuanWaktu != null) {
pesanan.namaSatuanWaktu = satuanWaktu.namaSatuanWaktu;
}
pesananList.add(pesanan);
}
return pesananList;
} catch (e) {
print('Error getting all pesanan: $e');
return [];
}
}
Future<PesananModel?> getPesananById(String id) async {
try {
final response =
await _supabase
.from(_tableName)
.select('*, aset(nama), auth_users(full_name)')
.eq('id', id)
.single();
final pesanan = PesananModel.fromJson(response);
// Attach the asset name
if (response['aset'] != null) {
pesanan.namaAset = response['aset']['nama'];
}
// Attach the user name
if (response['auth_users'] != null) {
pesanan.namaUser = response['auth_users']['full_name'];
}
// Get and attach satuan waktu name
final satuanWaktu = await getSatuanWaktuById(pesanan.satuanWaktuId);
if (satuanWaktu != null) {
pesanan.namaSatuanWaktu = satuanWaktu.namaSatuanWaktu;
}
return pesanan;
} catch (e) {
print('Error getting pesanan by ID: $e');
return null;
}
}
Future<String?> createPesanan({
required String asetId,
required String satuanWaktuId,
required String userId,
required DateTime tanggalPemesanan,
required String jamPemesanan,
required int durasi,
required int totalHarga,
}) async {
try {
final response =
await _supabase
.from(_tableName)
.insert({
'aset_id': asetId,
'satuan_waktu_id': satuanWaktuId,
'user_id': userId,
'status': 'pending',
'tanggal_pemesanan':
tanggalPemesanan.toIso8601String().split('T')[0],
'jam_pemesanan': jamPemesanan,
'durasi': durasi,
'total_harga': totalHarga,
})
.select('id')
.single();
return response['id'];
} catch (e) {
print('Error creating pesanan: $e');
return null;
}
}
Future<bool> updatePesananStatus(String id, String status) async {
try {
await _supabase
.from(_tableName)
.update({
'status': status,
'updated_at': DateTime.now().toIso8601String(),
})
.eq('id', id);
return true;
} catch (e) {
print('Error updating pesanan status: $e');
return false;
}
}
Future<bool> deletePesanan(String id) async {
try {
await _supabase.from(_tableName).delete().eq('id', id);
return true;
} catch (e) {
print('Error deleting pesanan: $e');
return false;
}
}
Future<SatuanWaktuModel?> getSatuanWaktuById(String id) async {
try {
final response =
await _supabase.from('satuan_waktu').select().eq('id', id).single();
return SatuanWaktuModel.fromJson(response);
} catch (e) {
print('Error getting satuan waktu by ID: $e');
return null;
}
}
}

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,242 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../data/providers/auth_provider.dart';
import '../../../routes/app_routes.dart';
class AuthController extends GetxController {
final AuthProvider _authProvider = Get.find<AuthProvider>();
final emailController = TextEditingController();
final passwordController = TextEditingController();
// Form fields for registration
final RxString email = ''.obs;
final RxString password = ''.obs;
final RxString nik = ''.obs;
final RxString phoneNumber = ''.obs;
final RxString selectedRole = 'WARGA'.obs; // Default role
// Form status
final RxBool isLoading = false.obs;
final RxBool isPasswordVisible = false.obs;
final RxString errorMessage = ''.obs;
// Role options
final List<String> roleOptions = ['WARGA', 'PETUGAS_MITRA'];
void togglePasswordVisibility() {
isPasswordVisible.value = !isPasswordVisible.value;
}
// Change role selection
void setRole(String? role) {
if (role != null) {
selectedRole.value = role;
}
}
void login() async {
// Clear previous error messages
errorMessage.value = '';
// Basic validation
if (emailController.text.isEmpty || passwordController.text.isEmpty) {
errorMessage.value = 'Email dan password tidak boleh kosong';
return;
}
if (!GetUtils.isEmail(emailController.text.trim())) {
errorMessage.value = 'Format email tidak valid';
return;
}
try {
isLoading.value = true;
// Use the actual Supabase authentication
final response = await _authProvider.signIn(
email: emailController.text.trim(),
password: passwordController.text,
);
// Check if login was successful
if (response.user != null) {
await _checkRoleAndNavigate();
} else {
errorMessage.value = 'Login gagal. Periksa email dan password Anda.';
}
} catch (e) {
errorMessage.value = 'Terjadi kesalahan: ${e.toString()}';
} finally {
isLoading.value = false;
}
}
Future<void> _checkRoleAndNavigate() async {
try {
// Get the user's role ID from the auth provider
final roleId = await _authProvider.getUserRoleId();
if (roleId == null) {
errorMessage.value = 'Tidak dapat memperoleh peran pengguna';
return;
}
// Get role name based on role ID
final roleName = await _authProvider.getRoleName(roleId);
// Navigate based on role name
if (roleName == null) {
_navigateToWargaDashboard(); // Default to warga if role name not found
return;
}
switch (roleName.toUpperCase()) {
case 'PETUGAS_BUMDES':
_navigateToPetugasBumdesDashboard();
break;
case 'WARGA':
default:
_navigateToWargaDashboard();
break;
}
} catch (e) {
errorMessage.value = 'Gagal navigasi: ${e.toString()}';
}
}
void _navigateToPetugasBumdesDashboard() {
Get.offAllNamed(Routes.PETUGAS_BUMDES_DASHBOARD);
}
void _navigateToWargaDashboard() {
Get.offAllNamed(Routes.WARGA_DASHBOARD);
}
void forgotPassword() async {
// Clear previous error messages
errorMessage.value = '';
// Basic validation
if (emailController.text.isEmpty) {
errorMessage.value = 'Email tidak boleh kosong';
return;
}
if (!GetUtils.isEmail(emailController.text.trim())) {
errorMessage.value = 'Format email tidak valid';
return;
}
try {
isLoading.value = true;
// Call Supabase to send password reset email
await _authProvider.client.auth.resetPasswordForEmail(
emailController.text.trim(),
);
// Show success message
Get.snackbar(
'Berhasil',
'Link reset password telah dikirim ke email Anda',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.green[100],
colorText: Colors.green[800],
icon: const Icon(Icons.check_circle, color: Colors.green),
);
// Return to login page after a short delay
await Future.delayed(const Duration(seconds: 2));
Get.back();
} catch (e) {
errorMessage.value = 'Terjadi kesalahan: ${e.toString()}';
} finally {
isLoading.value = false;
}
}
void goToSignUp() {
// Clear error message when navigating away
errorMessage.value = '';
Get.toNamed(Routes.REGISTER);
}
void goToForgotPassword() {
// Clear error message when navigating away
errorMessage.value = '';
Get.toNamed(Routes.FORGOT_PASSWORD);
}
@override
void onClose() {
emailController.dispose();
passwordController.dispose();
super.onClose();
}
// Register user implementation
Future<void> registerUser() async {
// Validate all required fields
if (email.value.isEmpty ||
password.value.isEmpty ||
nik.value.isEmpty ||
phoneNumber.value.isEmpty) {
errorMessage.value = 'Semua field harus diisi';
return;
}
// Basic validation for email
if (!GetUtils.isEmail(email.value.trim())) {
errorMessage.value = 'Format email tidak valid';
return;
}
// Basic validation for password
if (password.value.length < 6) {
errorMessage.value = 'Password minimal 6 karakter';
return;
}
// Basic validation for NIK
if (nik.value.length != 16) {
errorMessage.value = 'NIK harus 16 digit';
return;
}
// Basic validation for phone number
if (!phoneNumber.value.startsWith('08') || phoneNumber.value.length < 10) {
errorMessage.value =
'Nomor HP tidak valid (harus diawali 08 dan minimal 10 digit)';
return;
}
try {
isLoading.value = true;
errorMessage.value = '';
// Create user with Supabase
final response = await _authProvider.signUp(
email: email.value.trim(),
password: password.value,
data: {
'nik': nik.value.trim(),
'phone_number': phoneNumber.value.trim(),
'role': selectedRole.value,
},
);
if (response.user != null) {
// Registration successful
Get.offNamed(Routes.REGISTRATION_SUCCESS);
} else {
errorMessage.value = 'Gagal mendaftar. Silakan coba lagi.';
}
} catch (e) {
errorMessage.value = 'Terjadi kesalahan: ${e.toString()}';
print('Registration error: ${e.toString()}');
} finally {
isLoading.value = false;
}
}
}

View File

@ -0,0 +1,376 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/auth_controller.dart';
import '../../../theme/app_colors.dart';
class ForgotPasswordView extends GetView<AuthController> {
const ForgotPasswordView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// Background gradient
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [AppColors.primarySoft, AppColors.background],
),
),
),
// Background pattern
Opacity(
opacity: 0.03,
child: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/pattern.png'),
repeat: ImageRepeat.repeat,
scale: 4.0,
),
),
),
),
// Accent circle
Positioned(
top: -100,
right: -80,
child: Container(
width: 220,
height: 220,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
AppColors.primary.withOpacity(0.2),
Colors.transparent,
],
),
),
),
),
// Main content
SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Back button
Padding(
padding: const EdgeInsets.all(16.0),
child: IconButton(
icon: const Icon(Icons.arrow_back_ios_new_rounded),
color: AppColors.primary,
onPressed: () => Get.back(),
),
),
// Scrollable content
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 16),
_buildHeader(),
const SizedBox(height: 40),
_buildEmailField(),
const SizedBox(height: 32),
_buildResetButton(),
const SizedBox(height: 40),
_buildImportantInfo(),
const SizedBox(height: 24),
_buildBackToLoginLink(),
const SizedBox(height: 24),
],
),
),
),
),
],
),
),
],
),
);
}
Widget _buildHeader() {
return Column(
children: [
// Floating lock icon with animation effect
Container(
width: 110,
height: 110,
decoration: BoxDecoration(
color: AppColors.surface,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColors.primary.withOpacity(0.2),
blurRadius: 20,
spreadRadius: 5,
offset: const Offset(0, 10),
),
],
),
child: Center(
child: Icon(
Icons.lock_open_rounded,
size: 50,
color: AppColors.primary,
),
),
),
const SizedBox(height: 32),
Text(
'Lupa Password?',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
letterSpacing: 0.5,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Text(
'Masukkan email Anda di bawah ini dan kami akan mengirimkan link untuk reset password.',
style: TextStyle(
fontSize: 15,
color: AppColors.textSecondary,
height: 1.5,
),
textAlign: TextAlign.center,
),
],
);
}
Widget _buildEmailField() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 4.0, bottom: 8.0),
child: Text(
'Email',
style: TextStyle(
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
fontSize: 15,
),
),
),
Container(
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: AppColors.shadow,
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: TextField(
controller: controller.emailController,
keyboardType: TextInputType.emailAddress,
style: TextStyle(fontSize: 16, color: AppColors.textPrimary),
decoration: InputDecoration(
hintText: 'Masukkan email Anda',
hintStyle: TextStyle(color: AppColors.textLight),
prefixIcon: Icon(
Icons.email_outlined,
color: AppColors.iconGrey,
size: 22,
),
filled: true,
fillColor: AppColors.surface,
contentPadding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 16,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide(color: AppColors.primary, width: 1.5),
),
),
),
),
// Error message
Obx(
() =>
controller.errorMessage.value.isNotEmpty
? Container(
margin: const EdgeInsets.only(top: 16),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.errorLight,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(
Icons.error_outline,
color: AppColors.error,
size: 20,
),
const SizedBox(width: 8),
Expanded(
child: Text(
controller.errorMessage.value,
style: TextStyle(
color: AppColors.error,
fontSize: 13,
),
),
),
],
),
)
: const SizedBox.shrink(),
),
],
);
}
Widget _buildResetButton() {
return Obx(
() => SizedBox(
height: 56,
child: ElevatedButton(
onPressed:
controller.isLoading.value ? null : controller.forgotPassword,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
elevation: 2,
shadowColor: AppColors.primary.withOpacity(0.4),
disabledBackgroundColor: AppColors.primary.withOpacity(0.6),
),
child:
controller.isLoading.value
? const SizedBox(
height: 24,
width: 24,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Kirim Link Reset',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
),
),
const SizedBox(width: 8),
const Icon(Icons.send_rounded, size: 18),
],
),
),
),
);
}
Widget _buildImportantInfo() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.green.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.green.shade100),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.green.shade100,
shape: BoxShape.circle,
),
child: Icon(
Icons.info_outline,
size: 20,
color: Colors.green.shade700,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Informasi Penting',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.green.shade800,
),
),
const SizedBox(height: 4),
Text(
'Petunjuk reset password akan dikirim ke email Anda. Silakan periksa kotak masuk atau folder spam setelah permintaan reset password.',
style: TextStyle(
fontSize: 13,
color: Colors.green.shade900,
height: 1.4,
),
),
],
),
),
],
),
);
}
Widget _buildBackToLoginLink() {
return Center(
child: TextButton.icon(
onPressed: () => Get.back(),
icon: Icon(
Icons.arrow_back_rounded,
size: 16,
color: AppColors.primary,
),
label: Text(
'Kembali ke Login',
style: TextStyle(
color: AppColors.primary,
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
),
);
}
}

View File

@ -0,0 +1,368 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/auth_controller.dart';
import '../../../theme/app_colors.dart';
class LoginView extends GetView<AuthController> {
const LoginView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// Background gradient
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
AppColors.primaryLight.withOpacity(0.1),
AppColors.background,
AppColors.accentLight.withOpacity(0.1),
],
),
),
),
// Pattern overlay
Opacity(
opacity: 0.03,
child: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/pattern.png'),
repeat: ImageRepeat.repeat,
scale: 4.0,
),
),
),
),
// Accent circles
Positioned(
top: -40,
right: -20,
child: Container(
width: 150,
height: 150,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
AppColors.primary.withOpacity(0.2),
Colors.transparent,
],
),
),
),
),
Positioned(
bottom: -50,
left: -30,
child: Container(
width: 180,
height: 180,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
AppColors.accent.withOpacity(0.2),
Colors.transparent,
],
),
),
),
),
// Main content
SafeArea(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 50),
_buildHeader(),
const SizedBox(height: 40),
_buildLoginCard(),
const SizedBox(height: 24),
_buildRegisterLink(),
const SizedBox(height: 30),
],
),
),
),
),
],
),
);
}
Widget _buildHeader() {
return Center(
child: Hero(
tag: 'logo',
child: Image.asset(
'assets/images/logo.png',
width: 220,
height: 220,
errorBuilder: (context, error, stackTrace) {
return Icon(
Icons.apartment_rounded,
size: 180,
color: AppColors.primary,
);
},
),
),
);
}
Widget _buildLoginCard() {
return Card(
elevation: 4,
shadowColor: AppColors.shadow,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
child: Padding(
padding: const EdgeInsets.all(28.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Welcome text
Text(
'Selamat Datang',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 8),
Text(
'Masuk untuk melanjutkan ke akun Anda',
style: TextStyle(
fontSize: 14,
color: AppColors.textSecondary,
fontWeight: FontWeight.w400,
),
),
const SizedBox(height: 32),
// Email field
_buildInputLabel('Email'),
const SizedBox(height: 8),
_buildTextField(
controller: controller.emailController,
hintText: 'Masukkan email Anda',
prefixIcon: Icons.email_outlined,
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 24),
// Password field
_buildInputLabel('Password'),
const SizedBox(height: 8),
Obx(
() => _buildTextField(
controller: controller.passwordController,
hintText: 'Masukkan password Anda',
prefixIcon: Icons.lock_outline,
obscureText: !controller.isPasswordVisible.value,
suffixIcon: IconButton(
icon: Icon(
controller.isPasswordVisible.value
? Icons.visibility
: Icons.visibility_off,
color: AppColors.iconGrey,
),
onPressed: controller.togglePasswordVisibility,
),
),
),
// Forgot password
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: () => controller.goToForgotPassword(),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 0,
vertical: 8,
),
),
child: Text(
'Lupa sandi?',
style: TextStyle(
color: AppColors.primary,
fontWeight: FontWeight.w500,
),
),
),
),
const SizedBox(height: 32),
// Login button
Obx(
() => SizedBox(
width: double.infinity,
height: 56,
child: ElevatedButton(
onPressed:
controller.isLoading.value ? null : controller.login,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: AppColors.buttonText,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
elevation: controller.isLoading.value ? 0 : 2,
shadowColor: AppColors.primary.withOpacity(0.4),
),
child:
controller.isLoading.value
? const SizedBox(
height: 24,
width: 24,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Masuk',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
),
),
const SizedBox(width: 8),
const Icon(Icons.arrow_forward, size: 18),
],
),
),
),
),
// Error message
Obx(
() =>
controller.errorMessage.value.isNotEmpty
? Container(
margin: const EdgeInsets.only(top: 16),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.errorLight,
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
Icon(
Icons.error_outline,
color: AppColors.error,
size: 20,
),
const SizedBox(width: 8),
Expanded(
child: Text(
controller.errorMessage.value,
style: TextStyle(
color: AppColors.error,
fontSize: 13,
),
),
),
],
),
)
: const SizedBox.shrink(),
),
],
),
),
);
}
Widget _buildInputLabel(String label) {
return Text(
label,
style: TextStyle(
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
fontSize: 15,
),
);
}
Widget _buildTextField({
required TextEditingController controller,
required String hintText,
required IconData prefixIcon,
TextInputType keyboardType = TextInputType.text,
bool obscureText = false,
Widget? suffixIcon,
}) {
return TextField(
controller: controller,
keyboardType: keyboardType,
obscureText: obscureText,
style: TextStyle(fontSize: 16, color: AppColors.textPrimary),
decoration: InputDecoration(
hintText: hintText,
hintStyle: TextStyle(color: AppColors.textLight),
prefixIcon: Icon(prefixIcon, color: AppColors.iconGrey, size: 22),
suffixIcon: suffixIcon,
filled: true,
fillColor: AppColors.inputBackground,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide(color: AppColors.primary, width: 1.5),
),
),
);
}
Widget _buildRegisterLink() {
return Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Belum punya akun?",
style: TextStyle(color: AppColors.textSecondary),
),
TextButton(
onPressed: controller.goToSignUp,
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
),
child: Text(
'Daftar',
style: TextStyle(
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
),
),
],
),
);
}
}

Some files were not shown because too many files have changed in this diff Show More