This commit is contained in:
kyawkhantwin 2026-04-09 13:17:03 +06:30
commit f9923d9d26
145 changed files with 6516 additions and 0 deletions

45
.gitignore vendored Normal file
View File

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

45
.metadata Normal file
View File

@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
- platform: android
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
- platform: ios
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
- platform: linux
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
- platform: macos
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
- platform: web
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
- platform: windows
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
# 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'

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# cb_prestige_qr
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:
- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter)
- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources)
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.

32
analysis_options.yaml Normal file
View File

@ -0,0 +1,32 @@
# 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
analyzer:
plugins:
- custom_lint
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

14
android/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,44 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.example.cb_prestige_qr"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.cb_prestige_qr"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

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

View File

@ -0,0 +1,32 @@
<?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:color/transparent</item>
<!-- 🔥 IMPORTANT: allow drawing behind system bars -->
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<!-- 🔥 Make nav bar transparent -->
<item name="android:navigationBarColor">@android:color/transparent</item>
<!-- 🔥 Make status bar transparent -->
<item name="android:statusBarColor">@android:color/transparent</item>
<!-- 🔥 Needed for full screen layouts -->
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
</resources>

View File

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

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

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

View File

@ -0,0 +1,2 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true

View File

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

View File

@ -0,0 +1,26 @@
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.11.1" apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
}
include(":app")

BIN
assets/images/caro_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
assets/images/caro_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

BIN
assets/images/caro_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
assets/images/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
assets/images/splash_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
assets/mp3/splash.mp4 Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
<svg viewBox="-0.5 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M12 2.96997C10.0222 2.96997 8.08881 3.55646 6.44432 4.65527C4.79983 5.75409 3.51809 7.31581 2.76121 9.14307C2.00434 10.9703 1.8063 12.9811 2.19215 14.9209C2.578 16.8607 3.53041 18.6425 4.92894 20.041C6.32746 21.4395 8.10931 22.392 10.0491 22.7778C11.9889 23.1637 13.9996 22.9656 15.8269 22.2087C17.6541 21.4519 19.2159 20.1701 20.3147 18.5256C21.4135 16.8811 22 14.9478 22 12.97" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M12.7003 17.1099V18.21C12.7003 18.3877 12.6296 18.5582 12.504 18.6838C12.3783 18.8095 12.2079 18.8799 12.0302 18.8799C11.8525 18.8799 11.6821 18.8095 11.5565 18.6838C11.4308 18.5582 11.3602 18.3877 11.3602 18.21V17.0801C10.9165 17.0072 10.4917 16.8468 10.1106 16.6082C9.72943 16.3695 9.39958 16.0573 9.14023 15.6899C9.04577 15.57 8.99311 15.4226 8.99023 15.27C8.99014 15.1834 9.00763 15.0975 9.04166 15.0178C9.07568 14.9382 9.12553 14.8662 9.18817 14.8064C9.25082 14.7466 9.32495 14.7 9.4061 14.6697C9.48724 14.6393 9.57371 14.6258 9.66025 14.6299C9.74612 14.6294 9.83102 14.648 9.90884 14.6843C9.98667 14.7206 10.0554 14.774 10.1102 14.8401C10.4301 15.258 10.8643 15.574 11.3602 15.75V13.21C10.0302 12.69 9.36023 11.9099 9.36023 10.8999C9.38027 10.3592 9.5928 9.84343 9.9595 9.44556C10.3262 9.04769 10.8229 8.79397 11.3602 8.72998V7.62988C11.3602 7.45219 11.4308 7.2819 11.5565 7.15625C11.6821 7.0306 11.8525 6.95996 12.0302 6.95996C12.2079 6.95996 12.3783 7.0306 12.504 7.15625C12.6296 7.2819 12.7003 7.45219 12.7003 7.62988V8.71997C13.0724 8.77828 13.4289 8.91103 13.7485 9.11035C14.0681 9.30967 14.3442 9.57137 14.5602 9.87988C14.6555 9.99235 14.7117 10.1329 14.7202 10.28C14.7229 10.3657 14.7084 10.451 14.6774 10.531C14.6464 10.611 14.5997 10.684 14.54 10.7456C14.4803 10.8072 14.4088 10.856 14.3299 10.8894C14.2509 10.9228 14.166 10.94 14.0802 10.9399C13.9906 10.9394 13.9022 10.9196 13.8211 10.8816C13.74 10.8436 13.668 10.7884 13.6102 10.72C13.3718 10.4221 13.0575 10.1942 12.7003 10.0601V12.3101L12.9503 12.4099C14.2203 12.9099 15.0103 13.63 15.0103 14.77C14.9954 15.3808 14.7481 15.9629 14.3189 16.3977C13.8897 16.8325 13.3108 17.0871 12.7003 17.1099ZM11.3602 11.73V10.0999C11.1988 10.1584 11.0599 10.2662 10.963 10.408C10.8662 10.5497 10.8162 10.7183 10.8203 10.8899C10.8186 11.0673 10.8688 11.2414 10.9647 11.3906C11.0607 11.5399 11.1981 11.6579 11.3602 11.73ZM13.5502 14.8C13.5502 14.32 13.2203 14.03 12.7003 13.8V15.8C12.9387 15.7639 13.1561 15.6427 13.3123 15.459C13.4685 15.2752 13.553 15.0412 13.5502 14.8Z" fill="#000000"></path> <path d="M21.9998 2.91992L16.3398 8.57992" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M20.8698 8.5798H16.3398V4.0498" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> </g></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

6
assets/svg/coin_send.svg Normal file
View File

