initial push

master
Sneha 3 weeks ago
commit c72957afb4

43
.gitignore vendored

@ -0,0 +1,43 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.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

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

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

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

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

@ -0,0 +1,58 @@
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"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader("UTF-8") { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) {
flutterVersionCode = "1"
}
def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) {
flutterVersionName = "1.0"
}
android {
namespace = "com.example.bookatanker"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.bookatanker"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.debug
}
}
}
flutter {
source = "../.."
}

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

@ -0,0 +1,51 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bookatanker">>
<application
android:label="bookatanker"
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" />
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyDJpK9RVhlBejtJu9xSGfneuTN6HOfJgSM"/>
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~4354546703"/>
</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>

@ -0,0 +1,6 @@
package com.example.bookatanker;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
}

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

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

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

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

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

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

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

@ -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-7.6.3-all.zip

@ -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 "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.9.22" apply false
}
include ":app"

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

34
ios/.gitignore vendored

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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>Bookatanker</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>bookatanker</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>

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

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

@ -0,0 +1,221 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'common/settings.dart';
import 'confirm_mobilenumber.dart';
class ChangePasswordScreen extends StatefulWidget {
@override
_ChangePasswordScreenState createState() => _ChangePasswordScreenState();
}
class _ChangePasswordScreenState extends State<ChangePasswordScreen> {
final _formKey = GlobalKey<FormState>();
final _currentPasswordController = TextEditingController();
final _newPasswordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
bool _obscureCurrent = true;
bool _obscureNew = true;
bool _obscureConfirm = true;
void _savePassword() {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Password changed successfully")),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
surfaceTintColor: Colors.transparent, // <- remove M3 tint
scrolledUnderElevation: 0, // <- keep white when scrolled
title: Text(
"Change Password",
style: fontTextStyle(16, Color(0xFF000000), FontWeight.w800),
),
iconTheme: IconThemeData(color: Color(0XFF2A2A2A)),
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Padding(
padding:
const EdgeInsets.fromLTRB(8, 8, 8, 8), // Add padding if needed
child: Image.asset(
'images/backbutton_appbar.png', // Replace with your image path
fit: BoxFit.contain,
color: Color(0XFF2A2A2A),
height: 24,
width: 24,
),
),
),
),
body: SingleChildScrollView(
// Prevents overflow
padding: const EdgeInsets.fromLTRB(20, 28, 20, 20),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 26),
Text(
"You can only change your password once every 14 days.\nYou can change your password again on XY Dec 2024",
style: fontTextStyle(12, Color(0xFF7E7F80), FontWeight.w400),
),
SizedBox(height: 20),
Text("Current Password",
style:
fontTextStyle(12, Color(0xFF101214), FontWeight.w700)),
SizedBox(height: 8),
TextFormField(
controller: _currentPasswordController,
obscureText: _obscureCurrent,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter current password",
hintStyle:
fontTextStyle(14, Color(0xFF2A2A2A), FontWeight.w400),
suffixIcon: IconButton(
icon: Icon(_obscureCurrent
? Icons.visibility_off
: Icons.visibility),
onPressed: () {
setState(() {
_obscureCurrent = !_obscureCurrent;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return "Please enter current password";
}
return null;
},
),
SizedBox(height: 16),
// New Password
Text("New Password*",
style: fontTextStyle(12, Color(0xFF101214), FontWeight.w700)),
SizedBox(height: 8),
TextFormField(
controller: _newPasswordController,
obscureText: _obscureNew,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter new password",
hintStyle:
fontTextStyle(14, Color(0xFF2A2A2A), FontWeight.w400),
suffixIcon: IconButton(
icon: Icon(_obscureNew
? Icons.visibility_off
: Icons.visibility),
onPressed: () {
setState(() {
_obscureNew = !_obscureNew;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return "Please enter new password";
} else if (value.length < 6) {
return "Password must be at least 6 characters";
}
return null;
},
),
SizedBox(height: 16),
// Confirm Password
Text("Confirm Password*",
style: fontTextStyle(12, Color(0xFF101214), FontWeight.w700)),
SizedBox(height: 8),
TextFormField(
controller: _confirmPasswordController,
obscureText: _obscureConfirm,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Confirm new password",
hintStyle:
fontTextStyle(14, Color(0xFF2A2A2A), FontWeight.w400),
suffixIcon: IconButton(
icon: Icon(_obscureConfirm
? Icons.visibility_off
: Icons.visibility),
onPressed: () {
setState(() {
_obscureConfirm = !_obscureConfirm;
});
},
),
),
validator: (value) {
if (value != _newPasswordController.text) {
return "Passwords do not match";
}
return null;
},
),
SizedBox(height: 24),
// Save and Login button
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _savePassword,
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 16),
backgroundColor: Color(0xFF101214),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
child: Text(
"Save and Login",
style: fontTextStyle(14, Color(0xFFFFFFFF), FontWeight.w600),
),
),
),
SizedBox(height: 40),
// Forgot Password link
Center(
child: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => ConfirmMobileScreen()),
);
},
child: Text(
"Forgot Password?",
style: fontTextStyle(14, Color(0xFF101214), FontWeight.w600),
),
),
),
SizedBox(height: 20),
],
),
),
),
);
}
}

@ -0,0 +1,98 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:geocoding/geocoding.dart';
class LocationPage extends StatefulWidget {
const LocationPage({Key? key}) : super(key: key);
@override
State<LocationPage> createState() => _LocationPageState();
}
class _LocationPageState extends State<LocationPage> {
String? _currentAddress;
Position? _currentPosition;
Future<bool> _handleLocationPermission() async {
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'Location services are disabled. Please enable the services')));
return false;
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Location permissions are denied')));
return false;
}
}
if (permission == LocationPermission.deniedForever) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'Location permissions are permanently denied, we cannot request permissions.')));
return false;
}
return true;
}
Future<void> _getCurrentPosition() async {
final hasPermission = await _handleLocationPermission();
if (!hasPermission) return;
await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
.then((Position position) {
setState(() => _currentPosition = position);
_getAddressFromLatLng(_currentPosition!);
}).catchError((e) {
debugPrint(e);
});
}
Future<void> _getAddressFromLatLng(Position position) async {
await placemarkFromCoordinates(
_currentPosition!.latitude, _currentPosition!.longitude)
.then((List<Placemark> placemarks) {
Placemark place = placemarks[0];
_currentAddress = '${place.street}, ${place.subLocality}, ${place.locality}, ${place.postalCode}, ${place.country}';
print(place);
setState(() {
});
}).catchError((e) {
debugPrint(e);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Location Page")),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('LAT: ${_currentPosition?.latitude ?? ""}'),
Text('LNG: ${_currentPosition?.longitude ?? ""}'),
Text('ADDRESS: ${_currentAddress ?? ""}'),
const SizedBox(height:32),
ElevatedButton(
onPressed: _getCurrentPosition,
child: const Text("Get Current Location"),
)
],
),
),
),
);
}
}

@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
class Dashboard extends StatefulWidget {
const Dashboard({super.key});
@override
State<Dashboard> createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

@ -0,0 +1,6 @@
class APIKeys {
APIKeys._();
static String androidApiKey = "AIzaSyDJpK9RVhlBejtJu9xSGfneuTN6HOfJgSM";
static String iosApiKey = "YOUR IOS KEY HERE";
}

@ -0,0 +1,250 @@
import 'package:flutter/material.dart';
import 'package:google_maps_place_picker_mb/google_maps_place_picker.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:io' show Platform;
import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
class GooglePlacePicker extends StatefulWidget {
GooglePlacePicker({Key? key}) : super(key: key);
static final kInitialPosition = LatLng(17.4167312, 78.4519109);
final GoogleMapsFlutterPlatform mapsImplementation =
GoogleMapsFlutterPlatform.instance;
@override
State<GooglePlacePicker> createState() => _GooglePlacePickerState();
}
class _GooglePlacePickerState extends State<GooglePlacePicker> {
PickResult? selectedPlace;
bool _showPlacePickerInContainer = false;
bool _showGoogleMapInContainer = false;
bool _mapsInitialized = false;
String _mapsRenderer = "latest";
void initRenderer() {
if (_mapsInitialized) return;
if (widget.mapsImplementation is GoogleMapsFlutterAndroid) {
switch (_mapsRenderer) {
case "legacy":
(widget.mapsImplementation as GoogleMapsFlutterAndroid)
.initializeWithRenderer(AndroidMapRenderer.legacy);
break;
case "latest":
(widget.mapsImplementation as GoogleMapsFlutterAndroid)
.initializeWithRenderer(AndroidMapRenderer.latest);
break;
}
}
setState(() {
_mapsInitialized = true;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Google Map Place Picker Demo"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (!_mapsInitialized &&
widget.mapsImplementation
is GoogleMapsFlutterAndroid) ...[
Switch(
value: (widget.mapsImplementation
as GoogleMapsFlutterAndroid)
.useAndroidViewSurface,
onChanged: (value) {
setState(() {
(widget.mapsImplementation
as GoogleMapsFlutterAndroid)
.useAndroidViewSurface = value;
});
}),
Text("Hybrid Composition"),
]
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (!_mapsInitialized &&
widget.mapsImplementation
is GoogleMapsFlutterAndroid) ...[
Text("Renderer: "),
Radio(
groupValue: _mapsRenderer,
value: "auto",
onChanged: (value) {
setState(() {
_mapsRenderer = "auto";
});
}),
Text("Auto"),
Radio(
groupValue: _mapsRenderer,
value: "legacy",
onChanged: (value) {
setState(() {
_mapsRenderer = "legacy";
});
}),
Text("Legacy"),
Radio(
groupValue: _mapsRenderer,
value: "latest",
onChanged: (value) {
setState(() {
_mapsRenderer = "latest";
});
}),
Text("Latest"),
]
],
),
!_showPlacePickerInContainer
? ElevatedButton(
child: Text("Load Place Picker"),
onPressed: () {
initRenderer();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return PlacePicker(
resizeToAvoidBottomInset:
false, // only works in page mode, less flickery
apiKey: Platform.isAndroid
? "AIzaSyB4FhQFeC9JMeIadUwIO6HkoD4UFG6N-HM"
: "IOS API KEY",
hintText: "Find a place ...",
searchingText: "Please wait ...",
selectText: "Select place",
outsideOfPickAreaText: "Place not in area",
initialPosition: GooglePlacePicker.kInitialPosition,
useCurrentLocation: true,
selectInitialPosition: true,
usePinPointingSearch: true,
usePlaceDetailSearch: true,
zoomGesturesEnabled: true,
zoomControlsEnabled: true,
onMapCreated: (GoogleMapController controller) {
print("Map created");
},
onPlacePicked: (PickResult result) {
print(
"Place picked: ${result.formattedAddress}");
setState(() {
selectedPlace = result;
Navigator.of(context).pop();
});
},
onMapTypeChanged: (MapType mapType) {
print(
"Map type changed to ${mapType.toString()}");
},
);
},
),
);
},
)
: Container(),
!_showPlacePickerInContainer
? ElevatedButton(
child: Text("Load Place Picker in Container"),
onPressed: () {
initRenderer();
setState(() {
_showPlacePickerInContainer = true;
});
},
)
: Container(
width: MediaQuery.of(context).size.width * 0.75,
height: MediaQuery.of(context).size.height * 0.35,
child: PlacePicker(
//forceAndroidLocationManager: true,
apiKey: Platform.isAndroid
? "AIzaSyB4FhQFeC9JMeIadUwIO6HkoD4UFG6N-HM"
: "IOS API KEY",
hintText: "Find a place ...",
searchingText: "Please wait ...",
selectText: "Select place",
initialPosition: GooglePlacePicker.kInitialPosition,
useCurrentLocation: true,
selectInitialPosition: true,
usePinPointingSearch: true,
usePlaceDetailSearch: true,
zoomGesturesEnabled: true,
zoomControlsEnabled: true,
onPlacePicked: (PickResult result) {
setState(() {
selectedPlace = result;
_showPlacePickerInContainer = false;
});
},
onTapBack: () {
setState(() {
_showPlacePickerInContainer = false;
});
})),
if (selectedPlace != null) ...[
Text(selectedPlace!.formattedAddress!),
Text("(lat: " +
selectedPlace!.geometry!.location.lat.toString() +
", lng: " +
selectedPlace!.geometry!.location.lng.toString() +
")"),
],
// #region Google Map Example without provider
_showPlacePickerInContainer
? Container()
: ElevatedButton(
child: Text("Toggle Google Map w/o Provider"),
onPressed: () {
initRenderer();
setState(() {
_showGoogleMapInContainer =
!_showGoogleMapInContainer;
});
},
),
!_showGoogleMapInContainer
? Container()
: Container(
width: MediaQuery.of(context).size.width * 0.75,
height: MediaQuery.of(context).size.height * 0.25,
child: GoogleMap(
zoomGesturesEnabled: false,
zoomControlsEnabled: false,
myLocationButtonEnabled: false,
compassEnabled: false,
mapToolbarEnabled: false,
initialCameraPosition: new CameraPosition(
target: GooglePlacePicker.kInitialPosition, zoom: 15),
mapType: MapType.normal,
myLocationEnabled: true,
onMapCreated: (GoogleMapController controller) {},
onCameraIdle: () {},
onCameraMoveStarted: () {},
onCameraMove: (CameraPosition position) {},
)),
!_showGoogleMapInContainer ? Container() : TextField(),
// #endregion
],
),
));
}
}

