Initial commit

This commit is contained in:
Venba Team 2025-03-20 10:29:01 +05:30
commit 7b1e7cf624
171 changed files with 16783 additions and 0 deletions

45
.gitignore vendored Normal file
View File

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

45
.metadata Normal file
View File

@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
base_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
- platform: android
create_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
base_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
- platform: ios
create_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
base_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
- platform: linux
create_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
base_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
- platform: macos
create_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
base_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
- platform: web
create_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
base_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
- platform: windows
create_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
base_revision: 68415ad1d920f6fe5ec284f5c2febf7c4dd5b0b3
# 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 @@
# frontend
Travel Expense Management
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

28
analysis_options.yaml Normal file
View File

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

13
android/.gitignore vendored Normal file
View File

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

44
android/app/build.gradle Normal file
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.travelexpense.ts.frontend"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.travelexpense.ts.frontend"
// 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.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="frontend"
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.travelexpense.ts.frontend
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity()

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

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

View File

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

View File

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

18
android/build.gradle Normal file
View File

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

View File

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

View File

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

25
android/settings.gradle Normal file
View File

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

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,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

44
ios/Podfile Normal file
View File

@ -0,0 +1,44 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

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.travelexpense.ts.frontend;
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.travelexpense.ts.frontend.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.travelexpense.ts.frontend.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.travelexpense.ts.frontend.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.travelexpense.ts.frontend;
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.travelexpense.ts.frontend;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View File

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

View File

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

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

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Frontend</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>frontend</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,23 @@
import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'login_widget.dart';
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: ResponsiveBuilder(
builder: (context, sizingInformation) {
bool isDesktop = sizingInformation.deviceScreenType == DeviceScreenType.desktop;
bool isTablet = sizingInformation.deviceScreenType == DeviceScreenType.tablet;
return Center(
child: LoginWidget(isDesktop: isDesktop, isTablet: isTablet),
);
},
),
);
}
}

View File

@ -0,0 +1,186 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:frontend/config/apiUrl.dart';
import 'package:http/http.dart' as http;
import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LoginWidget extends StatefulWidget {
final bool isDesktop;
final bool isTablet;
const LoginWidget({Key? key, required this.isDesktop, required this.isTablet}) : super(key: key);
@override
_LoginWidgetState createState() => _LoginWidgetState();
}
class _LoginWidgetState extends State<LoginWidget> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
bool _obscureText = true;
void _login(BuildContext context) async {
if (_formKey.currentState!.validate()) {
const String url = '$apiUrl/auth/login';
try {
final response = await http.post(
Uri.parse(url),
headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
body: jsonEncode({
'email': _emailController.text.trim(),
'password': _passwordController.text.trim(),
}),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
print("data- $data");
final token = data['token']; // Assuming the token is in response
final userId = data['user_id'].toString();
// Save token to SharedPreferences
final prefs = await SharedPreferences.getInstance();
await prefs.setString('auth_token', token);
await prefs.setString('userId', userId);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Login Successful")),
);
context.go('/home'); // Navigate to home
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Login Failed: ${jsonDecode(response.body)['message']}")),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Error: $e")),
);
}
}
}
@override
/// Layout
Widget build(BuildContext context) {
double formWidth = widget.isTablet ? 400 : 300;
return Row(
children: [
if (widget.isDesktop)
Expanded(
flex: 1,
child: Container(
color: Colors.blueAccent,
child: const Center(
child: Text(
"Welcome Back!",
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white),
),
),
),
),
Expanded(
flex: 1,
child: Center(
child: Padding(
padding: const EdgeInsets.all(40),
child: _buildForm(width: formWidth), // Fixed form width
),
),
),
],
);
}
/// **Reusable Login Form**
Widget _buildForm({required double width}) {
return SizedBox(
width: width,
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"Hello! Welcome Back",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.blueAccent),
),
const SizedBox(height: 20),
/// **Email Field**
_buildLabel("EMAIL"),
TextFormField(
controller: _emailController,
decoration: _inputDecoration("Enter your email"),
validator: (value) => value == null || value.isEmpty ? 'Required Email' : null,
),
const SizedBox(height: 16),
/// **Password Field**
_buildLabel("PASSWORD"),
TextFormField(
controller: _passwordController,
obscureText: _obscureText,
decoration: _inputDecoration("Enter your password").copyWith(
suffixIcon: IconButton(
icon: Icon(_obscureText ? Icons.visibility_off : Icons.visibility, color: Colors.blueAccent),
onPressed: () => setState(() => _obscureText = !_obscureText),
),
),
validator: (value) => value == null || value.isEmpty ? 'Required Password' : null,
),
const SizedBox(height: 20),
/// **Login Button**
ElevatedButton(
onPressed: () => _login(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueAccent, // Button color
foregroundColor: Colors.white, // Text color
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
child: const Text("Login"),
),
],
),
),
);
}
/// **Reusable Label Widget**
Widget _buildLabel(String text) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
text,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.blueAccent),
),
),
);
}
/// **Reusable Input Decoration**
InputDecoration _inputDecoration(String hint) {
return InputDecoration(
labelText: hint,
floatingLabelBehavior: FloatingLabelBehavior.never,
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
);
}
}

View File

@ -0,0 +1,213 @@
import 'dart:convert';
import 'package:frontend/config/apiUrl.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class LoginPage1 extends StatefulWidget {
const LoginPage1({super.key});
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage1> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: LoginWidget(),
);
}
}
// StatefulWidget for Body Content
class LoginWidget extends StatefulWidget{
const LoginWidget({super.key});
@override
_LoginWidgetState createState() => _LoginWidgetState();
}
class _LoginWidgetState extends State<LoginWidget>{
final _formKey = GlobalKey<FormState>();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
bool _obscureText = true;
void _login(BuildContext context) async {
if (_formKey.currentState!.validate()) {
// const String url = '$apiUrl/auth/login'; // Replace with actual API URL
const String url = 'https://apitest.tripapprovaltool.com/auth/login';
try {
final response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'},
body: jsonEncode({
'email': _emailController.text.trim(),
'password': _passwordController.text.trim(),
}),
);
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Login Successful")),
);
context.go('/home'); // Login Success
}
else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Login Failed: ${jsonDecode(response.body)['message']}")),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Error: $e")),
);
}
}
}
@override
Widget build(BuildContext context){
double myHeight = MediaQuery.of(context).size.height;
double myWidth = MediaQuery.of(context).size.width;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Center(
child: Text(
"Hello! , Welcome Back",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blueAccent
),
),
),
),
SizedBox(height: myHeight/10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: const Text(
"EMAIL",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.blueAccent
),
),
),
SizedBox(height:8),
TextFormField(
controller: _emailController,
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: 'Enter the email',
labelStyle: TextStyle(color: Colors.grey,),
floatingLabelBehavior: FloatingLabelBehavior.never,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), // Rounded corners// Remove the default border
borderSide: BorderSide.none
),
),
validator: (value){
if(value == null || value.isEmpty){
return 'Required Email';
}
return null;
},
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: const Text(
"PASSWORD",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.blueAccent
),
),
),
SizedBox(height:8),
TextFormField(
controller:_passwordController,
style: const TextStyle(fontSize: 12),
obscureText: _obscureText,
decoration: InputDecoration(
labelText: "Enter the password",
labelStyle: TextStyle(color: Colors.grey,),
floatingLabelBehavior: FloatingLabelBehavior.never,
suffixIcon: IconButton(
icon: Icon(
_obscureText ? Icons.visibility_off : Icons.visibility,
color: Colors.blueAccent,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText; // Toggle state
});
},
),
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none
)
),
validator: (value){
if(value == null || value.isEmpty){
return 'Required Password';
}
return null;
},
),
const SizedBox(height: 20),
Center(
child: ElevatedButton(
onPressed: (){
_login(context);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueAccent, // Change button color
foregroundColor: Colors.white, // Text color
padding: const EdgeInsets.symmetric(horizontal: 24,vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
)
),
child: const Text(
"Login")
),
),
],
),
),
);
}
}

View File

@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
appBar: isDesktop ? null : const CustomAppBar(title:'Home'),
drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
body:
Row(
children: [
if(isDesktop)
CustomDrawer(isDesktop: true),
const Expanded(child: Center(child: Text("Home Page Content"))),
],
),
);
});
}
}

View File