@ -0,0 +1,6 @@
<svg viewBox="-0.5 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2.96997C10.0222 2.96997 8.08881 3.55646 6.44432 4.65527C4.79983 5.75409 3.51809 7.31581 2.76121 9.14307C2.00434 10.9703 1.8063 12.9811 2.19215 14.9209C2.578 16.8607 3.53041 18.6425 4.92894 20.041C6.32746 21.4395 8.10931 22.392 10.0491 22.7778C11.9889 23.1637 13.9996 22.9656 15.8269 22.2087C17.6541 21.4519 19.2159 20.1701 20.3147 18.5256C21.4135 16.8811 22 14.9478 22 12.97" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12.7003 17.1099V18.21C12.7003 18.3877 12.6296 18.5582 12.504 18.6838C12.3783 18.8095 12.2079 18.8799 12.0302 18.8799C11.8525 18.8799 11.6821 18.8095 11.5565 18.6838C11.4308 18.5582 11.3602 18.3877 11.3602 18.21V17.0801C10.9165 17.0072 10.4917 16.8468 10.1106 16.6082C9.72943 16.3695 9.39958 16.0573 9.14023 15.6899C9.04577 15.57 8.99311 15.4226 8.99023 15.27C8.99014 15.1834 9.00763 15.0975 9.04166 15.0178C9.07568 14.9382 9.12553 14.8662 9.18817 14.8064C9.25082 14.7466 9.32495 14.7 9.4061 14.6697C9.48724 14.6393 9.57371 14.6258 9.66025 14.6299C9.74612 14.6294 9.83102 14.648 9.90884 14.6843C9.98667 14.7206 10.0554 14.774 10.1102 14.8401C10.4301 15.258 10.8643 15.574 11.3602 15.75V13.21C10.0302 12.69 9.36023 11.9099 9.36023 10.8999C9.38027 10.3592 9.5928 9.84343 9.9595 9.44556C10.3262 9.04769 10.8229 8.79397 11.3602 8.72998V7.62988C11.3602 7.45219 11.4308 7.2819 11.5565 7.15625C11.6821 7.0306 11.8525 6.95996 12.0302 6.95996C12.2079 6.95996 12.3783 7.0306 12.504 7.15625C12.6296 7.2819 12.7003 7.45219 12.7003 7.62988V8.71997C13.0724 8.77828 13.4289 8.91103 13.7485 9.11035C14.0681 9.30967 14.3442 9.57137 14.5602 9.87988C14.6555 9.99235 14.7117 10.1329 14.7202 10.28C14.7229 10.3657 14.7084 10.451 14.6774 10.531C14.6464 10.611 14.5997 10.684 14.54 10.7456C14.4803 10.8072 14.4088 10.856 14.3299 10.8894C14.2509 10.9228 14.166 10.94 14.0802 10.9399C13.9906 10.9394 13.9022 10.9196 13.8211 10.8816C13.74 10.8436 13.668 10.7884 13.6102 10.72C13.3718 10.4221 13.0575 10.1942 12.7003 10.0601V12.3101L12.9503 12.4099C14.2203 12.9099 15.0103 13.63 15.0103 14.77C14.9954 15.3808 14.7481 15.9629 14.3189 16.3977C13.8897 16.8325 13.3108 17.0871 12.7003 17.1099Z" fill="#000000"/>
<path d="M16.3398 8.57992L21.9998 2.91992" stroke="#000000" stroke-width="1.5" stroke-linecap="round"/>
<path d="M17.4707 2.91992H22.0007V7.44992" stroke="#000000" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

34
ios/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,24 @@
<?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>
</dict>
</plist>

View File

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

View File

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

View File

@ -0,0 +1,620 @@
// !$*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 */; };
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.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>"; };
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.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 */,
7884E8672EC3CC0400C636F2 /* SceneDelegate.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 */,
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift 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 = 13.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.cbPrestigeQr;
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.cbPrestigeQr.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.cbPrestigeQr.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.cbPrestigeQr.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 = 13.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 = 13.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.cbPrestigeQr;
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.cbPrestigeQr;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,101 @@
<?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"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
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"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

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

View File

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

View File

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

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

@ -0,0 +1,70 @@
<?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>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Cb Prestige Qr</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>cb_prestige_qr</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>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</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>
</dict>
</plist>

View File

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

View File

@ -0,0 +1,6 @@
import Flutter
import UIKit
class SceneDelegate: FlutterSceneDelegate {
}

View File

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

View File

@ -0,0 +1,127 @@
import 'dart:ui';
import 'package:flutter/material.dart';
class GlobalLoadingOverlay extends StatelessWidget {
const GlobalLoadingOverlay({
super.key,
required this.isLoading,
});
final bool isLoading;
@override
Widget build(BuildContext context) {
if (!isLoading) return const SizedBox.shrink();
return SizedBox.expand(
child: AbsorbPointer(
absorbing: true,
child: Stack(
children: [
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12),
child: const SizedBox.expand(),
),
ColoredBox(
color: Colors.black.withOpacity(0.55),
),
const Center(
child: _AnimatedLoader(),
),
],
),
),
);
}
}
class _AnimatedLoader extends StatefulWidget {
const _AnimatedLoader();
@override
State<_AnimatedLoader> createState() => _AnimatedLoaderState();
}
class _AnimatedLoaderState extends State<_AnimatedLoader>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _mainOpacity;
late final Animation<double> _glowOpacity;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 2200),
)..repeat(reverse: true);
/// Main fade (smooth breathing)
_mainOpacity = TweenSequence<double>([
TweenSequenceItem(
tween: Tween(begin: 0.0, end: 1.0),
weight: 50,
),
TweenSequenceItem(
tween: Tween(begin: 1.0, end: 0.0),
weight: 50,
),
]).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
),
);
/// Glow fade (slightly offset feel)
_glowOpacity = Tween<double>(
begin: 0.2,
end: 0.5,
).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return AnimatedBuilder(
animation: _controller,
builder: (context, _) {
return Opacity(
opacity: _mainOpacity.value,
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: colorScheme.primary,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(_glowOpacity.value),
blurRadius: 50,
spreadRadius: 12,
),
],
),
child: Image.asset(
'assets/images/logo_white.png',
width: 100,
height: 100,
colorBlendMode: BlendMode.srcIn,
),
),
);
},
);
}
}

View File