@ -0,0 +1,33 @@
import 'package:bookatanker/common/settings.dart';
import 'package:flutter/material.dart';
class Dialogs {
static Future<void> showLoadingDialog(BuildContext context, GlobalKey key) async {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new WillPopScope(
onWillPop: () async => false,
child: SimpleDialog(
key: key,
backgroundColor: Colors.white,
children: <Widget>[
Center(
child: Column(children: [
CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(primaryColor),
),
SizedBox(
height: 10,
),
Text(
AppSettings.preloadText,
style: PreloaderText(),
),
]),
)
]));
});
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,99 @@
import 'package:bookatanker/common/settings.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../login.dart';
import 'package:firebase_core/firebase_core.dart';
import 'dashboard.dart';
void main() async{
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
await Firebase.initializeApp();
//FirebaseMessaging.onBackgroundMessage(_messageHandler);
runApp(new Splash());
}
class Splash extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Aquick Driver',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new SplashScreen(),
);
}
}
class SplashScreen extends StatefulWidget {
const SplashScreen({ super.key });
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
final storage = FlutterSecureStorage(
aOptions: AndroidOptions(
resetOnError: true,
encryptedSharedPreferences: true,
),
);
Future<void> _checkLoginStatus() async {
await Future.delayed(Duration(seconds: 2)); // Simulate splash delay
String? authToken = await storage.read(key: 'authToken');
if (authToken != null) {
await AppSettings.loadDataFromMemory();
//AppSettings.fcmId=await AppSettings.getData('FCM_TOKEN', 'STRING');
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => Dashboard()),
);
} else {
// Navigate to Login Screen otherwise
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
);
}
}
@override
void initState() {
//loginCheck();
super.initState();
_checkLoginStatus();
/* Future.delayed(
const Duration(seconds: 3),
() => Navigator.push(
context,
MaterialPageRoute(builder: (context) => _defaultHome),
));*/
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(
decoration: const BoxDecoration(
image: DecorationImage(image: AssetImage("images/final_splash_screen.jpg"), fit: BoxFit.fill,),
),
),
],
)
);
}
}

@ -0,0 +1,138 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'common/settings.dart';
import 'otp.dart';
class ConfirmMobileScreen extends StatefulWidget {
const ConfirmMobileScreen({super.key});
@override
State<ConfirmMobileScreen> createState() => _ConfirmMobileScreenState();
}
class _ConfirmMobileScreenState extends State<ConfirmMobileScreen> {
final TextEditingController mobileController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white, // Full white background
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
iconTheme: IconThemeData(color: Color(0XFF2A2A2A)),
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Padding(
padding:
const EdgeInsets.fromLTRB(8, 8, 8, 8), // Add padding if needed
child: Image.asset(
'images/backbutton_appbar.png', // Replace with your image path
fit: BoxFit.contain,
color: Color(0XFF2A2A2A),
height: 24,
width: 24,
),
),
),
title: Text(
"Confirm Mobile Number",
style: fontTextStyle(16, Color(0xFF000000), FontWeight.w800),
),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 30),
Center(
child: Text(
"Enter registered mobile number",
style: fontTextStyle(16, Color(0xFF101214), FontWeight.w800),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 40),
// Label
Text(
"Mobile Number",
style: fontTextStyle(12, Color(0xFF2F3036), FontWeight.w700),
),
const SizedBox(height: 8),
// TextField
TextField(
controller: mobileController,
keyboardType: TextInputType.phone,
inputFormatters: [
LengthLimitingTextInputFormatter(10), // max 10 digits
FilteringTextInputFormatter.digitsOnly, // only digits
],
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: "Enter your number",
hintStyle: fontTextStyle(14, Color(0xFF2A2A2A), FontWeight.w400),
),
),
const SizedBox(height: 40),
// Continue button
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0XFF101214),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
onPressed: () {
String mobile = mobileController.text.trim();
if (mobile.length == 10) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OtpScreen(
mobileNumber: mobile, // PASS NUMBER
),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Please enter 10 digits ❌"),
),
);
}
},
child: Text(
"Continue",
style: fontTextStyle(
14,
Colors.white,
FontWeight.w600),
),
),
),
],
),
),
);
}
}

@ -0,0 +1,233 @@
import 'package:flutter/material.dart';
import 'changepassword.dart';
import 'common/settings.dart';
class CreatePasswordScreen extends StatefulWidget {
const CreatePasswordScreen({super.key});
@override
_CreatePasswordScreenState createState() => _CreatePasswordScreenState();
}
class _CreatePasswordScreenState extends State<CreatePasswordScreen> {
final _formKey = GlobalKey<FormState>();
final _newPasswordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
bool _obscureNew = true;
bool _obscureConfirm = true;
@override
void dispose() {
_newPasswordController.dispose();
_confirmPasswordController.dispose();
super.dispose();
}
Future<void> _savePassword() async {
if (!_formKey.currentState!.validate()) return;
// TODO: call your API to set password here
// await yourApi.setPassword(_newPasswordController.text);
// Optional: show success then navigate
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Password changed successfully")),
);
// Small delay so the SnackBar is noticeable (optional)
await Future.delayed(const Duration(milliseconds: 300));
if (!mounted) return;
// Navigate to Select Tour Tanker page and clear back stack
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (_) => ChangePasswordScreen(), // replace with your actual page
),
(route) => false,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true, // shifts UI up when keyboard opens
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
surfaceTintColor: Colors.transparent, // <- remove M3 tint
scrolledUnderElevation: 0,
title: Text(
"Change Password",
style: fontTextStyle(14, Color(0xFF101214), FontWeight.w500),
),
iconTheme: IconThemeData(color: Color(0XFF2A2A2A)),
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Padding(
padding:
const EdgeInsets.fromLTRB(8, 8, 8, 8), // Add padding if needed
child: Image.asset(
'images/backbutton_appbar.png', // Replace with your image path
fit: BoxFit.contain,
color: Color(0XFF2A2A2A),
height: 24,
width: 24,
),
),
),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20.0),
physics: const BouncingScrollPhysics(),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Centered title & subtitle with top padding
Padding(
padding: const EdgeInsets.only(top: 40, bottom: 24),
child: Column(
children: [
Text(
"Create Password",
textAlign: TextAlign.center,
style: fontTextStyle(16, Color(0xFF000000),FontWeight.w800,),
),
SizedBox(height: 6),
Text(
"Create a strong password for your account",
textAlign: TextAlign.center,
style: fontTextStyle(12, Color(0xFF7E7F80), FontWeight.w400,
),
),
],
),
),
// New Password
Align(
alignment: Alignment.centerLeft,
child: Text(
"New Password*",
style: fontTextStyle(12, Color(0xFF101214), FontWeight.w700,
),
),
),
const SizedBox(height: 8),
TextFormField(
controller: _newPasswordController,
obscureText: _obscureNew,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: "Enter new password",
hintStyle: fontTextStyle(14, Color(0xFF2A2A2A), FontWeight.w400,
),
suffixIcon: IconButton(
icon: Icon(
_obscureNew ? Icons.visibility_off : Icons.visibility,
),
onPressed: () => setState(() {
_obscureNew = !_obscureNew;
}),
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return "Please enter new password";
}
if (value.length < 6) {
return "Password must be at least 6 characters";
}
return null;
},
),
const SizedBox(height: 20),
// Confirm Password
Align(
alignment: Alignment.centerLeft,
child: Text(
"Confirm Password*",
style: fontTextStyle(12, Color(0xFF101214), FontWeight.w700,
),
),
),
const SizedBox(height: 8),
TextFormField(
controller: _confirmPasswordController,
obscureText: _obscureConfirm,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: "Confirm new password",
hintStyle: fontTextStyle(14, Color(0xFF2A2A2A), FontWeight.w400,
),
suffixIcon: IconButton(
icon: Icon(
_obscureConfirm ? Icons.visibility_off : Icons.visibility,
),
onPressed: () => setState(() {
_obscureConfirm = !_obscureConfirm;
}),
),
),
validator: (value) {
if (value != _newPasswordController.text) {
return "Passwords do not match";
}
return null;
},
),
const SizedBox(height: 30),
// Save & Login Button
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _savePassword,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
backgroundColor: Color(0XFF101214),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
child: Text(
"Save and Continue",
style: fontTextStyle(14, Color(0xFFFFFFFF), FontWeight.w600,
),
),
),
),
],
),
),
),
);
}
}
class SelectTourTankerPage extends StatelessWidget {
const SelectTourTankerPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Select Tour Tanker")),
body: const Center(
child: Text(
"Select Tour Tanker page goes here",
style: TextStyle(fontSize: 16),
),
),
);
}
}