@ -0,0 +1,476 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/apiUrl.dart';
import '../../data/models/searchUser.dart';
import '../../widgets/custom_text_traveller.dart';
class UserSelectionDialog extends StatefulWidget{
final String title;
final void Function(String) onSubmit;
UserSelectionDialog({Key? key, required this.title, required this.onSubmit}) : super(key: key);
@override
_UserSelectionDialogState createState() => _UserSelectionDialogState();
}
class _UserSelectionDialogState extends State<UserSelectionDialog>{
TextEditingController _controller = TextEditingController();
TextEditingController _searchController = TextEditingController();
// List<String> _filteredUsers = [];
// List<String> _users = [];
List<SearchUser> _users = [];
List<SearchUser> _filteredUsers = [];
bool _showTravellerForm = false;
final _formKey = GlobalKey<FormState>();
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
Future<void> fetchUsers() async {
final String apiUrldata = '$apiUrl/api/users';
try {
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final Map<String,dynamic> responseBody = json.decode(response.body);
print("API Response: $responseBody"); // Debugging
if (responseBody.containsKey('data') && responseBody['data'] is List) {
List<dynamic> userList = responseBody['data'];
// Extract first name and last name correctly
// setState(() {
// _users = userList
// .where((user) => user['first_name'] != null && user['last_name'] != null)
// .map((user) => "${user['first_name']} ${user['last_name']}")
// .toList();
// _filteredUsers = List.from(_users);
// });
setState(() {
_users = userList.map((user) => SearchUser.fromJson(user)).toList();
_filteredUsers = List.from(_users);
});
print("Users fetched: ${_users.length}");
for (var user in _users) {
print("${user.firstName} ${user.lastName}");
}
} else {
throw Exception("Unexpected response format: Expected a List but got ${responseBody.runtimeType}");
}
} else {
throw Exception('Failed to load users. Status Code: ${response.statusCode}');
}
} catch (e) {
print("Error fetching users: $e");
}
}
void _filterUsers(String query) {
print("Filtering users...");
setState(() {
if (query.isEmpty) {
_filteredUsers = List.from(_users);
} else {
_filteredUsers = _users.where((user) {
List<String> searchFields = [
"${user.firstName} ${user.lastName}".toLowerCase(),
user.email.toLowerCase() ?? "",
user.userId.toLowerCase() ?? "",
user.travellerId ?? "",
user.mobileNo ?? "",
user.alternateMobileNo ?? ""
];
return searchFields.any((field) => field.contains(query.toLowerCase()));
}).toList();
}
});
print("Filtered Users:");
for (var user in _filteredUsers) {
print("${user.firstName} ${user.lastName}");
}
}
@override
void initState() {
super.initState();
fetchUsers();
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Container(
color: Colors.white,
width: 400, // Adjust width as needed
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min, // Ensures content doesn't expand unnecessarily
children: [
Text("Please Select User", style: TextStyle(fontSize: 14)),
SizedBox(height: 10),
// Search Field
TextField(
controller: _searchController,
onChanged: (query) {
setState(() {
_showTravellerForm = false;
});
_filterUsers(query);
},
decoration: InputDecoration(
hintText: "Search for a user",
hintStyle: TextStyle(fontSize: 14),
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: Colors.blueAccent, width: 2),
),
),
),
if (widget.title == "Others") ...[
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("or create a new traveler", style: TextStyle(fontSize: 14, color: Color(0xFF575A74))),
TextButton(
onPressed: () {
setState(() {
_showTravellerForm = true;
_searchController.clear();
});
},
child: Text("Create", style: TextStyle(fontSize: 14, color: Colors.blueAccent)),
),
],
),
],
SizedBox(height: 5),
// User List or Message
_searchController.text.isNotEmpty
? SizedBox(
height: 300, // Limit height to avoid overflow
child: _filteredUsers.isEmpty
? Center(
child: Text(
"No users found",
style: TextStyle(fontSize: 14, color: Colors.grey),
),
)
: ListView.builder(
itemCount: _filteredUsers.length,
itemBuilder: (context, index) {
final user = _filteredUsers[index];
return ListTile(
title: Text("${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"),
subtitle: Text("ID: ${user.userId}"),
onTap: () {
String selectedUser = "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
setState(() {
_searchController.text = selectedUser;
});
print("Selected: $selectedUser, ID: ${user.userId}");
},
);
},
),
) : SizedBox.shrink(),
// Traveler Form
if (_showTravellerForm)
Container(
height: 300, // Set a defined height
color: Color(0xFFF4F4FB),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: TravelerForm(
formKey: _formKey,
firstNameController: TextEditingController(),
lastNameController: TextEditingController(),
emailController: TextEditingController(),
mobileController: TextEditingController(),
),
),
),
// Actions
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.blueAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Colors.blueAccent, width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () => Navigator.pop(context),
child: Text("Cancel",),
),
SizedBox(width: 10),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueAccent,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Colors.blueAccent, width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: () {
widget.onSubmit(_searchController.text);
Navigator.pop(context);
},
child: Text("Submit"),
),
],
),
],
),
),
);
}
}
class TravelerForm extends StatefulWidget {
final TextEditingController firstNameController;
final TextEditingController lastNameController;
final TextEditingController emailController;
final TextEditingController mobileController;
final GlobalKey<FormState> formKey;
TravelerForm({
required this.formKey,
required this.firstNameController,
required this.lastNameController,
required this.emailController,
required this.mobileController,
});
@override
_TravelerFormState createState() => _TravelerFormState();
}
class _TravelerFormState extends State<TravelerForm> {
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
bool _validateForm() {
if (widget.formKey.currentState != null) {
return widget.formKey.currentState!.validate();
}
return false; // Return false when form is not initialized
}
String? validateMobile(String? value) {
if (value == null || value.isEmpty) {
return 'Mobile number is required';
}
if (!RegExp(r'^[0-9]{10}$').hasMatch(value)) {
return 'Enter a valid 10-digit mobile number';
}
return null;
}
String? validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$').hasMatch(value)) {
return 'Enter a valid email address';
}
return null;
}
void _onSubmit(BuildContext context) {
bool isValid = _validateForm();
print("Form Validation Result: $isValid");
if (isValid) {
print("Validation Success");
_submitForm(context);
} else {
print("Validation Failed"); // This should now print if validation fails
}
}
Future<void> _submitForm(BuildContext context) async {
Map<String, String> requestBody = {
"first_name": widget.firstNameController.text,
"last_name": widget.lastNameController.text,
"email": widget.emailController.text,
"mobile": widget.mobileController.text,
};
String apiUrlData = '$apiUrl/api/travellers/create';
try {
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
print("response1");
var response = await http.post(
Uri.parse(apiUrlData),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
body: jsonEncode(requestBody),
);
if (response.statusCode == 200 || response.statusCode == 201) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Traveller added successfully!",
style: TextStyle(color: Colors.white), // Set text color
),
backgroundColor: Colors.green,),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Error: ${response.body}")),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Failed to connect to server.")),
);
}
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInfo) {
double widthFactor;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
if (sizingInfo.deviceScreenType == DeviceScreenType.desktop) {
widthFactor = 0.23;
} else if (sizingInfo.deviceScreenType == DeviceScreenType.tablet) {
widthFactor = 0.8;
} else {
widthFactor = 1.0;
}
return Form(
key: widget.formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("Create Traveler", style: TextStyle(color: Colors.black54)),
SizedBox(height: 7),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(children: _buildFormFirstRow(isDesktop)),
),
),
SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () {
widget.formKey.currentState?.reset();
},
child: Text("Clear", style: TextStyle(color: Colors.grey)),
),
TextButton(
onPressed: () => _onSubmit(context),
child: Text("Add", style: TextStyle(color: Colors.blueAccent)),
),
],
),
],
),
);
},
);
}
List<Widget> _buildFormFirstRow(bool isDesktop) {
return [
CustomTextField(
controller: widget.firstNameController,
labelText: "First Name",
validator: (value) => value!.isEmpty ? "Enter first name" : null,
),
SizedBox(height: 15),
CustomTextField(
controller: widget.lastNameController,
labelText: "Last Name",
validator: (value) => value!.isEmpty ? "Enter last name" : null,
),
SizedBox(height: 15),
CustomTextField(
controller: widget.emailController,
labelText: "Email",
keyboardType: TextInputType.emailAddress,
validator: validateEmail,
),
SizedBox(height: 15),
CustomTextField(
controller: widget.mobileController,
labelText: "Mobile Number",
keyboardType: TextInputType.phone,
validator: validateMobile,
),
];
}
}

View File