@ -0,0 +1,53 @@
import 'dart:async';
import 'package:cb_prestige_qr/core/presentation/widgets/global_loading_overlay.dart';
import 'package:flutter/material.dart';
class StartLoadingOverlay extends StatefulWidget {
const StartLoadingOverlay({
super.key,
required this.child,
this.enabled = true,
this.duration = const Duration(seconds: 2),
});
final Widget child;
final bool enabled;
final Duration duration;
@override
State<StartLoadingOverlay> createState() => _StartLoadingOverlayState();
}
class _StartLoadingOverlayState extends State<StartLoadingOverlay> {
Timer? _timer;
var _isLoading = false;
@override
void initState() {
super.initState();
if (!widget.enabled) return;
_isLoading = true;
_timer = Timer(widget.duration, () {
if (!mounted) return;
setState(() => _isLoading = false);
});
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
widget.child,
GlobalLoadingOverlay(isLoading: _isLoading),
],
);
}
}

View File

@ -0,0 +1,173 @@
import 'dart:async';
import 'dart:ui';
import 'package:cb_prestige_qr/core/utils/ScanShell.dart';
import 'package:cb_prestige_qr/core/presentation/widgets/global_loading_overlay.dart';
import 'package:cb_prestige_qr/core/widgets/CenterNavButton.dart';
import 'package:cb_prestige_qr/core/widgets/NavItem.dart';
import 'package:cb_prestige_qr/features/analysis/presentation/pages/analysis_page.dart';
import 'package:cb_prestige_qr/features/history/presentation/pages/history_page.dart';
import 'package:cb_prestige_qr/features/home/presentation/pages/home.dart';
import 'package:cb_prestige_qr/features/settings/presentation/pages/settings_page.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
final navIndexNotifierProvider = NotifierProvider<NavIndexNotifier, int>(
NavIndexNotifier.new,
);
class NavIndexNotifier extends Notifier<int> {
@override
int build() => 0;
void setIndex(int index) {
if (state == index) return;
state = index;
}
}
class MainShell extends ConsumerStatefulWidget {
const MainShell({super.key});
@override
ConsumerState<MainShell> createState() => _MainShellState();
}
class _MainShellState extends ConsumerState<MainShell> {
Timer? _timer;
var _isLoading = false;
void _startLoading([Duration duration = const Duration(seconds: 1)]) {
_timer?.cancel();
if (!_isLoading) setState(() => _isLoading = true);
_timer = Timer(duration, () {
if (!mounted) return;
setState(() => _isLoading = false);
});
}
@override
void initState() {
super.initState();
final initialIndex = ref.read(navIndexNotifierProvider);
if (initialIndex != 0) _startLoading();
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
final selectedIndex = ref.watch(navIndexNotifierProvider);
final theme = Theme.of(context);
ref.listen<int>(navIndexNotifierProvider, (previous, next) {
if (previous == null || previous == next) return;
if (next == 0) return;
_startLoading();
});
final pages = [
const HomeView(),
const AnalysisPage(),
const SizedBox(),
const HistoryPage(),
const SettingsPage(),
];
return Stack(
children: [
Scaffold(
extendBody: true,
body: IndexedStack(index: selectedIndex, children: pages),
bottomNavigationBar: Padding(
padding: const EdgeInsets.symmetric(horizontal: 0),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Container(
height: 70 + MediaQuery.of(context).padding.bottom,
decoration: BoxDecoration(
color: theme.scaffoldBackgroundColor,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withAlpha(31)),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(31),
blurRadius: 25,
offset: const Offset(0, 10),
),
],
),
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).padding.bottom,
),
child: Row(
children: [
Expanded(
child: NavItem(
icon: Icons.home_rounded,
isActive: selectedIndex == 0,
onTap: () => ref
.read(navIndexNotifierProvider.notifier)
.setIndex(0),
),
),
Expanded(
child: NavItem(
icon: Icons.analytics,
isActive: selectedIndex == 1,
onTap: () => ref
.read(navIndexNotifierProvider.notifier)
.setIndex(1),
),
),
Expanded(
child: CenterNavButton(
isActive: false,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const ScanFlow(),
),
);
},
),
),
Expanded(
child: NavItem(
icon: Icons.history_rounded,
isActive: selectedIndex == 3,
onTap: () => ref
.read(navIndexNotifierProvider.notifier)
.setIndex(3),
),
),
Expanded(
child: NavItem(
icon: Icons.person_rounded,
isActive: selectedIndex == 4,
onTap: () => ref
.read(navIndexNotifierProvider.notifier)
.setIndex(4),
),
),
],
),
),
),
),
),
),
),
GlobalLoadingOverlay(isLoading: _isLoading),
],
);
}
}

View File

@ -0,0 +1,11 @@
import 'package:cb_prestige_qr/features/scan/presentation/pages/scan_page.dart';
import 'package:flutter/material.dart';
class ScanFlow extends StatelessWidget {
const ScanFlow({super.key});
@override
Widget build(BuildContext context) {
return const ScanPage();
}
}

View File

@ -0,0 +1,64 @@
import 'package:flutter/material.dart';
class CenterNavButton extends StatelessWidget {
final bool isActive;
final VoidCallback onTap;
const CenterNavButton({
super.key,
required this.isActive,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(20),
splashColor: theme.colorScheme.primary.withAlpha(26),
child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
margin: const EdgeInsets.symmetric(horizontal: 6, vertical: 10),
decoration: BoxDecoration(
color: isActive
? theme.colorScheme.primary.withAlpha(31)
: Colors.transparent,
borderRadius: BorderRadius.circular(20),
),
child: Center(
child: AnimatedScale(
duration: const Duration(milliseconds: 200),
scale: isActive ? 1.15 : 1.0,
child: Container(
height: 48,
width: 48,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
theme.colorScheme.primary,
theme.colorScheme.primary.withAlpha(179),
],
),
boxShadow: [
BoxShadow(
color: theme.colorScheme.primary.withAlpha(102),
blurRadius: 16,
offset: const Offset(0, 6),
),
],
),
child: const Icon(
Icons.qr_code_scanner,
color: Colors.white,
size: 24,
),
),
),
),
),
);
}
}

View File