@ -0,0 +1,7 @@
library google_maps_place_picker_mb;
export 'src/models/pick_result.dart';
export 'src/components/floating_card.dart';
export 'src/components/rounded_frame.dart';
export 'src/models/circle_area.dart';
export 'src/place_picker.dart';

@ -0,0 +1,161 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_webservice/geocoding.dart';
import 'package:google_maps_webservice/places.dart';
import 'package:http/http.dart';
import 'package:provider/provider.dart';
import '../src/models/pick_result.dart';
import '../src/place_picker.dart';
class PlaceProvider extends ChangeNotifier {
PlaceProvider(
String apiKey,
String? proxyBaseUrl,
Client? httpClient,
Map<String, dynamic> apiHeaders,
) {
places = GoogleMapsPlaces(
apiKey: apiKey,
baseUrl: proxyBaseUrl,
httpClient: httpClient,
apiHeaders: apiHeaders as Map<String, String>?,
);
geocoding = GoogleMapsGeocoding(
apiKey: apiKey,
baseUrl: proxyBaseUrl,
httpClient: httpClient,
apiHeaders: apiHeaders as Map<String, String>?,
);
}
static PlaceProvider of(BuildContext context, {bool listen = true}) =>
Provider.of<PlaceProvider>(context, listen: listen);
late GoogleMapsPlaces places;
late GoogleMapsGeocoding geocoding;
String? sessionToken;
bool isOnUpdateLocationCooldown = false;
LocationAccuracy? desiredAccuracy;
bool isAutoCompleteSearching = false;
Future<void> updateCurrentLocation(bool forceAndroidLocationManager) async {
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled don't continue
// accessing the position and request users of the
// App to enable the location services.
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// Permissions are denied, next time you could try
// requesting permissions again (this is also where
// Android's shouldShowRequestPermissionRationale
// returned true. According to Android guidelines
// your App should show an explanatory UI now.
return Future.error('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best,
);
currentPosition = position;
// notifyListeners();
}
Position? _currentPosition;
Position? get currentPosition => _currentPosition;
set currentPosition(Position? newPosition) {
_currentPosition = newPosition;
notifyListeners();
}
Timer? _debounceTimer;
Timer? get debounceTimer => _debounceTimer;
set debounceTimer(Timer? timer) {
_debounceTimer = timer;
notifyListeners();
}
CameraPosition? _previousCameraPosition;
CameraPosition? get prevCameraPosition => _previousCameraPosition;
setPrevCameraPosition(CameraPosition? prePosition) {
_previousCameraPosition = prePosition;
}
CameraPosition? _currentCameraPosition;
CameraPosition? get cameraPosition => _currentCameraPosition;
setCameraPosition(CameraPosition? newPosition) {
_currentCameraPosition = newPosition;
}
PickResult? _selectedPlace;
PickResult? get selectedPlace => _selectedPlace;
set selectedPlace(PickResult? result) {
_selectedPlace = result;
notifyListeners();
}
SearchingState _placeSearchingState = SearchingState.Idle;
SearchingState get placeSearchingState => _placeSearchingState;
set placeSearchingState(SearchingState newState) {
_placeSearchingState = newState;
notifyListeners();
}
GoogleMapController? _mapController;
GoogleMapController? get mapController => _mapController;
set mapController(GoogleMapController? controller) {
_mapController = controller;
notifyListeners();
}
PinState _pinState = PinState.Preparing;
PinState get pinState => _pinState;
set pinState(PinState newState) {
_pinState = newState;
notifyListeners();
}
bool _isSeachBarFocused = false;
bool get isSearchBarFocused => _isSeachBarFocused;
set isSearchBarFocused(bool focused) {
_isSeachBarFocused = focused;
notifyListeners();
}
MapType _mapType = MapType.normal;
MapType get mapType => _mapType;
setMapType(MapType mapType, {bool notify = false}) {
_mapType = mapType;
if (notify) notifyListeners();
}
switchMapType() {
_mapType = MapType.values[(_mapType.index + 1) % MapType.values.length];
if (_mapType == MapType.none) _mapType = MapType.normal;
notifyListeners();
}
}

@ -0,0 +1,16 @@
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
class SearchProvider extends ChangeNotifier {
static SearchProvider of(BuildContext context, {bool listen = true}) =>
Provider.of<SearchProvider>(context, listen: listen);
String prevSearchTerm = "";
String _searchTerm = "";
String get searchTerm => _searchTerm;
set searchTerm(String newValue) {
_searchTerm = newValue;
notifyListeners();
}
}

@ -0,0 +1,341 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_webservice/places.dart';
import 'package:provider/provider.dart';
import 'package:bookatanker/google_maps_place_picker_mb/google_maps_place_picker.dart';
import 'package:bookatanker/google_maps_place_picker_mb/providers/place_provider.dart';
import 'package:bookatanker/google_maps_place_picker_mb/providers/search_provider.dart';
import 'package:bookatanker/google_maps_place_picker_mb/src/components/prediction_tile.dart';
import 'package:bookatanker/google_maps_place_picker_mb/src/components/rounded_frame.dart';
import 'package:bookatanker/google_maps_place_picker_mb/src/controllers/autocomplete_search_controller.dart';
class AutoCompleteSearch extends StatefulWidget {
const AutoCompleteSearch(
{Key? key,
required this.sessionToken,
required this.onPicked,
required this.appBarKey,
this.hintText = "Search here",
this.searchingText = "Searching...",
this.hidden = false,
this.height = 40,
this.contentPadding = EdgeInsets.zero,
this.debounceMilliseconds,
this.onSearchFailed,
required this.searchBarController,
this.autocompleteOffset,
this.autocompleteRadius,
this.autocompleteLanguage,
this.autocompleteComponents,
this.autocompleteTypes,
this.strictbounds,
this.region,
this.initialSearchString,
this.searchForInitialValue,
this.autocompleteOnTrailingWhitespace})
: super(key: key);
final String? sessionToken;
final String? hintText;
final String? searchingText;
final bool hidden;
final double height;
final EdgeInsetsGeometry contentPadding;
final int? debounceMilliseconds;
final ValueChanged<Prediction> onPicked;
final ValueChanged<String>? onSearchFailed;
final SearchBarController searchBarController;
final num? autocompleteOffset;
final num? autocompleteRadius;
final String? autocompleteLanguage;
final List<String>? autocompleteTypes;
final List<Component>? autocompleteComponents;
final bool? strictbounds;
final String? region;
final GlobalKey appBarKey;
final String? initialSearchString;
final bool? searchForInitialValue;
final bool? autocompleteOnTrailingWhitespace;
@override
AutoCompleteSearchState createState() => AutoCompleteSearchState();
}
class AutoCompleteSearchState extends State<AutoCompleteSearch> {
TextEditingController controller = TextEditingController();
FocusNode focus = FocusNode();
OverlayEntry? overlayEntry;
SearchProvider provider = SearchProvider();
@override
void initState() {
super.initState();
if (widget.initialSearchString != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
controller.text = widget.initialSearchString!;
if (widget.searchForInitialValue!) {
_onSearchInputChange();
}
});
}
controller.addListener(_onSearchInputChange);
focus.addListener(_onFocusChanged);
widget.searchBarController.attach(this);
}
@override
void dispose() {
controller.removeListener(_onSearchInputChange);
controller.dispose();
focus.removeListener(_onFocusChanged);
focus.dispose();
_clearOverlay();
super.dispose();
}
@override
Widget build(BuildContext context) {
return !widget.hidden
? ChangeNotifierProvider.value(
value: provider,
child: RoundedFrame(
height: widget.height,
padding: const EdgeInsets.only(right: 10),
color: Theme.of(context).brightness == Brightness.dark
? Colors.black54
: Colors.white,
borderRadius: BorderRadius.circular(20),
elevation: 4.0,
child: Row(
children: <Widget>[
SizedBox(width: 10),
Icon(Icons.search),
SizedBox(width: 10),
Expanded(child: _buildSearchTextField()),
_buildTextClearIcon(),
],
),
),
)
: Container();
}
Widget _buildSearchTextField() {
return TextField(
controller: controller,
focusNode: focus,
decoration: InputDecoration(
hintText: widget.hintText,
border: InputBorder.none,
errorBorder: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
isDense: true,
contentPadding: widget.contentPadding,
),
);
}
Widget _buildTextClearIcon() {
return Selector<SearchProvider, String>(
selector: (_, provider) => provider.searchTerm,
builder: (_, data, __) {
if (data.length > 0) {
return Padding(
padding: const EdgeInsets.only(right: 8.0),
child: GestureDetector(
child: Icon(
Icons.clear,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black,
),
onTap: () {
clearText();
},
),
);
} else {
return SizedBox(width: 10);
}
});
}
_onSearchInputChange() {
if (!mounted) return;
this.provider.searchTerm = controller.text;
PlaceProvider provider = PlaceProvider.of(context, listen: false);
if (controller.text.isEmpty) {
provider.debounceTimer?.cancel();
_searchPlace(controller.text);
return;
}
if (controller.text.trim() == this.provider.prevSearchTerm.trim()) {
provider.debounceTimer?.cancel();
return;
}
if (!widget.autocompleteOnTrailingWhitespace! &&
controller.text.substring(controller.text.length - 1) == " ") {
provider.debounceTimer?.cancel();
return;
}
if (provider.debounceTimer?.isActive ?? false) {
provider.debounceTimer!.cancel();
}
provider.debounceTimer =
Timer(Duration(milliseconds: widget.debounceMilliseconds!), () {
_searchPlace(controller.text.trim());
});
}
_onFocusChanged() {
PlaceProvider provider = PlaceProvider.of(context, listen: false);
provider.isSearchBarFocused = focus.hasFocus;
provider.debounceTimer?.cancel();
provider.placeSearchingState = SearchingState.Idle;
}
_searchPlace(String searchTerm) {
this.provider.prevSearchTerm = searchTerm;
_clearOverlay();
if (searchTerm.length < 1) return;
_displayOverlay(_buildSearchingOverlay());
_performAutoCompleteSearch(searchTerm);
}
_clearOverlay() {
if (overlayEntry != null) {
overlayEntry!.remove();
overlayEntry = null;
}
}
_displayOverlay(Widget overlayChild) {
_clearOverlay();
final RenderBox? appBarRenderBox =
widget.appBarKey.currentContext!.findRenderObject() as RenderBox?;
final translation = appBarRenderBox?.getTransformTo(null).getTranslation();
final Offset offset = translation != null
? Offset(translation.x, translation.y)
: Offset(0.0, 0.0);
final screenWidth = MediaQuery.of(context).size.width;
overlayEntry = OverlayEntry(
builder: (context) => Positioned(
top: appBarRenderBox!.paintBounds.shift(offset).top +
appBarRenderBox.size.height,
left: screenWidth * 0.025,
right: screenWidth * 0.025,
child: Material(
elevation: 4.0,
child: overlayChild,
),
),
);
Overlay.of(context)!.insert(overlayEntry!);
}
Widget _buildSearchingOverlay() {
return Container(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
child: Row(
children: <Widget>[
SizedBox(
height: 24,
width: 24,
child: CircularProgressIndicator(strokeWidth: 3),
),
SizedBox(width: 24),
Expanded(
child: Text(
widget.searchingText ?? "Searching...",
style: TextStyle(fontSize: 16),
),
)
],
),
);
}
Widget _buildPredictionOverlay(List<Prediction> predictions) {
return ListBody(
children: predictions
.map(
(p) => PredictionTile(
prediction: p,
onTap: (selectedPrediction) {
resetSearchBar();
widget.onPicked(selectedPrediction);
},
),
)
.toList(),
);
}
_performAutoCompleteSearch(String searchTerm) async {
PlaceProvider provider = PlaceProvider.of(context, listen: false);
if (searchTerm.isNotEmpty) {
final PlacesAutocompleteResponse response =
await provider.places.autocomplete(
searchTerm,
sessionToken: widget.sessionToken,
location: provider.currentPosition == null
? null
: Location(
lat: provider.currentPosition!.latitude,
lng: provider.currentPosition!.longitude),
offset: widget.autocompleteOffset,
radius: widget.autocompleteRadius,
language: widget.autocompleteLanguage,
types: widget.autocompleteTypes ?? const [],
components: widget.autocompleteComponents ?? const [],
strictbounds: widget.strictbounds ?? false,
region: widget.region,
);
if (response.errorMessage?.isNotEmpty == true ||
response.status == "REQUEST_DENIED") {
if (widget.onSearchFailed != null) {
widget.onSearchFailed!(response.status);
}
return;
}
_displayOverlay(_buildPredictionOverlay(response.predictions));
}
}
clearText() {
provider.searchTerm = "";
controller.clear();
}
resetSearchBar() {
clearText();
focus.unfocus();
}
clearOverlay() {
_clearOverlay();
}
}