@ -0,0 +1,680 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class AccomodationScreen extends StatefulWidget {
final Map<String, String> formData;
final Function(String tab, String key, String value) updateFormData;
final Function(bool) onClose; // Callback function
AccomodationScreen({required this.formData, required this.updateFormData,
required this.onClose,});
@override
_AccomodationScreenState createState() => _AccomodationScreenState();
}
class _AccomodationScreenState extends State<AccomodationScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final FocusNode _destinationFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _checkInFocusNode = FocusNode();
final FocusNode _checkInTimeFocusNode = FocusNode();
final FocusNode _checkOutFocusNode = FocusNode();
final FocusNode _checkOutTimeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late Map<String, TextEditingController> _controllers;
late TextEditingController _destinationController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _checkInController = TextEditingController();
late TextEditingController _checkInTimeController = TextEditingController();
late TextEditingController _checkOutController = TextEditingController();
late TextEditingController _checkOutTimeController = TextEditingController();
late TextEditingController _commentsController = TextEditingController();
bool _destinationFocused = false;
bool _isHotelNameFocused = false;
bool _checkInFocus = false;
bool _checkInTimeFocus = false;
bool _checkOutFocus = false;
bool _checkOutTimeFocus = false;
bool _commentsFocus = false;
@override
void initState() {
super.initState();
_destinationFocusNode.addListener(() {
setState(() {
_destinationFocused = _destinationFocusNode.hasFocus;
});
_hotelNameFocusNode.addListener(() {
setState(() {
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
});
});
_checkInFocusNode.addListener(() {
setState(() {
_checkInFocus = _checkInFocusNode.hasFocus;
});
});
_checkInTimeFocusNode.addListener(() {
setState(() {
_checkInTimeFocus = _checkInTimeFocusNode.hasFocus;
});
});
_checkOutFocusNode.addListener(() {
setState(() {
_checkInFocus = _checkInFocusNode.hasFocus;
});
});
_checkOutTimeFocusNode.addListener(() {
setState(() {
_checkInTimeFocus = _checkOutTimeFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
setState(() {
_commentsFocus = _commentsFocusNode.hasFocus;
});
});
});
List<String> fields = [
"_destination",
"_hotelName",
"_Check_In",
"_Check_In_Time",
"_Check_Out",
"_Check_Out_Time",
"_comments"
];
_destinationController =
TextEditingController(text: widget.formData["destination"] ?? "");
_hotelNameController =
TextEditingController(text: widget.formData["_hotelName"] ?? "");
_checkInController =
TextEditingController(text: widget.formData["_Check_In"] ?? "");
_checkInTimeController =
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
_checkOutController =
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
_checkOutTimeController =
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
_commentsController =
TextEditingController(text: widget.formData["_comments"] ?? "");
// Save data when user types
_destinationController.addListener(() {
widget.updateFormData(
"Accommodation", "destination", _destinationController.text);
}); // Save data when user types
_hotelNameController.addListener(() {
widget.updateFormData(
"Accommodation", "_hotelName", _hotelNameController.text);
});
_checkInController.addListener(() {
widget.updateFormData(
"Accommodation", "_Check_In", _checkInController.text);
});
_checkInTimeController.addListener(() {
widget.updateFormData(
"Accommodation", "_Check_In_Time", _checkInTimeController.text);
});
_checkOutController.addListener(() {
widget.updateFormData(
"Accommodation", "_Check_Out", _checkOutController.text);
});
_checkOutTimeController.addListener(() {
widget.updateFormData(
"Accommodation", "_Check_Out_Time", _checkOutTimeController.text);
});
_commentsController.addListener(() {
widget.updateFormData(
"Accommodation", "_comments", _commentsController.text);
});
}
@override
void dispose() {
_destinationFocusNode.dispose();
_destinationController.dispose();
_hotelNameController.dispose();
_checkInController.dispose();
_checkInTimeController.dispose();
_checkOutController.dispose();
_checkOutTimeController.dispose();
_commentsController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {
print("Close icon clicked");
widget.onClose(false);
},
child: Icon(
Icons.close,
size: 18,
color: Color(0xFF575A74),
),
),
),
Text("Accomodation Booking",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
SizedBox(
height: 6,
),
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
],
),
),
),
);
});
}
List<Widget> _buildAccomadtionForm(isDesktop) {
return [
isDesktop
? Row(
children: _buildFirstRow(isDesktop),
)
: Column(
children: _buildFirstRow(isDesktop),
),
SizedBox(height: 10), // Spacing between first and second row
isDesktop
? Row(
children: _buildSecondRow(isDesktop),
)
: Column(
children: _buildSecondRow(isDesktop),
),
SizedBox(height: 10),
isDesktop
? Row(
children: _buildThirdRow(isDesktop),
)
: Column(
children: _buildThirdRow(isDesktop),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Destination",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _destinationFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _destinationFocusNode,
controller: _destinationController,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Destination",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Hotel Name",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
controller: _hotelNameController,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Hotel Name",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
];
}
List<Widget> _buildSecondRow(bool isDesktop) {
DateTime? _selectedCheckInDate;
TimeOfDay? _selectedCheckInTime;
Future<void> _selectCheckInDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today)
? _selectedCheckInDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckInDate) {
setState(() {
_selectedCheckInDate = pickedDate;
_checkInController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
Future<void> _selectCheckInTime(BuildContext context) async {
TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: _selectedCheckInTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckInTime) {
setState(() {
_selectedCheckInTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(now.year, now.month, now.day, pickedTime.hour,
pickedTime.minute),
);
_checkInTimeController.text = formattedTime;
});
}
}
//-------------------------------Check-In End
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_checkOutController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
Future<void> _selectCheckOutTime(BuildContext context) async {
TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(now.year, now.month, now.day, pickedTime.hour,
pickedTime.minute),
);
_checkOutTimeController.text = formattedTime;
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Check-in*",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _checkInFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckInDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _checkInFocusNode,
controller: _checkInController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time (Check-in)",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _checkInTimeFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckInTime(context),
child: AbsorbPointer(
child: TextField(
focusNode: _checkInTimeFocusNode,
controller: _checkInTimeController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Time",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Check-out*",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _checkOutFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _checkOutFocusNode,
controller: _checkOutController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time (Check-out)",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _checkOutTimeFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutTime(context),
child: AbsorbPointer(
child: TextField(
focusNode: _checkOutTimeFocusNode,
controller: _checkOutTimeController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Time",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.4
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: _commentsFocusNode,
controller: _commentsController,
maxLines: 6,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
)
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog or screen
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Close",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
// TODO: Implement save logic
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
];
}
}

View File

@ -0,0 +1,715 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class BusScreen extends StatefulWidget {
final Map<String, String> formData;
final Map<String, dynamic>? apiData;
final Function(String tab, String key, String value) updateFormData;
final Function(bool) onClose;
BusScreen({required this.formData, required this.updateFormData,
required this.onClose, this.apiData});
@override
_BusScreenState createState() => _BusScreenState();
}
class _BusScreenState extends State<BusScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Map<String, String?> selectedValues = {};
final FocusNode _tripTypeFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _fromFocusNode = FocusNode();
final FocusNode _toFocusNode = FocusNode();
final FocusNode _dateFocusNode = FocusNode();
final FocusNode _timeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late Map<String, TextEditingController> _controllers;
late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _fromController = TextEditingController();
late TextEditingController _toController = TextEditingController();
late TextEditingController _dateController = TextEditingController();
late TextEditingController _timeController = TextEditingController();
late TextEditingController _commentsController = TextEditingController();
bool _tripTypeFocused = false;
bool _isHotelNameFocused = false;
bool _fromFocus = false;
bool _toFocus = false;
bool _dateFocus = false;
bool _timeFocus = false;
bool _commentsFocus = false;
@override
void initState() {
super.initState();
_tripTypeFocusNode.addListener(() {
setState(() {
_tripTypeFocused = _tripTypeFocusNode.hasFocus;
});
_hotelNameFocusNode.addListener(() {
setState(() {
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
});
});
_fromFocusNode.addListener(() {
setState(() {
_fromFocus = _fromFocusNode.hasFocus;
});
});
_toFocusNode.addListener(() {
setState(() {
_toFocus = _toFocusNode.hasFocus;
});
});
_dateFocusNode.addListener(() {
setState(() {
_dateFocus = _fromFocusNode.hasFocus;
});
});
_timeFocusNode.addListener(() {
setState(() {
_timeFocus = _timeFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
setState(() {
_commentsFocus = _commentsFocusNode.hasFocus;
});
});
});
_tripTypeController =
TextEditingController(text: widget.formData["tripType"] ?? "");
_hotelNameController =
TextEditingController(text: widget.formData["_hotelName"] ?? "");
_fromController =
TextEditingController(text: widget.formData["_from"] ?? "");
_toController =
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
_dateController =
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
_timeController =
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
_commentsController =
TextEditingController(text: widget.formData["_comments"] ?? "");
// Save data when user types
_tripTypeController.addListener(() {
widget.updateFormData(
"Bus", "_tripType", _tripTypeController.text);
}); // Save data when user types
_hotelNameController.addListener(() {
widget.updateFormData(
"Bus", "_hotelName", _hotelNameController.text);
});
_fromController.addListener(() {
widget.updateFormData(
"Bus", "_from", _fromController.text);
});
_toController.addListener(() {
widget.updateFormData(
"Bus", "_Check_In_Time", _toController.text);
});
_dateController.addListener(() {
widget.updateFormData(
"Bus", "_Check_Out", _dateController.text);
});
_timeController.addListener(() {
widget.updateFormData(
"Bus", "_Check_Out_Time", _timeController.text);
});
_commentsController.addListener(() {
widget.updateFormData(
"Bus", "_comments", _commentsController.text);
});
}
@override
void dispose() {
_tripTypeFocusNode.dispose();
_tripTypeController.dispose();
_hotelNameController.dispose();
_fromController.dispose();
_toController.dispose();
_dateController.dispose();
_timeController.dispose();
_commentsController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {
widget.onClose(false);
},
child: Icon(
Icons.close,
size: 18,
color: Color(0xFF575A74),
),
),
),
Text("Bus Booking List",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
SizedBox(
height: 6,
),
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
],
),
),
),
);
});
}
List<Widget> _buildAccomadtionForm (bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) {
return [
isDesktop ? Row(children: children) : Column(children: children),
SizedBox(height: 10),
];
}
List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop),
_buildSecondRow(isDesktop)
];
return [
// ...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container
...rowBuilders.expand((row) => buildResponsiveRow(row)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Trip Type",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
isDesktop ? Row(children: _buildTripType(isDesktop)
) :
Column(
children: _buildTripType(isDesktop)
)
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
];
}
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
widget.updateFormData("Flight", "trip_type", newValue ?? "");
}
: null,
items: dropdownItems,
),
),
),
];
}
List<Widget> _builClassType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Class *",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
}
: null,
items: dropdownItems,
),
// child: TextField(
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Select Class",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
),
),
],
),
];
}
List<Widget> _buildSecondRow(bool isDesktop) {
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
Future<void> _selectCheckOutTime(BuildContext context) async {
TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(now.year, now.month, now.day, pickedTime.hour,
pickedTime.minute),
);
_timeController.text = formattedTime;
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"From",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _fromFocusNode,
controller: _fromController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "From",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"To",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _toFocusNode,
controller: _toController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "To",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _dateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _timeFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutTime(context),
child: AbsorbPointer(
child: TextField(
focusNode: _timeFocusNode,
controller: _timeController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Time",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.4
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: _commentsFocusNode,
controller: _commentsController,
maxLines: 6,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
)
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog or screen
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Close",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
// TODO: Implement save logic
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
];
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,615 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class InsuranceScreen extends StatefulWidget {
final Map<String, String> formData;
final Map<String, dynamic>? apiData;
final Function(String tab, String key, String value) updateFormData;
final Function(bool) onClose;
InsuranceScreen({required this.formData, required this.updateFormData,
required this.onClose, required this.apiData});
@override
_InsuranceScreenState createState() => _InsuranceScreenState();
}
class _InsuranceScreenState extends State<InsuranceScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Map<String, String?> selectedValues = {};
final FocusNode _tripTypeFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _fromFocusNode = FocusNode();
final FocusNode _toFocusNode = FocusNode();
final FocusNode _dateFocusNode = FocusNode();
final FocusNode _timeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late Map<String, TextEditingController> _controllers;
late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _startdateController = TextEditingController();
late TextEditingController _endDateController = TextEditingController();
late TextEditingController _commentsController = TextEditingController();
bool _isHotelNameFocused = false;
bool _dateFocus = false;
bool _commentsFocus = false;
String? selectedTripType;
@override
void initState() {
super.initState();
// _tripTypeFocusNode.addListener(() {
// setState(() {
// _tripTypeFocused = _tripTypeFocusNode.hasFocus;
// });
// });
_hotelNameFocusNode.addListener(() {
setState(() {
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
});
});
_dateFocusNode.addListener(() {
setState(() {
_dateFocus = _fromFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
setState(() {
_commentsFocus = _commentsFocusNode.hasFocus;
});
});
_tripTypeController =
TextEditingController(text: widget.formData["tripType"] ?? "");
_startdateController =
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
_commentsController =
TextEditingController(text: widget.formData["_comments"] ?? "");
// Save data when user types
_tripTypeController.addListener(() {
widget.updateFormData(
"Insurance", "_tripType", _tripTypeController.text);
}); // Save data when user types
_startdateController.addListener(() {
widget.updateFormData(
"Insurance", "_Check_Out", _startdateController.text);
});
_commentsController.addListener(() {
widget.updateFormData(
"Insurance", "_comments", _commentsController.text);
});
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
@override
void dispose() {
_tripTypeFocusNode.dispose();
_tripTypeController.dispose();
_startdateController.dispose();
_commentsController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {
widget.onClose(false);
},
child: Icon(
Icons.close,
size: 18,
color: Color(0xFF575A74),
),
),
),
Text("Insurance Booking List",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
SizedBox(
height: 6,
),
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
],
),
),
),
);
});
}
List<Widget> _buildAccomadtionForm (bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) {
return [
isDesktop ? Row(children: children) : Column(children: children),
SizedBox(height: 10),
];
}
List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop),
_buildSecondRow(isDesktop)
];
return [
...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container
...rowBuilders.expand((row) => buildResponsiveRow(row)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Insurance Type",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
isDesktop ? Row(children: _buildTripType(isDesktop)
) :
Column(
children: _buildTripType(isDesktop)
)
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
];
}
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['insurance_type_of_insurance'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
print(selectedPurpose);
}
: null,
items: dropdownItems,
),
),
),
];
}
List<Widget> _builClassType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Class *",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
}
: null,
items: dropdownItems,
),
// child: TextField(
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Select Class",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
),
),
],
),
];
}
List<Widget> _buildSecondRow(bool isDesktop) {
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_startdateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
Future<void> _selectEndCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_endDateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Start Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _startdateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"End Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectEndCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _endDateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.4
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: _commentsFocusNode,
controller: _commentsController,
maxLines: 6,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
)
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog or screen
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Close",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
// TODO: Implement save logic
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
];
}
}

View File

@ -0,0 +1,615 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class MiscellaneousScreen extends StatefulWidget {
final Map<String, String> formData;
final Map<String, dynamic>? apiData;
final Function(String tab, String key, String value) updateFormData;
final Function(bool) onClose;
MiscellaneousScreen({required this.formData, required this.updateFormData,
required this.onClose, required this.apiData});
@override
_MiscellaneousScreenState createState() => _MiscellaneousScreenState();
}
class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Map<String, String?> selectedValues = {};
final FocusNode _tripTypeFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _fromFocusNode = FocusNode();
final FocusNode _toFocusNode = FocusNode();
final FocusNode _dateFocusNode = FocusNode();
final FocusNode _timeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late Map<String, TextEditingController> _controllers;
late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _startdateController = TextEditingController();
late TextEditingController _endDateController = TextEditingController();
late TextEditingController _commentsController = TextEditingController();
bool _isHotelNameFocused = false;
bool _dateFocus = false;
bool _commentsFocus = false;
String? selectedTripType;
@override
void initState() {
super.initState();
// _tripTypeFocusNode.addListener(() {
// setState(() {
// _tripTypeFocused = _tripTypeFocusNode.hasFocus;
// });
// });
_hotelNameFocusNode.addListener(() {
setState(() {
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
});
});
_dateFocusNode.addListener(() {
setState(() {
_dateFocus = _fromFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
setState(() {
_commentsFocus = _commentsFocusNode.hasFocus;
});
});
_tripTypeController =
TextEditingController(text: widget.formData["tripType"] ?? "");
_startdateController =
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
_commentsController =
TextEditingController(text: widget.formData["_comments"] ?? "");
// Save data when user types
_tripTypeController.addListener(() {
widget.updateFormData(
"Miscellaneous", "_tripType", _tripTypeController.text);
}); // Save data when user types
_startdateController.addListener(() {
widget.updateFormData(
"Miscellaneous", "_Check_Out", _startdateController.text);
});
_commentsController.addListener(() {
widget.updateFormData(
"Miscellaneous", "_comments", _commentsController.text);
});
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
@override
void dispose() {
_tripTypeFocusNode.dispose();
_tripTypeController.dispose();
_startdateController.dispose();
_commentsController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {
widget.onClose(false);
},
child: Icon(
Icons.close,
size: 18,
color: Color(0xFF575A74),
),
),
),
Text("Miscellaneous Booking List",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
SizedBox(
height: 6,
),
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
],
),
),
),
);
});
}
List<Widget> _buildAccomadtionForm (bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) {
return [
isDesktop ? Row(children: children) : Column(children: children),
SizedBox(height: 10),
];
}
List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop),
_buildSecondRow(isDesktop)
];
return [
...buildResponsiveRow(_buildFirstRow(isDesktop)),
// // Iterate over rowBuilders and wrap each in a responsive container
// ...rowBuilders.expand((row) => buildResponsiveRow(row)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Special Type",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
isDesktop ? Row(children: _buildTripType(isDesktop)
) :
Column(
children: _buildTripType(isDesktop)
)
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
];
}
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['miscellaneous_special_request'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedSpecialType = dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options";
return [
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedSpecialType,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedSpecialType = newValue;
});
print(selectedSpecialType);
}
: null,
items: dropdownItems,
),
),
),
];
}
List<Widget> _builClassType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedSpecialType = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Class *",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedSpecialType,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedSpecialType = newValue;
});
}
: null,
items: dropdownItems,
),
// child: TextField(
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Select Class",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
),
),
],
),
];
}
List<Widget> _buildSecondRow(bool isDesktop) {
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_startdateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
Future<void> _selectEndCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_endDateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Start Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _startdateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"End Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectEndCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _endDateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.4
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: _commentsFocusNode,
controller: _commentsController,
maxLines: 6,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
)
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog or screen
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Close",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
// TODO: Implement save logic
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
];
}
}