@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
class NavItem extends StatelessWidget {
final IconData icon;
final bool isActive;
final VoidCallback onTap;
const NavItem({
super.key,
required this.icon,
required this.isActive,
required this.onTap,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(20),
splashColor: theme.colorScheme.primary.withAlpha(26),
child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
margin: const EdgeInsets.symmetric(horizontal: 6, vertical: 10),
decoration: BoxDecoration(
color: isActive
? theme.colorScheme.primary.withAlpha(31)
: Colors.transparent,
borderRadius: BorderRadius.circular(20),
),
child: Center(
child: AnimatedScale(
duration: const Duration(milliseconds: 200),
scale: isActive ? 1.2 : 1.0,
child: Icon(
icon,
size: 26,
color: isActive
? theme.colorScheme.primary
: theme.colorScheme.onSurfaceVariant,
),
),
),
),
);
}
}

View File

@ -0,0 +1,64 @@
import 'dart:math';
import '../../domain/entities/analysis_content.dart';
import '../../domain/entities/analysis_series_point.dart';
import '../../domain/entities/analysis_summary.dart';
abstract class AnalysisLocalDataSource {
AnalysisContent getContent({required int rangeDays, DateTime? now});
}
class AnalysisLocalDataSourceImpl implements AnalysisLocalDataSource {
const AnalysisLocalDataSourceImpl();
@override
AnalysisContent getContent({required int rangeDays, DateTime? now}) {
final effectiveNow = now ?? DateTime.now();
final normalizedNow = DateTime(
effectiveNow.year,
effectiveNow.month,
effectiveNow.day,
);
final clampedDays = rangeDays.clamp(1, 365);
final startDate = normalizedNow.subtract(Duration(days: clampedDays - 1));
final rand = Random(clampedDays * 7919 + normalizedNow.day);
final series = <AnalysisSeriesPoint>[];
var totalScans = 0;
var totalPointsUsed = 0;
for (var i = 0; i < clampedDays; i++) {
final date = startDate.add(Duration(days: i));
final base = 30 + (15 * sin((i / max(1, clampedDays - 1)) * pi)).round();
final noise = rand.nextInt(14) - 7;
final scans = max(0, base + noise);
final pointsUsed = max(0, (scans * (2 + rand.nextInt(3))) ~/ 3);
totalScans += scans;
totalPointsUsed += pointsUsed;
series.add(
AnalysisSeriesPoint(
date: date,
scanCount: scans,
pointsUsed: pointsUsed,
),
);
}
final users = 1240 + rand.nextInt(100);
final activeUsers = (users * (0.22 + rand.nextDouble() * 0.08)).round();
final successRate = 0.965 + rand.nextDouble() * 0.03;
return AnalysisContent(
summary: AnalysisSummary(
users: users,
activeUsers: activeUsers,
scans: totalScans,
successRate: successRate,
pointsUsed: totalPointsUsed,
),
series: series,
);
}
}

View File

@ -0,0 +1,14 @@
import '../../domain/entities/analysis_content.dart';
import '../../domain/repositories/analysis_repository.dart';
import '../data_sources/analysis_local_data_source.dart';
class AnalysisRepositoryImpl implements AnalysisRepository {
const AnalysisRepositoryImpl(this._localDataSource);
final AnalysisLocalDataSource _localDataSource;
@override
Future<AnalysisContent> getAnalysisContent({required int rangeDays}) async {
return _localDataSource.getContent(rangeDays: rangeDays);
}
}

View File

@ -0,0 +1,9 @@
import 'analysis_series_point.dart';
import 'analysis_summary.dart';
class AnalysisContent {
const AnalysisContent({required this.summary, required this.series});
final AnalysisSummary summary;
final List<AnalysisSeriesPoint> series;
}

View File

@ -0,0 +1,11 @@
class AnalysisSeriesPoint {
const AnalysisSeriesPoint({
required this.date,
required this.scanCount,
required this.pointsUsed,
});
final DateTime date;
final int scanCount;
final int pointsUsed;
}

View File

@ -0,0 +1,23 @@
class AnalysisSummary {
const AnalysisSummary({
required this.users,
required this.activeUsers,
required this.scans,
required this.successRate,
required this.pointsUsed,
});
/// Number of users for the selected range (not all-time).
final int users;
/// Active users for the selected range (not all-time).
final int activeUsers;
/// Scans for the selected range (not all-time).
final int scans;
final double successRate;
/// Points used for the selected range (not all-time).
final int pointsUsed;
}

View File

@ -0,0 +1,5 @@
import '../entities/analysis_content.dart';
abstract class AnalysisRepository {
Future<AnalysisContent> getAnalysisContent({required int rangeDays});
}

View File

@ -0,0 +1,12 @@
import '../entities/analysis_content.dart';
import '../repositories/analysis_repository.dart';
class GetAnalysisContent {
const GetAnalysisContent(this._repository);
final AnalysisRepository _repository;
Future<AnalysisContent> call({required int rangeDays}) {
return _repository.getAnalysisContent(rangeDays: rangeDays);
}
}

View File

@ -0,0 +1,8 @@
enum AnalysisChartMetric { scans, pointsUsed }
extension AnalysisChartMetricX on AnalysisChartMetric {
String get label => switch (this) {
AnalysisChartMetric.scans => 'Scans',
AnalysisChartMetric.pointsUsed => 'Points Used',
};
}

View File

@ -0,0 +1,15 @@
enum AnalysisRangePreset { today, last7Days, last30Days }
extension AnalysisRangePresetX on AnalysisRangePreset {
int get days => switch (this) {
AnalysisRangePreset.today => 1,
AnalysisRangePreset.last7Days => 7,
AnalysisRangePreset.last30Days => 30,
};
String get label => switch (this) {
AnalysisRangePreset.today => 'Today',
AnalysisRangePreset.last7Days => '7 days',
AnalysisRangePreset.last30Days => '30 days',
};
}

View File