@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
class AnimatedPin extends StatefulWidget {
AnimatedPin({
Key? key,
this.child,
});
final Widget? child;
@override
_AnimatedPinState createState() => _AnimatedPinState();
}
class _AnimatedPinState extends State<AnimatedPin>
with TickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return JumpingContainer(controller: _controller, child: widget.child);
}
}
class JumpingContainer extends AnimatedWidget {
const JumpingContainer({
Key? key,
required AnimationController controller,
this.child,
}) : super(key: key, listenable: controller);
final Widget? child;
Animation<double> get _progress => listenable as Animation<double>;
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(0, -10 + _progress.value * 10),
child: child,
);
}
}

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:bookatanker/google_maps_place_picker_mb/src/components/rounded_frame.dart';
class FloatingCard extends StatelessWidget {
const FloatingCard({
Key? key,
this.topPosition,
this.leftPosition,
this.rightPosition,
this.bottomPosition,
this.width,
this.height,
this.borderRadius = BorderRadius.zero,
this.elevation = 0.0,
this.color,
this.child,
}) : super(key: key);
final double? topPosition;
final double? leftPosition;
final double? bottomPosition;
final double? rightPosition;
final double? width;
final double? height;
final BorderRadius borderRadius;
final double elevation;
final Color? color;
final Widget? child;
@override
Widget build(BuildContext context) {
return Positioned(
top: topPosition,
left: leftPosition,
right: rightPosition,
bottom: bottomPosition,
child: RoundedFrame(
width: width,
height: height,
borderRadius: borderRadius,
elevation: elevation,
color: color,
child: child,
),
);
}
}

@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
import 'package:google_maps_webservice/places.dart';
class PredictionTile extends StatelessWidget {
final Prediction prediction;
final ValueChanged<Prediction>? onTap;
PredictionTile({required this.prediction, this.onTap});
@override
Widget build(BuildContext context) {
return ListTile(
leading: Icon(Icons.location_on),
title: RichText(
text: TextSpan(
children: _buildPredictionText(context),
),
),
onTap: () {
if (onTap != null) {
onTap!(prediction);
}
},
);
}
List<TextSpan> _buildPredictionText(BuildContext context) {
final List<TextSpan> result = <TextSpan>[];
final textColor = Colors.black;
if (prediction.matchedSubstrings.length > 0) {
MatchedSubstring matchedSubString = prediction.matchedSubstrings[0];
// There is no matched string at the beginning.
if (matchedSubString.offset > 0) {
result.add(
TextSpan(
text: prediction.description
?.substring(0, matchedSubString.offset as int?),
style: TextStyle(
color: textColor, fontSize: 16, fontWeight: FontWeight.w300),
),
);
}
// Matched strings.
result.add(
TextSpan(
text: prediction.description?.substring(
matchedSubString.offset as int,
matchedSubString.offset + matchedSubString.length as int?),
style: TextStyle(
color: textColor, fontSize: 16, fontWeight: FontWeight.w500),
),
);
// Other strings.
if (matchedSubString.offset + matchedSubString.length <
(prediction.description?.length ?? 0)) {
result.add(
TextSpan(
text: prediction.description?.substring(
matchedSubString.offset + matchedSubString.length as int),
style: TextStyle(
color: textColor, fontSize: 16, fontWeight: FontWeight.w300),
),
);
}
// If there is no matched strings, but there are predicts. (Not sure if this happens though)
} else {
result.add(
TextSpan(
text: prediction.description,
style: TextStyle(
color: textColor, fontSize: 16, fontWeight: FontWeight.w300),
),
);
}
return result;
}
}

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
class RoundedFrame extends StatelessWidget {
const RoundedFrame({
Key? key,
this.margin,
this.padding,
this.width,
this.height,
this.child,
this.color,
this.borderRadius = BorderRadius.zero,
this.borderColor = Colors.transparent,
this.elevation = 0.0,
this.materialType,
}) : super(key: key);
final EdgeInsetsGeometry? margin;
final EdgeInsetsGeometry? padding;
final double? width;
final double? height;
final Widget? child;
final Color? color;
final Color borderColor;
final BorderRadius borderRadius;
final double elevation;
final MaterialType? materialType;
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
margin: margin,
padding: padding,
child: Material(
type: color == Colors.transparent
? MaterialType.transparency
: materialType ?? MaterialType.canvas,
color: color,
shape: RoundedRectangleBorder(
borderRadius: borderRadius, side: BorderSide(color: borderColor)),
elevation: elevation,
child: ClipRRect(
borderRadius: borderRadius,
child: child,
),
),
);
}
}

@ -0,0 +1,24 @@
import 'package:flutter/cupertino.dart';
import 'package:bookatanker/google_maps_place_picker_mb/src/autocomplete_search.dart';
class SearchBarController extends ChangeNotifier {
late AutoCompleteSearchState _autoCompleteSearch;
attach(AutoCompleteSearchState searchWidget) {
_autoCompleteSearch = searchWidget;
}
/// Just clears text.
clear() {
_autoCompleteSearch.clearText();
}
/// Clear and remove focus (Dismiss keyboard)
reset() {
_autoCompleteSearch.resetSearchBar();
}
clearOverlay() {
_autoCompleteSearch.clearOverlay();
}
}