View File

@ -0,0 +1,994 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class ForexScreen extends StatefulWidget {
final Map<String, String> formData;
final Map<String, dynamic>? apiData;
final Function(String tab, String key, String value) updateFormData;
final Function(bool) onClose;
ForexScreen({required this.formData, required this.updateFormData,
required this.onClose, this.apiData});
@override
_ForexScreenState createState() => _ForexScreenState();
}
class _ForexScreenState extends State<ForexScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Map<String, String?> selectedValues = {};
bool isChecked = false; // State variable for checkbox
final FocusNode _tripTypeFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _fromFocusNode = FocusNode();
final FocusNode _toFocusNode = FocusNode();
final FocusNode _dateFocusNode = FocusNode();
final FocusNode _timeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late Map<String, TextEditingController> _controllers;
late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _fromController = TextEditingController();
late TextEditingController _toController = TextEditingController();
late TextEditingController _dateController = TextEditingController();
late TextEditingController _endDateController = TextEditingController();
late TextEditingController _timeController = TextEditingController();
late TextEditingController _commentsController = TextEditingController();
bool _tripTypeFocused = false;
bool _isHotelNameFocused = false;
bool _fromFocus = false;
bool _toFocus = false;
bool _dateFocus = false;
bool _timeFocus = false;
bool _commentsFocus = false;
@override
void initState() {
super.initState();
_tripTypeFocusNode.addListener(() {
setState(() {
_tripTypeFocused = _tripTypeFocusNode.hasFocus;
});
_hotelNameFocusNode.addListener(() {
setState(() {
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
});
});
_fromFocusNode.addListener(() {
setState(() {
_fromFocus = _fromFocusNode.hasFocus;
});
});
_toFocusNode.addListener(() {
setState(() {
_toFocus = _toFocusNode.hasFocus;
});
});
_dateFocusNode.addListener(() {
setState(() {
_dateFocus = _fromFocusNode.hasFocus;
});
});
_timeFocusNode.addListener(() {
setState(() {
_timeFocus = _timeFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
setState(() {
_commentsFocus = _commentsFocusNode.hasFocus;
});
});
});
_tripTypeController =
TextEditingController(text: widget.formData["tripType"] ?? "");
_hotelNameController =
TextEditingController(text: widget.formData["_hotelName"] ?? "");
_fromController =
TextEditingController(text: widget.formData["_from"] ?? "");
_toController =
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
_dateController =
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
_endDateController =
TextEditingController(text: widget.formData["_Check_In"] ?? "");
_timeController =
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
_commentsController =
TextEditingController(text: widget.formData["_comments"] ?? "");
// Save data when user types
_tripTypeController.addListener(() {
widget.updateFormData(
"Bus", "_tripType", _tripTypeController.text);
}); // Save data when user types
_hotelNameController.addListener(() {
widget.updateFormData(
"Bus", "_hotelName", _hotelNameController.text);
});
_fromController.addListener(() {
widget.updateFormData(
"Bus", "_from", _fromController.text);
});
_toController.addListener(() {
widget.updateFormData(
"Bus", "_Check_In_Time", _toController.text);
});
_dateController.addListener(() {
widget.updateFormData(
"Bus", "_Check_Out", _dateController.text);
});
_endDateController.addListener(() {
widget.updateFormData(
"Bus", "_Check_In", _endDateController.text);
});
_timeController.addListener(() {
widget.updateFormData(
"Bus", "_Check_Out_Time", _timeController.text);
});
_commentsController.addListener(() {
widget.updateFormData(
"Bus", "_comments", _commentsController.text);
});
}
@override
void dispose() {
_tripTypeFocusNode.dispose();
_tripTypeController.dispose();
_hotelNameController.dispose();
_fromController.dispose();
_toController.dispose();
_dateController.dispose();
_endDateController.dispose();
_timeController.dispose();
_commentsController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {
widget.onClose(false);
},
child: Icon(
Icons.close,
size: 18,
color: Color(0xFF575A74),
),
),
),
Text("Forex List",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
SizedBox(
height: 6,
),
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
],
),
),
),
);
});
}
List<Widget> _buildAccomadtionForm (bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) {
return [
isDesktop ? Row(children: children) : Column(children: children),
SizedBox(height: 10),
];
}
List<List<Widget>> rowBuilders = [
_builClassType(isDesktop),
_buildSecondRow(isDesktop)
];
return [
...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container
...rowBuilders.expand((row) => buildResponsiveRow(row)),
...buildResponsiveRow(_buildCardDetailsRow(isDesktop)),
...buildResponsiveRow(_buildFprexCard(isDesktop)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
];
}
List<Widget> _buildFirstRow(isDesktop) {
DateTime? _selectedCheckOutDate;
DateTime? _selectedEndDate;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
};
Future<void> _selectForexEndDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedEndDate != null && _selectedEndDate!.isAfter(today)
? _selectedEndDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedEndDate) {
setState(() {
_selectedEndDate = pickedDate;
_endDateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Forex Start Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _dateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Forex End Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectForexEndDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _endDateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
];
}
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
widget.updateFormData("Flight", "trip_type", newValue ?? "");
}
: null,
items: dropdownItems,
),
),
),
];
}
List<Widget> _builClassType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Class *",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
}
: null,
items: dropdownItems,
),
// child: TextField(
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Select Class",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
),
),
],
),
];
}
List<Widget> _buildSecondRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Transport",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _fromFocusNode,
controller: _fromController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "From",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Accomodation",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _toFocusNode,
controller: _toController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "To",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Telephone",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _fromFocusNode,
controller: _fromController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "From",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Other Expenses",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus,
isDesktop: isDesktop,
color:Colors.transparent,
child: SizedBox(
height: 40,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Rs ",
// focusNode: _toFocusNode,
// controller: _toController,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600,color: Color(0xFF575A74)),
// decoration: const InputDecoration(
// labelText: "To",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
),
),
),
),
],
),
];
}
List<Widget> _buildCardDetailsRow(bool isDesktop) {
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Card Number*",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _fromFocusNode,
controller: _fromController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "From",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Currency*",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
}
: null,
items: dropdownItems,
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Card*",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _toFocusNode,
controller: _toController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "To",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Cash*",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _fromFocusNode,
controller: _fromController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "From",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Delivery Location",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.4
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: _commentsFocusNode,
controller: _commentsController,
maxLines: 3,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
)
];
}
List<Widget> _buildFprexCard(bool isDesktop) {
return [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Checkbox(
value: isChecked,
side: BorderSide(
color: Colors.grey, // Change border color
width: 1, // Adjust thickness
),
onChanged: (bool? value) {
setState(() {
isChecked = value!;
});
},
),
Text(
"Check If You Don't Have a forex Account",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74)),
),
],
)
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog or screen
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Close",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
// TODO: Implement save logic
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
];
}
}

View File

@ -0,0 +1,798 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class TaxiScreen extends StatefulWidget {
final Map<String, String> formData;
final Map<String, dynamic>? apiData;
final Function(String tab, String key, String value) updateFormData;
final Function(bool) onClose;
TaxiScreen({required this.formData, required this.updateFormData,
required this.onClose, this.apiData});
@override
_TaxiScreenState createState() => _TaxiScreenState();
}
class _TaxiScreenState extends State<TaxiScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Map<String, String?> selectedValues = {};
final FocusNode _tripTypeFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _fromFocusNode = FocusNode();
final FocusNode _toFocusNode = FocusNode();
final FocusNode _dateFocusNode = FocusNode();
final FocusNode _timeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late Map<String, TextEditingController> _controllers;
late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _fromController = TextEditingController();
late TextEditingController _toController = TextEditingController();
late TextEditingController _dateController = TextEditingController();
late TextEditingController _timeController = TextEditingController();
late TextEditingController _commentsController = TextEditingController();
bool _tripTypeFocused = false;
bool _isHotelNameFocused = false;
bool _fromFocus = false;
bool _toFocus = false;
bool _dateFocus = false;
bool _timeFocus = false;
bool _commentsFocus = false;
@override
void initState() {
super.initState();
_addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus);
_addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
_addFocusListener(_fromFocusNode, (focus) => _fromFocus = focus);
_addFocusListener(_toFocusNode, (focus) => _toFocus = focus);
_addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus);
_addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus);
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
// _commentsFocusNode.addListener(() {
// setState(() {
// _commentsFocus = _commentsFocusNode.hasFocus;
// });
// });
// });
_tripTypeController =
TextEditingController(text: widget.formData["tripType"] ?? "");
_hotelNameController =
TextEditingController(text: widget.formData["_hotelName"] ?? "");
_fromController =
TextEditingController(text: widget.formData["_from"] ?? "");
_toController =
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
_dateController =
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
_timeController =
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
_commentsController =
TextEditingController(text: widget.formData["_comments"] ?? "");
// Save data when user types
List<TextEditingController> controllers = [
_tripTypeController, _hotelNameController, _fromController, _toController, _dateController,
_timeController, _commentsController
];
List<String> keys = [
"_tripType", "_hotelName", "_from", "_Check_In_Time", "_Check_Out",
"_Check_Out_Time", "_comments"
];
for (int i = 0; i < controllers.length; i++) {
controllers[i].addListener(() {
widget.updateFormData("Flight", keys[i], controllers[i].text);
});
}
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
@override
void dispose() {
_tripTypeFocusNode.dispose();
_tripTypeController.dispose();
_hotelNameController.dispose();
_fromController.dispose();
_toController.dispose();
_dateController.dispose();
_timeController.dispose();
_commentsController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {
widget.onClose(false);
},
child: Icon(
Icons.close,
size: 18,
color: Color(0xFF575A74),
),
),
),
Text("Taxi Booking List",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
SizedBox(
height: 6,
),
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
],
),
),
),
);
});
}
List<Widget> _buildAccomadtionForm (bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) {
return [
isDesktop ? Row(children: children) : Column(children: children),
SizedBox(height: 10),
];
}
List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop),
_buildSecondRow(isDesktop)
];
return [
// Iterate over rowBuilders and wrap each in a responsive container
...rowBuilders.expand((row) => buildResponsiveRow(row)),
...buildResponsiveRow(_buildFirstRow(isDesktop)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
];
}
List<Widget> _buildFirstRow(isDesktop) {
List<dynamic> purposeList = widget.apiData?['taxt_car_type'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Taxi Required For",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
isDesktop ? Row(children: _buildTripType(isDesktop)
) :
Column(
children: _buildTripType(isDesktop)
)
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Number of Passenger",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _fromFocusNode,
controller: _fromController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Destination",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Car Type",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
widget.updateFormData("Flight", "trip_type", newValue ?? "");
}
: null,
items: dropdownItems,
),
),
),
],
),
if (isDesktop)
SizedBox.shrink()
else
SizedBox(
height: 8,
),
];
}
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['taxi_car_required_for'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
widget.updateFormData("Flight", "trip_type", newValue ?? "");
}
: null,
items: dropdownItems,
),
),
),
];
}
List<Widget> _builClassType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Class *",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
}
: null,
items: dropdownItems,
),
// child: TextField(
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Select Class",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
),
),
],
),
];
}
List<Widget> _buildSecondRow(bool isDesktop) {
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
Future<void> _selectCheckOutTime(BuildContext context) async {
TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(now.year, now.month, now.day, pickedTime.hour,
pickedTime.minute),
);
_timeController.text = formattedTime;
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Destination",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _fromFocusNode,
controller: _fromController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Destination",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Location of Pickup",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _toFocusNode,
controller: _toController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Location of Pickup",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _dateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _timeFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutTime(context),
child: AbsorbPointer(
child: TextField(
focusNode: _timeFocusNode,
controller: _timeController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Time",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.4
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: _commentsFocusNode,
controller: _commentsController,
maxLines: 6,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
)
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog or screen
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Close",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
// TODO: Implement save logic
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
];
}
}