@ -0,0 +1,70 @@
import 'package:flutter/foundation.dart';
@immutable
class AnalysisSeriesPointUiModel {
const AnalysisSeriesPointUiModel({
required this.label,
required this.scanCount,
required this.pointsUsed,
});
final String label;
final int scanCount;
final int pointsUsed;
}
@immutable
class AnalysisSummaryUiModel {
const AnalysisSummaryUiModel({
required this.users,
required this.activeUsers,
required this.scans,
required this.successRatePercent,
required this.pointsUsed,
});
static const empty = AnalysisSummaryUiModel(
users: 0,
activeUsers: 0,
scans: 0,
successRatePercent: 0,
pointsUsed: 0,
);
final int users;
final int activeUsers;
final int scans;
final int successRatePercent;
final int pointsUsed;
}
@immutable
class AnalysisUiState {
AnalysisUiState({
int? rangeDays,
String? rangeLabel,
AnalysisSummaryUiModel? summary,
List<AnalysisSeriesPointUiModel>? series,
double? averageScansPerDay,
double? averagePointsPerDay,
double? pointsPerScan,
int? activeUserRatePercent,
}) : rangeDays = rangeDays ?? 7,
rangeLabel = rangeLabel ?? '7 days',
summary = summary ?? AnalysisSummaryUiModel.empty,
series = series ?? const <AnalysisSeriesPointUiModel>[],
averageScansPerDay = averageScansPerDay ?? 0,
averagePointsPerDay = averagePointsPerDay ?? 0,
pointsPerScan = pointsPerScan ?? 0,
activeUserRatePercent = activeUserRatePercent ?? 0;
final int? rangeDays;
final String? rangeLabel;
final AnalysisSummaryUiModel? summary;
final List<AnalysisSeriesPointUiModel>? series;
final double? averageScansPerDay;
final double? averagePointsPerDay;
final double? pointsPerScan;
final int? activeUserRatePercent;
}

View File

@ -0,0 +1,128 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../data/data_sources/analysis_local_data_source.dart';
import '../../data/repositories/analysis_repository_impl.dart';
import '../../domain/entities/analysis_content.dart';
import '../../domain/repositories/analysis_repository.dart';
import '../../domain/use_cases/get_analysis_content.dart';
import 'analysis_chart_metric.dart';
import 'analysis_range.dart';
import 'analysis_ui_state.dart';
final analysisRangeNotifierProvider =
NotifierProvider<AnalysisRangeNotifier, AnalysisRangePreset>(
AnalysisRangeNotifier.new,
);
final analysisChartMetricNotifierProvider =
NotifierProvider<AnalysisChartMetricNotifier, AnalysisChartMetric>(
AnalysisChartMetricNotifier.new,
);
class AnalysisRangeNotifier extends Notifier<AnalysisRangePreset> {
@override
AnalysisRangePreset build() => AnalysisRangePreset.last7Days;
void setRange(AnalysisRangePreset preset) => state = preset;
}
class AnalysisChartMetricNotifier extends Notifier<AnalysisChartMetric> {
@override
AnalysisChartMetric build() => AnalysisChartMetric.scans;
void setMetric(AnalysisChartMetric metric) => state = metric;
}
final _analysisLocalDataSourceProvider = Provider<AnalysisLocalDataSource>(
(ref) => const AnalysisLocalDataSourceImpl(),
);
final _analysisRepositoryProvider = Provider<AnalysisRepository>(
(ref) => AnalysisRepositoryImpl(ref.watch(_analysisLocalDataSourceProvider)),
);
final _getAnalysisContentProvider = Provider<GetAnalysisContent>(
(ref) => GetAnalysisContent(ref.watch(_analysisRepositoryProvider)),
);
final analysisViewModelProvider =
AsyncNotifierProvider<AnalysisViewModel, AnalysisUiState>(
AnalysisViewModel.new,
);
class AnalysisViewModel extends AsyncNotifier<AnalysisUiState> {
@override
Future<AnalysisUiState> build() async {
final rangePreset = ref.watch(analysisRangeNotifierProvider);
final content = await ref.watch(_getAnalysisContentProvider)(
rangeDays: rangePreset.days,
);
return _mapContentToUiState(
content: content,
rangeDays: rangePreset.days,
rangeLabel: rangePreset.label,
);
}
AnalysisUiState _mapContentToUiState({
required AnalysisContent content,
required int rangeDays,
required String rangeLabel,
}) {
final scans = content.summary.scans;
final pointsUsed = content.summary.pointsUsed;
final users = content.summary.users;
final activeUsers = content.summary.activeUsers;
final averageScansPerDay = scans / (rangeDays == 0 ? 1 : rangeDays);
final averagePointsPerDay = pointsUsed / (rangeDays == 0 ? 1 : rangeDays);
final double pointsPerScan = scans == 0
? 0
: (pointsUsed.toDouble() / scans.toDouble());
final activeUserRatePercent = users == 0
? 0
: ((activeUsers / users) * 100).round();
return AnalysisUiState(
rangeDays: rangeDays,
rangeLabel: rangeLabel,
summary: AnalysisSummaryUiModel(
users: content.summary.users,
activeUsers: content.summary.activeUsers,
scans: content.summary.scans,
successRatePercent: (content.summary.successRate * 100).round(),
pointsUsed: content.summary.pointsUsed,
),
series: content.series
.map(
(p) => AnalysisSeriesPointUiModel(
label: _labelForDate(p.date, rangeDays: rangeDays),
scanCount: p.scanCount,
pointsUsed: p.pointsUsed,
),
)
.toList(growable: false),
averageScansPerDay: averageScansPerDay,
averagePointsPerDay: averagePointsPerDay,
pointsPerScan: pointsPerScan,
activeUserRatePercent: activeUserRatePercent,
);
}
String _labelForDate(DateTime date, {required int rangeDays}) {
if (rangeDays <= 1) return 'Today';
if (rangeDays <= 7) {
return switch (date.weekday) {
DateTime.monday => 'Mon',
DateTime.tuesday => 'Tue',
DateTime.wednesday => 'Wed',
DateTime.thursday => 'Thu',
DateTime.friday => 'Fri',
DateTime.saturday => 'Sat',
DateTime.sunday => 'Sun',
_ => '',
};
}
return '${date.month}/${date.day}';
}
}

View File