@ -0,0 +1,612 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_webservice/geocoding.dart';
import 'package:google_maps_webservice/places.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
import 'package:bookatanker/google_maps_place_picker_mb/google_maps_place_picker.dart';
import 'package:bookatanker/google_maps_place_picker_mb/providers/place_provider.dart';
import 'package:bookatanker/google_maps_place_picker_mb/src/components/animated_pin.dart';
typedef SelectedPlaceWidgetBuilder = Widget Function(
BuildContext context,
PickResult? selectedPlace,
SearchingState state,
bool isSearchBarFocused,
);
typedef PinBuilder = Widget Function(
BuildContext context,
PinState state,
);
class GoogleMapPlacePicker extends StatelessWidget {
const GoogleMapPlacePicker({
Key? key,
required this.initialTarget,
required this.appBarKey,
this.selectedPlaceWidgetBuilder,
this.pinBuilder,
this.onSearchFailed,
this.onMoveStart,
this.onMapCreated,
this.debounceMilliseconds,
this.enableMapTypeButton,
this.enableMyLocationButton,
this.onToggleMapType,
this.onMyLocation,
this.onPlacePicked,
this.usePinPointingSearch,
this.usePlaceDetailSearch,
this.selectInitialPosition,
this.language,
this.pickArea,
this.forceSearchOnZoomChanged,
this.hidePlaceDetailsWhenDraggingPin,
this.onCameraMoveStarted,
this.onCameraMove,
this.onCameraIdle,
this.selectText,
this.outsideOfPickAreaText,
this.zoomGesturesEnabled = true,
this.zoomControlsEnabled = false,
this.fullMotion = false,
}) : super(key: key);
final LatLng initialTarget;
final GlobalKey appBarKey;
final SelectedPlaceWidgetBuilder? selectedPlaceWidgetBuilder;
final PinBuilder? pinBuilder;
final ValueChanged<String>? onSearchFailed;
final VoidCallback? onMoveStart;
final MapCreatedCallback? onMapCreated;
final VoidCallback? onToggleMapType;
final VoidCallback? onMyLocation;
final ValueChanged<PickResult>? onPlacePicked;
final int? debounceMilliseconds;
final bool? enableMapTypeButton;
final bool? enableMyLocationButton;
final bool? usePinPointingSearch;
final bool? usePlaceDetailSearch;
final bool? selectInitialPosition;
final String? language;
final CircleArea? pickArea;
final bool? forceSearchOnZoomChanged;
final bool? hidePlaceDetailsWhenDraggingPin;
/// GoogleMap pass-through events:
final Function(PlaceProvider)? onCameraMoveStarted;
final CameraPositionCallback? onCameraMove;
final Function(PlaceProvider)? onCameraIdle;
// strings
final String? selectText;
final String? outsideOfPickAreaText;
/// Zoom feature toggle
final bool zoomGesturesEnabled;
final bool zoomControlsEnabled;
/// Use never scrollable scroll-view with maximum dimensions to prevent unnecessary re-rendering.
final bool fullMotion;
_searchByCameraLocation(PlaceProvider provider) async {
// We don't want to search location again if camera location is changed by zooming in/out.
if (forceSearchOnZoomChanged == false &&
provider.prevCameraPosition != null &&
provider.prevCameraPosition!.target.latitude ==
provider.cameraPosition!.target.latitude &&
provider.prevCameraPosition!.target.longitude ==
provider.cameraPosition!.target.longitude) {
provider.placeSearchingState = SearchingState.Idle;
return;
}
provider.placeSearchingState = SearchingState.Searching;
final GeocodingResponse response =
await provider.geocoding.searchByLocation(
Location(
lat: provider.cameraPosition!.target.latitude,
lng: provider.cameraPosition!.target.longitude),
language: language,
);
if (response.errorMessage?.isNotEmpty == true ||
response.status == "REQUEST_DENIED") {
print("Camera Location Search Error: " + response.errorMessage!);
if (onSearchFailed != null) {
onSearchFailed!(response.status);
}
provider.placeSearchingState = SearchingState.Idle;
return;
}
if (usePlaceDetailSearch!) {
final PlacesDetailsResponse detailResponse =
await provider.places.getDetailsByPlaceId(
response.results[0].placeId,
language: language,
);
if (detailResponse.errorMessage?.isNotEmpty == true ||
detailResponse.status == "REQUEST_DENIED") {
print("Fetching details by placeId Error: " +
detailResponse.errorMessage!);
if (onSearchFailed != null) {
onSearchFailed!(detailResponse.status);
}
provider.placeSearchingState = SearchingState.Idle;
return;
}
provider.selectedPlace =
PickResult.fromPlaceDetailResult(detailResponse.result);
} else {
provider.selectedPlace =
PickResult.fromGeocodingResult(response.results[0]);
}
provider.placeSearchingState = SearchingState.Idle;
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
if (this.fullMotion)
SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
alignment: AlignmentDirectional.center,
children: [
_buildGoogleMap(context),
_buildPin(),
],
))),
if (!this.fullMotion) _buildGoogleMap(context),
if (!this.fullMotion) _buildPin(),
_buildFloatingCard(),
_buildMapIcons(context),
_buildZoomButtons()
],
);
}
Widget _buildGoogleMapInner(PlaceProvider? provider, MapType mapType) {
CameraPosition initialCameraPosition =
CameraPosition(target: this.initialTarget, zoom: 15);
return GoogleMap(
zoomGesturesEnabled: this.zoomGesturesEnabled,
zoomControlsEnabled:
false, // we use our own implementation that supports iOS as well, see _buildZoomButtons()
myLocationButtonEnabled: false,
compassEnabled: false,
mapToolbarEnabled: false,
initialCameraPosition: initialCameraPosition,
mapType: mapType,
myLocationEnabled: true,
circles: pickArea != null && pickArea!.radius > 0
? Set<Circle>.from([pickArea])
: Set<Circle>(),
onMapCreated: (GoogleMapController controller) {
if (provider == null) return;
provider.mapController = controller;
provider.setCameraPosition(null);
provider.pinState = PinState.Idle;
// When select initialPosition set to true.
if (selectInitialPosition!) {
provider.setCameraPosition(initialCameraPosition);
_searchByCameraLocation(provider);
}
if (onMapCreated != null) {
onMapCreated!(controller);
}
},
onCameraIdle: () {
if (provider == null) return;
if (provider.isAutoCompleteSearching) {
provider.isAutoCompleteSearching = false;
provider.pinState = PinState.Idle;
provider.placeSearchingState = SearchingState.Idle;
return;
}
// Perform search only if the setting is to true.
if (usePinPointingSearch!) {
// Search current camera location only if camera has moved (dragged) before.
if (provider.pinState == PinState.Dragging) {
// Cancel previous timer.
if (provider.debounceTimer?.isActive ?? false) {
provider.debounceTimer!.cancel();
}
provider.debounceTimer =
Timer(Duration(milliseconds: debounceMilliseconds!), () {
_searchByCameraLocation(provider);
});
}
}
provider.pinState = PinState.Idle;
if (onCameraIdle != null) {
onCameraIdle!(provider);
}
},
onCameraMoveStarted: () {
if (provider == null) return;
if (onCameraMoveStarted != null) {
onCameraMoveStarted!(provider);
}
provider.setPrevCameraPosition(provider.cameraPosition);
// Cancel any other timer.
provider.debounceTimer?.cancel();
// Update state, dismiss keyboard and clear text.
provider.pinState = PinState.Dragging;
// Begins the search state if the hide details is enabled
if (this.hidePlaceDetailsWhenDraggingPin!) {
provider.placeSearchingState = SearchingState.Searching;
}
onMoveStart!();
},
onCameraMove: (CameraPosition position) {
if (provider == null) return;
provider.setCameraPosition(position);
if (onCameraMove != null) {
onCameraMove!(position);
}
},
// gestureRecognizers make it possible to navigate the map when it's a
// child in a scroll view e.g ListView, SingleChildScrollView...
gestureRecognizers: Set()
..add(Factory<EagerGestureRecognizer>(() => EagerGestureRecognizer())),
);
}
Widget _buildGoogleMap(BuildContext context) {
return Selector<PlaceProvider, MapType>(
selector: (_, provider) => provider.mapType,
builder: (_, data, __) => this._buildGoogleMapInner(
PlaceProvider.of(context, listen: false), data));
}
Widget _buildPin() {
return Center(
child: Selector<PlaceProvider, PinState>(
selector: (_, provider) => provider.pinState,
builder: (context, state, __) {
if (pinBuilder == null) {
return _defaultPinBuilder(context, state);
} else {
return Builder(
builder: (builderContext) =>
pinBuilder!(builderContext, state));
}
},
),
);
}
Widget _defaultPinBuilder(BuildContext context, PinState state) {
if (state == PinState.Preparing) {
return Container();
} else if (state == PinState.Idle) {
return Stack(
children: <Widget>[
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.place, size: 36, color: Colors.red),
SizedBox(height: 42),
],
),
),
Center(
child: Container(
width: 5,
height: 5,
decoration: BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
),
),
],
);
} else {
return Stack(
children: <Widget>[
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedPin(
child: Icon(Icons.place, size: 36, color: Colors.red)),
SizedBox(height: 42),
],
),
),
Center(
child: Container(
width: 5,
height: 5,
decoration: BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
),
),
],
);
}
}
Widget _buildFloatingCard() {
return Selector<PlaceProvider,
Tuple4<PickResult?, SearchingState, bool, PinState>>(
selector: (_, provider) => Tuple4(
provider.selectedPlace,
provider.placeSearchingState,
provider.isSearchBarFocused,
provider.pinState),
builder: (context, data, __) {
if ((data.item1 == null && data.item2 == SearchingState.Idle) ||
data.item3 == true ||
data.item4 == PinState.Dragging &&
this.hidePlaceDetailsWhenDraggingPin!) {
return Container();
} else {
if (selectedPlaceWidgetBuilder == null) {
return _defaultPlaceWidgetBuilder(context, data.item1, data.item2);
} else {
return Builder(
builder: (builderContext) => selectedPlaceWidgetBuilder!(
builderContext, data.item1, data.item2, data.item3));
}
}
},
);
}
Widget _buildZoomButtons() {
return Selector<PlaceProvider, Tuple2<GoogleMapController?, LatLng?>>(
selector: (_, provider) => new Tuple2<GoogleMapController?, LatLng?>(
provider.mapController, provider.cameraPosition?.target),
builder: (context, data, __) {
if (!this.zoomControlsEnabled ||
data.item1 == null ||
data.item2 == null) {
return Container();
} else {
return Positioned(
bottom: 50,
right: 10,
child: Card(
elevation: 4.0,
child: Container(
width: 40,
height: 100,
child: Column(
children: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
double currentZoomLevel =
await data.item1!.getZoomLevel();
currentZoomLevel = currentZoomLevel + 2;
data.item1!.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: data.item2!,
zoom: currentZoomLevel,
),
),
);
}),
SizedBox(height: 2),
IconButton(
icon: Icon(Icons.remove),
onPressed: () async {
double currentZoomLevel =
await data.item1!.getZoomLevel();
currentZoomLevel = currentZoomLevel - 2;
if (currentZoomLevel < 0) currentZoomLevel = 0;
data.item1!.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: data.item2!,
zoom: currentZoomLevel,
),
),
);
}),
],
),
),
),
);
}
},
);
}
Widget _defaultPlaceWidgetBuilder(
BuildContext context, PickResult? data, SearchingState state) {
return FloatingCard(
bottomPosition: MediaQuery.of(context).size.height * 0.1,
leftPosition: MediaQuery.of(context).size.width * 0.15,
rightPosition: MediaQuery.of(context).size.width * 0.15,
width: MediaQuery.of(context).size.width * 0.7,
borderRadius: BorderRadius.circular(12.0),
elevation: 4.0,
color: Theme.of(context).cardColor,
child: state == SearchingState.Searching
? _buildLoadingIndicator()
: _buildSelectionDetails(context, data!),
);
}
Widget _buildLoadingIndicator() {
return Container(
height: 48,
child: const Center(
child: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(),
),
),
);
}
Widget _buildSelectionDetails(BuildContext context, PickResult result) {
bool canBePicked = pickArea == null ||
pickArea!.radius <= 0 ||
Geolocator.distanceBetween(
pickArea!.center.latitude,
pickArea!.center.longitude,
result.geometry!.location.lat,
result.geometry!.location.lng) <=
pickArea!.radius;
MaterialStateColor buttonColor = MaterialStateColor.resolveWith(
(states) => canBePicked ? Colors.lightGreen : Colors.red);
return Container(
margin: EdgeInsets.all(10),
child: Column(
children: <Widget>[
Text(
result.types!.length==1?
result.formattedAddress!:result.name!+', '+result.formattedAddress!,
style: TextStyle(fontSize: 18),
textAlign: TextAlign.center,
),
SizedBox(height: 10),
(canBePicked && (selectText?.isEmpty ?? true)) ||
(!canBePicked && (outsideOfPickAreaText?.isEmpty ?? true))
? SizedBox.fromSize(
size: Size(56, 56), // button width and height
child: ClipOval(
child: Material(
child: InkWell(
overlayColor: buttonColor,
onTap: () {
if (canBePicked) {
onPlacePicked!(result);
}
},
child: Icon(
canBePicked
? Icons.check_sharp
: Icons.app_blocking_sharp,
color: buttonColor)),
),
),
)
: SizedBox.fromSize(
size: Size(MediaQuery.of(context).size.width * 0.8,
56), // button width and height
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Material(
child: InkWell(
overlayColor: buttonColor,
onTap: () {
if (canBePicked) {
onPlacePicked!(result);
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
canBePicked
? Icons.check_sharp
: Icons.app_blocking_sharp,
color: buttonColor),
SizedBox.fromSize(size: new Size(10, 0)),
Text(
canBePicked
? selectText!
: outsideOfPickAreaText!,
style: TextStyle(color: buttonColor))
],
)),
),
),
)
],
),
);
}
Widget _buildMapIcons(BuildContext context) {
if (appBarKey.currentContext == null) {
return Container();
}
final RenderBox appBarRenderBox =
appBarKey.currentContext!.findRenderObject() as RenderBox;
return Positioned(
top: appBarRenderBox.size.height,
right: 15,
child: Column(
children: <Widget>[
enableMapTypeButton!
? Container(
width: 35,
height: 35,
child: RawMaterialButton(
shape: CircleBorder(),
fillColor: Theme.of(context).brightness == Brightness.dark
? Colors.black54
: Colors.white,
elevation: 4.0,
onPressed: onToggleMapType,
child: Icon(Icons.layers),
),
)
: Container(),
SizedBox(height: 10),
enableMyLocationButton!
? Container(
width: 35,
height: 35,
child: RawMaterialButton(
shape: CircleBorder(),
fillColor: Theme.of(context).brightness == Brightness.dark
? Colors.black54
: Colors.white,
elevation: 4.0,
onPressed: onMyLocation,
child: Icon(Icons.my_location),
),
)
: Container(),
],
),
);
}
}