View File

@ -0,0 +1,726 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class TrainScreen extends StatefulWidget {
final Map<String, String> formData;
final Map<String, dynamic>? apiData;
final Function(String tab, String key, String value) updateFormData;
final Function(bool) onClose;
TrainScreen({required this.formData, required this.updateFormData,
required this.onClose, this.apiData});
@override
_BusScreenState createState() => _BusScreenState();
}
class _BusScreenState extends State<TrainScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Map<String, String?> selectedValues = {};
final FocusNode _trainNoFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _fromFocusNode = FocusNode();
final FocusNode _toFocusNode = FocusNode();
final FocusNode _dateFocusNode = FocusNode();
final FocusNode _timeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late Map<String, TextEditingController> _controllers;
late TextEditingController _trainNoController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _fromController = TextEditingController();
late TextEditingController _toController = TextEditingController();
late TextEditingController _dateController = TextEditingController();
late TextEditingController _timeController = TextEditingController();
late TextEditingController _commentsController = TextEditingController();
bool _trainNoFocused = false;
bool _isHotelNameFocused = false;
bool _fromFocus = false;
bool _toFocus = false;
bool _dateFocus = false;
bool _timeFocus = false;
bool _commentsFocus = false;
@override
void initState() {
super.initState();
_trainNoFocusNode.addListener(() {
setState(() {
_trainNoFocused = _trainNoFocusNode.hasFocus;
});
});
_hotelNameFocusNode.addListener(() {
setState(() {
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
});
});
_fromFocusNode.addListener(() {
setState(() {
_fromFocus = _fromFocusNode.hasFocus;
});
});
_toFocusNode.addListener(() {
setState(() {
_toFocus = _toFocusNode.hasFocus;
});
});
_dateFocusNode.addListener(() {
setState(() {
_dateFocus = _fromFocusNode.hasFocus;
});
});
_timeFocusNode.addListener(() {
setState(() {
_timeFocus = _timeFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
setState(() {
_commentsFocus = _commentsFocusNode.hasFocus;
});
});
_trainNoController =
TextEditingController(text: widget.formData["trainNo"] ?? "");
_hotelNameController =
TextEditingController(text: widget.formData["_hotelName"] ?? "");
_fromController =
TextEditingController(text: widget.formData["_from"] ?? "");
_toController =
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
_dateController =
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
_timeController =
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
_commentsController =
TextEditingController(text: widget.formData["_comments"] ?? "");
// Save data when user types
_trainNoController.addListener(() {
widget.updateFormData(
"Train", "_trainNo", _trainNoController.text);
}); // Save data when user types
_hotelNameController.addListener(() {
widget.updateFormData(
"Train", "_hotelName", _hotelNameController.text);
});
_fromController.addListener(() {
widget.updateFormData(
"Train", "_from", _fromController.text);
});
_toController.addListener(() {
widget.updateFormData(
"Train", "_Check_In_Time", _toController.text);
});
_dateController.addListener(() {
widget.updateFormData(
"Train", "_Check_Out", _dateController.text);
});
_timeController.addListener(() {
widget.updateFormData(
"Train", "_Check_Out_Time", _timeController.text);
});
_commentsController.addListener(() {
widget.updateFormData(
"Train", "_comments", _commentsController.text);
});
}
@override
void dispose() {
_trainNoFocusNode.dispose();
_trainNoController.dispose();
_hotelNameController.dispose();
_fromController.dispose();
_toController.dispose();
_dateController.dispose();
_timeController.dispose();
_commentsController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {
widget.onClose(false);
},
child: Icon(
Icons.close,
size: 18,
color: Color(0xFF575A74),
),
),
),
Text("Train Booking List",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
SizedBox(
height: 6,
),
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
],
),
),
),
);
});
}
List<Widget> _buildAccomadtionForm (bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) {
return [
isDesktop ? Row(children: children) : Column(children: children),
SizedBox(height: 10),
];
}
List<List<Widget>> rowBuilders = [
_builClassType(isDesktop),
_buildSecondRow(isDesktop)
];
return [
...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container
...rowBuilders.expand((row) => buildResponsiveRow(row)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Train Number",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
isDesktop ? Row(children: _buildTripType(isDesktop)
) :
Column(
children: _buildTripType(isDesktop)
)
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
];
}
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
CustomTextFieldWrapper(
isFocused: _trainNoFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _trainNoFocusNode,
controller: _trainNoController,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Train number",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
// child: DropdownButtonFormField<String>(
// focusNode: _trainNoFocusNode, // Assign the correct focus node
// value: selectedPurpose,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10), // Proper padding
// ),
// onChanged: purposeList.isNotEmpty
// ? (newValue) {
// setState(() {
// selectedPurpose = newValue;
// });
// print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
//
//
// widget.updateFormData("Flight", "trip_type", newValue ?? "");
// }
// : null,
//
// items: dropdownItems,
// ),
),
),
];
}
List<Widget> _builClassType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['train_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Class *",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
}
: null,
items: dropdownItems,
),
// child: TextField(
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Select Class",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
),
),
],
),
];
}
List<Widget> _buildSecondRow(bool isDesktop) {
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
Future<void> _selectCheckOutTime(BuildContext context) async {
TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(now.year, now.month, now.day, pickedTime.hour,
pickedTime.minute),
);
_timeController.text = formattedTime;
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"From",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _fromFocusNode,
controller: _fromController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "From",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"To",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _toFocusNode,
controller: _toController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "To",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _dateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _timeFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutTime(context),
child: AbsorbPointer(
child: TextField(
focusNode: _timeFocusNode,
controller: _timeController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Time",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon:
Icon(Icons.access_time, size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.4
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: _commentsFocusNode,
controller: _commentsController,
maxLines: 6,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
)
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog or screen
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Close",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
// TODO: Implement save logic
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
];
}
}

View File

@ -0,0 +1,627 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class VisaScreen extends StatefulWidget {
final Map<String, String> formData;
final Map<String, dynamic>? apiData;
final Function(String tab, String key, String value) updateFormData;
final Function(bool) onClose;
VisaScreen({required this.formData, required this.updateFormData,
required this.onClose, this.apiData});
@override
_VisaScreenState createState() => _VisaScreenState();
}
class _VisaScreenState extends State<VisaScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Map<String, String?> selectedValues = {};
final FocusNode _tripTypeFocusNode = FocusNode();
final FocusNode _hotelNameFocusNode = FocusNode();
final FocusNode _fromFocusNode = FocusNode();
final FocusNode _toFocusNode = FocusNode();
final FocusNode _dateFocusNode = FocusNode();
final FocusNode _timeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late Map<String, TextEditingController> _controllers;
late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _fromController = TextEditingController();
late TextEditingController _toController = TextEditingController();
late TextEditingController _dateController = TextEditingController();
late TextEditingController _timeController = TextEditingController();
late TextEditingController _commentsController = TextEditingController();
bool _tripTypeFocused = false;
bool _isHotelNameFocused = false;
bool _fromFocus = false;
bool _toFocus = false;
bool _dateFocus = false;
bool _timeFocus = false;
bool _commentsFocus = false;
@override
void initState() {
super.initState();
_addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus);
_addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
_addFocusListener(_fromFocusNode, (focus) => _fromFocus = focus);
_addFocusListener(_toFocusNode, (focus) => _toFocus = focus);
_addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus);
_addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus);
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
// _commentsFocusNode.addListener(() {
// setState(() {
// _commentsFocus = _commentsFocusNode.hasFocus;
// });
// });
// });
_tripTypeController =
TextEditingController(text: widget.formData["tripType"] ?? "");
_hotelNameController =
TextEditingController(text: widget.formData["_hotelName"] ?? "");
_fromController =
TextEditingController(text: widget.formData["_from"] ?? "");
_toController =
TextEditingController(text: widget.formData["_Check_In_Time"] ?? "");
_dateController =
TextEditingController(text: widget.formData["_Check_Out"] ?? "");
_timeController =
TextEditingController(text: widget.formData["_Check_Out_Time"] ?? "");
_commentsController =
TextEditingController(text: widget.formData["_comments"] ?? "");
// Save data when user types
List<TextEditingController> controllers = [
_tripTypeController, _hotelNameController, _fromController, _toController, _dateController,
_timeController, _commentsController
];
List<String> keys = [
"_tripType", "_hotelName", "_from", "_Check_In_Time", "_Check_Out",
"_Check_Out_Time", "_comments"
];
for (int i = 0; i < controllers.length; i++) {
controllers[i].addListener(() {
widget.updateFormData("Flight", keys[i], controllers[i].text);
});
}
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
@override
void dispose() {
_tripTypeFocusNode.dispose();
_tripTypeController.dispose();
_hotelNameController.dispose();
_fromController.dispose();
_toController.dispose();
_dateController.dispose();
_timeController.dispose();
_commentsController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Container(
color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {
widget.onClose(false);
},
child: Icon(
Icons.close,
size: 18,
color: Color(0xFF575A74),
),
),
),
Text("Visa Registration",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
SizedBox(
height: 6,
),
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
],
),
),
),
);
});
}
List<Widget> _buildAccomadtionForm (bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) {
return [
isDesktop ? Row(children: children) : Column(children: children),
SizedBox(height: 10),
];
}
List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop),
_buildSecondRow(isDesktop)
];
return [
...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container
...rowBuilders.expand((row) => buildResponsiveRow(row)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Type of Visa",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
isDesktop ? Row(children: _buildTripType(isDesktop)
) :
Column(
children: _buildTripType(isDesktop)
)
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
];
}
List<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['visa_type_of_visa'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
CustomTextFieldWrapper(
isFocused: _tripTypeFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _tripTypeFocusNode, // Assign the correct focus node
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
widget.updateFormData("Flight", "trip_type", newValue ?? "");
}
: null,
items: dropdownItems,
),
),
),
];
}
List<Widget> _builClassType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Country",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
}
: null,
items: dropdownItems,
),
// child: TextField(
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Select Class",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
),
),
],
),
];
}
List<Widget> _buildSecondRow(bool isDesktop) {
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
// ____________
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Country",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
}
: null,
items: dropdownItems,
),
// child: TextField(
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
// controller: _hotelNameController,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// labelText: "Select Class",
// labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
// floatingLabelBehavior: FloatingLabelBehavior.never,
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(vertical: 16),
// ),
// ),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _dateController,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
],
),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.4
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: _commentsFocusNode,
controller: _commentsController,
maxLines: 6,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
)
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog or screen
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[400], // Light grey color
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Close",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
// TODO: Implement save logic
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
];
}
}

View File

@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
class AccomodationListWidget extends StatelessWidget {
const AccomodationListWidget({super.key});
@override
Widget build(BuildContext context) {
return
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Accomodation Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width ,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
),
columns: const [
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
],
),
);
}
List<DataRow> _buildDataRows() {
List<Map<String, String>> data = [
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
];
return data.map((bus) {
return DataRow(cells: [
DataCell(Text(bus["tripType"]!)),
DataCell(Text(bus["class"]!)),
DataCell(Text(bus["from"]!)),
DataCell(Text(bus["to"]!)),
DataCell(Row(
children: [
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
// View action
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
// Edit action
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
// Delete action
},
),
],
)),
]);
}).toList();
}
}

View File