@ -0,0 +1,578 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../manager/analysis_range.dart';
import '../manager/analysis_chart_metric.dart';
import '../manager/analysis_ui_state.dart';
import '../manager/analysis_view_model.dart';
import '../widgets/analysis_line_chart.dart';
class AnalysisPage extends ConsumerWidget {
const AnalysisPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final rangePreset = ref.watch(analysisRangeNotifierProvider);
final chartMetric = ref.watch(analysisChartMetricNotifierProvider);
final stateAsync = ref.watch(analysisViewModelProvider);
return Scaffold(
backgroundColor: theme.scaffoldBackgroundColor,
appBar: AppBar(
title: const Text('Analysis'),
backgroundColor: theme.scaffoldBackgroundColor,
surfaceTintColor: theme.scaffoldBackgroundColor,
elevation: 0,
scrolledUnderElevation: 0,
),
body: RefreshIndicator(
onRefresh: () async => ref.invalidate(analysisViewModelProvider),
child: ListView(
physics: const AlwaysScrollableScrollPhysics(),
padding: const EdgeInsets.fromLTRB(16, 12, 16, 24),
children: [
_RangeSelector(
selected: rangePreset,
onSelected: (preset) => ref
.read(analysisRangeNotifierProvider.notifier)
.setRange(preset),
),
const SizedBox(height: 10),
_ChartMetricSelector(
selected: chartMetric,
onSelected: (metric) => ref
.read(analysisChartMetricNotifierProvider.notifier)
.setMetric(metric),
),
const SizedBox(height: 12),
stateAsync.when(
data: (state) =>
_AnalysisBody(state: state, chartMetric: chartMetric),
loading: () => const _LoadingBody(),
error: (error, stackTrace) => _ErrorBody(
onRetry: () => ref.invalidate(analysisViewModelProvider),
),
),
],
),
),
);
}
}
class _RangeSelector extends StatelessWidget {
const _RangeSelector({required this.selected, required this.onSelected});
final AnalysisRangePreset selected;
final ValueChanged<AnalysisRangePreset> onSelected;
@override
Widget build(BuildContext context) {
return SegmentedButton<AnalysisRangePreset>(
segments: AnalysisRangePreset.values
.map(
(preset) => ButtonSegment(value: preset, label: Text(preset.label)),
)
.toList(growable: false),
selected: <AnalysisRangePreset>{selected},
onSelectionChanged: (selection) {
if (selection.isEmpty) return;
onSelected(selection.first);
},
);
}
}
class _AnalysisBody extends StatelessWidget {
const _AnalysisBody({required this.state, required this.chartMetric});
final AnalysisUiState state;
final AnalysisChartMetric chartMetric;
@override
Widget build(BuildContext context) {
final summary = state.summary ?? AnalysisSummaryUiModel.empty;
final series = state.series ?? const <AnalysisSeriesPointUiModel>[];
final rangeLabel = state.rangeLabel ?? '';
final values = series
.map(
(p) => switch (chartMetric) {
AnalysisChartMetric.scans => p.scanCount.toDouble(),
AnalysisChartMetric.pointsUsed => p.pointsUsed.toDouble(),
},
)
.toList(growable: false);
final totalValue = switch (chartMetric) {
AnalysisChartMetric.scans => summary.scans,
AnalysisChartMetric.pointsUsed => summary.pointsUsed,
};
final xLabels = series.map((p) => p.label).toList(growable: false);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_ChartCard(
title: chartMetric.label,
subtitle: rangeLabel,
totalValue: totalValue,
chart: AnalysisLineChart(values: values),
xLabels: xLabels,
),
const SizedBox(height: 12),
_InsightRow(state: state),
const SizedBox(height: 12),
_KpiGrid(summary: summary),
],
);
}
}
class _ChartCard extends StatelessWidget {
const _ChartCard({
required this.title,
required this.subtitle,
required this.totalValue,
required this.chart,
required this.xLabels,
});
final String title;
final String subtitle;
final int totalValue;
final Widget chart;
final List<String> xLabels;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
return Container(
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: colorScheme.outlineVariant.withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.06),
blurRadius: 14,
offset: const Offset(0, 6),
),
],
),
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
totalValue.toString(),
style: textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 4),
const SizedBox.shrink(),
],
),
],
),
const SizedBox(height: 12),
chart,
if (xLabels.isNotEmpty) ...[
const SizedBox(height: 10),
_XAxisLabels(labels: xLabels),
],
],
),
),
);
}
}
class _KpiGrid extends StatelessWidget {
const _KpiGrid({required this.summary});
final AnalysisSummaryUiModel summary;
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
childAspectRatio: 1.5,
children: [
_KpiCard(
title: 'Scans',
value: summary.scans.toString(),
icon: Icons.qr_code_scanner_rounded,
),
_KpiCard(
title: 'Users',
value: summary.users.toString(),
icon: Icons.people_alt_rounded,
),
_KpiCard(
title: 'Active Users',
value: summary.activeUsers.toString(),
icon: Icons.person_pin_circle_rounded,
),
_KpiCard(
title: 'Points Used',
value: summary.pointsUsed.toString(),
icon: Icons.stars_rounded,
),
_KpiCard(
title: 'Points/User',
value: summary.users == 0
? '0'
: (summary.pointsUsed / summary.users).toStringAsFixed(2),
icon: Icons.functions_rounded,
),
_KpiCard(
title: 'Success Rate',
value: '${summary.successRatePercent}%',
icon: Icons.verified_rounded,
),
],
);
}
}
class _ChartMetricSelector extends StatelessWidget {
const _ChartMetricSelector({
required this.selected,
required this.onSelected,
});
final AnalysisChartMetric selected;
final ValueChanged<AnalysisChartMetric> onSelected;
@override
Widget build(BuildContext context) {
return SegmentedButton<AnalysisChartMetric>(
segments: AnalysisChartMetric.values
.map(
(metric) => ButtonSegment(value: metric, label: Text(metric.label)),
)
.toList(growable: false),
selected: <AnalysisChartMetric>{selected},
onSelectionChanged: (selection) {
if (selection.isEmpty) return;
onSelected(selection.first);
},
);
}
}
class _XAxisLabels extends StatelessWidget {
const _XAxisLabels({required this.labels});
final List<String> labels;
@override
Widget build(BuildContext context) {
final textStyle = Theme.of(context).textTheme.labelSmall;
final onSurfaceVariant = Theme.of(context).colorScheme.onSurfaceVariant;
final count = labels.length;
final desiredTicks = 4;
final step = count <= desiredTicks ? 1 : (count - 1) ~/ (desiredTicks - 1);
final ticks = <int>{0, count - 1};
for (var i = 0; i < count; i += step) {
ticks.add(i);
}
return Row(
children: List.generate(count, (i) {
final show = ticks.contains(i);
return Expanded(
child: Align(
alignment: i == 0
? Alignment.centerLeft
: i == count - 1
? Alignment.centerRight
: Alignment.center,
child: Text(
show ? labels[i] : '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: textStyle?.copyWith(color: onSurfaceVariant),
),
),
);
}),
);
}
}
class _InsightRow extends StatelessWidget {
const _InsightRow({required this.state});
final AnalysisUiState state;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
final averageScansPerDay = state.averageScansPerDay ?? 0;
final averagePointsPerDay = state.averagePointsPerDay ?? 0;
final pointsPerScan = state.pointsPerScan ?? 0;
final activeUserRatePercent = state.activeUserRatePercent ?? 0;
return Wrap(
spacing: 12,
runSpacing: 12,
children:
[
_InsightCard(
title: 'Avg scans/day',
value: averageScansPerDay.toStringAsFixed(1),
icon: Icons.stacked_line_chart_rounded,
),
_InsightCard(
title: 'Avg points/day',
value: averagePointsPerDay.toStringAsFixed(1),
icon: Icons.trending_up_rounded,
),
_InsightCard(
title: 'Points/scan',
value: pointsPerScan.toStringAsFixed(2),
icon: Icons.bolt_rounded,
),
_InsightCard(
title: 'Active rate',
value: '$activeUserRatePercent%',
icon: Icons.percent_rounded,
),
]
.map((w) {
return SizedBox(
width: (MediaQuery.of(context).size.width - 16 * 2 - 12) / 2,
child: w,
);
})
.toList(growable: false),
);
}
}
class _InsightCard extends StatelessWidget {
const _InsightCard({
required this.title,
required this.value,
required this.icon,
});
final String title;
final String value;
final IconData icon;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
return Container(
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: colorScheme.outlineVariant.withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.06),
blurRadius: 14,
offset: const Offset(0, 6),
),
],
),
child: Padding(
padding: const EdgeInsets.fromLTRB(14, 14, 14, 14),
child: Row(
children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration(
color: colorScheme.primaryContainer,
shape: BoxShape.circle,
),
alignment: Alignment.center,
child: Icon(
icon,
color: colorScheme.onPrimaryContainer,
size: 20,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
value,
style: textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 2),
Text(
title,
style: textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
],
),
),
);
}
}
class _KpiCard extends StatelessWidget {
const _KpiCard({
required this.title,
required this.value,
required this.icon,
});
final String title;
final String value;
final IconData icon;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
return Container(
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: colorScheme.outlineVariant.withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.06),
blurRadius: 14,
offset: const Offset(0, 6),
),
],
),
child: Padding(
padding: const EdgeInsets.fromLTRB(14, 14, 14, 14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration(
color: colorScheme.primaryContainer,
shape: BoxShape.circle,
),
alignment: Alignment.center,
child: Icon(
icon,
color: colorScheme.onPrimaryContainer,
size: 20,
),
),
const Spacer(),
Text(
value,
style: textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 2),
Text(
title,
style: textTheme.bodySmall?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
],
),
),
);
}
}
class _LoadingBody extends StatelessWidget {
const _LoadingBody();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 40),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: const [
CircularProgressIndicator(),
SizedBox(height: 12),
Text('Loading analytics...'),
],
),
),
);
}
}
class _ErrorBody extends StatelessWidget {
const _ErrorBody({required this.onRetry});
final VoidCallback onRetry;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 40),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Unable to load analytics'),
const SizedBox(height: 10),
TextButton(onPressed: onRetry, child: const Text('Retry')),
],
),
),
);
}
}