@ -0,0 +1,22 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:uuid/uuid.dart';
class CircleArea extends Circle {
CircleArea({
required LatLng center,
required double radius,
Color? fillColor,
Color? strokeColor,
int strokeWidth = 2,
}) : super(
circleId: CircleId(Uuid().v4()),
center: center,
radius: radius,
fillColor: fillColor ?? Colors.blue.withAlpha(32),
strokeColor: strokeColor ?? Colors.blue.withAlpha(192),
strokeWidth: strokeWidth,
);
}

@ -0,0 +1,91 @@
import 'package:google_maps_webservice/geocoding.dart';
import 'package:google_maps_webservice/places.dart';
class PickResult {
PickResult({
this.placeId,
this.geometry,
this.formattedAddress,
this.types,
this.addressComponents,
this.adrAddress,
this.formattedPhoneNumber,
this.id,
this.reference,
this.icon,
this.name,
this.openingHours,
this.photos,
this.internationalPhoneNumber,
this.priceLevel,
this.rating,
this.scope,
this.url,
this.vicinity,
this.utcOffset,
this.website,
this.reviews,
});
final String? placeId;
final Geometry? geometry;
final String? formattedAddress;
final List<String>? types;
final List<AddressComponent>? addressComponents;
// Below results will not be fetched if 'usePlaceDetailSearch' is set to false (Defaults to false).
final String? adrAddress;
final String? formattedPhoneNumber;
final String? id;
final String? reference;
final String? icon;
final String? name;
final OpeningHoursDetail? openingHours;
final List<Photo>? photos;
final String? internationalPhoneNumber;
final PriceLevel? priceLevel;
final num? rating;
final String? scope;
final String? url;
final String? vicinity;
final num? utcOffset;
final String? website;
final List<Review>? reviews;
factory PickResult.fromGeocodingResult(GeocodingResult result) {
return PickResult(
placeId: result.placeId,
geometry: result.geometry,
formattedAddress: result.formattedAddress,
types: result.types,
addressComponents: result.addressComponents,
);
}
factory PickResult.fromPlaceDetailResult(PlaceDetails result) {
return PickResult(
placeId: result.placeId,
geometry: result.geometry,
formattedAddress: result.formattedAddress,
types: result.types,
addressComponents: result.addressComponents,
adrAddress: result.adrAddress,
formattedPhoneNumber: result.formattedPhoneNumber,
id: result.id,
reference: result.reference,
icon: result.icon,
name: result.name,
openingHours: result.openingHours,
photos: result.photos,
internationalPhoneNumber: result.internationalPhoneNumber,
priceLevel: result.priceLevel,
rating: result.rating,
scope: result.scope,
url: result.url,
vicinity: result.vicinity,
utcOffset: result.utcOffset,
website: result.website,
reviews: result.reviews,
);
}
}