@ -0,0 +1,152 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/apiUrl.dart';
import '../../data/models/plan.dart';
class BusListWidget extends StatefulWidget{
const BusListWidget({super.key});
@override
_BusListWidgetState createState() => _BusListWidgetState();
}
class _BusListWidgetState extends State<BusListWidget> {
// const BusListWidget({super.key});
late Future<List<Plan>> futurePlans;
@override
void initState(){
super.initState();
futurePlans = fetchPlans();
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
// Fetch API Data
Future<List<Plan>> fetchPlans() async {
final String apiUrldata = '$apiUrl/api/plans';
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
List<dynamic> plansJson = data['data'];
return plansJson.map((json) => Plan.fromJson(json)).toList();
} else {
throw Exception('Failed to load plans');
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Bus Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Container(
// color: Colors.blueGrey,
width: double.infinity,
child: Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Expanded(
child: SizedBox(
width: MediaQuery.of(context).size.width ,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
),
columns: const [
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
),
),
],
),
);
}
List<DataRow> _buildDataRows() {
List<Map<String, String>> data = [
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
];
return data.map((bus) {
return DataRow(cells: [
DataCell(Text(bus["tripType"]!)),
DataCell(Text(bus["class"]!)),
DataCell(Text(bus["from"]!)),
DataCell(Text(bus["to"]!)),
DataCell(Row(
children: [
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
// View action
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
// Edit action
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
// Delete action
},
),
],
)),
]);
}).toList();
}
}

View File

@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
class FlightListWidget extends StatelessWidget {
const FlightListWidget({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Flight Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
// width: 1000,
width: MediaQuery.of(context).size.width ,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
),
columns: const [
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
],
),
);
}
List<DataRow> _buildDataRows() {
List<Map<String, String>> data = [
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
];
return data.map((bus) {
return DataRow(cells: [
DataCell(Text(bus["tripType"]!)),
DataCell(Text(bus["class"]!)),
DataCell(Text(bus["from"]!)),
DataCell(Text(bus["to"]!)),
DataCell(Row(
children: [
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
// View action
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
// Edit action
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
// Delete action
},
),
],
)),
]);
}).toList();
}
}

View File

@ -0,0 +1,151 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/apiUrl.dart';
import '../../data/models/plan.dart';
class ForexListWidget extends StatefulWidget{
const ForexListWidget({super.key});
@override
_ForexListWidgetState createState() => _ForexListWidgetState();
}
class _ForexListWidgetState extends State<ForexListWidget> {
// const BusListWidget({super.key});
late Future<List<Plan>> futurePlans;
@override
void initState(){
super.initState();
futurePlans = fetchPlans();
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
// Fetch API Data
Future<List<Plan>> fetchPlans() async {
final String apiUrldata = '$apiUrl/api/plans';
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
List<dynamic> plansJson = data['data'];
return plansJson.map((json) => Plan.fromJson(json)).toList();
} else {
throw Exception('Failed to load plans');
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Forex List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Container(
// color: Colors.blueGrey,
width: double.infinity,
child: Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Expanded(
child: SizedBox(
width: MediaQuery.of(context).size.width ,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
),
columns: const [
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
),
),
],
),
);
}
List<DataRow> _buildDataRows() {
List<Map<String, String>> data = [
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
];
return data.map((bus) {
return DataRow(cells: [
DataCell(Text(bus["tripType"]!)),
DataCell(Text(bus["class"]!)),
DataCell(Text(bus["from"]!)),
DataCell(Text(bus["to"]!)),
DataCell(Row(
children: [
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
// View action
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
// Edit action
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
// Delete action
},
),
],
)),
]);
}).toList();
}
}

View File

@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
class InsuranceListWidget extends StatelessWidget {
const InsuranceListWidget({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Insurance Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width ,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
),
columns: const [
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
],
),
),
);
}
List<DataRow> _buildDataRows() {
List<Map<String, String>> data = [
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
];
return data.map((bus) {
return DataRow(cells: [
DataCell(Text(bus["tripType"]!)),
DataCell(Text(bus["class"]!)),
DataCell(Text(bus["from"]!)),
DataCell(Text(bus["to"]!)),
DataCell(Row(
children: [
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
// View action
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
// Edit action
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
// Delete action
},
),
],
)),
]);
}).toList();
}
}

View File

@ -0,0 +1,110 @@
import 'package:flutter/material.dart';
class BusListWidget extends StatelessWidget {
const BusListWidget({super.key});
@override
Widget build(BuildContext context) {
return _buildTable("Bus Booking List", _getBusData());
}
List<Map<String, String>> _getBusData() {
return [
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
];
}
}
class TrainListWidget extends StatelessWidget {
const TrainListWidget({super.key});
@override
Widget build(BuildContext context) {
return _buildTable("Train Booking List", _getTrainData());
}
List<Map<String, String>> _getTrainData() {
return [
{"tripType": "Express", "class": "Sleeper", "from": "Chicago", "to": "Boston"},
{"tripType": "Local", "class": "First-Class", "from": "Houston", "to": "Dallas"},
];
}
}
class TaxiListWidget extends StatelessWidget {
const TaxiListWidget({super.key});
@override
Widget build(BuildContext context) {
return _buildTable("Taxi Booking List", _getTaxiData());
}
List<Map<String, String>> _getTaxiData() {
return [
{"tripType": "City Ride", "class": "Sedan", "from": "Manhattan", "to": "Brooklyn"},
{"tripType": "Airport Transfer", "class": "SUV", "from": "JFK", "to": "Times Square"},
];
}
}
// Reusable function for building DataTable
Widget _buildTable(String title, List<Map<String, String>> data) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
border: TableBorder(
top: BorderSide(color: Colors.black12),
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal borders
),
columns: const [
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: data.map((item) {
return DataRow(cells: [
DataCell(Text(item["tripType"]!)),
DataCell(Text(item["class"]!)),
DataCell(Text(item["from"]!)),
DataCell(Text(item["to"]!)),
DataCell(Row(
children: [
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
// View action
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
// Edit action
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
// Delete action
},
),
],
)),
]);
}).toList(),
),
),
],
),
);
}

View File

@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
class MiscellaneousListWidget extends StatelessWidget {
const MiscellaneousListWidget({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Miscellaneous Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width ,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
),
columns: const [
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
],
),
),
);
}
List<DataRow> _buildDataRows() {
List<Map<String, String>> data = [
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
];
return data.map((bus) {
return DataRow(cells: [
DataCell(Text(bus["tripType"]!)),
DataCell(Text(bus["class"]!)),
DataCell(Text(bus["from"]!)),
DataCell(Text(bus["to"]!)),
DataCell(Row(
children: [
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
// View action
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
// Edit action
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
// Delete action
},
),
],
)),
]);
}).toList();
}
}

View File

@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
class TaxiListWidget extends StatelessWidget {
const TaxiListWidget({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Taxi Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width ,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
),
columns: const [
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
],
),
);
}
List<DataRow> _buildDataRows() {
List<Map<String, String>> data = [
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
];
return data.map((bus) {
return DataRow(cells: [
DataCell(Text(bus["tripType"]!)),
DataCell(Text(bus["class"]!)),
DataCell(Text(bus["from"]!)),
DataCell(Text(bus["to"]!)),
DataCell(Row(
children: [
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
// View action
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
// Edit action
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
// Delete action
},
),
],
)),
]);
}).toList();
}
}

View File

@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
class TrainListWidget extends StatelessWidget {
const TrainListWidget({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Train Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width ,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
),
columns: const [
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
],
),
),
);
}
List<DataRow> _buildDataRows() {
List<Map<String, String>> data = [
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
];
return data.map((bus) {
return DataRow(cells: [
DataCell(Text(bus["tripType"]!)),
DataCell(Text(bus["class"]!)),
DataCell(Text(bus["from"]!)),
DataCell(Text(bus["to"]!)),
DataCell(Row(
children: [
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
// View action
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
// Edit action
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
// Delete action
},
),
],
)),
]);
}).toList();
}
}

View File

@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
class VisaListWidget extends StatelessWidget {
const VisaListWidget({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Visa Booking List",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width ,
child: DataTable(
border: TableBorder(
bottom: BorderSide(color: Colors.black12),
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
),
columns: const [
DataColumn(label: Text('Trip Type')),
DataColumn(label: Text('Class')),
DataColumn(label: Text('From')),
DataColumn(label: Text('To')),
DataColumn(label: Text('Actions')),
],
rows: _buildDataRows(),
),
),
),
),
],
),
),
);
}
List<DataRow> _buildDataRows() {
List<Map<String, String>> data = [
{"tripType": "One-Way", "class": "Economy", "from": "NYC", "to": "LA"},
{"tripType": "Round-Trip", "class": "Business", "from": "SF", "to": "Seattle"},
];
return data.map((bus) {
return DataRow(cells: [
DataCell(Text(bus["tripType"]!)),
DataCell(Text(bus["class"]!)),
DataCell(Text(bus["from"]!)),
DataCell(Text(bus["to"]!)),
DataCell(Row(
children: [
IconButton(
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
onPressed: () {
// View action
},
),
IconButton(
icon: Icon(Icons.edit, color: Colors.green),
onPressed: () {
// Edit action
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
// Delete action
},
),
],
)),
]);
}).toList();
}
}

View File