View File

@ -0,0 +1,115 @@
import 'dart:math';
import 'package:flutter/material.dart';
class AnalysisLineChart extends StatelessWidget {
const AnalysisLineChart({super.key, required this.values, this.height = 140});
final List<double> values;
final double height;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return SizedBox(
height: height,
width: double.infinity,
child: CustomPaint(
painter: _LineChartPainter(
values: values,
lineColor: colorScheme.primary,
fillColor: colorScheme.primary.withOpacity(0.16),
gridColor: colorScheme.outlineVariant.withOpacity(0.35),
),
),
);
}
}
class _LineChartPainter extends CustomPainter {
_LineChartPainter({
required this.values,
required this.lineColor,
required this.fillColor,
required this.gridColor,
});
final List<double> values;
final Color lineColor;
final Color fillColor;
final Color gridColor;
@override
void paint(Canvas canvas, Size size) {
if (values.isEmpty) return;
final padding = const EdgeInsets.fromLTRB(4, 6, 4, 6);
final chartRect = Rect.fromLTWH(
padding.left,
padding.top,
max(0, size.width - padding.horizontal),
max(0, size.height - padding.vertical),
);
_paintGrid(canvas, chartRect);
final minValue = values.reduce(min);
final maxValue = values.reduce(max);
final span = max(1e-6, maxValue - minValue);
final dx = values.length == 1 ? 0.0 : chartRect.width / (values.length - 1);
Offset pointAt(int i) {
final x = chartRect.left + dx * i;
final normalized = (values[i] - minValue) / span;
final y = chartRect.bottom - normalized * chartRect.height;
return Offset(x, y);
}
final linePath = Path()..moveTo(pointAt(0).dx, pointAt(0).dy);
for (var i = 1; i < values.length; i++) {
final p = pointAt(i);
linePath.lineTo(p.dx, p.dy);
}
final fillPath = Path.from(linePath)
..lineTo(chartRect.right, chartRect.bottom)
..lineTo(chartRect.left, chartRect.bottom)
..close();
final fillPaint = Paint()..color = fillColor;
canvas.drawPath(fillPath, fillPaint);
final linePaint = Paint()
..color = lineColor
..style = PaintingStyle.stroke
..strokeWidth = 2.5
..strokeCap = StrokeCap.round;
canvas.drawPath(linePath, linePaint);
final last = pointAt(values.length - 1);
final dotPaint = Paint()..color = lineColor;
canvas.drawCircle(last, 4, dotPaint);
canvas.drawCircle(last, 8, Paint()..color = lineColor.withOpacity(0.16));
}
void _paintGrid(Canvas canvas, Rect rect) {
final paint = Paint()
..color = gridColor
..style = PaintingStyle.stroke
..strokeWidth = 1;
for (var i = 1; i <= 3; i++) {
final y = rect.top + rect.height * (i / 4);
canvas.drawLine(Offset(rect.left, y), Offset(rect.right, y), paint);
}
}
@override
bool shouldRepaint(covariant _LineChartPainter oldDelegate) {
return oldDelegate.values != values ||
oldDelegate.lineColor != lineColor ||
oldDelegate.fillColor != fillColor ||
oldDelegate.gridColor != gridColor;
}
}