@ -0,0 +1,535 @@
import 'dart:async';
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_api_headers/google_api_headers.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_webservice/places.dart';
import 'package:http/http.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';
import 'package:bookatanker/google_maps_place_picker_mb/google_maps_place_picker.dart';
import 'package:bookatanker/google_maps_place_picker_mb/providers/place_provider.dart';
import 'package:bookatanker/google_maps_place_picker_mb/src/autocomplete_search.dart';
import 'package:bookatanker/google_maps_place_picker_mb/src/controllers/autocomplete_search_controller.dart';
import 'package:bookatanker/google_maps_place_picker_mb/src/google_map_place_picker.dart';
typedef IntroModalWidgetBuilder = Widget Function(
BuildContext context,
Function? close,
);
enum PinState { Preparing, Idle, Dragging }
enum SearchingState { Idle, Searching }
class PlacePicker extends StatefulWidget {
const PlacePicker({
Key? key,
required this.apiKey,
this.onPlacePicked,
required this.initialPosition,
this.useCurrentLocation,
this.desiredLocationAccuracy = LocationAccuracy.high,
this.onMapCreated,
this.hintText,
this.searchingText,
this.selectText,
this.outsideOfPickAreaText,
this.onAutoCompleteFailed,
this.onGeocodingSearchFailed,
this.proxyBaseUrl,
this.httpClient,
this.selectedPlaceWidgetBuilder,
this.pinBuilder,
this.introModalWidgetBuilder,
this.autoCompleteDebounceInMilliseconds = 500,
this.cameraMoveDebounceInMilliseconds = 750,
this.initialMapType = MapType.normal,
this.enableMapTypeButton = true,
this.enableMyLocationButton = true,
this.myLocationButtonCooldown = 10,
this.usePinPointingSearch = true,
this.usePlaceDetailSearch = false,
this.autocompleteOffset,
this.autocompleteRadius,
this.autocompleteLanguage,
this.autocompleteComponents,
this.autocompleteTypes,
this.strictbounds,
this.region,
this.pickArea,
this.selectInitialPosition = false,
this.resizeToAvoidBottomInset = true,
this.initialSearchString,
this.searchForInitialValue = false,
this.forceAndroidLocationManager = false,
this.forceSearchOnZoomChanged = false,
this.automaticallyImplyAppBarLeading = true,
this.autocompleteOnTrailingWhitespace = false,
this.hidePlaceDetailsWhenDraggingPin = true,
this.onTapBack,
this.onCameraMoveStarted,
this.onCameraMove,
this.onCameraIdle,
this.onMapTypeChanged,
this.zoomGesturesEnabled = true,
this.zoomControlsEnabled = false,
}) : super(key: key);
final String apiKey;
final LatLng initialPosition;
final bool? useCurrentLocation;
final LocationAccuracy desiredLocationAccuracy;
final String? hintText;
final String? searchingText;
final String? selectText;
final String? outsideOfPickAreaText;
final ValueChanged<String>? onAutoCompleteFailed;
final ValueChanged<String>? onGeocodingSearchFailed;
final int autoCompleteDebounceInMilliseconds;
final int cameraMoveDebounceInMilliseconds;
final MapType initialMapType;
final bool enableMapTypeButton;
final bool enableMyLocationButton;
final int myLocationButtonCooldown;
final bool usePinPointingSearch;
final bool usePlaceDetailSearch;
final num? autocompleteOffset;
final num? autocompleteRadius;
final String? autocompleteLanguage;
final List<String>? autocompleteTypes;
final List<Component>? autocompleteComponents;
final bool? strictbounds;
final String? region;
/// If set the picker can only pick addresses in the given circle area.
/// The section will be highlighted.
final CircleArea? pickArea;
/// If true the [body] and the scaffold's floating widgets should size
/// themselves to avoid the onscreen keyboard whose height is defined by the
/// ambient [MediaQuery]'s [MediaQueryData.viewInsets] `bottom` property.
///
/// For example, if there is an onscreen keyboard displayed above the
/// scaffold, the body can be resized to avoid overlapping the keyboard, which
/// prevents widgets inside the body from being obscured by the keyboard.
///
/// Defaults to true.
final bool resizeToAvoidBottomInset;
final bool selectInitialPosition;
/// By using default setting of Place Picker, it will result result when user hits the select here button.
///
/// If you managed to use your own [selectedPlaceWidgetBuilder], then this WILL NOT be invoked, and you need use data which is
/// being sent with [selectedPlaceWidgetBuilder].
final ValueChanged<PickResult>? onPlacePicked;
/// optional - builds selected place's UI
///
/// It is provided by default if you leave it as a null.
/// INPORTANT: If this is non-null, [onPlacePicked] will not be invoked, as there will be no default 'Select here' button.
final SelectedPlaceWidgetBuilder? selectedPlaceWidgetBuilder;
/// optional - builds customized pin widget which indicates current pointing position.
///
/// It is provided by default if you leave it as a null.
final PinBuilder? pinBuilder;
/// optional - builds customized introduction panel.
///
/// None is provided / the map is instantly accessible if you leave it as a null.
final IntroModalWidgetBuilder? introModalWidgetBuilder;
/// optional - sets 'proxy' value in google_maps_webservice
///
/// In case of using a proxy the baseUrl can be set.
/// The apiKey is not required in case the proxy sets it.
/// (Not storing the apiKey in the app is good practice)
final String? proxyBaseUrl;
/// optional - set 'client' value in google_maps_webservice
///
/// In case of using a proxy url that requires authentication
/// or custom configuration
final BaseClient? httpClient;
/// Initial value of autocomplete search
final String? initialSearchString;
/// Whether to search for the initial value or not
final bool searchForInitialValue;
/// On Android devices you can set [forceAndroidLocationManager]
/// to true to force the plugin to use the [LocationManager] to determine the
/// position instead of the [FusedLocationProviderClient]. On iOS this is ignored.
final bool forceAndroidLocationManager;
/// Allow searching place when zoom has changed. By default searching is disabled when zoom has changed in order to prevent unwilling API usage.
final bool forceSearchOnZoomChanged;
/// Whether to display appbar backbutton. Defaults to true.
final bool automaticallyImplyAppBarLeading;
/// Will perform an autocomplete search, if set to true. Note that setting
/// this to true, while providing a smoother UX experience, may cause
/// additional unnecessary queries to the Places API.
///
/// Defaults to false.
final bool autocompleteOnTrailingWhitespace;
final bool hidePlaceDetailsWhenDraggingPin;
// Raised when clicking on the back arrow.
// This will not listen for the system back button on Android devices.
// If this is not set, but the back button is visible through automaticallyImplyLeading,
// the Navigator will try to pop instead.
final VoidCallback? onTapBack;
/// GoogleMap pass-through events:
/// Callback method for when the map is ready to be used.
///
/// Used to receive a [GoogleMapController] for this [GoogleMap].
final MapCreatedCallback? onMapCreated;
/// Called when the camera starts moving.
///
/// This can be initiated by the following:
/// 1. Non-gesture animation initiated in response to user actions.
/// For example: zoom buttons, my location button, or marker clicks.
/// 2. Programmatically initiated animation.
/// 3. Camera motion initiated in response to user gestures on the map.
/// For example: pan, tilt, pinch to zoom, or rotate.
final Function(PlaceProvider)? onCameraMoveStarted;
/// Called repeatedly as the camera continues to move after an
/// onCameraMoveStarted call.
///
/// This may be called as often as once every frame and should
/// not perform expensive operations.
final CameraPositionCallback? onCameraMove;
/// Called when camera movement has ended, there are no pending
/// animations and the user has stopped interacting with the map.
final Function(PlaceProvider)? onCameraIdle;
/// Called when the map type has been changed.
final Function(MapType)? onMapTypeChanged;
/// Allow user to make visible the zoom button & toggle on & off zoom gestures
final bool zoomGesturesEnabled;
final bool zoomControlsEnabled;
@override
_PlacePickerState createState() => _PlacePickerState();
}
class _PlacePickerState extends State<PlacePicker> {
GlobalKey appBarKey = GlobalKey();
late final Future<PlaceProvider> _futureProvider;
PlaceProvider? provider;
SearchBarController searchBarController = SearchBarController();
bool showIntroModal = true;
@override
void initState() {
super.initState();
_futureProvider = _initPlaceProvider();
}
@override
void dispose() {
searchBarController.dispose();
super.dispose();
}
Future<PlaceProvider> _initPlaceProvider() async {
final headers = await const GoogleApiHeaders().getHeaders();
final provider = PlaceProvider(
widget.apiKey,
widget.proxyBaseUrl,
widget.httpClient,
headers,
);
provider.sessionToken = const Uuid().v4();
provider.desiredAccuracy = widget.desiredLocationAccuracy;
provider.setMapType(widget.initialMapType);
if (widget.useCurrentLocation != null && widget.useCurrentLocation!) {
await provider.updateCurrentLocation(widget.forceAndroidLocationManager);
}
return provider;
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
searchBarController.clearOverlay();
return Future.value(true);
},
child: FutureBuilder<PlaceProvider>(
future: _futureProvider,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasData) {
provider = snapshot.data;
return MultiProvider(
providers: [
ChangeNotifierProvider<PlaceProvider>.value(value: provider!),
],
child: Stack(children: [
Scaffold(
key: ValueKey<int>(provider.hashCode),
resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
extendBodyBehindAppBar: true,
appBar: AppBar(
key: appBarKey,
automaticallyImplyLeading: false,
iconTheme: Theme.of(context).iconTheme,
elevation: 0,
backgroundColor: Colors.transparent,
titleSpacing: 0.0,
title: _buildSearchBar(context),
),
body: _buildMapWithLocation(),
),
_buildIntroModal(context),
]),
);
}
final children = <Widget>[];
if (snapshot.hasError) {
children.addAll([
Icon(
Icons.error_outline,
color: Colors.red,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
)
]);
} else {
children.add(CircularProgressIndicator());
}
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: children,
),
),
);
},
));
}
Widget _buildSearchBar(BuildContext context) {
return Row(
children: <Widget>[
widget.automaticallyImplyAppBarLeading || widget.onTapBack != null
? IconButton(
onPressed: () {
if (!showIntroModal ||
widget.introModalWidgetBuilder == null) {
if (widget.onTapBack != null) {
widget.onTapBack!();
return;
}
Navigator.maybePop(context);
}
},
icon: Icon(
Platform.isIOS ? Icons.arrow_back_ios : Icons.arrow_back,
),
color: Colors.black.withAlpha(128),
padding: EdgeInsets.zero)
: SizedBox(width: 15),
Expanded(
child: AutoCompleteSearch(
appBarKey: appBarKey,
searchBarController: searchBarController,
sessionToken: provider!.sessionToken,
hintText: widget.hintText,
searchingText: widget.searchingText,
debounceMilliseconds: widget.autoCompleteDebounceInMilliseconds,
onPicked: (prediction) {
_pickPrediction(prediction);
},
onSearchFailed: (status) {
if (widget.onAutoCompleteFailed != null) {
widget.onAutoCompleteFailed!(status);
}
},
autocompleteOffset: widget.autocompleteOffset,
autocompleteRadius: widget.autocompleteRadius,
autocompleteLanguage: widget.autocompleteLanguage,
autocompleteComponents: widget.autocompleteComponents,
autocompleteTypes: widget.autocompleteTypes,
strictbounds: widget.strictbounds,
region: widget.region,
initialSearchString: widget.initialSearchString,
searchForInitialValue: widget.searchForInitialValue,
autocompleteOnTrailingWhitespace:
widget.autocompleteOnTrailingWhitespace),
),
SizedBox(width: 5),
],
);
}
_pickPrediction(Prediction prediction) async {
provider!.placeSearchingState = SearchingState.Searching;
final PlacesDetailsResponse response =
await provider!.places.getDetailsByPlaceId(
prediction.placeId!,
sessionToken: provider!.sessionToken,
language: widget.autocompleteLanguage,
);
if (response.errorMessage?.isNotEmpty == true ||
response.status == "REQUEST_DENIED") {
if (widget.onAutoCompleteFailed != null) {
widget.onAutoCompleteFailed!(response.status);
}
return;
}
provider!.selectedPlace = PickResult.fromPlaceDetailResult(response.result);
// Prevents searching again by camera movement.
provider!.isAutoCompleteSearching = true;
await _moveTo(provider!.selectedPlace!.geometry!.location.lat,
provider!.selectedPlace!.geometry!.location.lng);
provider!.placeSearchingState = SearchingState.Idle;
}
_moveTo(double latitude, double longitude) async {
GoogleMapController? controller = provider!.mapController;
if (controller == null) return;
await controller.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(latitude, longitude),
zoom: 16,
),
),
);
}
_moveToCurrentPosition() async {
if (provider!.currentPosition != null) {
await _moveTo(provider!.currentPosition!.latitude,
provider!.currentPosition!.longitude);
}
}
Widget _buildMapWithLocation() {
if (provider!.currentPosition == null) {
return _buildMap(widget.initialPosition);
}
return _buildMap(LatLng(provider!.currentPosition!.latitude,
provider!.currentPosition!.longitude));
}
Widget _buildMap(LatLng initialTarget) {
return GoogleMapPlacePicker(
fullMotion: !widget.resizeToAvoidBottomInset,
initialTarget: initialTarget,
appBarKey: appBarKey,
selectedPlaceWidgetBuilder: widget.selectedPlaceWidgetBuilder,
pinBuilder: widget.pinBuilder,
onSearchFailed: widget.onGeocodingSearchFailed,
debounceMilliseconds: widget.cameraMoveDebounceInMilliseconds,
enableMapTypeButton: widget.enableMapTypeButton,
enableMyLocationButton: widget.enableMyLocationButton,
usePinPointingSearch: widget.usePinPointingSearch,
usePlaceDetailSearch: widget.usePlaceDetailSearch,
onMapCreated: widget.onMapCreated,
selectInitialPosition: widget.selectInitialPosition,
language: widget.autocompleteLanguage,
pickArea: widget.pickArea,
forceSearchOnZoomChanged: widget.forceSearchOnZoomChanged,
hidePlaceDetailsWhenDraggingPin: widget.hidePlaceDetailsWhenDraggingPin,
selectText: widget.selectText,
outsideOfPickAreaText: widget.outsideOfPickAreaText,
onToggleMapType: () {
provider!.switchMapType();
if (widget.onMapTypeChanged != null) {
widget.onMapTypeChanged!(provider!.mapType);
}
},
onMyLocation: () async {
// Prevent to click many times in short period.
if (provider!.isOnUpdateLocationCooldown == false) {
provider!.isOnUpdateLocationCooldown = true;
Timer(Duration(seconds: widget.myLocationButtonCooldown), () {
provider!.isOnUpdateLocationCooldown = false;
});
await provider!
.updateCurrentLocation(widget.forceAndroidLocationManager);
await _moveToCurrentPosition();
}
},
onMoveStart: () {
searchBarController.reset();
},
onPlacePicked: widget.onPlacePicked,
onCameraMoveStarted: widget.onCameraMoveStarted,
onCameraMove: widget.onCameraMove,
onCameraIdle: widget.onCameraIdle,
zoomGesturesEnabled: widget.zoomGesturesEnabled,
zoomControlsEnabled: widget.zoomControlsEnabled,
);
}
Widget _buildIntroModal(BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return showIntroModal && widget.introModalWidgetBuilder != null
? Stack(children: [
Positioned(
top: 0,
right: 0,
bottom: 0,
left: 0,
child: Material(
type: MaterialType.canvas,
color: Color.fromARGB(128, 0, 0, 0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
child: ClipRect(),
),
),
widget.introModalWidgetBuilder!(context, () {
setState(() {
showIntroModal = false;
});
})
])
: Container();
});
}
}