@ -0,0 +1,744 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:frontend/Screens/plans/dynamic_itinerary_stepper.dart';
import 'package:go_router/go_router.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import '../../config/apiUrl.dart';
import '../../data/models/plan.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
import '../../widgets/custom_text_field.dart';
import '../dialog/user_selection_dialog.dart';
class CreatePlan extends StatefulWidget {
const CreatePlan({super.key});
@override
_CreatePlansState createState() => _CreatePlansState();
}
class _CreatePlansState extends State<CreatePlan> {
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
Container(
color: Color(0xFFF4F4FB),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16),
child: Row(children: [
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.create_new_folder_outlined,
color: Color(0xFF84869A),
size: 23,
),
),
Text(
"New Plan",
style: TextStyle(fontSize: 18),
),
],
),
Spacer(),
Container(
color: Color(0xFFE9EBF6),
child: IconButton(
icon: Icon(Icons.close),
onPressed: () {
context.go('/listPlan');
},
),
)
]),
),
Expanded(
child: Container(
color: Colors.white,
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(26.0),
child: CreateNewPlan(isDesktop: isDesktop),
),
),
),
)
],
),
);
});
}
}
class CreateNewPlan extends StatefulWidget {
final bool isDesktop;
const CreateNewPlan({super.key, required this.isDesktop});
@override
_CreateNewPlansState createState() => _CreateNewPlansState();
}
class _CreateNewPlansState extends State<CreateNewPlan> {
final FocusNode _textFieldFocusNode = FocusNode(); // Declare FocusNode
bool _isTextFieldFocused = false;
late String _selectedOption = "Option 1";
late String? _selectedIsBillable = "Billable";
Map<String, dynamic>? storedData;
Map<String, dynamic>? apiData; // Store API response here
bool isLoading = true; // Track loading state
@override
void initState() {
super.initState();
fetchPlans();
_textFieldFocusNode.addListener(() {
setState(() {
_isTextFieldFocused = _textFieldFocusNode.hasFocus;
});
});
}
@override
void dispose() {
_textFieldFocusNode.dispose();
super.dispose();
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('userId');
}
Future<void> fetchPlans() async {
final String apiUrldata = '$apiUrl/api/getDropdownMaster';
final token = await getToken();
final userId = "1";
// final userId = await getUserId();
print("SUSRTRT- $userId");
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
try {
final data = json.decode(response.body);
print(data);
if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception("Invalid response format: 'data' field is missing or not a Map");
}
Map<String, dynamic> plansJson = data['data']; // 'data' is a Map, not a List
setState(() {
apiData = data['data']; // Store API response in state
isLoading = false;
});
} catch (e) {
throw Exception('Error parsing response: $e');
}
} else {
throw Exception('Failed to load plans');
}
}
Future<void> getStoredData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? jsonString = prefs.getString('api_response');
if (jsonString != null) {
storedData = json.decode(jsonString);
print('Stored Data: $storedData');
} else {
storedData = null;
}
return;
}
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Plan This Trip For", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
isMobile
? SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: _buildPlanTrip(isMobile),
),
)
: Row(
children: _buildPlanTrip(isMobile),
),
],
),
),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Divider(
color: Color(0xFFE6E7F5), // Change color
thickness: 0.5,
),
),
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Trip Title", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isTextFieldFocused,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _textFieldFocusNode,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Trip Title",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
],
),
],
),
SizedBox(
height: 10,
),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Trip Type", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
isMobile
? SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: _buildTripType(isMobile),
),
)
: Row(
children: _buildTripType(isMobile),
),
],
),
),
],
),
SizedBox(
height: 15,
),
isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildCostIsBillable())
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _buildCostIsBillable()),
SizedBox(
height: 8,
),
isDesktop
? Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildNonDescriptionColumn(),
SizedBox(width: 25),
_buildDescriptionColumn(isDesktop),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildNonDescriptionColumn(),
SizedBox(height: 15),
_buildDescriptionColumn(isDesktop),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Divider(
color: Color(0xFFE6E7F5), // Change color
thickness: 0.5,
),
),
Row(
children: [
Expanded(child: DynamicItinerary(apiData: apiData)), // Wrap with Expanded if needed
],
),
],
);
});
}
/// Extracted helper function
List<Widget> _buildCostIsBillable() {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Cost Center *", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus
isDesktop: widget.isDesktop,
child: SizedBox(
height: 45, // Set appropriate height
child: DropdownButtonFormField<String>(
value: "Option 1",
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(horizontal: 10), // Proper padding
),
onChanged: (newValue) {},
items: [
DropdownMenuItem(value: "Option 1", child: Text("Option 1")),
DropdownMenuItem(value: "Option 2", child: Text("Option 2")),
],
),
),
),
],
),
SizedBox(width: 25),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Is Billable ", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
Row(
children: [
Row(
children: [
Radio<String>(
value: "Billable",
groupValue: _selectedIsBillable,
activeColor: Colors.blueAccent,
onChanged: (value) {
setState(() {
_selectedIsBillable = value;
});
},
),
Text("Billable"),
],
),
SizedBox(width: 20), // Spacing
Row(
children: [
Radio<String>(
value: "Non Billable",
groupValue: _selectedIsBillable,
activeColor: Colors.blueAccent,
onChanged: (value) {
setState(() {
_selectedIsBillable = value;
});
},
),
Text("Non Billable"),
],
),
],
),
],
)
];
}
List<Widget> _buildPlanTrip(bool isDesktop) {
List<Map<String, String>> options = [
{"title": "Self", "value": "Option 1"},
{"title": "Other Employee", "value": "Option 2"},
{"title": "Others", "value": "Option 3"},
];
return options.map((option) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: CustomTextFieldWrapper(
color: Color(0xFFF4F4FB),
width: option["value"] == "Option 2" ? 175 : 120, // Adjust width conditionally
isFocused: _selectedOption == option["value"],
isDesktop: isDesktop,
child: RadioListTile<String>(
activeColor: Colors.blueAccent,
contentPadding: EdgeInsets.zero,
dense: true,
title: Text(option["title"]!),
value: option["value"]!,
groupValue: _selectedOption,
onChanged: (value) {
setState(() {
_selectedOption = value!;
if(value == 'Option 2' || value == 'Option 3' ){
_showInputDialog(option["title"]!);
}
});
},
),
),
);
}).toList();
}
List<Widget> _buildTripType(bool isMobile) {
return [
CustomTextFieldWrapper(
color: Color(0xFFF4F4FB),
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
width: 120,
isFocused: _selectedOption == "Option 1",
isDesktop: widget.isDesktop,
child: SizedBox(
height: 35,
child: Material(
color: Colors.transparent,
child: RadioListTile<String>(
activeColor: Colors.blueAccent,
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
dense: true,
title: Text("Domestic"),
value: "Option 1",
groupValue: _selectedOption,
onChanged: (value) {
setState(() {
_selectedOption = value!;
});
},
),
),
),
),
SizedBox(width: 20),
CustomTextFieldWrapper(
color: Color(0xFFF4F4FB),
width: 150,
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2),
isFocused: _selectedOption == "Option 2",
isDesktop: widget.isDesktop,
child: RadioListTile<String>(
activeColor: Colors.blueAccent,
contentPadding: EdgeInsets.zero,
dense: true,
title: Text("International"),
value: "Option 2",
groupValue: _selectedOption,
onChanged: (value) {
setState(() {
_selectedOption = value!;
});
},
),
),
];
}
Widget _buildNonDescriptionColumn (){
// 'plan_purpose_of_travel' Starts ------------------------------------------------------
List<dynamic> purposeList = apiData?['plan_purpose_of_travel'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
// 'plan_functional_department' Starts ---------------------------------------------
List<dynamic> funcDeptList = apiData?['plan_functional_department'] ?? [];
List<DropdownMenuItem<String>> dropdownFuncDeptItems = funcDeptList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownFuncDeptItems.isEmpty) {
dropdownFuncDeptItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
String? selectedFuncDept = dropdownFuncDeptItems.isNotEmpty ? dropdownFuncDeptItems.first.value : null;
// 'plan_functional_department' End
return Column(
children: [
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Purpose of Travel *", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus
isDesktop: widget.isDesktop,
child: SizedBox(
height: 45, // Set appropriate height
child: DropdownButtonFormField<String>(
value: selectedPurpose,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedPurpose = newValue;
});
}
: null,
items: dropdownItems,
),
),
),
],
),
],
),
SizedBox(
height: 8,
),
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Functional Department", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus
isDesktop: widget.isDesktop,
child: SizedBox(
height: 45, // Set appropriate height
child: DropdownButtonFormField<String>(
value: selectedFuncDept,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(
horizontal: 10), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedFuncDept = newValue;
});
}
: null,
items: dropdownFuncDeptItems,
),
),
),
],
),
],
),
SizedBox(
height: 15,
),
]
);
}
Widget _buildDescriptionColumn(isDesktop){
return Column(
children: [
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Description", // Your label
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus
width: isDesktop? MediaQuery.of(context).size.width * 0.5 :
MediaQuery.of(context).size.width * 0.85 ,
isDesktop: widget.isDesktop,
child: TextField(
focusNode: _textFieldFocusNode,
maxLines: 6,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
),
],
),
],
);
}
void _showInputDialog(String title){
showDialog(
context: context,
builder: (BuildContext context){
return UserSelectionDialog(
title:title,
onSubmit: (input){
print("USer entered : $input");
}
);
}
);
}
}

View File

@ -0,0 +1,266 @@
import 'package:easy_stepper/easy_stepper.dart';
import 'package:flutter/material.dart';
import 'package:frontend/Screens/itnerary_list/accomodation_list.dart';
import 'package:frontend/Screens/itnerary_list/bus_list.dart';
import 'package:frontend/Screens/itnerary_list/flight_list.dart';
import 'package:frontend/Screens/itnerary_list/taxi_list.dart';
import 'package:frontend/Screens/itnerary_list/train_list.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../itnerary/accomodations.dart';
import '../itnerary/bus.dart';
import '../itnerary/flights.dart';
import '../itnerary/forex.dart';
import '../itnerary/insurance.dart';
import '../itnerary/miscellaneous.dart';
import '../itnerary/taxi.dart';
import '../itnerary/train.dart';
import '../itnerary/visa.dart';
import '../itnerary_list/forex_list.dart';
import '../itnerary_list/insurance_list.dart';
import '../itnerary_list/miscellaneous_list.dart';
import '../itnerary_list/visa_list.dart';
class DynamicItinerary extends StatefulWidget {
final Map<String, dynamic>? apiData;
const DynamicItinerary({super.key, required this.apiData});
@override
_DynamicItineraryState createState() => _DynamicItineraryState();
}
class _DynamicItineraryState extends State<DynamicItinerary> {
String selectedOption = "";
String selectedListOption = "";
bool isSelected = false;
// Store form values for each tab
final Map<String, Map<String, String>> formData = {
"Flight": {}, // Stores data for the Flight tab
"Train": {}, // Stores data for the Train tab
"Taxi": {}, // Stores data for the Car tab
"Bus": {}, // Stores data for the Bus tab
"Insurance": {}, // Stores data for the Bus tab
"Accommodation": {},// Stores data for the Accommodation tab
"Miscellaneous": {},// Stores data for the Accommodation tab
"Forex": {},
"Visa": {},// Stores data for the Accommodation tab
};
void handleClose(bool value) {
setState(() {
isSelected = value;
});
}
Widget build(BuildContext context) {
Widget selectedWidget;
Widget selectedListWidget;
// void updateFormData(String tab, String key, String value) {
// setState(() {
// formData[tab]![key] = value; // Update the stored data
// });
// }
void updateFormData(String tab, String key, String value) {
setState(() {
if (!formData.containsKey(tab)) {
formData[tab] = {}; // Initialize if null
}
formData[tab]![key] = value; // Update the stored data
});
}
switch (selectedListOption) {
case "Train":
selectedListWidget = TrainListWidget();
break;
case "Taxi":
selectedListWidget = TaxiListWidget();
break;
case "Bus":
selectedListWidget = BusListWidget();
break;
case "Insurance":
selectedListWidget = InsuranceListWidget();
break;
case "Visa":
selectedListWidget = VisaListWidget();
break;
case "Forex":
selectedListWidget = ForexListWidget();
break;
case "Accommodation":
selectedListWidget = AccomodationListWidget();
break;
case "Miscellaneous":
selectedListWidget = MiscellaneousListWidget();
break;
case "Flight":
default:
selectedListWidget = FlightListWidget();
break;
}
switch (selectedOption) {
case "Train":
selectedWidget = TrainScreen(onClose: handleClose,formData: formData["Train"]!, updateFormData: updateFormData, apiData: widget.apiData,);
// selectedWidget = TrainScreen(formData: formData["Train"]!);
break;
case "Taxi":
selectedWidget = TaxiScreen(onClose: handleClose, formData: formData["Taxi"]!, updateFormData: updateFormData, apiData: widget.apiData,);
break;
case "Bus":
selectedWidget = BusScreen(onClose: handleClose, formData: formData["Bus"]!, updateFormData: updateFormData, apiData: widget.apiData,);
break;
case "Insurance":
selectedWidget = InsuranceScreen(onClose: handleClose, formData: formData["Insurance"]!, updateFormData: updateFormData, apiData: widget.apiData,);
break;
case "Visa":
selectedWidget = VisaScreen(onClose: handleClose, formData: formData["Insurance"]!, updateFormData: updateFormData, apiData: widget.apiData,);
break;
case "Miscellaneous":
selectedWidget =MiscellaneousScreen(formData: formData["Miscellaneous"]!, updateFormData: updateFormData, onClose: handleClose, apiData: widget.apiData);
break;
case "Accommodation":
selectedWidget = AccomodationScreen( onClose: handleClose,formData: formData["Accommodation"]!, updateFormData: updateFormData);
break;
case "Forex":
selectedWidget = ForexScreen( onClose: handleClose,formData: formData["Forex"]!, updateFormData: updateFormData, apiData: widget.apiData);
break;
case "Flight":
default:
selectedWidget = FlightScreen(onClose: handleClose,formData: formData["Flight"]!, updateFormData: updateFormData, apiData: widget.apiData,);
break;
}
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isMobile = sizingInfo.isMobile;
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Column(
// mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Itinerary",
style: TextStyle(
fontSize: 18,
),),
],
),
SizedBox(height: 8),
Container(
decoration: BoxDecoration(
border: Border.all(color: Color(0xFFF4F4FB)),
borderRadius: BorderRadius.circular(1),
color: Color(0xFFF4F4FB),
),
padding: EdgeInsets.all(5),
child: isMobile ?
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: _buildOptions(),
),
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// mainAxisSize: MainAxisSize.min,
children: _buildOptions(),
),
),
Column(
// mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(height: 2),
isSelected ? selectedWidget : selectedListWidget,
// TrainScreen()
],
),
],
);
});
}
List<Widget> _buildOptions() {
return [
_buildOption("Flight"),
SizedBox(width: 20),
_buildOption("Taxi"),
SizedBox(width: 20),
_buildOption("Train"),
SizedBox(width: 20),
_buildOption("Bus"),
SizedBox(width: 20),
_buildOption("Accommodation"),
SizedBox(width: 20),
_buildOption("Forex"),
SizedBox(width: 20),
_buildOption("Insurance"),
SizedBox(width: 20),
_buildOption("Visa"),
SizedBox(width: 20),
_buildOption("Miscellaneous"),
];
}
Widget _buildOption(String title) {
return GestureDetector(
onTap: () {
setState(() {
selectedListOption = title;
isSelected = false;
});
},
child: Row(
children: [
Text(title,
style: TextStyle(
fontSize: 13,
color: selectedListOption == title ? Colors.blueAccent : Color(0xFF575A74),
fontWeight:
selectedListOption == title ? FontWeight.bold : FontWeight.normal,)),
SizedBox(width: 5),
SizedBox(width: 5),
if (selectedListOption == title)
GestureDetector(
onTap: () {
setState(() {
selectedOption = title;
isSelected = true;
});
},
child: Icon(
Icons.add_circle,
color: Colors.blueAccent,
size: 18,
),
),
]
),
);
}
}