View File

@ -0,0 +1,111 @@
import 'dart:math';
import '../../domain/entities/history_item.dart';
abstract class HistoryLocalDataSource {
List<HistoryItem> getHistory({int limit = 20});
}
class HistoryLocalDataSourceImpl implements HistoryLocalDataSource {
const HistoryLocalDataSourceImpl();
@override
List<HistoryItem> getHistory({int limit = 20}) {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
final rand = Random(99173 + today.millisecondsSinceEpoch);
const merchants = <String>[
'CB Prestige',
'Prestige Rewards',
'City Center',
'Junction Mall',
'Market Place',
'Downtown Branch',
'North Branch',
];
const buyItems = <String>[
'Coffee',
'Lunch',
'Movie Ticket',
'Grocery',
'Mobile Top-up',
'Taxi',
'Shopping',
'Snacks',
];
const promoItems = <String>[
'Promo Bonus',
'Referral Reward',
'Campaign Gift',
'Welcome Gift',
'Cashback Bonus',
];
// Build daily buckets so grouping looks realistic (multiple transactions/day).
final items = <HistoryItem>[];
var dayOffset = 0;
while (items.length < limit && dayOffset < 60) {
final date = today.subtract(Duration(days: dayOffset));
// More activity on recent days, less on older days.
final maxPerDay = dayOffset <= 1
? 6
: dayOffset <= 6
? 4
: 2;
final countForDay = 1 + rand.nextInt(maxPerDay);
for (var i = 0; i < countForDay && items.length < limit; i++) {
// Business-hour-ish distribution with some night activity.
final hour = rand.nextInt(100) < 82
? 8 + rand.nextInt(13)
: rand.nextInt(24);
final minute = rand.nextInt(60);
final occurredAt = date.add(Duration(hours: hour, minutes: minute));
final merchant = merchants[rand.nextInt(merchants.length)];
final isRedeem = rand.nextInt(100) < 65; // spending more common
final title = isRedeem
? buyItems[rand.nextInt(buyItems.length)]
: promoItems[rand.nextInt(promoItems.length)];
final amountAbs = isRedeem
? (500 + rand.nextInt(15000)).toDouble()
: (200 + rand.nextInt(6000)).toDouble();
final signedAmount = isRedeem ? -amountAbs : amountAbs;
items.add(
HistoryItem(
title: title,
subtitle: merchant,
occurredAt: occurredAt,
amount: signedAmount,
),
);
}
dayOffset++;
}
// Guarantee we have at least one earn and one redeem entry so both states show.
final hasRedeem = items.any((e) => e.amount < 0);
final hasEarn = items.any((e) => e.amount > 0);
if (items.isNotEmpty && (!hasRedeem || !hasEarn)) {
final first = items.first;
final flipToRedeem = !hasRedeem;
items[0] = HistoryItem(
title: first.title,
subtitle: first.subtitle,
occurredAt: first.occurredAt,
amount: flipToRedeem ? -first.amount.abs() : first.amount.abs(),
);
}
items.sort((a, b) => b.occurredAt.compareTo(a.occurredAt));
return items;
}
}

View File

@ -0,0 +1,15 @@
import '../../domain/entities/history_content.dart';
import '../../domain/repositories/history_repository.dart';
import '../data_sources/history_local_data_source.dart';
class HistoryRepositoryImpl implements HistoryRepository {
const HistoryRepositoryImpl(this._localDataSource);
final HistoryLocalDataSource _localDataSource;
@override
Future<HistoryContent> getHistory({int limit = 20}) async {
final items = _localDataSource.getHistory(limit: limit);
return HistoryContent(items: items);
}
}

View File

@ -0,0 +1,7 @@
import 'history_item.dart';
class HistoryContent {
const HistoryContent({required this.items});
final List<HistoryItem> items;
}

View File

@ -0,0 +1,13 @@
class HistoryItem {
const HistoryItem({
required this.title,
required this.subtitle,
required this.occurredAt,
required this.amount,
});
final String title;
final String subtitle;
final DateTime occurredAt;
final double amount;
}

View File

@ -0,0 +1,5 @@
import '../entities/history_content.dart';
abstract class HistoryRepository {
Future<HistoryContent> getHistory({int limit = 20});
}

View File

@ -0,0 +1,12 @@
import '../entities/history_content.dart';
import '../repositories/history_repository.dart';
class GetHistory {
const GetHistory(this._repository);
final HistoryRepository _repository;
Future<HistoryContent> call({int limit = 20}) {
return _repository.getHistory(limit: limit);
}
}

View File

@ -0,0 +1,41 @@
import 'package:flutter/foundation.dart';
@immutable
class HistoryItemUiModel {
const HistoryItemUiModel({
required this.title,
required this.subtitle,
required this.timeLabel,
required this.amountLabel,
this.isRedeem,
});
final String title;
final String subtitle;
final String timeLabel;
final String amountLabel;
final bool? isRedeem;
}
@immutable
class HistorySectionUiModel {
const HistorySectionUiModel({
required this.dateLabel,
required this.totalAmountLabel,
required this.totalTransactionsLabel,
required this.items,
});
final String dateLabel;
final String totalAmountLabel;
final String totalTransactionsLabel;
final List<HistoryItemUiModel> items;
}
@immutable
class HistoryUiState {
const HistoryUiState({List<HistorySectionUiModel>? sections})
: sections = sections ?? const <HistorySectionUiModel>[];
final List<HistorySectionUiModel>? sections;
}

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