@ -0,0 +1,202 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'common/settings.dart';
import 'otp.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final TextEditingController mobileController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
bool isPasswordVisible = false;
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true, // Important
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView( // 👈 FIX
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 250),
Text(
"Welcome!",
style: fontTextStyle(
26, Colors.black, FontWeight.w700),
),
const SizedBox(height: 30),
TextField(
controller: mobileController,
keyboardType: TextInputType.phone,
inputFormatters: [
LengthLimitingTextInputFormatter(10), // max 10 digits
FilteringTextInputFormatter.digitsOnly, // only digits
],
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: "Enter your number",
hintStyle: fontTextStyle(14, Color(0xFF2A2A2A), FontWeight.w400),
),
),
const SizedBox(height: 15),
TextField(
controller: passwordController,
obscureText: !isPasswordVisible,
decoration: InputDecoration(
hintText: "Password",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
suffixIcon: IconButton(
icon: Icon(
isPasswordVisible
? Icons.visibility
: Icons.visibility_off,
),
onPressed: () {
setState(() {
isPasswordVisible =
!isPasswordVisible;
});
},
),
),
),
const SizedBox(height: 10),
Text(
"Forgot Password?",
style: fontTextStyle(
13, const Color(0XFF1D7AFC),
FontWeight.w500),
),
const SizedBox(height: 25),
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor:
const Color(0XFF1D7AFC),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(25),
),
),
child: Text(
"Login",
style: fontTextStyle(
14, Colors.white,
FontWeight.w600),
),
),
),
const SizedBox(height: 15),
SizedBox(
width: double.infinity,
height: 48,
child: OutlinedButton(
onPressed: () {
String mobile = mobileController.text.trim();
if (mobile.length != 10) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Enter 10 digit mobile number"),
),
);
return;
}
if (!mobile.startsWith(RegExp(r'[6-9]'))) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Mobile must start with 6-9"),
),
);
return;
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
OtpScreen(mobileNumber: mobile),
),
);
},
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(25),
),
),
child: const Text(
"Login without Password",
style: TextStyle(color: Colors.blue),
),
),
),
const SizedBox(height: 30),
const Divider(),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
height: 48,
child: OutlinedButton(
onPressed: () {
},
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(25),
),
),
child: const Text(
"Sign Up",
style: TextStyle(color: Colors.blue),
),
),
),
],
),
),
),
),
);
}
}

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'common/splash_screen.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
// Create a global navigator key
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Water Management App',
navigatorKey: navigatorKey, // Set the navigator key
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: SplashScreen(), // Your initial screen
);
}
}

@ -0,0 +1,238 @@
import 'package:bookatanker/signup.dart';
import 'package:flutter/material.dart';
import 'common/settings.dart';
import 'createpassword.dart';
class OtpScreen extends StatefulWidget {
final String mobileNumber;
const OtpScreen({
Key? key,
required this.mobileNumber,
}) : super(key: key);
@override
State<OtpScreen> createState() => _OtpScreenState();
}
class _OtpScreenState extends State<OtpScreen> {
final List<TextEditingController> _controllers =
List.generate(6, (_) => TextEditingController());
@override
void dispose() {
for (var controller in _controllers) {
controller.dispose();
}
super.dispose();
}
String maskMobileNumber(String number) {
if (number.length < 3) return number;
final stars = '*' * (number.length - 3);
final lastThree = number.substring(number.length - 3);
return '$stars$lastThree';
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 100),
/// Title
Text(
"Enter Confirmation Code",
style: fontTextStyle(
16,
const Color(0xFF101214),
FontWeight.w600),
),
const SizedBox(height: 8),
/// Mobile Text
Text(
'A 6-digit code was sent to\n+91 ${maskMobileNumber(widget.mobileNumber)}',
textAlign: TextAlign.center,
style: fontTextStyle(
12,
const Color(0xFF7E7F80),
FontWeight.w400),
),
const SizedBox(height: 30),
/// OTP Boxes
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: List.generate(
6,
(index) => SizedBox(
width: 45,
child: TextField(
controller:
_controllers[index],
textAlign: TextAlign.center,
keyboardType:
TextInputType.number,
maxLength: 1,
style: fontTextStyle(
14,
const Color(0xFF2A2A2A),
FontWeight.w400),
decoration:
InputDecoration(
counterText: "",
border:
OutlineInputBorder(
borderRadius:
BorderRadius.circular(
8),
),
),
onChanged: (value) {
if (value.isNotEmpty &&
index < 5) {
FocusScope.of(context)
.nextFocus();
}
else if (value.isEmpty &&
index > 0) {
FocusScope.of(context)
.previousFocus();
}
},
),
),
),
),
const SizedBox(height: 40),
/// Resend Code
TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
const CreatePasswordScreen(),
),
);
},
child: Text(
"Resend Code",
style: fontTextStyle(
14,
const Color(0XFF1D7AFC),
FontWeight.w600),
),
),
const SizedBox(height: 20),
/// Continue Button
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
final otp =
_controllers
.map((c) => c.text)
.join();
if (otp.length == 6) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) =>
const SignUpScreen(),
),
);
}
else {
ScaffoldMessenger.of(
context)
.showSnackBar(
const SnackBar(
content: Text(
"Please enter 6 digit OTP"),
),
);
}
},
style: ElevatedButton
.styleFrom(
backgroundColor:
const Color(
0XFF1D7AFC),
padding:
const EdgeInsets
.symmetric(
vertical: 16),
shape:
RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
30),
),
),
child: Text(
"Continue",
style: fontTextStyle(
14,
Colors.white,
FontWeight.w600),
),
),
),
],
),
),
),
);
}
}

@ -0,0 +1,148 @@
import 'package:flutter/material.dart';
import 'common/settings.dart';
import 'otp.dart';
class SignUpScreen extends StatefulWidget {
const SignUpScreen({Key? key}) : super(key: key);
@override
State<SignUpScreen> createState() => _SignUpScreenState();
}
class _SignUpScreenState extends State<SignUpScreen> {
TextEditingController mobileController = TextEditingController();
TextEditingController emailController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Colors.white,
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 120),
/// Title
Text(
"Sign Up",
style: fontTextStyle(
22, Colors.black, FontWeight.w700),
),
const SizedBox(height: 5),
/// Subtitle
Text(
"Sign up using your mobile number",
style: fontTextStyle(
13,
const Color(0xFF7E7F80),
FontWeight.w400),
),
const SizedBox(height: 60),
/// Mobile Number Field
TextField(
controller: mobileController,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
hintText: "Mobile Number *",
hintStyle: fontTextStyle(
14,
const Color(0xFF7E7F80),
FontWeight.w400),
contentPadding:
const EdgeInsets.symmetric(
horizontal: 15,
vertical: 18),
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(8),
),
),
),
const SizedBox(height: 20),
/// Email Field
TextField(
controller: emailController,
keyboardType:
TextInputType.emailAddress,
decoration: InputDecoration(
hintText: "Email ID (optional)",
hintStyle: fontTextStyle(
14,
const Color(0xFF7E7F80),
FontWeight.w400),
contentPadding:
const EdgeInsets.symmetric(
horizontal: 15,
vertical: 18),
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(8),
),
),
),
const SizedBox(height: 35),
/// Sign Up Button
SizedBox(
width: double.infinity,
height: 52,
child: ElevatedButton(
onPressed: () {
if (mobileController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("Enter Mobile Number"),
),
);
return;
}
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => OtpScreen(
mobileNumber: mobileController.text,
),
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0XFF1D7AFC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
child: Text(
"Sign Up",
style: fontTextStyle(
15,
Colors.white,
FontWeight.w600),
),
),
),
],
),
),
),
),
);
}
}

1
linux/.gitignore vendored

@ -0,0 +1 @@
flutter/ephemeral

@ -0,0 +1,145 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "bookatanker")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.bookatanker")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endforeach(bundled_library)
# Copy the native assets provided by the build.dart from all packages.
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
install(DIRECTORY "${NATIVE_ASSETS_DIR}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()

@ -0,0 +1,88 @@
# This file controls Flutter-level build steps. It should not be edited.
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)

@ -0,0 +1,23 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

@ -0,0 +1,26 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
flutter_secure_storage_linux
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

@ -0,0 +1,6 @@
#include "my_application.h"
int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}

@ -0,0 +1,124 @@
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
// Use a header bar when running in GNOME as this is the common style used
// by applications and is the setup most users will be using (e.g. Ubuntu
// desktop).
// If running on X and not using GNOME then just use a traditional title bar
// in case the window manager does more exotic layout, e.g. tiling.
// If running on Wayland assume the header bar will work (may need changing
// if future cases occur).
gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
GdkScreen* screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
}
#endif
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "bookatanker");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "bookatanker");
}
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
g_autoptr(GError) error = nullptr;
if (!g_application_register(application, nullptr, &error)) {
g_warning("Failed to register: %s", error->message);
*exit_status = 1;
return TRUE;
}
g_application_activate(application);
*exit_status = 0;
return TRUE;
}
// Implements GApplication::startup.
static void my_application_startup(GApplication* application) {
//MyApplication* self = MY_APPLICATION(object);
// Perform any actions required at application startup.
G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
}
// Implements GApplication::shutdown.
static void my_application_shutdown(GApplication* application) {
//MyApplication* self = MY_APPLICATION(object);
// Perform any actions required at application shutdown.
G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
}
// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
G_APPLICATION_CLASS(klass)->startup = my_application_startup;
G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_NON_UNIQUE,
nullptr));
}

@ -0,0 +1,18 @@
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication)
/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_

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

Loading…
Cancel
Save