View File

@ -0,0 +1,218 @@
import 'dart:convert';
import 'package:frontend/data/models/plan.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:frontend/config/apiUrl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../routes/custom_appBar.dart';
import '../../routes/custom_drawer.dart';
class ListPlans extends StatefulWidget{
const ListPlans({super.key});
@override
_ListPlansState createState() => _ListPlansState();
}
class _ListPlansState extends State<ListPlans>{
late Future<List<Plan>> futurePlans;
@override
void initState(){
super.initState();
futurePlans = fetchPlans();
}
Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('auth_token');
}
// Fetch API Data
Future<List<Plan>> fetchPlans() async {
final String apiUrldata = '$apiUrl/api/plans';
final token = await getToken();
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final response = await http.get(
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
},
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
List<dynamic> plansJson = data['data'];
return plansJson.map((json) => Plan.fromJson(json)).toList();
} else {
throw Exception('Failed to load plans');
}
}
Widget build(BuildContext context) {
return ResponsiveBuilder(builder: (context, sizingInfo) {
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
return Scaffold(
backgroundColor: Colors.white,
appBar: isDesktop ? null : const CustomAppBar(title: 'Home'),
drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
body: Row(
children: [
if (isDesktop) CustomDrawer(isDesktop: true),
Expanded(child: buildTableLayout(isDesktop))
],
),
);
});
}
Widget buildTableLayout(isDesktop) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
const Text('Plans List',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
IconButton(
icon: const Icon(Icons.keyboard_arrow_down),
onPressed: () {},
),
],
),
ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.blueAccent),
onPressed: () {
context.go('/createPlan');
if (!isDesktop) Navigator.pop(context);
},
child: Row(
children: [
Icon(Icons.add_circle,color: Colors.white,),
SizedBox(width: 5,),
Text('NewPlan'),
],
),
),
],
),
const SizedBox(height: 10),
FutureBuilder<List<Plan>>(
future: futurePlans, // Use the futurePlans variable
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text("Error: ${snapshot.error}"));
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
return const Center(child: Text("No plans available"));
}
List<Plan> plans = snapshot.data!; // Extract the list of plans
return ResponsiveBuilder(
builder: (context, sizingInfo) {
bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop;
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
// scrollDirection: isTabletOrDesktop ? Axis.vertical : Axis.horizontal,
child: SizedBox(
// constraints: isTabletOrDesktop
// ? const BoxConstraints(maxWidth: double.infinity)
// : BoxConstraints.tightFor(width: 600),
width: MediaQuery.of(context).size.width ,
child: DataTable(
// columnSpacing: 50.0,
dividerThickness: 0.5, // Reduce the thickness of row dividers
border: TableBorder(
horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), // Reduce horizontal line thickness
),
columns: const [
DataColumn(label: Text('Plan ID', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
DataColumn(label: Text('Trip Title', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
DataColumn(label: Text('Trip Type', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
DataColumn(label: Text('Cost Center', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Functional Department', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Purpose Of Travel', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
// DataColumn(label: Text('Description', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
//
DataColumn(label: Text('Is Billable', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
DataColumn(label: Text('Status', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
DataColumn(label: Text('Actions', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
],
rows: plans.map((plan) {
return DataRow(cells: [
DataCell(Text(plan.planId)),
// DataCell(Text(plan.tripTitle)),
DataCell(Row(
children: [
Flexible(
child: Text(
plan.tripTitle,
softWrap: true,
overflow: TextOverflow.ellipsis, // Adds "..." if text is too long
),
),
],
)),
DataCell(Text(plan.tripType)),
DataCell(Text(plan.costCenter)),
// DataCell(Text(plan.functionalDepartment)),
// DataCell(Text(plan.purposeOfTravel)),
// DataCell(Text(plan.description)),
//
DataCell(Text(plan.isBillable)),
DataCell(Text(plan.status)),
DataCell(
TextButton(
onPressed: () {
print("View button clicked for ${plan.tripTitle}");
},
child: const Text('View',
style: TextStyle(color: Colors.blueAccent)),
),
),
]);
}).toList(),
),
),
);
},
);
},
),
],
),
);
}
}

16
lib/app.dart Normal file
View File

@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
import 'package:frontend/routes/custom_router.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'TRIP MANAGEMENT',
routerConfig: router,
debugShowCheckedModeBanner: false,
);
}
}

4
lib/config/apiUrl.dart Normal file
View File

@ -0,0 +1,4 @@
//api url
const String apiUrl = 'http://apitest.tripapprovaltool.com';

40
lib/data/models/plan.dart Normal file
View File

@ -0,0 +1,40 @@
class Plan {
final String planId;
final String tripTitle;
final String tripType;
final String status;
final String costCenter;
final String functionalDepartment;
final String purposeOfTravel;
final String soNumber;
final String description;
final String isBillable;
Plan({
required this.planId,
required this.tripTitle,
required this.tripType,
required this.status,
required this.costCenter,
required this.functionalDepartment,
required this.purposeOfTravel,
required this.soNumber,
required this.description,
required this.isBillable,
});
factory Plan.fromJson(Map<String, dynamic> json) {
return Plan(
planId: json['plan_id'],
tripTitle: json['trip_title'],
tripType: json['trip_type_value'],
status: json['status'] == "0" ? "Inactive" : "Active",
costCenter: json['cost_center_value'] ?? '',
functionalDepartment: json['functional_department_value'] ?? '',
purposeOfTravel: json['purpose_of_travel_value'] ?? '',
soNumber: json['so_number'] ?? '',
description: json['description'] ?? '',
isBillable: json['is_billable_value'] ?? '',
);
}
}

View File

@ -0,0 +1,33 @@
class SearchUser{
final String userId;
final String travellerId;
final String firstName;
final String lastName;
final String email;
final String mobileNo;
final String alternateMobileNo;
SearchUser({
required this.userId,
required this.travellerId,
required this.firstName,
required this.lastName,
required this.email,
required this.mobileNo,
required this.alternateMobileNo,
});
factory SearchUser.fromJson(Map<String, dynamic> json){
return SearchUser(
userId: json['user_id'].toString(),
travellerId: json['traveller_id'].toString(),
firstName: json['first_name'].toString()?? '',
lastName: json['last_name'].toString()?? '',
email: json['email'].toString()?? '',
mobileNo: json['mobile_no'].toString()?? '',
alternateMobileNo: json['alternate_mobile_no'].toString()?? '',
);
}
}

6
lib/main.dart Normal file
View File

@ -0,0 +1,6 @@
import 'package:flutter/material.dart';
import 'app.dart';
void main() {
runApp(const MyApp());
}

View File

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final String title;
final List<Widget>? actions; // Optional actions (icons, buttons)
final bool showBackButton;
const CustomAppBar({
super.key,
required this.title,
this.actions,
this.showBackButton = false, // Default: No back button
});
@override
Widget build(BuildContext context) {
return AppBar(
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
centerTitle: true,
backgroundColor: Colors.blueAccent, // Customize color
elevation: 4, // Shadow effect
leading: showBackButton
? IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context), // Back navigation
)
: null,
actions: actions,
);
}
@override
Size get preferredSize => const Size.fromHeight(56); // Standard AppBar height
}

View File

@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:responsive_builder/responsive_builder.dart';
class CustomDrawer extends StatelessWidget{
final bool isDesktop;
const CustomDrawer({super.key, required this.isDesktop});
@override
Widget build(BuildContext context){
Widget drawerContent = Column(
children: [
SizedBox(
height: 80,
child: const DrawerHeader(
decoration: BoxDecoration(color: Colors.blue),
child: SizedBox(
width: double.infinity,
child: Text('Menu', style: TextStyle(color: Colors.white, fontSize: 20)))
),
),
_buildDrawerItem(context, Icons.home,'Home', '/home'),
_buildExpandableItem(context,Icons.assessment,'Plans',[
_buildSubDrawerItem(context,'My Plans','/listPlan'),
_buildSubDrawerItem(context,'PlanB','/PlanB')
]),
_buildDrawerItem(context,Icons.login,'Login','/')
],
);
if (isDesktop) {
// Sidebar for Desktop (always visible)**
return Container(
width: 250, // Fixed width for sidebar
color: Colors.grey.shade50,
child: drawerContent,
);
} else {
// Drawer for Mobile & Tablet**
return Drawer(child: ListView(padding: EdgeInsets.zero, children: [drawerContent]));
}
}
/// **Reusable Drawer Item**
Widget _buildDrawerItem(BuildContext context, IconData icon, String title, String route)
{
return ListTile(
leading: Icon(icon),
title: Text(title),
onTap: (){
context.go(route);
if (!isDesktop) Navigator.pop(context);
},
);
}
Widget _buildExpandableItem(BuildContext context, IconData icon, String title, List<Widget>children){
return ExpansionTile(
leading: Icon(icon),
title: Text(title),
shape: const Border(), // Removes top and bottom dividers
childrenPadding: const EdgeInsets.only(left: 40), // Indent sub-items
children: children,
);
}
Widget _buildSubDrawerItem(BuildContext context, String title, String route)
{
return ListTile(
title: Text(title),
onTap: (){
context.go(route);
if (!isDesktop) Navigator.pop(context);
},
);
}
}

View File

@ -0,0 +1,28 @@
import 'package:frontend/Screens/authentication/login/login_page.dart';
import 'package:frontend/Screens/authentication/loginPage1.dart';
import 'package:frontend/Screens/dashboard/home_page.dart';
import 'package:frontend/Screens/plans/create_plans.dart';
import 'package:frontend/Screens/plans/list_plans.dart';
import 'package:go_router/go_router.dart';
final GoRouter router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => LoginPage(),
),
GoRoute(
path: '/home',
builder: (context, state) => HomePage(),
),
GoRoute(
path: '/listPlan',
builder: (context, state) => ListPlans(),
), GoRoute(
path: '/createPlan',
builder: (context, state) => CreatePlan(),
),
],
);

View File

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
class CustomRadioButtonWrapper extends StatefulWidget {
final bool isFocused;
final bool isDesktop;
final List<String> options;
final String selectedValue;
final Function(String) onChanged;
const CustomRadioButtonWrapper({
Key? key,
required this.isFocused,
required this.isDesktop,
required this.options,
required this.selectedValue,
required this.onChanged,
}) : super(key: key);
@override
_CustomRadioButtonWrapperState createState() => _CustomRadioButtonWrapperState();
}
class _CustomRadioButtonWrapperState extends State<CustomRadioButtonWrapper> {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Select Destination",
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold, color: Colors.black),
),
SizedBox(height: 5),
Column(
children: widget.options.map((option) {
return RadioListTile<String>(
title: Text(option, style: TextStyle(fontSize: 12)),
value: option,
groupValue: widget.selectedValue,
onChanged: (value) {
if (value != null) {
widget.onChanged(value);
}
},
);
}).toList(),
),
],
);
}
}

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