Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c51c678e99 | |||
|
|
23c48a068f | ||
|
|
c95471987e | ||
|
|
10664ee9fa | ||
|
|
6600766724 | ||
|
|
398294df42 | ||
|
|
ad3276c898 | ||
|
|
12b385d7e0 | ||
|
|
085a7648f3 | ||
| c183d84a86 | |||
| fec555991d | |||
| 9c73361a2f | |||
| 3df1be39a8 | |||
| 503f66756e | |||
| a927f3ce0e | |||
| f7cebd9371 | |||
| 8b515a3c27 | |||
|
|
774da7ad6c | ||
|
|
2d645274a9 | ||
|
|
b3d927662c | ||
|
|
190cbcc0ea | ||
|
|
dfec3fd9e2 | ||
|
|
c23ce74bc4 |
@ -1,3 +0,0 @@
|
|||||||
APPWRITE_PROJECT_ID=
|
|
||||||
APPWRITE_PROJECT_NAME=
|
|
||||||
APPWRITE_PUBLIC_ENDPOINT=
|
|
||||||
7
.envExample
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
APPWRITE_PROJECT_ID=<APPWRITE_PROJECT_ID>
|
||||||
|
APPWRITE_PROJECT_NAME=<Flutter Projects Name>
|
||||||
|
APPWRITE_PUBLIC_ENDPOINT=<YOUR_API_ENDPOINT/v1>
|
||||||
|
PTV_GEOLINK_API_KEY=<YOUR_GEOLINK_API_KEY>
|
||||||
|
APPWRITE_DATABASE_ID=<DatabaseID>
|
||||||
|
APPWRITE_COLLECTION_ID=<Collection_ID>
|
||||||
|
TANKSTOPS_BASE_URL=https://api.e-control.at/sprit/1.0/search/gas-stations/by-address?latitude=<lat>&longitude=<lon>&fuelType=DIE<SUPSuperOrDIEDiesel>&includeClosed=false
|
||||||
@ -14,7 +14,7 @@ This guide will help you quickly set up, customize, and build your Flutter app.
|
|||||||
Clone this repository to your local machine using Git or directly from `Android Studio`:
|
Clone this repository to your local machine using Git or directly from `Android Studio`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/appwrite/starter-for-flutter
|
git clone https://gitea.joshihomeserver.ipv64.net/josiadmin/MyNewAppWriteTankApp.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, open the repository URL in `Android Studio` to clone it directly.
|
Alternatively, open the repository URL in `Android Studio` to clone it directly.
|
||||||
|
|||||||
@ -7,6 +7,11 @@
|
|||||||
|
|
||||||
# The following line activates a set of recommended lints for Flutter apps,
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
# packages, and plugins designed to encourage good coding practices.
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
analyzer:
|
||||||
|
errors:
|
||||||
|
unused_field: ignore
|
||||||
|
avoid_print: ignore
|
||||||
|
unnecessary_overrides: ignore
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
|
|||||||
@ -5,8 +5,8 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
ndkVersion = "25.1.8937393"
|
ndkVersion = "27.0.12077973"
|
||||||
namespace = "io.appwrite.flutter"
|
namespace = "com.example.flutter_new_tank_app310725"
|
||||||
compileSdk = flutter.compileSdkVersion
|
compileSdk = flutter.compileSdkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@ -19,7 +19,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "io.appwrite.flutter"
|
applicationId = "com.example.flutter_new_tank_app310725"
|
||||||
|
|
||||||
minSdk = flutter.minSdkVersion
|
minSdk = flutter.minSdkVersion
|
||||||
targetSdk = flutter.targetSdkVersion
|
targetSdk = flutter.targetSdkVersion
|
||||||
|
|||||||
@ -1,11 +1,18 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<application
|
<application
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="AppwriteStarterKit">
|
android:label="Tank Guru">
|
||||||
|
<service android:name="com.baseflow.geolocator.GeolocatorLocationService"
|
||||||
|
android:foregroundServiceType="location" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package io.appwrite.flutter
|
package com.example.flutter_new_tank_app310725
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 69 B |
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item>
|
|
||||||
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
|
||||||
</item>
|
|
||||||
<item android:bottom="24dp">
|
|
||||||
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
|
|
||||||
</item>
|
|
||||||
</layer-list>
|
|
||||||
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 69 B |
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item>
|
|
||||||
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
|
||||||
</item>
|
|
||||||
<item android:bottom="24dp">
|
|
||||||
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
|
|
||||||
</item>
|
|
||||||
</layer-list>
|
|
||||||
|
Before Width: | Height: | Size: 69 B |
@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item>
|
<item android:drawable="?android:colorBackground" />
|
||||||
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
|
||||||
</item>
|
<!-- You can insert your own image assets here -->
|
||||||
<item>
|
<!-- <item>
|
||||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
<bitmap
|
||||||
</item>
|
android:gravity="center"
|
||||||
<item android:bottom="24dp">
|
android:src="@mipmap/launch_image" />
|
||||||
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
|
</item> -->
|
||||||
</item>
|
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 69 B |
@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item>
|
<item android:drawable="@android:color/white" />
|
||||||
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
|
||||||
</item>
|
<!-- You can insert your own image assets here -->
|
||||||
<item>
|
<!-- <item>
|
||||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
<bitmap
|
||||||
</item>
|
android:gravity="center"
|
||||||
<item android:bottom="24dp">
|
android:src="@mipmap/launch_image" />
|
||||||
<bitmap android:gravity="bottom" android:src="@drawable/branding"/>
|
</item> -->
|
||||||
</item>
|
|
||||||
</layer-list>
|
</layer-list>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 44 KiB |
@ -1,21 +0,0 @@
|
|||||||
<?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">
|
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
|
||||||
<item name="android:windowFullscreen">true</item>
|
|
||||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
|
||||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
|
||||||
<item name="android:windowSplashScreenBackground">#19191D</item>
|
|
||||||
<item name="android:windowSplashScreenIconBackgroundColor">#19191D</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>
|
|
||||||
@ -5,10 +5,6 @@
|
|||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
the Flutter engine draws its first frame -->
|
the Flutter engine draws its first frame -->
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
|
||||||
<item name="android:windowFullscreen">true</item>
|
|
||||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
|
||||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
|
||||||
</style>
|
</style>
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
This theme determines the color of the Android Window while your
|
This theme determines the color of the Android Window while your
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
<?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">
|
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
|
||||||
<item name="android:windowFullscreen">true</item>
|
|
||||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
|
||||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
|
||||||
<item name="android:windowSplashScreenBackground">#EDEDF0</item>
|
|
||||||
<item name="android:windowSplashScreenIconBackgroundColor">#EDEDF0</item>
|
|
||||||
</style>
|
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
|
||||||
This theme determines the color of the Android Window while your
|
|
||||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
|
||||||
running.
|
|
||||||
|
|
||||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
|
||||||
<item name="android:windowBackground">?android:colorBackground</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
||||||
@ -2,14 +2,16 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
<!-- 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">
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
<item name="android:windowTranslucentStatus">true</item>
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
the Flutter engine draws its first frame -->
|
||||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
|
||||||
<item name="android:windowFullscreen">true</item>
|
|
||||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
|
||||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
|
||||||
</style>
|
</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">
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
<item name="android:windowBackground">?android:colorBackground</item>
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
||||||
|
|||||||
33
build.sh
Normal file → Executable file
@ -1,4 +1,29 @@
|
|||||||
flutter build web \
|
#!/bin/bash
|
||||||
--dart-define=APPWRITE_PROJECT_ID="$APPWRITE_PROJECT_ID" \
|
|
||||||
--dart-define=APPWRITE_PROJECT_NAME="$APPWRITE_PROJECT_NAME" \
|
# Check if .env file exists
|
||||||
--dart-define=APPWRITE_PUBLIC_ENDPOINT="$APPWRITE_PUBLIC_ENDPOINT"
|
if [ ! -f .env ]; then
|
||||||
|
{
|
||||||
|
echo "APPWRITE_PROJECT_ID=$APPWRITE_PROJECT_ID"
|
||||||
|
echo "APPWRITE_PROJECT_NAME=$APPWRITE_PROJECT_NAME"
|
||||||
|
echo "APPWRITE_PUBLIC_ENDPOINT=$APPWRITE_PUBLIC_ENDPOINT"
|
||||||
|
} >> .env
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read .env file and convert it to --dart-define arguments
|
||||||
|
ARGS=""
|
||||||
|
while IFS='=' read -r key value || [ -n "$key" ]; do
|
||||||
|
# Ignore empty lines and comments
|
||||||
|
if [[ -n "$key" && ! "$key" =~ ^# ]]; then
|
||||||
|
ARGS+=" --dart-define=${key}=\"${value}\""
|
||||||
|
fi
|
||||||
|
done < .env
|
||||||
|
|
||||||
|
# Build Flutter web
|
||||||
|
eval flutter build web "$ARGS"
|
||||||
|
|
||||||
|
# If --preview flag is provided, run a local preview server
|
||||||
|
if [ "$1" == "--preview" ]; then
|
||||||
|
echo "Starting preview server at http://localhost:3000..."
|
||||||
|
cd build/web || exit 1
|
||||||
|
python3 -m http.server 3000
|
||||||
|
fi
|
||||||
|
|||||||
@ -21,6 +21,6 @@
|
|||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
<string>12.0</string>
|
<string>13.0</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# Uncomment this line to define a global platform for your project
|
# Uncomment this line to define a global platform for your project
|
||||||
# platform :ios, '12.0'
|
# platform :ios, '13.0'
|
||||||
|
|
||||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|||||||
@ -6,11 +6,16 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- flutter_web_auth_2 (3.0.0):
|
- flutter_web_auth_2 (3.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- geolocator_apple (1.2.0):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- printing (1.0.0):
|
||||||
|
- Flutter
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
||||||
@ -19,8 +24,10 @@ DEPENDENCIES:
|
|||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||||
- flutter_web_auth_2 (from `.symlinks/plugins/flutter_web_auth_2/ios`)
|
- flutter_web_auth_2 (from `.symlinks/plugins/flutter_web_auth_2/ios`)
|
||||||
|
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
|
- printing (from `.symlinks/plugins/printing/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
@ -32,22 +39,28 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||||
flutter_web_auth_2:
|
flutter_web_auth_2:
|
||||||
:path: ".symlinks/plugins/flutter_web_auth_2/ios"
|
:path: ".symlinks/plugins/flutter_web_auth_2/ios"
|
||||||
|
geolocator_apple:
|
||||||
|
:path: ".symlinks/plugins/geolocator_apple/darwin"
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
|
printing:
|
||||||
|
:path: ".symlinks/plugins/printing/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
flutter_native_splash: f71420956eb811e6d310720fee915f1d42852e7a
|
flutter_native_splash: 6cad9122ea0fad137d23137dd14b937f3e90b145
|
||||||
flutter_web_auth_2: 06d500582775790a0d4c323222fcb6d7990f9603
|
flutter_web_auth_2: 5c8d9dcd7848b5a9efb086d24e7a9adcae979c80
|
||||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||||
|
printing: 54ff03f28fe9ba3aa93358afb80a8595a071dd07
|
||||||
|
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||||
|
|
||||||
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
|
PODFILE CHECKSUM: 4f1c12611da7338d21589c0b2ecd6bd20b109694
|
||||||
|
|
||||||
COCOAPODS: 1.16.2
|
COCOAPODS: 1.16.2
|
||||||
|
|||||||
@ -454,10 +454,11 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
@ -465,21 +466,21 @@
|
|||||||
};
|
};
|
||||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
baseConfigurationReference = 9AAEF3FADF29177722D56E5C /* Pods-Runner.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = Y9G8UWM9SH;
|
DEVELOPMENT_TEAM = NTNRKTMT87;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.appwrite.flutter;
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flutter_new_tank_app310725;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@ -497,7 +498,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.appwrite.flutter.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flutter_new_tank_app310725.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
@ -515,7 +516,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.appwrite.flutter.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flutter_new_tank_app310725.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
@ -531,7 +532,7 @@
|
|||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.appwrite.flutter.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flutter_new_tank_app310725.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||||
@ -587,7 +588,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@ -638,10 +639,11 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
@ -658,14 +660,14 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = Y9G8UWM9SH;
|
DEVELOPMENT_TEAM = NTNRKTMT87;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.appwrite.flutter;
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flutter_new_tank_app310725;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@ -677,21 +679,21 @@
|
|||||||
};
|
};
|
||||||
97C147071CF9000F007C117D /* Release */ = {
|
97C147071CF9000F007C117D /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
baseConfigurationReference = 6CF6DACC519FF36038FF3CFC /* Pods-Runner.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
DEVELOPMENT_TEAM = Y9G8UWM9SH;
|
DEVELOPMENT_TEAM = NTNRKTMT87;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = io.appwrite.flutter;
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.flutter_new_tank_app310725;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@ -711,7 +713,7 @@
|
|||||||
331C808A294A63A400263BE5 /* Profile */,
|
331C808A294A63A400263BE5 /* Profile */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Profile;
|
||||||
};
|
};
|
||||||
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
@ -721,7 +723,7 @@
|
|||||||
249021D3217E4FDB00AE95B9 /* Profile */,
|
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Profile;
|
||||||
};
|
};
|
||||||
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
@ -731,7 +733,7 @@
|
|||||||
249021D4217E4FDB00AE95B9 /* Profile */,
|
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||||
);
|
);
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Profile;
|
||||||
};
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
@ -54,11 +55,13 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
|
enableGPUValidationMode = "1"
|
||||||
allowLocationSimulation = "YES">
|
allowLocationSimulation = "YES">
|
||||||
<BuildableProductRunnable
|
<BuildableProductRunnable
|
||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
|
|||||||
@ -2,6 +2,14 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string>This app needs access to your location to find nearby gas stations and services.</string>
|
||||||
|
<key>NSLocationAlwaysUsageDescription</key>
|
||||||
|
<string>This app needs access to your location to track your position even when the app is in the background.</string>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>location</string>
|
||||||
|
</array>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
|||||||
15
lib/app.dart
@ -1,19 +1,24 @@
|
|||||||
import 'package:appwrite_flutter_starter_kit/home.dart';
|
//import 'pages/appwritetest/home.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import './utils/extensions/sample_routes.dart';
|
||||||
|
import './pages/login/login_view.dart';
|
||||||
|
|
||||||
class AppwriteApp extends StatelessWidget {
|
class AppwriteApp extends StatelessWidget {
|
||||||
const AppwriteApp({super.key});
|
const AppwriteApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return GetMaterialApp(
|
||||||
title: 'Appwrite StarterKit',
|
title: 'Tank Guru AppWrite',
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
colorScheme: ColorScheme.dark(),
|
||||||
),
|
),
|
||||||
home: const AppwriteStarterKit(),
|
initialRoute: LoginPage.namedRoute,
|
||||||
|
getPages: SampleRouts.samplePages,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
lib/data/models/chart_model.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
class ChartData {
|
||||||
|
final double dateInMilliseconds;
|
||||||
|
final double liters;
|
||||||
|
final double pricePerLiter;
|
||||||
|
final double totalPrice;
|
||||||
|
|
||||||
|
ChartData({
|
||||||
|
required this.dateInMilliseconds,
|
||||||
|
required this.liters,
|
||||||
|
required this.pricePerLiter,
|
||||||
|
required this.totalPrice,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class PricePoints {
|
||||||
|
final double x;
|
||||||
|
final double y;
|
||||||
|
|
||||||
|
PricePoints({required this.x, required this.y});
|
||||||
|
}
|
||||||
|
|
||||||
|
class SumDataModel {
|
||||||
|
late String? szMonth;
|
||||||
|
late String? szSumme;
|
||||||
|
late String? szVerbrauch;
|
||||||
|
late int? mnTankungen;
|
||||||
|
|
||||||
|
SumDataModel(this.szMonth, this.szSumme, this.szVerbrauch, this.mnTankungen);
|
||||||
|
}
|
||||||
|
|
||||||
|
class YearModel {
|
||||||
|
final String szDescription;
|
||||||
|
final int mnYear;
|
||||||
|
|
||||||
|
YearModel(this.szDescription, this.mnYear);
|
||||||
|
}
|
||||||
294
lib/data/models/gas_model.dart
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
// ignore_for_file: unnecessary_this
|
||||||
|
|
||||||
|
class GasModel {
|
||||||
|
int? id;
|
||||||
|
String? name;
|
||||||
|
Location? location;
|
||||||
|
Contact? contact;
|
||||||
|
List<OpeningHours>? openingHours;
|
||||||
|
OfferInformation? offerInformation;
|
||||||
|
PaymentMethods? paymentMethods;
|
||||||
|
PaymentArrangements? paymentArrangements;
|
||||||
|
int? position;
|
||||||
|
bool? open;
|
||||||
|
double? distance;
|
||||||
|
List<Prices>? prices;
|
||||||
|
|
||||||
|
GasModel(
|
||||||
|
{int? id,
|
||||||
|
String? name,
|
||||||
|
Location? location,
|
||||||
|
Contact? contact,
|
||||||
|
List<OpeningHours>? openingHours,
|
||||||
|
OfferInformation? offerInformation,
|
||||||
|
PaymentMethods? paymentMethods,
|
||||||
|
PaymentArrangements? paymentArrangements,
|
||||||
|
int? position,
|
||||||
|
bool? open,
|
||||||
|
double? distance,
|
||||||
|
List<Prices>? prices}) {
|
||||||
|
if (id != null) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
if (name != null) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
if (location != null) {
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
if (contact != null) {
|
||||||
|
this.contact = contact;
|
||||||
|
}
|
||||||
|
if (openingHours != null) {
|
||||||
|
this.openingHours = openingHours;
|
||||||
|
}
|
||||||
|
if (offerInformation != null) {
|
||||||
|
this.offerInformation = offerInformation;
|
||||||
|
}
|
||||||
|
if (paymentMethods != null) {
|
||||||
|
this.paymentMethods = paymentMethods;
|
||||||
|
}
|
||||||
|
if (paymentArrangements != null) {
|
||||||
|
this.paymentArrangements = paymentArrangements;
|
||||||
|
}
|
||||||
|
if (position != null) {
|
||||||
|
this.position = position;
|
||||||
|
|
||||||
|
if (open != null) {
|
||||||
|
this.open = open;
|
||||||
|
}
|
||||||
|
if (distance != null) {
|
||||||
|
this.distance = distance;
|
||||||
|
}
|
||||||
|
if (prices != null) {
|
||||||
|
this.prices = prices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GasModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
id = json['id'];
|
||||||
|
name = json['name'];
|
||||||
|
location =
|
||||||
|
json['location'] != null ? Location.fromJson(json['location']) : null;
|
||||||
|
contact =
|
||||||
|
json['contact'] != null ? Contact.fromJson(json['contact']) : null;
|
||||||
|
if (json['openingHours'] != null) {
|
||||||
|
openingHours = <OpeningHours>[];
|
||||||
|
json['openingHours'].forEach((v) {
|
||||||
|
openingHours!.add(OpeningHours.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
offerInformation = json['offerInformation'] != null
|
||||||
|
? OfferInformation.fromJson(json['offerInformation'])
|
||||||
|
: null;
|
||||||
|
paymentMethods = json['paymentMethods'] != null
|
||||||
|
? PaymentMethods.fromJson(json['paymentMethods'])
|
||||||
|
: null;
|
||||||
|
paymentArrangements = json['paymentArrangements'] != null
|
||||||
|
? PaymentArrangements.fromJson(json['paymentArrangements'])
|
||||||
|
: null;
|
||||||
|
position = json['position'];
|
||||||
|
open = json['open'];
|
||||||
|
distance = json['distance'];
|
||||||
|
if (json['prices'] != null) {
|
||||||
|
prices = <Prices>[];
|
||||||
|
json['prices'].forEach((v) {
|
||||||
|
prices!.add(Prices.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Location {
|
||||||
|
String? address;
|
||||||
|
String? postalCode;
|
||||||
|
String? city;
|
||||||
|
double? latitude;
|
||||||
|
double? longitude;
|
||||||
|
|
||||||
|
Location(
|
||||||
|
{String? address,
|
||||||
|
String? postalCode,
|
||||||
|
String? city,
|
||||||
|
double? latitude,
|
||||||
|
double? longitude}) {
|
||||||
|
if (address != null) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
if (postalCode != null) {
|
||||||
|
this.postalCode = postalCode;
|
||||||
|
}
|
||||||
|
if (city != null) {
|
||||||
|
this.city = city;
|
||||||
|
}
|
||||||
|
if (latitude != null) {
|
||||||
|
this.latitude = latitude;
|
||||||
|
}
|
||||||
|
if (longitude != null) {
|
||||||
|
this.longitude = longitude;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Location.fromJson(Map<String, dynamic> json) {
|
||||||
|
address = json['address'];
|
||||||
|
postalCode = json['postalCode'];
|
||||||
|
city = json['city'];
|
||||||
|
latitude = json['latitude'];
|
||||||
|
longitude = json['longitude'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Contact {
|
||||||
|
String? telephone;
|
||||||
|
String? website;
|
||||||
|
|
||||||
|
Contact({String? telephone, String? website}) {
|
||||||
|
if (telephone != null) {
|
||||||
|
this.telephone = telephone;
|
||||||
|
}
|
||||||
|
if (website != null) {
|
||||||
|
this.website = website;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Contact.fromJson(Map<String, dynamic> json) {
|
||||||
|
telephone = json['telephone'];
|
||||||
|
website = json['website'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpeningHours {
|
||||||
|
String? day;
|
||||||
|
String? label;
|
||||||
|
int? order;
|
||||||
|
String? from;
|
||||||
|
String? to;
|
||||||
|
|
||||||
|
OpeningHours(
|
||||||
|
{String? day, String? label, int? order, String? from, String? to}) {
|
||||||
|
if (day != null) {
|
||||||
|
this.day = day;
|
||||||
|
}
|
||||||
|
if (label != null) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
if (order != null) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
if (from != null) {
|
||||||
|
this.from = from;
|
||||||
|
}
|
||||||
|
if (to != null) {
|
||||||
|
this.to = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OpeningHours.fromJson(Map<String, dynamic> json) {
|
||||||
|
day = json['day'];
|
||||||
|
label = json['label'];
|
||||||
|
order = json['order'];
|
||||||
|
from = json['from'];
|
||||||
|
to = json['to'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OfferInformation {
|
||||||
|
bool? service;
|
||||||
|
bool? selfService;
|
||||||
|
bool? unattended;
|
||||||
|
|
||||||
|
OfferInformation({bool? service, bool? selfService, bool? unattended}) {
|
||||||
|
if (service != null) {
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
if (selfService != null) {
|
||||||
|
this.selfService = selfService;
|
||||||
|
}
|
||||||
|
if (unattended != null) {
|
||||||
|
this.unattended = unattended;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OfferInformation.fromJson(Map<String, dynamic> json) {
|
||||||
|
service = json['service'];
|
||||||
|
selfService = json['selfService'];
|
||||||
|
unattended = json['unattended'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentMethods {
|
||||||
|
bool? cash;
|
||||||
|
bool? debitCard;
|
||||||
|
bool? creditCard;
|
||||||
|
String? others;
|
||||||
|
|
||||||
|
PaymentMethods( {bool? cash, bool? debitCard, bool? creditCard, String? others}) {
|
||||||
|
if (cash != null) {
|
||||||
|
this.cash = cash;
|
||||||
|
}
|
||||||
|
if (debitCard != null) {
|
||||||
|
this.debitCard = debitCard;
|
||||||
|
}
|
||||||
|
if (creditCard != null) {
|
||||||
|
this.creditCard = creditCard;
|
||||||
|
}
|
||||||
|
if (others != null) {
|
||||||
|
this.others = others;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PaymentMethods.fromJson(Map<String, dynamic> json) {
|
||||||
|
cash = json['cash'];
|
||||||
|
debitCard = json['debitCard'];
|
||||||
|
creditCard = json['creditCard'];
|
||||||
|
others = json['others'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PaymentArrangements {
|
||||||
|
bool? cooperative;
|
||||||
|
bool? clubCard;
|
||||||
|
|
||||||
|
PaymentArrangements({bool? cooperative, bool? clubCard}) {
|
||||||
|
if (cooperative != null) {
|
||||||
|
this.cooperative = cooperative;
|
||||||
|
}
|
||||||
|
if (clubCard != null) {
|
||||||
|
this.clubCard = clubCard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PaymentArrangements.fromJson(Map<String, dynamic> json) {
|
||||||
|
cooperative = json['cooperative'];
|
||||||
|
clubCard = json['clubCard'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Prices {
|
||||||
|
String? fuelType;
|
||||||
|
double? amount;
|
||||||
|
String? label;
|
||||||
|
|
||||||
|
Prices({String? fuelType, double? amount, String? label}) {
|
||||||
|
if (fuelType != null) {
|
||||||
|
this.fuelType = fuelType;
|
||||||
|
}
|
||||||
|
if (amount != null) {
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
if (label != null) {
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Prices.fromJson(Map<String, dynamic> json) {
|
||||||
|
fuelType = json['fuelType'];
|
||||||
|
amount = json['amount'];
|
||||||
|
label = json['label'];
|
||||||
|
}
|
||||||
|
}
|
||||||
10
lib/data/models/login_model.dart
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
class AppWriteLoginModel {
|
||||||
|
String clientName;
|
||||||
|
String clientId;
|
||||||
|
|
||||||
|
AppWriteLoginModel({
|
||||||
|
this.clientName ='',
|
||||||
|
this.clientId='',
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
15
lib/data/models/map_model.dart
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
class AppWriteMapTrackPointsModel {
|
||||||
|
String szUserId;
|
||||||
|
String szAppWriteTrackUniqueId;
|
||||||
|
double mnLongitudeTrackPoint;
|
||||||
|
double mnLatitudeTrackPoint;
|
||||||
|
String szDateTimeTrackPoint;
|
||||||
|
|
||||||
|
AppWriteMapTrackPointsModel({
|
||||||
|
required this.szUserId,
|
||||||
|
required this.szAppWriteTrackUniqueId,
|
||||||
|
required this.mnLongitudeTrackPoint,
|
||||||
|
required this.mnLatitudeTrackPoint,
|
||||||
|
required this.szDateTimeTrackPoint,
|
||||||
|
});
|
||||||
|
}
|
||||||
58
lib/data/models/tank_model.dart
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
class AppWriteTankModel {
|
||||||
|
String documentId;
|
||||||
|
String userId;
|
||||||
|
String date;
|
||||||
|
String odometer;
|
||||||
|
String liters;
|
||||||
|
String pricePerLiter;
|
||||||
|
String location;
|
||||||
|
String? imageFileId;
|
||||||
|
String? imageFileName;
|
||||||
|
String? imageFileUrl;
|
||||||
|
int? mnIndexCount;
|
||||||
|
String? szSummePreis;
|
||||||
|
|
||||||
|
AppWriteTankModel({
|
||||||
|
required this.documentId,
|
||||||
|
required this.userId,
|
||||||
|
required this.date,
|
||||||
|
required this.odometer,
|
||||||
|
required this.liters,
|
||||||
|
required this.pricePerLiter,
|
||||||
|
required this.location,
|
||||||
|
this.imageFileId,
|
||||||
|
this.imageFileName,
|
||||||
|
this.imageFileUrl,
|
||||||
|
}):szSummePreis = (double.tryParse(liters) != null && double.tryParse(pricePerLiter) != null)
|
||||||
|
? (double.parse(liters) * double.parse(pricePerLiter)).toStringAsFixed(2)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
factory AppWriteTankModel.fromMap(Map<String, dynamic> map) {
|
||||||
|
return AppWriteTankModel(
|
||||||
|
documentId: map['\$id'] ?? '',
|
||||||
|
userId: map['userId'] ?? '',
|
||||||
|
date: map['date'] ?? '',
|
||||||
|
odometer: map['odometer']?.toString() ?? '',
|
||||||
|
liters: map['liters']?.toString() ?? '',
|
||||||
|
pricePerLiter: map['pricePerLiter']?.toString() ?? '',
|
||||||
|
location: map['location'] ?? '',
|
||||||
|
imageFileId: map['imageFileId'],
|
||||||
|
imageFileName: map['imageFileName'],
|
||||||
|
imageFileUrl: map['imageFileUrl'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'\$id': documentId,
|
||||||
|
'userId': userId,
|
||||||
|
'date': date,
|
||||||
|
'odometer': odometer,
|
||||||
|
'liters': liters,
|
||||||
|
'pricePerLiter': pricePerLiter,
|
||||||
|
'location': location,
|
||||||
|
'imageFileId': imageFileId,
|
||||||
|
'imageFileName': imageFileName,
|
||||||
|
'imageFileUrl': imageFileUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,20 +1,22 @@
|
|||||||
|
import 'package:appwrite/models.dart' as models;
|
||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:appwrite/appwrite.dart';
|
import 'package:appwrite/appwrite.dart';
|
||||||
import 'package:appwrite_flutter_starter_kit/data/models/log.dart';
|
import '../../data/models/log.dart';
|
||||||
import 'package:appwrite_flutter_starter_kit/data/models/project_info.dart';
|
import '../../data/models/project_info.dart';
|
||||||
|
|
||||||
/// A repository responsible for handling network interactions with the Appwrite server.
|
/// A repository responsible for handling network interactions with the Appwrite server.
|
||||||
///
|
///
|
||||||
/// It provides a helper method to ping the server.
|
/// It provides a helper method to ping the server.
|
||||||
class AppwriteRepository {
|
class AppwriteRepository {
|
||||||
static const String pingPath = "/ping";
|
static const String pingPath = "/ping";
|
||||||
static const String appwriteProjectId = String.fromEnvironment('APPWRITE_PROJECT_ID');
|
// static const String appwriteProjectId = String.fromEnvironment('APPWRITE_PROJECT_ID');
|
||||||
static const String appwriteProjectName = String.fromEnvironment('APPWRITE_PROJECT_NAME');
|
// static const String appwriteProjectName = String.fromEnvironment('APPWRITE_PROJECT_NAME');
|
||||||
static const String appwritePublicEndpoint = String.fromEnvironment('APPWRITE_PUBLIC_ENDPOINT');
|
// static const String appwritePublicEndpoint = String.fromEnvironment('APPWRITE_PUBLIC_ENDPOINT');
|
||||||
|
|
||||||
final Client _client = Client()
|
final Client _client = Client()
|
||||||
.setProject(appwriteProjectId)
|
.setProject(dotenv.get('APPWRITE_PROJECT_ID'))
|
||||||
.setEndpoint(appwritePublicEndpoint);
|
.setEndpoint(dotenv.get('APPWRITE_PUBLIC_ENDPOINT'));
|
||||||
|
|
||||||
late final Account _account;
|
late final Account _account;
|
||||||
late final Databases _databases;
|
late final Databases _databases;
|
||||||
@ -31,9 +33,9 @@ class AppwriteRepository {
|
|||||||
|
|
||||||
ProjectInfo getProjectInfo() {
|
ProjectInfo getProjectInfo() {
|
||||||
return ProjectInfo(
|
return ProjectInfo(
|
||||||
endpoint: appwritePublicEndpoint,
|
endpoint: dotenv.get('APPWRITE_PUBLIC_ENDPOINT'),
|
||||||
projectId: appwriteProjectId,
|
projectId: dotenv.get('APPWRITE_PROJECT_ID'),
|
||||||
projectName: appwriteProjectName,
|
projectName: dotenv.get('APPWRITE_PROJECT_NAME'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,4 +70,64 @@ class AppwriteRepository {
|
|||||||
String _getCurrentDate() {
|
String _getCurrentDate() {
|
||||||
return DateFormat("MMM dd, HH:mm").format(DateTime.now());
|
return DateFormat("MMM dd, HH:mm").format(DateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<dynamic> logout() async =>
|
||||||
|
await _account.deleteSession(sessionId: 'current');
|
||||||
|
|
||||||
|
Future<models.Session> login(Map map) async =>
|
||||||
|
await _account.createEmailPasswordSession(
|
||||||
|
email: map['email'],
|
||||||
|
password: map['password'],
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<models.Session> signUpAnonymus() async =>
|
||||||
|
await _account.createAnonymousSession();
|
||||||
|
|
||||||
|
Future<models.User> signup(Map map) async => _account.create(
|
||||||
|
userId: ID.unique(),
|
||||||
|
email: map['email'],
|
||||||
|
password: map['password'],
|
||||||
|
name: map['name'],
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<models.User> get getCurrentUser => _account.get();
|
||||||
|
|
||||||
|
// Tank Stop CRUD operations
|
||||||
|
// Create, Update, Get, List Tank Stops
|
||||||
|
Future<models.Document> createTankStop(Map map) async {
|
||||||
|
final response = await _databases.createDocument(
|
||||||
|
databaseId: dotenv.get('APPWRITE_DATABASE_ID'),
|
||||||
|
collectionId: dotenv.get('APPWRITE_COLLECTION_ID'),
|
||||||
|
documentId: ID.unique(),
|
||||||
|
data: map,
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<models.DocumentList> listTankStops(String userId) async {
|
||||||
|
final response = await _databases.listDocuments(
|
||||||
|
databaseId: dotenv.get('APPWRITE_DATABASE_ID'),
|
||||||
|
collectionId: dotenv.get('APPWRITE_COLLECTION_ID'),
|
||||||
|
queries: [Query.equal('userId', userId)],
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<models.Document> updateTankStop(String documentId, Map<String, dynamic> map) async {
|
||||||
|
final response = await _databases.updateDocument(
|
||||||
|
databaseId: dotenv.get('APPWRITE_DATABASE_ID'),
|
||||||
|
collectionId: dotenv.get('APPWRITE_COLLECTION_ID'),
|
||||||
|
documentId: documentId,
|
||||||
|
data: map,
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> deleteTankStop(String documentId) async {
|
||||||
|
return await _databases.deleteDocument(
|
||||||
|
databaseId: dotenv.get('APPWRITE_DATABASE_ID'),
|
||||||
|
collectionId: dotenv.get('APPWRITE_COLLECTION_ID'),
|
||||||
|
documentId: documentId,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
lib/data/repository/gasstation_repository.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
class GasStationRepository {
|
||||||
|
static final GasStationRepository _instance =
|
||||||
|
GasStationRepository._internal();
|
||||||
|
|
||||||
|
/// Singleton instance getter
|
||||||
|
factory GasStationRepository() => _instance;
|
||||||
|
|
||||||
|
//Constructor???
|
||||||
|
GasStationRepository._internal() {
|
||||||
|
//init for something
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> getGasStationsLocations(Map map) async {
|
||||||
|
List<dynamic> data = [];
|
||||||
|
var lat = map['lat'];
|
||||||
|
var lng = map['lng'];
|
||||||
|
var gas = map['gas'];
|
||||||
|
// Hier kannst du die Logik hinzufügen, um den Standort zu verwenden, z.B.
|
||||||
|
String baseUrl = dotenv.get('TANKSTOPS_BASE_URL');
|
||||||
|
String getGasLocationLink ='$baseUrl?latitude=$lat&longitude=$lng&fuelType=$gas&includeClosed=false';
|
||||||
|
final client = http.Client();
|
||||||
|
var response = await client.get(Uri.parse(getGasLocationLink),headers: {'Content-Type': 'application/json', 'charset': 'utf-8'});
|
||||||
|
//Response Data status
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
//Response is succsessful
|
||||||
|
data = json.decode(utf8.decode(response.bodyBytes)); //get response data
|
||||||
|
} else {
|
||||||
|
debugPrint(response.statusCode.toString());
|
||||||
|
}
|
||||||
|
client.close();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
97
lib/data/repository/location_repository.dart
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
|
||||||
|
class LocationRepository {
|
||||||
|
|
||||||
|
static final LocationRepository _instance = LocationRepository._internal();
|
||||||
|
|
||||||
|
/// Singleton instance getter
|
||||||
|
factory LocationRepository() => _instance;
|
||||||
|
|
||||||
|
//Constructor???
|
||||||
|
LocationRepository._internal() {
|
||||||
|
//init for something
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Überprüft, ob der Standortdienst aktiviert ist.
|
||||||
|
Future<bool> isLocationServiceEnabled() async {
|
||||||
|
return await Geolocator.isLocationServiceEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fragt die Berechtigung für den Standort ab.
|
||||||
|
Future<LocationPermission> checkPermission() async {
|
||||||
|
return await Geolocator.checkPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fordert die Berechtigung für den Standort an.
|
||||||
|
Future<LocationPermission> requestPermission() async {
|
||||||
|
return await Geolocator.requestPermission();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Liefert die aktuelle Position des Geräts.
|
||||||
|
/// Wirft eine Exception, wenn der Dienst nicht aktiviert ist oder keine Berechtigung vorliegt.
|
||||||
|
Future<Position> getCurrentPosition() async {
|
||||||
|
bool serviceEnabled = await isLocationServiceEnabled();
|
||||||
|
if (!serviceEnabled) {
|
||||||
|
// Standortdienste sind nicht aktiviert.
|
||||||
|
return Future.error('Location services are disabled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationPermission permission = await checkPermission();
|
||||||
|
if (permission == LocationPermission.denied) {
|
||||||
|
permission = await requestPermission();
|
||||||
|
if (permission == LocationPermission.denied) {
|
||||||
|
// Berechtigungen sind verweigert.
|
||||||
|
return Future.error('Location permissions are denied');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permission == LocationPermission.deniedForever) {
|
||||||
|
// Berechtigungen sind dauerhaft verweigert.
|
||||||
|
return Future.error(
|
||||||
|
'Location permissions are permanently denied, we cannot request permissions.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn alles in Ordnung ist, die Position zurückgeben.
|
||||||
|
return await Geolocator.getCurrentPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getNearbyLocation(Map map) async {
|
||||||
|
String locationOrt = '?';
|
||||||
|
var lat = map['lat'];
|
||||||
|
var lng = map['lng'];
|
||||||
|
// Hier kannst du die Logik hinzufügen, um den Standort zu verwenden, z.B.
|
||||||
|
String ptvGeoLink =
|
||||||
|
'https://api.myptv.com/geocoding/v1/locations/by-position/$lat/$lng?language=de&apiKey=${dotenv.get('PTV_GEOLINK_API_KEY')}';
|
||||||
|
final client = http.Client();
|
||||||
|
var response = await client.get(
|
||||||
|
Uri.parse(ptvGeoLink),
|
||||||
|
headers: {'Content-Type': 'application/json', 'charset': 'utf-8'},
|
||||||
|
);
|
||||||
|
//Response Data status
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
//Response is succsessful
|
||||||
|
Map<String, dynamic> data = json.decode(
|
||||||
|
utf8.decode(response.bodyBytes),
|
||||||
|
); //get response data
|
||||||
|
Map<String, dynamic> mapOfAddressfromPosition =
|
||||||
|
data['locations'][0]['address']; //get response address of position
|
||||||
|
if (mapOfAddressfromPosition.isNotEmpty) {
|
||||||
|
locationOrt =
|
||||||
|
'${mapOfAddressfromPosition['street'].toString()} ${mapOfAddressfromPosition['houseNumber'].toString()}, ${mapOfAddressfromPosition['postalCode'].toString()} ${mapOfAddressfromPosition['city'].toString()}';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debugPrint(response.statusCode.toString());
|
||||||
|
}
|
||||||
|
client.close();
|
||||||
|
|
||||||
|
return locationOrt;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
lib/icons/gasolineCyberpunk.png
Normal file
|
After Width: | Height: | Size: 613 KiB |
BIN
lib/icons/kilometer.png
Normal file
|
After Width: | Height: | Size: 695 B |
BIN
lib/images/backgroundPitstopBlack.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
lib/images/gasolineGuru.jpg
Normal file
|
After Width: | Height: | Size: 228 KiB |
@ -1,6 +1,6 @@
|
|||||||
import 'package:appwrite_flutter_starter_kit/app.dart';
|
|
||||||
import 'package:appwrite_flutter_starter_kit/utils/app_initializer.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import './app.dart';
|
||||||
|
import './utils/app_initializer.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
await AppInitializer.initialize();
|
await AppInitializer.initialize();
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import 'package:appwrite_flutter_starter_kit/data/models/log.dart';
|
|
||||||
import 'package:appwrite_flutter_starter_kit/data/models/status.dart';
|
|
||||||
import 'package:appwrite_flutter_starter_kit/data/repository/appwrite_repository.dart';
|
|
||||||
import 'package:appwrite_flutter_starter_kit/ui/components/checkered_background.dart';
|
|
||||||
import 'package:appwrite_flutter_starter_kit/ui/components/collapsible_bottomsheet.dart';
|
|
||||||
import 'package:appwrite_flutter_starter_kit/ui/components/connection_status_view.dart';
|
|
||||||
import 'package:appwrite_flutter_starter_kit/ui/components/getting_started_cards.dart';
|
|
||||||
import 'package:appwrite_flutter_starter_kit/ui/components/top_platform_view.dart';
|
|
||||||
import 'package:appwrite_flutter_starter_kit/utils/extensions/build_context.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../../data/models/log.dart';
|
||||||
|
import '../../data/models/status.dart';
|
||||||
|
import '../../data/repository/appwrite_repository.dart';
|
||||||
|
import '../../ui/components/checkered_background.dart';
|
||||||
|
import '../../ui/components/collapsible_bottomsheet.dart';
|
||||||
|
import '../../ui/components/connection_status_view.dart';
|
||||||
|
import '../../ui/components/getting_started_cards.dart';
|
||||||
|
import '../../ui/components/top_platform_view.dart';
|
||||||
|
import '../../utils/extensions/build_context.dart';
|
||||||
|
|
||||||
class AppwriteStarterKit extends StatefulWidget {
|
class AppwriteStarterKit extends StatefulWidget {
|
||||||
const AppwriteStarterKit({super.key});
|
const AppwriteStarterKit({super.key});
|
||||||
@ -26,7 +26,12 @@ class _AppwriteStarterKit extends State<AppwriteStarterKit> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: CheckeredBackground(
|
body: CheckeredBackground(
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
minimum: EdgeInsets.only(top: context.isLargeScreen ? 24 : 16),
|
minimum: EdgeInsets.only(
|
||||||
|
top: context.isExtraWideScreen
|
||||||
|
? 156
|
||||||
|
: context.isLargeScreen
|
||||||
|
? 24
|
||||||
|
: 32),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
86
lib/pages/gaslist/gaslist_controller.dart
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:get_storage/get_storage.dart';
|
||||||
|
import '../../data/models/gas_model.dart';
|
||||||
|
import '../../data/repository/appwrite_repository.dart';
|
||||||
|
import '../../pages/login/login_view.dart';
|
||||||
|
import '../../pages/tanklist/tanklist_view.dart';
|
||||||
|
|
||||||
|
import './widgets/map_view.dart' show MapDialogView;
|
||||||
|
import '../../data/repository/gasstation_repository.dart';
|
||||||
|
|
||||||
|
class GaslistController extends GetxController {
|
||||||
|
final _dataBox = GetStorage('MyUserStorage');
|
||||||
|
final szRxGasArt = 'DIE'.obs;
|
||||||
|
//Gas Station Repository
|
||||||
|
final GasStationRepository _gasStationRepository = GasStationRepository();
|
||||||
|
final AppwriteRepository _authRepository = AppwriteRepository();
|
||||||
|
final isLoadingList = false.obs;
|
||||||
|
var gasStationsList = <GasModel>[].obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
loadListData();
|
||||||
|
super.onInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {}
|
||||||
|
|
||||||
|
Future<void> loadListData() async {
|
||||||
|
isLoadingList(true);
|
||||||
|
var lat = _dataBox.read('lastLatitude');
|
||||||
|
var lng = _dataBox.read('lastLongitude');
|
||||||
|
var gas = szRxGasArt.value;
|
||||||
|
var result =
|
||||||
|
await getGasStationsFromApi({'lat': lat, 'lng': lng, 'gas': gas});
|
||||||
|
print('Gas Stations from API: $result');
|
||||||
|
//Hier die Logik zum Laden der Daten einfügen
|
||||||
|
gasStationsList.clear();
|
||||||
|
gasStationsList.refresh();
|
||||||
|
// add Map to GasModelList
|
||||||
|
for (var element in result) {
|
||||||
|
Map<String, dynamic> gasModelMap = (element as Map<String, dynamic>);
|
||||||
|
var gasModelItem = GasModel.fromJson(gasModelMap);
|
||||||
|
gasStationsList.add(gasModelItem);
|
||||||
|
}
|
||||||
|
//Simulate a delay for loading data
|
||||||
|
|
||||||
|
isLoadingList(false);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<dynamic> getGasStationsFromApi(Map map) async {
|
||||||
|
var result = await _gasStationRepository.getGasStationsLocations(map);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToListView() {
|
||||||
|
Get.offAndToNamed(TanklistPage.namedRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> logoutSessionAndGoToLoginPage() async {
|
||||||
|
// Handle logout logic here
|
||||||
|
print('Logout session and go to login page');
|
||||||
|
// Clear GetStorage session ID
|
||||||
|
_dataBox.remove('sessionId');
|
||||||
|
_dataBox.remove('userId');
|
||||||
|
_dataBox.remove('userName');
|
||||||
|
_dataBox.remove('userEmail');
|
||||||
|
print('Session ID removed from GetStorage');
|
||||||
|
await _authRepository.logout();
|
||||||
|
Get.offAndToNamed(LoginPage.namedRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> openDirectionMaps(double lat, double lng) async {
|
||||||
|
Get.dialog(
|
||||||
|
MapDialogView(
|
||||||
|
latitude: lat,
|
||||||
|
longitude: lng,
|
||||||
|
),
|
||||||
|
barrierDismissible: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
146
lib/pages/gaslist/gaslist_view.dart
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'gaslist_controller.dart';
|
||||||
|
|
||||||
|
class GaslistPage extends GetView<GaslistController> {
|
||||||
|
static const namedRoute = '/gas-stations-list-page';
|
||||||
|
const GaslistPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var gasCtrl = controller;
|
||||||
|
return PopScope(
|
||||||
|
canPop: false,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
shadowColor: Colors.grey,
|
||||||
|
title: const Text('Gas Stations'),
|
||||||
|
centerTitle: true,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.list, color: Colors.grey.shade300),
|
||||||
|
onPressed: () async {
|
||||||
|
// Handle go to Chart View
|
||||||
|
gasCtrl.goToListView();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.logout, color: Colors.grey.shade300),
|
||||||
|
onPressed: () async {
|
||||||
|
// Handle logout logic here
|
||||||
|
gasCtrl.logoutSessionAndGoToLoginPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Obx(
|
||||||
|
() => gasCtrl.isLoadingList.value == true
|
||||||
|
? Center(
|
||||||
|
child: Text('GasStations'),
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
child: Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: 50,
|
||||||
|
children: [
|
||||||
|
Divider(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
gasCtrl.szRxGasArt.value = 'DIE';
|
||||||
|
await gasCtrl.loadListData();
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.grey.shade800,
|
||||||
|
foregroundColor: Colors
|
||||||
|
.orange, // Hintergrundfarbe des Buttons
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text('Diesel'),
|
||||||
|
Text('DIE'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
gasCtrl.szRxGasArt.value = 'SUP';
|
||||||
|
await gasCtrl.loadListData();
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.grey.shade800,
|
||||||
|
foregroundColor: Colors
|
||||||
|
.orange, // Hintergrundfarbe des Buttons
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text('Benzin'),
|
||||||
|
Text('SUP'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: 5,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
var gasStation = gasCtrl.gasStationsList[index];
|
||||||
|
return ListTile(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
side: BorderSide(
|
||||||
|
color: Colors.grey, // Border color
|
||||||
|
width: 1.0, // Border thickness
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
// Handle item tap if needed
|
||||||
|
gasCtrl.openDirectionMaps(
|
||||||
|
gasStation.location!.latitude!,
|
||||||
|
gasStation.location!.longitude!);
|
||||||
|
},
|
||||||
|
title: Text(gasStation.name ?? 'No Name'),
|
||||||
|
subtitle: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(gasStation.location?.address ??
|
||||||
|
'No Address'),
|
||||||
|
Text(gasStation.distance != null
|
||||||
|
? '${gasStation.distance?.toStringAsFixed(2)} km'
|
||||||
|
: 'No Distance'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: gasStation.prices != null &&
|
||||||
|
gasStation.prices!.isNotEmpty
|
||||||
|
? Column(
|
||||||
|
children: [
|
||||||
|
Text(gasStation.prices?[0].fuelType ??
|
||||||
|
'N/A'),
|
||||||
|
Text(
|
||||||
|
'${gasStation.prices?[0].amount?.toStringAsPrecision(4) ?? 'N/A'} €'),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: const Text('N/A'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
76
lib/pages/gaslist/widgets/map_view.dart
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// map_dialog_view.dart
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
class MapDialogView extends StatelessWidget {
|
||||||
|
final double latitude;
|
||||||
|
final double longitude;
|
||||||
|
|
||||||
|
const MapDialogView({
|
||||||
|
super.key,
|
||||||
|
required this.latitude,
|
||||||
|
required this.longitude,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Funktion zum Öffnen der Karten-URL
|
||||||
|
void _openMap() async {
|
||||||
|
String url = '';
|
||||||
|
final coords = '$latitude,$longitude';
|
||||||
|
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
// Android: Startet die Google Maps Navigation
|
||||||
|
url =
|
||||||
|
'https://www.google.com/maps/dir/?api=1&destination=$coords'; // mode=d für Fahren
|
||||||
|
} else if (Platform.isIOS) {
|
||||||
|
// iOS: Startet die Apple Maps Navigation
|
||||||
|
url =
|
||||||
|
'https://maps.apple.com/?daddr=$coords&dirflg=d'; // daddr für destination, dirflg=d für driving
|
||||||
|
} else {
|
||||||
|
// Fallback-URL für die Google Maps Website mit Wegbeschreibung
|
||||||
|
url = 'https://www.google.com/maps/dir/?api=1&destination=$coords';
|
||||||
|
}
|
||||||
|
// Hier die URL-Logik einfügen, die Sie bereits kennen
|
||||||
|
//final url = 'http://googleusercontent.com/maps.google.com/8';
|
||||||
|
final uri = Uri.parse(url);
|
||||||
|
|
||||||
|
if (await canLaunchUrl(uri)) {
|
||||||
|
await launchUrl(uri);
|
||||||
|
} else {
|
||||||
|
// Eine Snackbar oder ein Dialog, um den Fehler zu melden
|
||||||
|
print('Fehler: Konnte Karten-App nicht öffnen.');
|
||||||
|
}
|
||||||
|
Get.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Karte öffnen'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start, // Passt die Höhe an den Inhalt an
|
||||||
|
children: [
|
||||||
|
Text('Koordinaten'),
|
||||||
|
Text('Latitude: $latitude'),
|
||||||
|
Text('Longitude: $longitude'),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _openMap, // Ruft die Methode zum Öffnen der Karte auf
|
||||||
|
child: const Text('Karten-App öffnen'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(), // Schließt den Dialog
|
||||||
|
child: const Text('Schließen'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
264
lib/pages/graph/graph_controller.dart
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
import 'package:appwrite/appwrite.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:get_storage/get_storage.dart';
|
||||||
|
|
||||||
|
import '../../data/models/chart_model.dart';
|
||||||
|
import '../../data/models/tank_model.dart';
|
||||||
|
import '../../data/repository/appwrite_repository.dart';
|
||||||
|
import '../../utils/extensions/static_helper.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class GraphController extends GetxController {
|
||||||
|
|
||||||
|
//AppWrite API-REST get Data
|
||||||
|
final AppwriteRepository _authRepository = AppwriteRepository();
|
||||||
|
|
||||||
|
final _dataBox = GetStorage('MyUserStorage');
|
||||||
|
final szRxUserId = 'NoUser'.obs;
|
||||||
|
final szBarTitle = ''.obs;
|
||||||
|
final listYearModel = <YearModel>[].obs;
|
||||||
|
late List<AppWriteTankModel> tankListOriginal;
|
||||||
|
final tankList = <AppWriteTankModel>[].obs;
|
||||||
|
final yearValue = DateTime.now().year.obs;
|
||||||
|
final blIsLoading = false.obs;
|
||||||
|
final dataPointsEuro = <double>[].obs;
|
||||||
|
final dataPointsGasoline = <double>[].obs;
|
||||||
|
final pointsEuro = <PricePoints>[].obs;
|
||||||
|
final pointsGasoline = <PricePoints>[].obs;
|
||||||
|
final sumListData = <SumDataModel>[].obs;
|
||||||
|
final mnCurrentAveragePerLiter = 0.00.obs;
|
||||||
|
final pointsPerLiter = <PricePoints>[].obs;
|
||||||
|
final dataPointsPerLiter = <double>[].obs;
|
||||||
|
final mnCurrentSummEuroYear = 0.0.obs;
|
||||||
|
final mnCurrentSummLiterYear = 0.0.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
szRxUserId(_dataBox.read('userId'));
|
||||||
|
_getTankList();
|
||||||
|
super.onInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {
|
||||||
|
super.onReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {}
|
||||||
|
|
||||||
|
void _getTankList() async {
|
||||||
|
blIsLoading(true);
|
||||||
|
bool isErrorByLoading = false;
|
||||||
|
String message = '';
|
||||||
|
try {
|
||||||
|
await _authRepository
|
||||||
|
.listTankStops(szRxUserId.value)
|
||||||
|
.then((tankListData) {
|
||||||
|
if (tankListData.documents.isEmpty) {
|
||||||
|
blIsLoading(false);
|
||||||
|
isErrorByLoading = true;
|
||||||
|
message = 'Leere Liste keine Daten vorhanden';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tankList.clear();
|
||||||
|
var data = tankListData.toMap();
|
||||||
|
List d = data['documents'].toList();
|
||||||
|
tankList.value = d
|
||||||
|
.map((e) => AppWriteTankModel.fromMap(e['data']))
|
||||||
|
.toList();
|
||||||
|
tankList.sort((a, b) {
|
||||||
|
final DateTime dateA = DateTime.parse(a.date);
|
||||||
|
final DateTime dateB = DateTime.parse(b.date);
|
||||||
|
return dateB.compareTo(dateA);
|
||||||
|
});
|
||||||
|
message = 'Liste wurde erfolgreich geladen';
|
||||||
|
})
|
||||||
|
.catchError((error) {
|
||||||
|
blIsLoading(true);
|
||||||
|
isErrorByLoading = true;
|
||||||
|
if (error is AppwriteException) {
|
||||||
|
message = error.message!;
|
||||||
|
} else {
|
||||||
|
message = 'Uuups da ist was schief gelaufen';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
blIsLoading(true);
|
||||||
|
isErrorByLoading = true;
|
||||||
|
message = 'Fehler beim Laden der Tankliste';
|
||||||
|
print('Error fetching tank list: $e');
|
||||||
|
}
|
||||||
|
String title = isErrorByLoading ? 'Fehler' : 'Erfolg';
|
||||||
|
Get.snackbar(
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
backgroundColor: isErrorByLoading ? Colors.red : Colors.green,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
duration: const Duration(seconds: 4),
|
||||||
|
);
|
||||||
|
tankListOriginal = tankList;
|
||||||
|
update();
|
||||||
|
setListMapYear();
|
||||||
|
getTankListPerYear();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> getHeadDescription() {
|
||||||
|
List<String> localListString = [];
|
||||||
|
tankList.sort((a, b) {
|
||||||
|
final DateTime dateA = DateTime.parse(a.date);
|
||||||
|
final DateTime dateB = DateTime.parse(b.date);
|
||||||
|
return dateB.compareTo(dateA);
|
||||||
|
});
|
||||||
|
if (tankList.isNotEmpty) {
|
||||||
|
for (var benzinItem in tankList) {
|
||||||
|
String szDay = '';
|
||||||
|
var dateDay = DateTime.parse(benzinItem.date);
|
||||||
|
if (dateDay.day <= 9) {
|
||||||
|
szDay = '0${dateDay.day}';
|
||||||
|
} else {
|
||||||
|
szDay = '${dateDay.day}';
|
||||||
|
}
|
||||||
|
String szMonth = '';
|
||||||
|
if (dateDay.month <= 9) {
|
||||||
|
szMonth = '0${dateDay.month}';
|
||||||
|
} else {
|
||||||
|
szMonth = '${dateDay.month}';
|
||||||
|
}
|
||||||
|
String szData = '$szDay$szMonth';
|
||||||
|
localListString.add(szData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
return localListString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setListMapYear() {
|
||||||
|
int year = 2022;
|
||||||
|
for (var i = year; i <= DateTime.now().year; i++) {
|
||||||
|
listYearModel.add(YearModel('Jahr $year', year));
|
||||||
|
year = year + 1;
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getTankListPerYear() {
|
||||||
|
blIsLoading.value = true;
|
||||||
|
tankList
|
||||||
|
.where((e) => DateTime.parse(e.date).year == yearValue.value)
|
||||||
|
.toList();
|
||||||
|
getDataPointsForGraph();
|
||||||
|
getMonthListData();
|
||||||
|
blIsLoading.value = false;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getDataPointsForGraph() {
|
||||||
|
//daten ermitteln für Jahr xxxx
|
||||||
|
//debugPrint('${yearValue.value}');
|
||||||
|
List<double> mnDataPointsEuro = [];
|
||||||
|
List<double> mnDataPointsGasoline = [];
|
||||||
|
// 08.08.23 Mod. add Price per Liter
|
||||||
|
List<double> mnDataPointsPerLiter = [];
|
||||||
|
var mnSummPerLiter = 0.0;
|
||||||
|
var mnTankListCount = tankList.length;
|
||||||
|
for (var item in tankList) {
|
||||||
|
var mnLiterGesamt = double.parse(item.liters);
|
||||||
|
var mnEuroGesamt =
|
||||||
|
double.parse(item.liters) * double.parse(item.pricePerLiter);
|
||||||
|
var mnPerLiter = double.parse(item.pricePerLiter);
|
||||||
|
mnDataPointsEuro.add(mnEuroGesamt);
|
||||||
|
mnDataPointsGasoline.add(mnLiterGesamt);
|
||||||
|
mnDataPointsPerLiter.add(mnPerLiter.toPrecision(2));
|
||||||
|
mnSummPerLiter += mnPerLiter;
|
||||||
|
}
|
||||||
|
if (mnSummPerLiter > 0 && mnTankListCount > 0) {
|
||||||
|
mnCurrentAveragePerLiter.value = (mnSummPerLiter / mnTankListCount)
|
||||||
|
.toPrecision(2);
|
||||||
|
}
|
||||||
|
dataPointsEuro.value = mnDataPointsEuro;
|
||||||
|
dataPointsGasoline.value = mnDataPointsGasoline;
|
||||||
|
dataPointsPerLiter.value = mnDataPointsPerLiter;
|
||||||
|
getPricePointsEuro();
|
||||||
|
getPricePointsGasoline();
|
||||||
|
getPricePointsPerLiter();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getMonthListData() {
|
||||||
|
if (tankList.isNotEmpty) {
|
||||||
|
sumListData.clear();
|
||||||
|
List<AppWriteTankModel> tankListPerYear = tankList;
|
||||||
|
List<dynamic> monthList = StaticHelper.listMonth;
|
||||||
|
for (var monthMap in monthList) {
|
||||||
|
var szMonth = monthMap['month'].toString();
|
||||||
|
var szValueMonth = monthMap['value'];
|
||||||
|
var result = StaticHelper.staticListGetDaysInBetween(
|
||||||
|
tankListPerYear,
|
||||||
|
szValueMonth,
|
||||||
|
yearValue.value,
|
||||||
|
);
|
||||||
|
var tankListPerMon = result as List<AppWriteTankModel>;
|
||||||
|
if (tankListPerMon.isNotEmpty) {
|
||||||
|
SumDataModel sumDataModel = SumDataModel('', '', '', 0);
|
||||||
|
sumDataModel.szMonth = szMonth;
|
||||||
|
sumDataModel.mnTankungen = tankListPerMon.length;
|
||||||
|
double mnSumEuro = 0.0;
|
||||||
|
double mnSumBenzin = 0.0;
|
||||||
|
for (var tankungModel in tankListPerMon) {
|
||||||
|
var mnEuroGesamt =
|
||||||
|
double.parse(tankungModel.liters) *
|
||||||
|
double.parse(tankungModel.pricePerLiter);
|
||||||
|
mnSumBenzin += double.parse(tankungModel.liters);
|
||||||
|
mnSumEuro += mnEuroGesamt;
|
||||||
|
}
|
||||||
|
sumDataModel.szVerbrauch = mnSumBenzin.toStringAsFixed(2);
|
||||||
|
sumDataModel.szSumme = mnSumEuro.toStringAsFixed(2);
|
||||||
|
sumListData.add(sumDataModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_currentSumForYear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void getPricePointsEuro() {
|
||||||
|
var listPoints = dataPointsEuro.indexed
|
||||||
|
.map((e) => PricePoints(x: e.$1.toDouble(), y: e.$2))
|
||||||
|
.toList();
|
||||||
|
pointsEuro.value = listPoints;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getPricePointsGasoline() {
|
||||||
|
var listPoints = dataPointsGasoline.indexed
|
||||||
|
.map((e) => PricePoints(x: e.$1.toDouble(), y: e.$2))
|
||||||
|
.toList();
|
||||||
|
pointsGasoline.value = listPoints;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getPricePointsPerLiter() {
|
||||||
|
var listPoints = dataPointsPerLiter.indexed
|
||||||
|
.map((e) => PricePoints(x: e.$1.toDouble(), y: e.$2))
|
||||||
|
.toList();
|
||||||
|
pointsPerLiter.value = listPoints;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _currentSumForYear() {
|
||||||
|
mnCurrentSummEuroYear.value = 0.0;
|
||||||
|
mnCurrentSummLiterYear.value = 0.0;
|
||||||
|
if (sumListData.isNotEmpty) {
|
||||||
|
for (var rxListItem in sumListData) {
|
||||||
|
mnCurrentSummEuroYear.value =
|
||||||
|
mnCurrentSummEuroYear.value + double.parse(rxListItem.szSumme!);
|
||||||
|
mnCurrentSummLiterYear.value =
|
||||||
|
mnCurrentSummLiterYear.value +
|
||||||
|
double.parse(rxListItem.szVerbrauch!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
148
lib/pages/graph/graph_view.dart
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import '../../utils/extensions/constants.dart';
|
||||||
|
import './graph_controller.dart';
|
||||||
|
import './widgets/chart_desc.dart';
|
||||||
|
import './widgets/chart_lines.dart';
|
||||||
|
import './widgets/chart_single_lines.dart';
|
||||||
|
import './widgets/my_list_tile_card.dart';
|
||||||
|
import '../../pages/tanklist/tanklist_view.dart';
|
||||||
|
|
||||||
|
class GraphPage extends GetView<GraphController> {
|
||||||
|
static const namedRoute = '/graph-statistic-page';
|
||||||
|
const GraphPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Size queryDisplaySize = MediaQuery.of(context).size;
|
||||||
|
var graphCtrl = controller;
|
||||||
|
return PopScope(
|
||||||
|
canPop: false,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 50,
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () =>
|
||||||
|
Get.offAndToNamed(TanklistPage.namedRoute),
|
||||||
|
icon: const Icon(Icons.list),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: queryDisplaySize.width - 70,
|
||||||
|
child: Obx(() => _displayDropDownMenue(graphCtrl)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8.0),
|
||||||
|
Obx(
|
||||||
|
() => Container(
|
||||||
|
color: Colors.grey.shade900,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ChartDescription(
|
||||||
|
backgroundColor: kColorEuroChart,
|
||||||
|
mnWidth: double.infinity,
|
||||||
|
szDescText: '€ Jahressumme',
|
||||||
|
szCurrentSumData:
|
||||||
|
graphCtrl.mnCurrentSummEuroYear.toStringAsFixed(2),
|
||||||
|
),
|
||||||
|
ChartDescription(
|
||||||
|
backgroundColor: kColorBenzinChart,
|
||||||
|
mnWidth: double.infinity,
|
||||||
|
szDescText: 'L Jahresverbrauch',
|
||||||
|
szCurrentSumData:
|
||||||
|
graphCtrl.mnCurrentSummLiterYear.toStringAsFixed(2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
LineChartLines(
|
||||||
|
firstLineColor: kColorEuroChart,
|
||||||
|
secondLineColor: kColorBenzinChart,
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => graphCtrl.blIsLoading.value
|
||||||
|
? const Center(
|
||||||
|
child: CircularProgressIndicator(color: Colors.cyan),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
Obx(
|
||||||
|
() => Container(
|
||||||
|
color: Colors.grey.shade900,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||||
|
child: ChartDescription(
|
||||||
|
backgroundColor: kColorPerLiterChart,
|
||||||
|
mnWidth: queryDisplaySize.width,
|
||||||
|
szDescText: '€/L Durchschnitt',
|
||||||
|
szCurrentSumData: graphCtrl.mnCurrentAveragePerLiter.value
|
||||||
|
.toStringAsFixed(2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
LineChartSingleLine(singleLineColor: kColorPerLiterChart),
|
||||||
|
const Divider(),
|
||||||
|
Obx(
|
||||||
|
() => graphCtrl.blIsLoading.value
|
||||||
|
? const Center(
|
||||||
|
child: CircularProgressIndicator(color: Colors.cyan),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: graphCtrl.sumListData.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return MyListTileCard(graphCtrl: graphCtrl, index: index);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
DropdownButtonFormField _displayDropDownMenue(GraphController controller) {
|
||||||
|
var graphCtrl = controller;
|
||||||
|
var list = graphCtrl.listYearModel;
|
||||||
|
var valueOfList = graphCtrl.yearValue.value;
|
||||||
|
return DropdownButtonFormField(
|
||||||
|
decoration: kInputDecorationDropDownMenueYear,
|
||||||
|
items: list.map((map) {
|
||||||
|
return DropdownMenuItem(
|
||||||
|
value: map.mnYear,
|
||||||
|
child: Text(map.szDescription),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
isDense: true,
|
||||||
|
isExpanded: true,
|
||||||
|
initialValue: valueOfList,
|
||||||
|
onChanged: ((value) {
|
||||||
|
valueOfList = value;
|
||||||
|
graphCtrl.yearValue.value = valueOfList;
|
||||||
|
if (graphCtrl.yearValue.value > 0) {
|
||||||
|
graphCtrl.getTankListPerYear();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
124
lib/pages/graph/widgets/bar_chart_widget.dart
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../../../utils/extensions/constants.dart';
|
||||||
|
import '../graph_controller.dart';
|
||||||
|
|
||||||
|
class BarChartWidget extends StatelessWidget {
|
||||||
|
final GraphController graphCtrl;
|
||||||
|
const BarChartWidget({super.key, required this.graphCtrl});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AspectRatio(
|
||||||
|
aspectRatio: 2,
|
||||||
|
child: BarChart(
|
||||||
|
BarChartData(
|
||||||
|
//alignment: BarChartAlignment.center,
|
||||||
|
maxY: 50,
|
||||||
|
baselineY: 0,
|
||||||
|
barTouchData: barTouchData,
|
||||||
|
titlesData: titlesData,
|
||||||
|
borderData: borderData,
|
||||||
|
barGroups: barGroups,
|
||||||
|
gridData: const FlGridData(show: false),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BarTouchData get barTouchData => BarTouchData(
|
||||||
|
enabled: false,
|
||||||
|
touchTooltipData: BarTouchTooltipData(
|
||||||
|
//tooltipBgColor: Colors.transparent,
|
||||||
|
tooltipPadding: EdgeInsets.zero,
|
||||||
|
tooltipMargin: 8,
|
||||||
|
getTooltipItem:
|
||||||
|
(
|
||||||
|
BarChartGroupData group,
|
||||||
|
int groupIndex,
|
||||||
|
BarChartRodData rod,
|
||||||
|
int rodIndex,
|
||||||
|
) {
|
||||||
|
return BarTooltipItem(
|
||||||
|
rod.toY.round().toString(),
|
||||||
|
const TextStyle(color: Colors.cyan, fontWeight: FontWeight.bold),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget getTitles(double value, TitleMeta meta) {
|
||||||
|
List<String> dayMonListString = graphCtrl.getHeadDescription();
|
||||||
|
for (var i = 0; i < dayMonListString.length; i++) {
|
||||||
|
if (i == value.toInt()) {
|
||||||
|
graphCtrl.szBarTitle.value = dayMonListString[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SideTitleWidget(
|
||||||
|
fitInside: SideTitleFitInsideData.fromTitleMeta(meta),
|
||||||
|
space: 4,
|
||||||
|
meta: meta,
|
||||||
|
child: Text(graphCtrl.szBarTitle.value, style: kBarTitleStyle),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlTitlesData get titlesData => FlTitlesData(
|
||||||
|
show: true,
|
||||||
|
bottomTitles: AxisTitles(
|
||||||
|
sideTitles: SideTitles(
|
||||||
|
showTitles: true,
|
||||||
|
reservedSize: 30,
|
||||||
|
getTitlesWidget: getTitles,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
leftTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||||
|
topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||||
|
rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||||
|
);
|
||||||
|
|
||||||
|
FlBorderData get borderData => FlBorderData(show: false);
|
||||||
|
|
||||||
|
LinearGradient get _barsGradient => LinearGradient(
|
||||||
|
colors: [Colors.blue.shade400, Colors.red.shade300],
|
||||||
|
begin: Alignment.bottomCenter,
|
||||||
|
end: Alignment.topCenter,
|
||||||
|
);
|
||||||
|
|
||||||
|
List<BarChartGroupData> get barGroups => [
|
||||||
|
BarChartGroupData(
|
||||||
|
x: 0,
|
||||||
|
barRods: [BarChartRodData(toY: 8, gradient: _barsGradient)],
|
||||||
|
showingTooltipIndicators: [0],
|
||||||
|
),
|
||||||
|
BarChartGroupData(
|
||||||
|
x: 1,
|
||||||
|
barRods: [BarChartRodData(toY: 10, gradient: _barsGradient)],
|
||||||
|
showingTooltipIndicators: [0],
|
||||||
|
),
|
||||||
|
BarChartGroupData(
|
||||||
|
x: 2,
|
||||||
|
barRods: [BarChartRodData(toY: 14, gradient: _barsGradient)],
|
||||||
|
showingTooltipIndicators: [0],
|
||||||
|
),
|
||||||
|
BarChartGroupData(
|
||||||
|
x: 3,
|
||||||
|
barRods: [BarChartRodData(toY: 15, gradient: _barsGradient)],
|
||||||
|
showingTooltipIndicators: [0],
|
||||||
|
),
|
||||||
|
BarChartGroupData(
|
||||||
|
x: 4,
|
||||||
|
barRods: [BarChartRodData(toY: 13, gradient: _barsGradient)],
|
||||||
|
showingTooltipIndicators: [0],
|
||||||
|
),
|
||||||
|
BarChartGroupData(
|
||||||
|
x: 5,
|
||||||
|
barRods: [BarChartRodData(toY: 10, gradient: _barsGradient)],
|
||||||
|
showingTooltipIndicators: [0],
|
||||||
|
),
|
||||||
|
BarChartGroupData(
|
||||||
|
x: 6,
|
||||||
|
barRods: [BarChartRodData(toY: 16, gradient: _barsGradient)],
|
||||||
|
showingTooltipIndicators: [0],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
43
lib/pages/graph/widgets/chart_desc.dart
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../utils/extensions/constants.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ChartDescription extends StatelessWidget {
|
||||||
|
final double mnWidth;
|
||||||
|
final Color backgroundColor;
|
||||||
|
final String szDescText;
|
||||||
|
final String szCurrentSumData;
|
||||||
|
const ChartDescription({
|
||||||
|
super.key,
|
||||||
|
required this.mnWidth,
|
||||||
|
required this.backgroundColor,
|
||||||
|
required this.szDescText,
|
||||||
|
required this.szCurrentSumData,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: mnWidth,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: backgroundColor,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
),
|
||||||
|
width: 70,
|
||||||
|
height: 20,
|
||||||
|
child: Center(
|
||||||
|
child: Text(szCurrentSumData, style: kChartDescriptionFontStyle),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(szDescText, style: kTextStyle),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
lib/pages/graph/widgets/chart_lines.dart
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import '../graph_controller.dart';
|
||||||
|
|
||||||
|
// 08.08.23 Mod. Line Chart with tree Lines
|
||||||
|
class LineChartLines extends GetView<GraphController> {
|
||||||
|
final Color firstLineColor;
|
||||||
|
final Color secondLineColor;
|
||||||
|
|
||||||
|
const LineChartLines({
|
||||||
|
super.key,
|
||||||
|
required this.firstLineColor,
|
||||||
|
required this.secondLineColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final GraphController gCtrl = controller;
|
||||||
|
return AspectRatio(
|
||||||
|
aspectRatio: 3,
|
||||||
|
child: Obx(
|
||||||
|
() => LineChart(
|
||||||
|
LineChartData(
|
||||||
|
lineBarsData: [
|
||||||
|
LineChartBarData(
|
||||||
|
color: firstLineColor,
|
||||||
|
spots: gCtrl.pointsEuro
|
||||||
|
.map((point) => FlSpot(point.x, point.y))
|
||||||
|
.toList(),
|
||||||
|
isCurved: false,
|
||||||
|
dotData: const FlDotData(show: true),
|
||||||
|
),
|
||||||
|
LineChartBarData(
|
||||||
|
isStepLineChart: false,
|
||||||
|
color: secondLineColor,
|
||||||
|
spots: gCtrl.pointsGasoline
|
||||||
|
.map((point) => FlSpot(point.x, point.y))
|
||||||
|
.toList(),
|
||||||
|
isCurved: false,
|
||||||
|
dotData: const FlDotData(show: true),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
lib/pages/graph/widgets/chart_single_lines.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import '../graph_controller.dart';
|
||||||
|
|
||||||
|
// 08.08.23 Mod. Line Chart with tree Lines
|
||||||
|
class LineChartSingleLine extends GetView<GraphController> {
|
||||||
|
final Color singleLineColor;
|
||||||
|
|
||||||
|
const LineChartSingleLine({super.key, required this.singleLineColor});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final GraphController gCtrl = controller;
|
||||||
|
return AspectRatio(
|
||||||
|
aspectRatio: 3.55,
|
||||||
|
child: Obx(
|
||||||
|
() => LineChart(
|
||||||
|
LineChartData(
|
||||||
|
lineBarsData: [
|
||||||
|
LineChartBarData(
|
||||||
|
color: singleLineColor,
|
||||||
|
spots: gCtrl.pointsPerLiter
|
||||||
|
.map((point) => FlSpot(point.x, point.y))
|
||||||
|
.toList(),
|
||||||
|
isCurved: false,
|
||||||
|
dotData: const FlDotData(show: true),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
lib/pages/graph/widgets/my_list_tile_card.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../utils/extensions/constants.dart';
|
||||||
|
import '../graph_controller.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class MyListTileCard extends StatelessWidget {
|
||||||
|
const MyListTileCard({
|
||||||
|
super.key,
|
||||||
|
required this.graphCtrl,
|
||||||
|
required this.index,
|
||||||
|
});
|
||||||
|
|
||||||
|
final GraphController graphCtrl;
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
shadowColor: Colors.grey.shade200,
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(
|
||||||
|
'${graphCtrl.sumListData[index].szMonth}',
|
||||||
|
style: kTextStyle,
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Monatsausgaben: ${graphCtrl.sumListData[index].szSumme} €',
|
||||||
|
style: kTextStyleSub,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Monatsverbrauch: ${graphCtrl.sumListData[index].szVerbrauch} L',
|
||||||
|
style: kTextStyleSub,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Tankungen: ${graphCtrl.sumListData[index].mnTankungen}',
|
||||||
|
style: kTextStyleSub,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
232
lib/pages/login/login_controller.dart
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
import 'package:appwrite/appwrite.dart';
|
||||||
|
import 'package:appwrite/models.dart' as models;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:get_storage/get_storage.dart';
|
||||||
|
|
||||||
|
import '../../data/repository/appwrite_repository.dart';
|
||||||
|
import '../../utils/extensions/static_helper.dart';
|
||||||
|
import '../tank/tank_view.dart';
|
||||||
|
|
||||||
|
class LoginController extends GetxController {
|
||||||
|
final AppwriteRepository _authRepository = AppwriteRepository();
|
||||||
|
|
||||||
|
final isVisible = false.obs;
|
||||||
|
//Form Key
|
||||||
|
final formKey = GlobalKey<FormState>();
|
||||||
|
bool isFormValid = false;
|
||||||
|
|
||||||
|
final _dataBox = GetStorage('MyUserStorage');
|
||||||
|
|
||||||
|
final emailController = TextEditingController();
|
||||||
|
final passwordController = TextEditingController();
|
||||||
|
final nameController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
print('LoginController initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {
|
||||||
|
super.onReady();
|
||||||
|
// Initialize any necessary data or state here
|
||||||
|
var isSessionId = _dataBox.hasData('sessionId');
|
||||||
|
if (isSessionId) {
|
||||||
|
// If session ID exists, navigate to TankPage
|
||||||
|
print('Session ID found, navigating to TankPage');
|
||||||
|
goToTankPage();
|
||||||
|
} else {
|
||||||
|
// If no session ID, initialize the login controller
|
||||||
|
print('No session ID found, initializing LoginController');
|
||||||
|
}
|
||||||
|
// This method is called when the controller is ready
|
||||||
|
print('LoginController is ready');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
// Clean up any resources or listeners here
|
||||||
|
emailController.dispose();
|
||||||
|
passwordController.dispose();
|
||||||
|
nameController.dispose();
|
||||||
|
// emailFocusNode.dispose();
|
||||||
|
// passwordFocusNode.dispose();
|
||||||
|
// nameFocusNode.dispose();
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearTextEditingController() {
|
||||||
|
emailController.clear();
|
||||||
|
passwordController.clear();
|
||||||
|
nameController.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
String? validateEmail(String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Bitte geben Sie eine E-Mail-Adresse ein';
|
||||||
|
}
|
||||||
|
if (!GetUtils.isEmail(value)) {
|
||||||
|
return 'Bitte geben Sie eine gültige E-Mail-Adresse ein';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? validatePassword(String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Bitte geben Sie ein Passwort ein';
|
||||||
|
}
|
||||||
|
if (value.length < 8) {
|
||||||
|
return 'Das Passwort muss mindestens 8 Zeichen lang sein';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? validateName(String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Bitte geben Sie Ihren Namen ein';
|
||||||
|
}
|
||||||
|
if (value.length < 3) {
|
||||||
|
return 'Der Name muss mindestens 3 Zeichen lang sein';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = 'NoMessage!';
|
||||||
|
var isError = false;
|
||||||
|
|
||||||
|
// Registrierung
|
||||||
|
Future<void> register() async {
|
||||||
|
isError = false;
|
||||||
|
message = 'NoMessage!';
|
||||||
|
isFormValid = formKey.currentState!.validate();
|
||||||
|
if (!isFormValid) {
|
||||||
|
print('Formular ist ungültig');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
print('Formular ist gültig');
|
||||||
|
formKey.currentState!.save();
|
||||||
|
try {
|
||||||
|
await _authRepository.signup({
|
||||||
|
'email': emailController.text,
|
||||||
|
'password': passwordController.text,
|
||||||
|
'name': nameController.text,
|
||||||
|
}).then((models.User userValue) {
|
||||||
|
// GetStorage data storage
|
||||||
|
isVisible(false);
|
||||||
|
print(
|
||||||
|
'User was stored and Loggedin: ${userValue.name}, ${userValue.$id}, ${userValue.email}',
|
||||||
|
);
|
||||||
|
// Store session ID in GetStorage
|
||||||
|
_dataBox.write('userId', userValue.$id);
|
||||||
|
_dataBox.write('userName', userValue.name);
|
||||||
|
_dataBox.write('userEmail', userValue.email);
|
||||||
|
// Go to TankPage
|
||||||
|
goToTankPage();
|
||||||
|
message = 'Sie wurden registriert und Angemeldet!!';
|
||||||
|
StaticHelper.getMySnackeBar('Erfolg', message, Colors.green);
|
||||||
|
}).catchError((error) {
|
||||||
|
if (error is AppwriteException) {
|
||||||
|
// Handle specific Appwrite exceptions
|
||||||
|
print('Appwrite Fehler: ${error.message}');
|
||||||
|
message = 'Appwrite Fehler: ${error.message}';
|
||||||
|
isError = true;
|
||||||
|
} else {
|
||||||
|
// Handle other types of errors
|
||||||
|
message = 'Allgemeiner Fehler: $error';
|
||||||
|
print('Allgemeiner Fehler: $error');
|
||||||
|
isError = true;
|
||||||
|
}
|
||||||
|
message = 'Fehler bei der Registrierung: $error';
|
||||||
|
print('Fehler bei der Registrierung: $error');
|
||||||
|
//Clear GetStorage session ID
|
||||||
|
StaticHelper.removeFromStorage();
|
||||||
|
isError = true;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
message = 'Fehler bei Registrierung: $e';
|
||||||
|
print('Fehler bei Registrierung: $e');
|
||||||
|
//Clear GetStorage session ID
|
||||||
|
StaticHelper.removeFromStorage();
|
||||||
|
isError = true;
|
||||||
|
}
|
||||||
|
if (isError) StaticHelper.getMySnackeBar('Fehler', message, Colors.red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _getCurrentLoggedinUser() async {
|
||||||
|
await _authRepository.getCurrentUser.then((models.User userValue) {
|
||||||
|
// GetStorage data storage
|
||||||
|
print(
|
||||||
|
'User was stored and Loggedin: ${userValue.name}, ${userValue.$id}, ${userValue.email}',
|
||||||
|
);
|
||||||
|
// Store session ID in GetStorage
|
||||||
|
_dataBox.write('userId', userValue.$id);
|
||||||
|
_dataBox.write('userName', userValue.name);
|
||||||
|
_dataBox.write('userEmail', userValue.email);
|
||||||
|
}).catchError((error) {
|
||||||
|
print('Fehler beim Abrufen des Benutzers: $error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login
|
||||||
|
Future<void> login() async {
|
||||||
|
message = 'NoMessage!';
|
||||||
|
isError = false;
|
||||||
|
|
||||||
|
isFormValid = formKey.currentState!.validate();
|
||||||
|
if (!isFormValid) {
|
||||||
|
print('Formular ist ungültig');
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
print('Formular ist gültig');
|
||||||
|
formKey.currentState!.save();
|
||||||
|
try {
|
||||||
|
await _authRepository.login({
|
||||||
|
'email': emailController.text,
|
||||||
|
'password': passwordController.text,
|
||||||
|
}).then((models.Session session) {
|
||||||
|
// Store session ID in GetStorage
|
||||||
|
_dataBox.write('sessionId', session.$id);
|
||||||
|
print('Session ID stored: ${_dataBox.read('sessionId')}');
|
||||||
|
print(
|
||||||
|
'Erfolgreich eingeloggt: ${session.$id}\n${session.providerUid}',
|
||||||
|
);
|
||||||
|
_getCurrentLoggedinUser().then((_) {
|
||||||
|
// Go to TankPage
|
||||||
|
goToTankPage();
|
||||||
|
StaticHelper.getMySnackeBar(
|
||||||
|
'Erfolg', 'Sie wurden Angemeldet!!', Colors.green);
|
||||||
|
});
|
||||||
|
}).catchError((error) {
|
||||||
|
if (error is AppwriteException) {
|
||||||
|
// Handle specific Appwrite exceptions
|
||||||
|
isError = true;
|
||||||
|
message = 'Appwrite Fehler: ${error.message}';
|
||||||
|
print('Appwrite Fehler: ${error.message}');
|
||||||
|
} else {
|
||||||
|
// Handle other types of errors
|
||||||
|
print('Allgemeiner Fehler: $error');
|
||||||
|
isError = true;
|
||||||
|
message = 'Allgemeiner Fehler: $error';
|
||||||
|
}
|
||||||
|
print('Fehler beim Login: $error');
|
||||||
|
isError = true;
|
||||||
|
message = 'Fehler beim Login: $error';
|
||||||
|
_authRepository.logout();
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print('Fehler beim Login: $e');
|
||||||
|
isError = true;
|
||||||
|
message = 'Fehler beim Login: $e';
|
||||||
|
_authRepository.logout();
|
||||||
|
}
|
||||||
|
if (isError) StaticHelper.getMySnackeBar('Fehler', message, Colors.red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToTankPage() {
|
||||||
|
Get.offAndToNamed(TankPage.namedRoute, arguments: {'from': 'Input'});
|
||||||
|
}
|
||||||
|
}
|
||||||
162
lib/pages/login/login_view.dart
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'login_controller.dart';
|
||||||
|
|
||||||
|
class LoginPage extends GetView<LoginController> {
|
||||||
|
static const namedRoute = '/login-page';
|
||||||
|
const LoginPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: SafeArea(
|
||||||
|
child: Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
headerTextTankGuru(),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
headerTextDescription(),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
loadingImage(),
|
||||||
|
inputFields(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//the structure of the login page
|
||||||
|
SizedBox loadingImage() {
|
||||||
|
return SizedBox(
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(40),
|
||||||
|
child: Image.asset('lib/images/gasolineGuru.jpg', fit: BoxFit.cover),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Text headerTextTankGuru() {
|
||||||
|
return Text(
|
||||||
|
'Tank GURU',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 35,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.teal[600],
|
||||||
|
letterSpacing: 2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Text headerTextDescription() {
|
||||||
|
return Text(
|
||||||
|
'Das ultimative Tanken',
|
||||||
|
style: TextStyle(fontSize: 20, color: Colors.grey[300]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget inputFields() {
|
||||||
|
return Obx(
|
||||||
|
() => SizedBox(
|
||||||
|
width: 300,
|
||||||
|
child: Form(
|
||||||
|
key: controller.formKey,
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(height: 20),
|
||||||
|
TextFormField(
|
||||||
|
validator: (value) => controller.validateEmail(value),
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
controller: controller.emailController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Email',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
TextFormField(
|
||||||
|
validator: (value) => controller.validatePassword(value),
|
||||||
|
keyboardType: TextInputType.visiblePassword,
|
||||||
|
controller: controller.passwordController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Password',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
obscureText: true,
|
||||||
|
),
|
||||||
|
if (controller.isVisible.value) ...[
|
||||||
|
SizedBox(height: 20),
|
||||||
|
TextFormField(
|
||||||
|
validator: (value) => controller.validateName(value),
|
||||||
|
keyboardType: TextInputType.name,
|
||||||
|
controller: controller.nameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Name',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
SizedBox(height: 20),
|
||||||
|
SizedBox(
|
||||||
|
width: 300,
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
controller.isVisible.value == false
|
||||||
|
? controller.login()
|
||||||
|
: controller.register();
|
||||||
|
},
|
||||||
|
child: controller.isVisible.value == false
|
||||||
|
? Text('Login')
|
||||||
|
: Text('Registrieren'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text('Noch kein Konto? '),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (!controller.isVisible.value) {
|
||||||
|
controller.isVisible.value = true;
|
||||||
|
} else {
|
||||||
|
controller.isVisible.value = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: controller.isVisible.value == false
|
||||||
|
? Text(
|
||||||
|
'Sign Up',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.teal,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
'Sign In',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.teal,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
251
lib/pages/print/print_controller.dart
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:pdf/pdf.dart';
|
||||||
|
import 'package:pdf/widgets.dart' as pw;
|
||||||
|
import 'package:printing/printing.dart';
|
||||||
|
|
||||||
|
import '../../data/models/tank_model.dart';
|
||||||
|
import '../tanklist/tanklist_view.dart';
|
||||||
|
|
||||||
|
class PrintController extends GetxController {
|
||||||
|
final argunments = Get.arguments;
|
||||||
|
final List<dynamic> tankList = Get.arguments['tankList'] ?? [];
|
||||||
|
final String year = Get.arguments['year'] ?? 'NoYear';
|
||||||
|
final String summeLiter = Get.arguments['summeLiter'] ?? '0.0';
|
||||||
|
final String summePreis = Get.arguments['summePreis'] ?? '0.0';
|
||||||
|
|
||||||
|
Future<void> printPdfReport() async {
|
||||||
|
final doc = pw.Document();
|
||||||
|
final font = await PdfGoogleFonts.robotoRegular();
|
||||||
|
final List<AppWriteTankModel> tankungen =
|
||||||
|
tankList.map((e) => AppWriteTankModel.fromMap(e)).toList();
|
||||||
|
|
||||||
|
// Daten nach Monat gruppieren
|
||||||
|
final dFormat = DateFormat('MMMM yyyy', 'de');
|
||||||
|
final Map<String, List<AppWriteTankModel>> tankungenByMonth = {};
|
||||||
|
for (var tankung in tankungen) {
|
||||||
|
final month = dFormat.format(DateTime.parse(tankung.date));
|
||||||
|
if (!tankungenByMonth.containsKey(month)) {
|
||||||
|
tankungenByMonth[month] = [];
|
||||||
|
}
|
||||||
|
tankungenByMonth[month]!.add(tankung);
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.addPage(
|
||||||
|
pw.Page(
|
||||||
|
pageFormat: PdfPageFormat.a4.copyWith(
|
||||||
|
marginBottom: 1.0 * PdfPageFormat.cm,
|
||||||
|
marginLeft: 1.0 * PdfPageFormat.cm,
|
||||||
|
marginRight: 1.0 * PdfPageFormat.cm,
|
||||||
|
marginTop: 1.0 * PdfPageFormat.cm),
|
||||||
|
build: (pw.Context context) {
|
||||||
|
return pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
//Header
|
||||||
|
// PDF-Design hier
|
||||||
|
pw.Text('Tankbericht $year',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
color: PdfColors.blue900,
|
||||||
|
font: font)),
|
||||||
|
pw.SizedBox(height: 10),
|
||||||
|
pw.Text('Alle Tankungen im Überblick',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontSize: 16, color: PdfColors.grey700, font: font)),
|
||||||
|
pw.Divider(color: PdfColors.grey400),
|
||||||
|
pw.SizedBox(height: 20),
|
||||||
|
// Data
|
||||||
|
// Dynamische Erstellung der monatlichen Abschnitte
|
||||||
|
...tankungenByMonth.entries.map((entry) {
|
||||||
|
final monthName = entry.key;
|
||||||
|
final monthlyData = entry.value;
|
||||||
|
|
||||||
|
// Monatliche Summen berechnen
|
||||||
|
final double monthlyLiters = monthlyData.fold(
|
||||||
|
0.0, (sum, item) => sum + double.parse(item.liters));
|
||||||
|
final double monthlyPrice = monthlyData.fold(
|
||||||
|
0.0,
|
||||||
|
(sum, item) =>
|
||||||
|
sum + double.parse(item.szSummePreis ?? '0.0'));
|
||||||
|
final double monthlyAvrLiterPrice =
|
||||||
|
monthlyPrice / monthlyLiters;
|
||||||
|
// Monatlicher Abschnitt
|
||||||
|
// Erstelle die Datenzeilen
|
||||||
|
final List<List<String>> tableData = monthlyData.map((item) {
|
||||||
|
var modDate = item.date.substring(5);
|
||||||
|
var modPreisPerLiter = item.pricePerLiter.padRight(5, '0');
|
||||||
|
return [
|
||||||
|
modDate,
|
||||||
|
item.location,
|
||||||
|
item.liters,
|
||||||
|
modPreisPerLiter,
|
||||||
|
item.szSummePreis ?? '0.0',
|
||||||
|
item.odometer
|
||||||
|
];
|
||||||
|
}).toList();
|
||||||
|
var monthAvrPricePerLiter = monthlyAvrLiterPrice.toStringAsFixed(3);
|
||||||
|
// Füge die Summenzeile hinzu
|
||||||
|
tableData.add([
|
||||||
|
'Summe',
|
||||||
|
'', // Leere Zelle für Ort
|
||||||
|
(monthlyLiters.toStringAsFixed(2)),
|
||||||
|
'$monthAvrPricePerLiter Ø', // Leere Zelle für Preis/L
|
||||||
|
(monthlyPrice.toStringAsFixed(2)),
|
||||||
|
''
|
||||||
|
]);
|
||||||
|
|
||||||
|
return pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Monatsüberschrift
|
||||||
|
pw.Text(
|
||||||
|
monthName,
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 18,
|
||||||
|
font: font,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
pw.SizedBox(height: 10),
|
||||||
|
// Tabelle für den jeweiligen Monat
|
||||||
|
// pw.TableHelper.fromTextArray(
|
||||||
|
// headers: [
|
||||||
|
// 'Datum',
|
||||||
|
// 'Ort',
|
||||||
|
// 'Menge (L)',
|
||||||
|
// 'Preis/L (€)',
|
||||||
|
// 'Summe (€)',
|
||||||
|
// 'KM-Stand'
|
||||||
|
// ],
|
||||||
|
// cellAlignment: pw.Alignment.centerLeft,
|
||||||
|
// border: pw.TableBorder.all(color: PdfColors.grey200),
|
||||||
|
// headerStyle: pw.TextStyle(
|
||||||
|
// fontWeight: pw.FontWeight.bold, font: font),
|
||||||
|
// data: tableData,
|
||||||
|
// cellStyle: pw.TextStyle(font: font),
|
||||||
|
// ),
|
||||||
|
|
||||||
|
// Manuelle Erstellung der Tabelle mit individueller Formatierung
|
||||||
|
pw.Table(
|
||||||
|
border: pw.TableBorder.all(color: PdfColors.grey200),
|
||||||
|
columnWidths: {
|
||||||
|
0: const pw.FlexColumnWidth(1.0), // Datum
|
||||||
|
1: const pw.FlexColumnWidth(4.0), // Ort
|
||||||
|
2: const pw.FlexColumnWidth(1.3), // Menge
|
||||||
|
3: const pw.FlexColumnWidth(1.3), // Preis/L
|
||||||
|
4: const pw.FlexColumnWidth(1.4), // Summe
|
||||||
|
5: const pw.FlexColumnWidth(1.3), // KM-Stand
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
// Kopfzeile mit grauem Hintergrund
|
||||||
|
pw.TableRow(
|
||||||
|
decoration:
|
||||||
|
const pw.BoxDecoration(color: PdfColors.grey200),
|
||||||
|
children: [
|
||||||
|
_buildHeaderCell('Datum', font),
|
||||||
|
_buildHeaderCell('Ort', font),
|
||||||
|
_buildHeaderCell('Menge(L)', font),
|
||||||
|
_buildHeaderCell('Preis/L(€)', font),
|
||||||
|
_buildHeaderCell('Summe(€)', font),
|
||||||
|
_buildHeaderCell('Km-Stand', font),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
// Datenzeilen
|
||||||
|
...monthlyData.map((item) {
|
||||||
|
var modDate = item.date.substring(5);
|
||||||
|
var modPreisPerLiter =
|
||||||
|
item.pricePerLiter.padRight(5, '0');
|
||||||
|
return pw.TableRow(
|
||||||
|
children: [
|
||||||
|
_buildDataCell(modDate, font),
|
||||||
|
_buildDataCell(item.location, font),
|
||||||
|
_buildDataCell(item.liters, font),
|
||||||
|
_buildDataCell(modPreisPerLiter, font),
|
||||||
|
_buildDataCell(item.szSummePreis!, font),
|
||||||
|
_buildDataCell(item.odometer, font),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
// Summenzeile mit ANDERER grauem Hintergrund
|
||||||
|
pw.TableRow(
|
||||||
|
decoration:
|
||||||
|
const pw.BoxDecoration(color: PdfColors.grey300),
|
||||||
|
children: [
|
||||||
|
_buildDataCell('', font),
|
||||||
|
_buildDataCell('Gesamt Summe Monat', font),
|
||||||
|
_buildDataCell('${monthlyLiters.toStringAsFixed(2)} L', font),
|
||||||
|
_buildDataCell('$monthAvrPricePerLiter Ø', font),
|
||||||
|
_buildDataCell('${monthlyPrice.toStringAsFixed(2)} €', font),
|
||||||
|
_buildDataCell('', font)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
pw.SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
//footer
|
||||||
|
pw.Spacer(),
|
||||||
|
pw.Divider(color: PdfColors.grey400),
|
||||||
|
pw.Container(
|
||||||
|
alignment: pw.Alignment.centerRight,
|
||||||
|
child: pw.Column(
|
||||||
|
crossAxisAlignment: pw.CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
pw.Text('Jahres-Gesamtmenge: $summeLiter L',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
font: font)),
|
||||||
|
pw.Text('Jahres-Gesamtsumme: $summePreis €',
|
||||||
|
style: pw.TextStyle(
|
||||||
|
fontWeight: pw.FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
color: PdfColors.blue700,
|
||||||
|
font: font)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Druckansicht öffnen
|
||||||
|
await Printing.layoutPdf(
|
||||||
|
onLayout: (PdfPageFormat format) async => doc.save(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToTankListPage() async {
|
||||||
|
await Get.offAndToNamed(TanklistPage.namedRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfs-Widgets für die Zellen
|
||||||
|
pw.Widget _buildHeaderCell(String text, pw.Font font) {
|
||||||
|
return pw.Container(
|
||||||
|
alignment: pw.Alignment.centerLeft,
|
||||||
|
padding: const pw.EdgeInsets.symmetric(vertical: 5, horizontal: 8),
|
||||||
|
child: pw.Text(
|
||||||
|
text,
|
||||||
|
style: pw.TextStyle(fontWeight: pw.FontWeight.bold, font: font),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pw.Widget _buildDataCell(String text, pw.Font font, {bool alignRight = false}) {
|
||||||
|
return pw.Container(
|
||||||
|
alignment: alignRight ? pw.Alignment.centerRight : pw.Alignment.centerLeft,
|
||||||
|
padding: const pw.EdgeInsets.symmetric(vertical: 5, horizontal: 8),
|
||||||
|
child: pw.Text(
|
||||||
|
text,
|
||||||
|
style: pw.TextStyle(font: font),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
lib/pages/print/print_view.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import './print_controller.dart';
|
||||||
|
|
||||||
|
class PrintPage extends GetView<PrintController> {
|
||||||
|
static const namedRoute = '/print-pdf-page';
|
||||||
|
const PrintPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var printCtrl = controller;
|
||||||
|
return PopScope(
|
||||||
|
canPop: false,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('PDF-Bericht'),
|
||||||
|
centerTitle: true,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.list, color: Colors.white),
|
||||||
|
onPressed: () {
|
||||||
|
// Handle logout logic here
|
||||||
|
printCtrl.goToTankListPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: ElevatedButton(
|
||||||
|
// Ruft die Controller-Methode auf
|
||||||
|
onPressed: () => printCtrl.printPdfReport(),
|
||||||
|
child: const Text('PDF erstellen & drucken'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
337
lib/pages/tank/tank_controller.dart
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
import 'package:appwrite/appwrite.dart';
|
||||||
|
import 'package:appwrite/models.dart' as models;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:get_storage/get_storage.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import '../../utils/extensions/static_helper.dart';
|
||||||
|
import '../../data/repository/location_repository.dart';
|
||||||
|
import '../../data/repository/appwrite_repository.dart';
|
||||||
|
import '../login/login_view.dart';
|
||||||
|
import '../tanklist/tanklist_view.dart';
|
||||||
|
|
||||||
|
class TankController extends GetxController {
|
||||||
|
final _dataBox = GetStorage('MyUserStorage');
|
||||||
|
//AppWrite API-REST get Data
|
||||||
|
final AppwriteRepository _authRepository = AppwriteRepository();
|
||||||
|
// GEOLOCATING Services
|
||||||
|
final LocationRepository _locationRepository = LocationRepository();
|
||||||
|
|
||||||
|
// Rx-Variablen für die UI, um auf Änderungen zu reagieren
|
||||||
|
final circleAvatarUserChar = 'A'.obs;
|
||||||
|
final userNameToDisplay = 'Test User'.obs;
|
||||||
|
final Rx<Position?> currentPosition = Rx<Position?>(null);
|
||||||
|
final Rx<bool> isLoading = false.obs;
|
||||||
|
final Rx<String?> errorMessage = Rx<String?>(null);
|
||||||
|
//final rxOrtString = '?'.obs;
|
||||||
|
final rxSessionIdString = '?'.obs;
|
||||||
|
final rxSummePreisString = '0.00'.obs;
|
||||||
|
// TextEditingController für die Formulareingaben
|
||||||
|
final formKeyTank = GlobalKey<FormState>();
|
||||||
|
bool isFormValid = false;
|
||||||
|
DateTime? _selectedDateTime;
|
||||||
|
final dateController = TextEditingController();
|
||||||
|
final f = DateFormat('yyyy-MM-dd');
|
||||||
|
final kilometerStandEdittingController = TextEditingController();
|
||||||
|
final mengeController = TextEditingController();
|
||||||
|
final pricePerLiterController = TextEditingController();
|
||||||
|
final ortController = TextEditingController();
|
||||||
|
|
||||||
|
final FocusNode firstFocusNode = FocusNode(); // Deklariere den FocusNode
|
||||||
|
|
||||||
|
final isEditMode = false.obs;
|
||||||
|
String documentId = '';
|
||||||
|
|
||||||
|
// Methode für das ausgewählte Datum
|
||||||
|
Future<void> selectDateTime(BuildContext context) async {
|
||||||
|
// 1. Datum auswählen
|
||||||
|
final DateTime? pickedDate = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: _selectedDateTime ?? DateTime.now(),
|
||||||
|
firstDate: DateTime(2000),
|
||||||
|
lastDate: DateTime(2101),
|
||||||
|
);
|
||||||
|
_selectedDateTime = pickedDate;
|
||||||
|
if (_selectedDateTime != null) {
|
||||||
|
dateController.text = _selectedDateTime!.toIso8601String().substring(
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Methode zum Abrufen des Standorts.
|
||||||
|
Future<void> fetchCurrentLocation() async {
|
||||||
|
isLoading.value = true;
|
||||||
|
errorMessage.value = null;
|
||||||
|
try {
|
||||||
|
final Position position = await _locationRepository.getCurrentPosition();
|
||||||
|
currentPosition.value = position;
|
||||||
|
final double latitude = position.latitude;
|
||||||
|
final double longitude = position.longitude;
|
||||||
|
// Hier kannst du die Logik hinzufügen, um den Standort zu verwenden, z.B.
|
||||||
|
// den Standort in der UI anzuzeigen oder an einen Server zu senden.
|
||||||
|
var map = {'lat': latitude, 'lng': longitude};
|
||||||
|
//save to local Storage lat and long
|
||||||
|
_dataBox.write('lastLatitude', latitude);
|
||||||
|
_dataBox.write('lastLongitude', longitude);
|
||||||
|
var loc = await _locationRepository.getNearbyLocation(map);
|
||||||
|
ortController.text = loc;
|
||||||
|
// Print Standortinformationen in der Konsole
|
||||||
|
print('Nearby Location: $loc');
|
||||||
|
print('Current Position: Latitude: $latitude, Longitude: $longitude');
|
||||||
|
} catch (e) {
|
||||||
|
// Hier fängst du die Fehler aus dem Repository auf
|
||||||
|
errorMessage.value = e.toString();
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearTextEditingController() {
|
||||||
|
formKeyTank.currentState!.reset();
|
||||||
|
// Den Fokus wieder auf das erste Feld legen
|
||||||
|
FocusScope.of(Get.context!).requestFocus(firstFocusNode);
|
||||||
|
// TextEditingController zurücksetzen
|
||||||
|
_selectedDateTime = null; // Datum zurücksetzen
|
||||||
|
dateController.clear();
|
||||||
|
kilometerStandEdittingController.clear();
|
||||||
|
mengeController.clear();
|
||||||
|
pricePerLiterController.clear();
|
||||||
|
ortController.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
String? validateKilometerStand(String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Bitte Kilometerstand eingeben';
|
||||||
|
}
|
||||||
|
if (!GetUtils.isNum(value)) {
|
||||||
|
return 'Bitte geben Sie eine gültige Zahl ein';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? validateMenge(String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Bitte Menge eingeben';
|
||||||
|
}
|
||||||
|
if (!GetUtils.isNum(value)) {
|
||||||
|
return 'Bitte geben Sie eine gültige Zahl ein';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? validatePricePerLiter(String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Bitte Preis pro Liter eingeben';
|
||||||
|
}
|
||||||
|
if (!GetUtils.isNum(value)) {
|
||||||
|
return 'Bitte geben Sie eine gültige Zahl ein';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? validateOrt(String? value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'Bitte Ort eingeben...';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Go to Login Page
|
||||||
|
void logoutSessionAndGoToLoginPage() async {
|
||||||
|
// Handle logout logic here
|
||||||
|
print('Logout session and go to login page');
|
||||||
|
// Clear GetStorage session ID
|
||||||
|
StaticHelper.removeFromStorage();
|
||||||
|
print('Session ID removed from GetStorage');
|
||||||
|
await _authRepository.logout();
|
||||||
|
Get.offAndToNamed(LoginPage.namedRoute);
|
||||||
|
StaticHelper.getMySnackeBar(
|
||||||
|
'Logout', 'Sie wurden abgemeldet!', Colors.blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
handleInputOrUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleInputOrUpdate() {
|
||||||
|
var mapFromArguments = Get.arguments as Map<String, dynamic>?;
|
||||||
|
if (mapFromArguments != null && mapFromArguments['from'] != null) {
|
||||||
|
isEditMode.value = mapFromArguments['from'] == 'Input' ? true : false;
|
||||||
|
}
|
||||||
|
//bei true ein insert bei false ein update
|
||||||
|
if (isEditMode.value) {
|
||||||
|
fetchCurrentLocation();
|
||||||
|
} else {
|
||||||
|
// Im Editiermodus, keine Standortabfrage durchführen
|
||||||
|
print('Im Editiermodus, keine Standortabfrage durchführen');
|
||||||
|
// Den Fokus auf das erste Eingabefeld setzen
|
||||||
|
FocusScope.of(Get.context!).requestFocus(firstFocusNode);
|
||||||
|
print('TankController is in Edit Mode');
|
||||||
|
if (mapFromArguments != null && mapFromArguments['data'] != null) {
|
||||||
|
var dataMap = mapFromArguments['data'] as Map<String, dynamic>;
|
||||||
|
print('Data Map for Edit Mode: $dataMap');
|
||||||
|
// Setze die documentId für spätere Updates
|
||||||
|
documentId = dataMap['\$id'] ?? '';
|
||||||
|
// Setze die Werte in die TextEditingController
|
||||||
|
dateController.text = dataMap['date'] ?? '';
|
||||||
|
kilometerStandEdittingController.text = dataMap['odometer'] ?? '';
|
||||||
|
mengeController.text = dataMap['liters'] ?? '';
|
||||||
|
pricePerLiterController.text = dataMap['pricePerLiter'] ?? '';
|
||||||
|
ortController.text = dataMap['location'] ?? '';
|
||||||
|
// Berechne den Gesamtpreis
|
||||||
|
updateSumPrice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() async {
|
||||||
|
super.onReady();
|
||||||
|
if (_dataBox.hasData('sessionId')) {
|
||||||
|
rxSessionIdString(_dataBox.read('sessionId').toString());
|
||||||
|
}
|
||||||
|
await getCurrentLoggedinUser();
|
||||||
|
FocusScope.of(Get.context!).requestFocus(firstFocusNode);
|
||||||
|
print('TankController is ready');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getCurrentLoggedinUser() async {
|
||||||
|
await _authRepository.getCurrentUser.then((models.User user) {
|
||||||
|
// Hier kannst du den Benutzernamen und das Avatar-Zeichen setzen
|
||||||
|
userNameToDisplay.value = user.name;
|
||||||
|
circleAvatarUserChar.value = user.name.substring(0, 1).toUpperCase();
|
||||||
|
}).catchError((error) {
|
||||||
|
print('Fehler beim Abrufen des Benutzers: $error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
dateController.dispose();
|
||||||
|
kilometerStandEdittingController.dispose();
|
||||||
|
mengeController.dispose();
|
||||||
|
pricePerLiterController.dispose();
|
||||||
|
ortController.dispose();
|
||||||
|
firstFocusNode.dispose(); // Dispose den FocusNode
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSumPrice() {
|
||||||
|
final double menge = double.tryParse(mengeController.text) ?? 0.0;
|
||||||
|
final double pricePerLiter =
|
||||||
|
double.tryParse(pricePerLiterController.text) ?? 0.0;
|
||||||
|
final double sumPrice = menge * pricePerLiter;
|
||||||
|
rxSummePreisString.value = sumPrice.toStringAsFixed(
|
||||||
|
2,
|
||||||
|
); // Formatieren auf 2 Dezimalstellen
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveTankstopp() async {
|
||||||
|
bool isErrorBySaving = false;
|
||||||
|
String messageToUser = '';
|
||||||
|
isFormValid = formKeyTank.currentState!.validate();
|
||||||
|
if (!isFormValid) {
|
||||||
|
print('Formular ist ungültig');
|
||||||
|
messageToUser = 'Bitte überprüfen Sie Ihre Eingaben.';
|
||||||
|
Get.snackbar(
|
||||||
|
'Fehler',
|
||||||
|
messageToUser,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
print('Formular ist gültig');
|
||||||
|
formKeyTank.currentState!.save();
|
||||||
|
try {
|
||||||
|
//false update a tank stop
|
||||||
|
if (isEditMode.value == false) {
|
||||||
|
await _authRepository.updateTankStop(documentId, {
|
||||||
|
'userId': _dataBox.read('userId'),
|
||||||
|
'date': f.format(DateTime.parse(dateController.text)),
|
||||||
|
'odometer': kilometerStandEdittingController.text,
|
||||||
|
'liters': mengeController.text,
|
||||||
|
'pricePerLiter': pricePerLiterController.text,
|
||||||
|
'location': ortController.text,
|
||||||
|
}).then((models.Document document) {
|
||||||
|
print('Tankstopp erfolgreich aktualisiert: ${document.data}');
|
||||||
|
messageToUser = 'Tankstopp erfolgreich aktualisiert!';
|
||||||
|
isErrorBySaving = false;
|
||||||
|
})
|
||||||
|
// Handle specific Appwrite exceptions
|
||||||
|
.catchError((error) {
|
||||||
|
isErrorBySaving = true;
|
||||||
|
if (error is AppwriteException) {
|
||||||
|
// Handle specific Appwrite exceptions
|
||||||
|
messageToUser = 'Appwrite Fehler: ${error.message}';
|
||||||
|
print('Appwrite Fehler: ${error.message}');
|
||||||
|
} else {
|
||||||
|
// Handle other types of errors
|
||||||
|
messageToUser = 'Allgemeiner Fehler: $error';
|
||||||
|
print('Allgemeiner Fehler: $error');
|
||||||
|
}
|
||||||
|
print('Fehler bei der speicherung: $error');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//true creating a tank stop
|
||||||
|
await _authRepository.createTankStop({
|
||||||
|
'userId': _dataBox.read('userId'),
|
||||||
|
'date': f.format(DateTime.parse(dateController.text)),
|
||||||
|
'odometer': kilometerStandEdittingController.text,
|
||||||
|
'liters': mengeController.text,
|
||||||
|
'pricePerLiter': pricePerLiterController.text,
|
||||||
|
'location': ortController.text,
|
||||||
|
}).then((models.Document document) {
|
||||||
|
print('Tankstopp erfolgreich gespeichert: ${document.data}');
|
||||||
|
messageToUser = 'Tankstopp erfolgreich gespeichert!';
|
||||||
|
isErrorBySaving = false;
|
||||||
|
})
|
||||||
|
// Handle specific Appwrite exceptions
|
||||||
|
.catchError((error) {
|
||||||
|
isErrorBySaving = true;
|
||||||
|
if (error is AppwriteException) {
|
||||||
|
// Handle specific Appwrite exceptions
|
||||||
|
messageToUser = 'Appwrite Fehler: ${error.message}';
|
||||||
|
print('Appwrite Fehler: ${error.message}');
|
||||||
|
} else {
|
||||||
|
// Handle other types of errors
|
||||||
|
messageToUser = 'Allgemeiner Fehler: $error';
|
||||||
|
print('Allgemeiner Fehler: $error');
|
||||||
|
}
|
||||||
|
print('Fehler bei der speicherung: $error');
|
||||||
|
});
|
||||||
|
// bei true ein insert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Handle any other exceptions that might occur
|
||||||
|
catch (e) {
|
||||||
|
isErrorBySaving = true;
|
||||||
|
messageToUser = 'Fehler beim Speichern des Tankstopps: $e';
|
||||||
|
print('Fehler beim speichern: $e');
|
||||||
|
}
|
||||||
|
if (!isErrorBySaving) {
|
||||||
|
clearTextEditingController();
|
||||||
|
}
|
||||||
|
// Handle button press logic here
|
||||||
|
String title = isErrorBySaving ? 'Fehler' : 'Erfolg';
|
||||||
|
Get.snackbar(
|
||||||
|
title,
|
||||||
|
messageToUser,
|
||||||
|
backgroundColor: isErrorBySaving ? Colors.red : Colors.green,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
duration: const Duration(seconds: 4),
|
||||||
|
);
|
||||||
|
goToTankStopsView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goToTankStopsView() async {
|
||||||
|
clearTextEditingController();
|
||||||
|
await Get.offAndToNamed(TanklistPage.namedRoute);
|
||||||
|
}
|
||||||
|
}
|
||||||
304
lib/pages/tank/tank_view.dart
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import './tank_controller.dart';
|
||||||
|
|
||||||
|
class TankPage extends GetView<TankController> {
|
||||||
|
static const namedRoute = '/tank-stop-page';
|
||||||
|
const TankPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var tankCtrl = controller;
|
||||||
|
return SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(
|
||||||
|
'Tankstopp erfassen',
|
||||||
|
style: TextStyle(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.transparent, // Mach die AppBar transparent
|
||||||
|
elevation: 0, // Entferne den Schatten unter der AppBar
|
||||||
|
centerTitle: true,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.list, color: Colors.grey.shade300),
|
||||||
|
onPressed: () async {
|
||||||
|
// Handle logout logic here
|
||||||
|
controller.goToTankStopsView();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.logout, color: Colors.grey.shade300),
|
||||||
|
onPressed: () async {
|
||||||
|
// Handle logout logic here
|
||||||
|
controller.logoutSessionAndGoToLoginPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
extendBodyBehindAppBar: true, // Erweitere den Body hinter die AppBar
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: double.infinity,
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage('lib/images/backgroundPitstopBlack.png'),
|
||||||
|
fit: BoxFit
|
||||||
|
.cover, // Skaliert das Bild, um den Container zu füllen
|
||||||
|
alignment: Alignment
|
||||||
|
.bottomCenter, // Richte das Bild am unteren Rand aus
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => PopScope(
|
||||||
|
canPop: false,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: MediaQuery.of(context).padding.top +
|
||||||
|
kToolbarHeight +
|
||||||
|
20,
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
bottom: 20,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
radius: 40,
|
||||||
|
child: Text(
|
||||||
|
controller.circleAvatarUserChar.value,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Text(
|
||||||
|
controller.userNameToDisplay.value,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
backgroundColor: Colors.black87,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
letterSpacing: 3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
inputFields(),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
color: Colors.black.withValues(alpha: 0.9),
|
||||||
|
),
|
||||||
|
child: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: 'Summe: ',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
),
|
||||||
|
children: <TextSpan>[
|
||||||
|
TextSpan(
|
||||||
|
// Hier kommt der Wert als separater TextSpan
|
||||||
|
text: controller.rxSummePreisString.value,
|
||||||
|
// Doppelt so groß wie der Standardtext',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 30, // Doppelt so groß wie 18
|
||||||
|
color: Colors.blue, // Blaue Farbe
|
||||||
|
fontWeight: FontWeight
|
||||||
|
.bold, // Optional: Fetter Text, um ihn hervorzuheben
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
SizedBox(
|
||||||
|
width: 350,
|
||||||
|
height: 50,
|
||||||
|
child: Obx(
|
||||||
|
() => ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Colors.blue.withValues(
|
||||||
|
alpha: 0.9,
|
||||||
|
), // Button-Farbe
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
10,
|
||||||
|
), // Abgerundete Ecken
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
controller.saveTankstopp();
|
||||||
|
},
|
||||||
|
child: tankCtrl.isEditMode.value == true ? Text(
|
||||||
|
'Tankstop erfassen',
|
||||||
|
style:
|
||||||
|
TextStyle(color: Colors.white, fontSize: 20),
|
||||||
|
): Text(
|
||||||
|
'Tankstop updaten',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white, fontSize: 20),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20, bottom: 50),
|
||||||
|
child: Text(
|
||||||
|
'Hinweis: Alle Felder sind Pflichtfelder.',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.red.shade300,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget inputFields() {
|
||||||
|
return SizedBox(
|
||||||
|
width: 320,
|
||||||
|
child: Form(
|
||||||
|
key: controller.formKeyTank,
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
validator: (value) => controller.validateOrt(value),
|
||||||
|
focusNode: controller.firstFocusNode, // Setze den FocusNode
|
||||||
|
//onTap: () => controller.selectDateTime(Get.context!),
|
||||||
|
controller: controller.ortController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Ort der Tankstelle',
|
||||||
|
labelStyle: const TextStyle(color: Colors.white, fontSize: 20),
|
||||||
|
errorStyle: const TextStyle(color: Colors.red, fontSize: 16),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.black.withValues(alpha: 0.9),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.white, width: 2),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide.none, // Entferne den Rand
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
TextFormField(
|
||||||
|
//focusNode: controller.firstFocusNode, // Setze den FocusNode
|
||||||
|
readOnly: true,
|
||||||
|
onTap: () => controller.selectDateTime(Get.context!),
|
||||||
|
keyboardType: TextInputType.text,
|
||||||
|
controller: controller.dateController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Datum',
|
||||||
|
labelStyle: const TextStyle(color: Colors.white, fontSize: 20),
|
||||||
|
errorStyle: const TextStyle(color: Colors.red, fontSize: 16),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.black.withValues(alpha: 0.9),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.white, width: 2),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide.none, // Entferne den Rand
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
TextFormField(
|
||||||
|
validator: (value) => controller.validateKilometerStand(value),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
controller: controller.kilometerStandEdittingController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Kilometerstand',
|
||||||
|
labelStyle: const TextStyle(color: Colors.white, fontSize: 20),
|
||||||
|
errorStyle: const TextStyle(color: Colors.red, fontSize: 16),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.black.withValues(alpha: 0.9),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.white, width: 2),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide.none, // Entferne den Rand
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
TextFormField(
|
||||||
|
validator: (value) => controller.validateMenge(value),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
controller: controller.mengeController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Menge (in Litern)',
|
||||||
|
labelStyle: const TextStyle(color: Colors.white, fontSize: 20),
|
||||||
|
errorStyle: const TextStyle(color: Colors.red, fontSize: 16),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.black.withValues(alpha: 0.9),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.white, width: 2),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide.none, // Entferne den Rand
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
TextFormField(
|
||||||
|
onChanged: (value) {
|
||||||
|
// Update the sum price when the price per liter changes
|
||||||
|
controller.updateSumPrice();
|
||||||
|
},
|
||||||
|
validator: (value) => controller.validatePricePerLiter(value),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
controller: controller.pricePerLiterController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Preis pro Liter',
|
||||||
|
labelStyle: const TextStyle(color: Colors.white, fontSize: 20),
|
||||||
|
errorStyle: const TextStyle(color: Colors.red, fontSize: 16),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(color: Colors.white, width: 2),
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.black.withValues(alpha: 0.9),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
//borderSide: BorderSide.none, // Entferne den Rand
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
179
lib/pages/tanklist/tanklist_controller.dart
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
import 'package:appwrite/appwrite.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:get_storage/get_storage.dart';
|
||||||
|
|
||||||
|
import '../login/login_view.dart';
|
||||||
|
import '../graph/graph_view.dart';
|
||||||
|
import '../../data/models/tank_model.dart';
|
||||||
|
import '../../data/repository/appwrite_repository.dart';
|
||||||
|
import '../../pages/tank/tank_view.dart';
|
||||||
|
import '../../pages/gaslist/gaslist_view.dart';
|
||||||
|
import '../print/print_view.dart';
|
||||||
|
|
||||||
|
class TanklistController extends GetxController {
|
||||||
|
final _dataBox = GetStorage('MyUserStorage');
|
||||||
|
final isloadingList = false.obs;
|
||||||
|
final rxTankListAlles = <AppWriteTankModel>[].obs;
|
||||||
|
final rxTankListActualYear = <AppWriteTankModel>[].obs;
|
||||||
|
final szRxUserId = 'NoUser'.obs;
|
||||||
|
//AppWrite API-REST get Data
|
||||||
|
final AppwriteRepository _authRepository = AppwriteRepository();
|
||||||
|
final szRxYear = DateTime.now().year.obs.toString().obs;
|
||||||
|
final szRxSummeYearLiter = '0.0'.obs;
|
||||||
|
final szRxSummePrice = '0.0'.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
szRxUserId(_dataBox.read('userId'));
|
||||||
|
super.onInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onReady() {
|
||||||
|
getTankList();
|
||||||
|
super.onReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {}
|
||||||
|
|
||||||
|
Future<void> getTankList() async {
|
||||||
|
isloadingList(true);
|
||||||
|
bool isErrorByLoading = false;
|
||||||
|
String message = '';
|
||||||
|
try {
|
||||||
|
await _authRepository
|
||||||
|
.listTankStops(szRxUserId.value)
|
||||||
|
.then((tankListData) {
|
||||||
|
if (tankListData.documents.isEmpty) {
|
||||||
|
isErrorByLoading = false;
|
||||||
|
message = 'Leere Liste keine Daten vorhanden';
|
||||||
|
isloadingList(false);
|
||||||
|
rxTankListAlles.clear();
|
||||||
|
rxTankListActualYear.clear();
|
||||||
|
szRxSummeYearLiter('0.0');
|
||||||
|
szRxSummePrice('0.0');
|
||||||
|
update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rxTankListAlles.clear();
|
||||||
|
var data = tankListData.toMap();
|
||||||
|
List d = data['documents'].toList();
|
||||||
|
rxTankListAlles.value =
|
||||||
|
d.map((e) => AppWriteTankModel.fromMap(e['data'])).toList();
|
||||||
|
rxTankListAlles.sort((a, b) {
|
||||||
|
final DateTime dateA = DateTime.parse(a.date);
|
||||||
|
final DateTime dateB = DateTime.parse(b.date);
|
||||||
|
return dateB.compareTo(dateA);
|
||||||
|
});
|
||||||
|
//Tank List per Actual year**********
|
||||||
|
rxTankListActualYear.clear();
|
||||||
|
rxTankListActualYear.value = rxTankListAlles
|
||||||
|
.where((tank) => tank.date.startsWith(szRxYear.value))
|
||||||
|
.toList();
|
||||||
|
// rxTankListActualYear.value = rxTankListAlles;
|
||||||
|
//Summe Liter aktuelles Jahr*********
|
||||||
|
szRxSummeYearLiter(
|
||||||
|
rxTankListActualYear.fold<double>(0.0, (previousValue, element) {
|
||||||
|
return previousValue + (double.tryParse(element.liters) ?? 0.0);
|
||||||
|
}).toStringAsFixed(2));
|
||||||
|
//Summe Preis aktuelles Jahr*********
|
||||||
|
szRxSummePrice(
|
||||||
|
rxTankListActualYear.fold<double>(0.0, (previousValue, element) {
|
||||||
|
return previousValue +
|
||||||
|
(double.tryParse(element.szSummePreis!) ?? 0.0);
|
||||||
|
}).toStringAsFixed(2));
|
||||||
|
message = 'Liste wurde erfolgreich geladen';
|
||||||
|
isErrorByLoading = false;
|
||||||
|
}).catchError((error) {
|
||||||
|
isErrorByLoading = true;
|
||||||
|
if (error is AppwriteException) {
|
||||||
|
message = error.message!;
|
||||||
|
} else {
|
||||||
|
message = 'Uuups da ist was schief gelaufen';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
isErrorByLoading = true;
|
||||||
|
message = 'Fehler beim Laden der Tankliste';
|
||||||
|
print('Error fetching tank list: $e');
|
||||||
|
} finally {
|
||||||
|
if (!isErrorByLoading) {
|
||||||
|
isloadingList(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String title = isErrorByLoading ? 'Fehler' : 'Erfolg';
|
||||||
|
print('$title: $message');
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToUpdatePage(AppWriteTankModel item) {
|
||||||
|
var map = item.toMap();
|
||||||
|
print('Go to Update Page with data: $map');
|
||||||
|
// Go to Update Page
|
||||||
|
Get.offAndToNamed(TankPage.namedRoute,
|
||||||
|
arguments: {'from': 'Update', 'data': map});
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToInputPage() {
|
||||||
|
Get.offAndToNamed(TankPage.namedRoute, arguments: {'from': 'Input'});
|
||||||
|
}
|
||||||
|
|
||||||
|
void logoutSessionAndGoToLoginPage() async {
|
||||||
|
// Handle logout logic here
|
||||||
|
print('Logout session and go to login page');
|
||||||
|
// Clear GetStorage session ID
|
||||||
|
_dataBox.remove('sessionId');
|
||||||
|
_dataBox.remove('userId');
|
||||||
|
_dataBox.remove('userName');
|
||||||
|
_dataBox.remove('userEmail');
|
||||||
|
print('Session ID removed from GetStorage');
|
||||||
|
await _authRepository.logout();
|
||||||
|
Get.offAndToNamed(LoginPage.namedRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToChartView() {
|
||||||
|
Get.offAndToNamed(GraphPage.namedRoute);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteTankStop(String documentId) async {
|
||||||
|
bool isErrorByLoading = false;
|
||||||
|
String message = '';
|
||||||
|
try {
|
||||||
|
await _authRepository.deleteTankStop(documentId).then((response) async {
|
||||||
|
await getTankList();
|
||||||
|
message = 'Tankstopp wurde erfolgreich gelöscht';
|
||||||
|
}).catchError((error) {
|
||||||
|
isErrorByLoading = true;
|
||||||
|
if (error is AppwriteException) {
|
||||||
|
message = error.message!;
|
||||||
|
} else {
|
||||||
|
message = 'Uuups da ist was schief gelaufen';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
isErrorByLoading = true;
|
||||||
|
message = 'Fehler beim Löschen des Tankstopps';
|
||||||
|
print('Error deleting tank stop: $e');
|
||||||
|
}
|
||||||
|
String title = isErrorByLoading ? 'Fehler' : 'Erfolg';
|
||||||
|
print('$title: $message');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> goToGasView() async {
|
||||||
|
//Go to Gas Station List Page
|
||||||
|
await Get.offAndToNamed(GaslistPage.namedRoute);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToPrintView() {
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> tankMap = rxTankListActualYear.map((e) => e.toMap()).toList();
|
||||||
|
Get.offAndToNamed(PrintPage.namedRoute, arguments: {
|
||||||
|
'tankList': tankMap,
|
||||||
|
'year': szRxYear.value,
|
||||||
|
'summeLiter': szRxSummeYearLiter.value,
|
||||||
|
'summePreis': szRxSummePrice.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
169
lib/pages/tanklist/tanklist_view.dart
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import './tanklist_controller.dart';
|
||||||
|
import './widgets/my_list_tile_item.dart';
|
||||||
|
|
||||||
|
class TanklistPage extends GetView<TanklistController> {
|
||||||
|
static const namedRoute = '/tank-stop-liste-page';
|
||||||
|
const TanklistPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var tankListCtrl = controller;
|
||||||
|
return PopScope(
|
||||||
|
canPop: false,
|
||||||
|
child: SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
shadowColor: Colors.grey,
|
||||||
|
title: Text('Tankstops'),
|
||||||
|
centerTitle: true,
|
||||||
|
//backgroundColor: Colors.grey.shade600,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.add_chart, color: Colors.grey.shade300),
|
||||||
|
onPressed: () async {
|
||||||
|
// Handle go to Chart View
|
||||||
|
tankListCtrl.goToChartView();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.local_gas_station_sharp, color: Colors.grey.shade300),
|
||||||
|
onPressed: () async {
|
||||||
|
// Handle go to Chart View
|
||||||
|
tankListCtrl.goToGasView();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.print,
|
||||||
|
color: Colors.grey.shade300),
|
||||||
|
onPressed: () async {
|
||||||
|
// Handle go to Chart View
|
||||||
|
tankListCtrl.goToPrintView();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.logout, color: Colors.grey.shade300),
|
||||||
|
onPressed: () async {
|
||||||
|
// Handle logout logic here
|
||||||
|
tankListCtrl.logoutSessionAndGoToLoginPage();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
floatingActionButton: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment
|
||||||
|
.end, // Positioniere die Buttons am unteren Ende
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end, // Richte sie rechts aus
|
||||||
|
mainAxisSize: MainAxisSize.min, // Nimm nur den benötigten Platz ein
|
||||||
|
children: [
|
||||||
|
FloatingActionButton(
|
||||||
|
onPressed: () => tankListCtrl.goToInputPage(),
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
child: Icon(Icons.add),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Obx(() => tankListCtrl.isloadingList.value == false
|
||||||
|
? Padding(
|
||||||
|
padding: EdgeInsetsGeometry.only(left: 25, right: 25),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(tankListCtrl.szRxYear.value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 25, color: Colors.orange)),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text('Jahresverbrauch',
|
||||||
|
style: TextStyle(fontSize: 14)),
|
||||||
|
Text(tankListCtrl.szRxSummeYearLiter.value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.orange)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text('Jahressumme',
|
||||||
|
style: TextStyle(fontSize: 14)),
|
||||||
|
Text(tankListCtrl.szRxSummePrice.value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.orange)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
color: Colors.grey.shade200,
|
||||||
|
height: 0.0,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: tankListCtrl.rxTankListActualYear.isNotEmpty ? ListView.builder(
|
||||||
|
padding: EdgeInsets.only(top: 8, bottom: 8),
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
itemBuilder: ((BuildContext context, int index) {
|
||||||
|
var item = tankListCtrl.rxTankListActualYear[index];
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.blue.withValues(
|
||||||
|
alpha: 0.35,
|
||||||
|
), // Die Farbe des Schattens
|
||||||
|
spreadRadius:
|
||||||
|
1, // Wie weit sich der Schatten ausbreitet
|
||||||
|
blurRadius:
|
||||||
|
1, // Wie stark der Schatten verschwommen ist
|
||||||
|
offset: const Offset(
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
), // Der Versatz des Schattens (x, y)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: MyListTileItem(
|
||||||
|
listItem: item,
|
||||||
|
onPressedEdit: () {
|
||||||
|
tankListCtrl.goToUpdatePage(item);
|
||||||
|
},
|
||||||
|
onPressedDelete: () {
|
||||||
|
tankListCtrl.deleteTankStop(item.documentId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 15),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
itemCount: tankListCtrl.rxTankListActualYear.length,
|
||||||
|
): Center(
|
||||||
|
child: Text('Keine Einträge für das aktuelle Jahr vorhanden',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18, color: Colors.grey.shade600)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
131
lib/pages/tanklist/widgets/my_list_tile_item.dart
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../data/models/tank_model.dart';
|
||||||
|
|
||||||
|
class MyListTileItem extends StatelessWidget {
|
||||||
|
const MyListTileItem(
|
||||||
|
{super.key, required this.listItem, required this.onPressedEdit, required this.onPressedDelete});
|
||||||
|
|
||||||
|
final AppWriteTankModel listItem;
|
||||||
|
final void Function()? onPressedEdit;
|
||||||
|
final void Function()? onPressedDelete;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return _myListItem(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _myListItem(BuildContext context) {
|
||||||
|
var textColor = Colors.orange.shade400;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Wrap(
|
||||||
|
alignment: WrapAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.date_range),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
listItem.date,
|
||||||
|
style: TextStyle(color: textColor, fontSize: 25),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Image.asset('lib/icons/kilometer.png'),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
'${listItem.odometer} km',
|
||||||
|
style: TextStyle(color: textColor, fontSize: 25),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(thickness: 3, color: Colors.black),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.local_gas_station),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
'${listItem.liters} L',
|
||||||
|
style: TextStyle(color: textColor, fontSize: 25),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(thickness: 1, color: Colors.black),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.euro),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
'${listItem.pricePerLiter} €/L ',
|
||||||
|
style: TextStyle(color: textColor, fontSize: 25),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(thickness: 1, color: Colors.black),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.price_change),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
Text('${listItem.szSummePreis} €',
|
||||||
|
style: TextStyle(color: textColor, fontSize: 25)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(thickness: 1, color: Colors.black),
|
||||||
|
Wrap(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.pin_drop),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
SizedBox(
|
||||||
|
width: 250,
|
||||||
|
child: Text(
|
||||||
|
listItem.location,
|
||||||
|
style: TextStyle(color: textColor, fontSize: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(thickness: 1, color: Colors.black),
|
||||||
|
Wrap(
|
||||||
|
spacing: 10,
|
||||||
|
children: [
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: onPressedEdit,
|
||||||
|
label: Text(
|
||||||
|
'Edit',
|
||||||
|
style: TextStyle(color: Colors.grey.shade200),
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.edit),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 3, 71, 128),
|
||||||
|
foregroundColor: Colors.grey.shade200,
|
||||||
|
shadowColor: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: onPressedDelete,
|
||||||
|
label: Text(
|
||||||
|
'Delete',
|
||||||
|
style: TextStyle(color: Colors.grey.shade200),
|
||||||
|
),
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 128, 3, 45),
|
||||||
|
foregroundColor: Colors.grey.shade200,
|
||||||
|
shadowColor: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:appwrite_flutter_starter_kit/utils/extensions/colors.dart';
|
import '../../utils/extensions/colors.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// Background color for gradients, blur, etc.
|
/// Background color for gradients, blur, etc.
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import 'package:appwrite_flutter_starter_kit/data/models/log.dart';
|
import '../../data/models/log.dart';
|
||||||
import 'package:appwrite_flutter_starter_kit/data/models/project_info.dart';
|
import '../../data/models/project_info.dart';
|
||||||
import 'package:appwrite_flutter_starter_kit/ui/components/responsive_layout.dart';
|
import '../../ui/components/responsive_layout.dart';
|
||||||
import 'package:appwrite_flutter_starter_kit/ui/components/single_wrap.dart';
|
import '../../ui/components/single_wrap.dart';
|
||||||
import 'package:appwrite_flutter_starter_kit/utils/extensions/colors.dart';
|
import '../../utils/extensions/colors.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class CollapsibleBottomSheet extends StatefulWidget {
|
class CollapsibleBottomSheet extends StatefulWidget {
|
||||||
@ -235,8 +235,8 @@ class ProjectSection extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Wrap(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
alignment: WrapAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
ProjectRow(title: "Endpoint", value: projectInfo.endpoint),
|
ProjectRow(title: "Endpoint", value: projectInfo.endpoint),
|
||||||
ProjectRow(title: "Project ID", value: projectInfo.projectId),
|
ProjectRow(title: "Project ID", value: projectInfo.projectId),
|
||||||
@ -491,7 +491,7 @@ class LogsTableRow extends StatelessWidget {
|
|||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
child: Text(
|
child: Text(
|
||||||
response.substring(0, 50),
|
response.length >= 50 ? response.substring(0, 50) : response,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import 'package:appwrite_flutter_starter_kit/utils/extensions/build_context.dart';
|
import '../../utils/extensions/build_context.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// A widget that animates a connection line with a checkmark in the middle.
|
/// A widget that animates a connection line with a checkmark in the middle.
|
||||||
@ -15,7 +15,7 @@ class ConnectionLine extends StatelessWidget {
|
|||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: context.widthFactor(
|
width: context.widthFactor(
|
||||||
mobileFactor: 0.25,
|
mobileFactor: 0.25,
|
||||||
largeScreenFactor: 0.15,
|
largeScreenFactor: 0.1,
|
||||||
),
|
),
|
||||||
child: Flex(
|
child: Flex(
|
||||||
direction: Axis.horizontal,
|
direction: Axis.horizontal,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import 'package:appwrite_flutter_starter_kit/data/models/status.dart';
|
import '../../data/models/status.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:appwrite_flutter_starter_kit/utils/extensions/build_context.dart';
|
import '../../ui/components/responsive_layout.dart';
|
||||||
|
import '../../utils/extensions/build_context.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
@ -8,7 +9,8 @@ class GettingStartedCards extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return ResponsiveLayout(
|
||||||
|
smallDeviceLayout: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -42,6 +44,51 @@ class GettingStartedCards extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
largeDeviceLayout: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: context.isExtraWideScreen ? 64 : 16.0, vertical: 16.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
spacing: 16,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: GeneralInfoCard(
|
||||||
|
title: "Edit your app",
|
||||||
|
link: null,
|
||||||
|
subtitle: const HighlightedText(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: GeneralInfoCard(
|
||||||
|
title: "Head to Appwrite Cloud",
|
||||||
|
link: "https://cloud.appwrite.io",
|
||||||
|
subtitle: const Text(
|
||||||
|
"Start managing your project from the Appwrite console",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Color(0xFF56565C),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: GeneralInfoCard(
|
||||||
|
title: "Explore docs",
|
||||||
|
link: "https://appwrite.io/docs",
|
||||||
|
subtitle: const Text(
|
||||||
|
"Discover the full power of Appwrite by diving into our documentation",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Color(0xFF56565C),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,9 +115,12 @@ class GeneralInfoCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LayoutBuilder(builder: (context, constraints) {
|
return LayoutBuilder(builder: (context, constraints) {
|
||||||
|
final double cardWidth = context.isExtraWideScreen
|
||||||
|
? constraints.maxWidth.clamp(0, 350)
|
||||||
|
: constraints.maxWidth;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
// `1` because we already have padding on sides.
|
width: cardWidth,
|
||||||
width: constraints.maxWidth * (context.isExtraWideScreen ? 0.55 : 1),
|
|
||||||
child: Card(
|
child: Card(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'package:appwrite_flutter_starter_kit/data/models/status.dart';
|
import '../../data/models/status.dart';
|
||||||
import 'package:appwrite_flutter_starter_kit/ui/icons/appwrite.dart';
|
import '../../ui/icons/appwrite.dart';
|
||||||
|
import '../../utils/extensions/build_context.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'connection_line.dart';
|
import 'connection_line.dart';
|
||||||
@ -21,9 +22,15 @@ class TopPlatformView extends StatelessWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
PlatformIcon(child: FlutterLogo(size: 40)),
|
PlatformIcon(
|
||||||
|
size: context.isExtraWideScreen ? 142 : 100,
|
||||||
|
child: FlutterLogo(size: context.isExtraWideScreen ? 56 : 40),
|
||||||
|
),
|
||||||
ConnectionLine(show: status == Status.success),
|
ConnectionLine(show: status == Status.success),
|
||||||
PlatformIcon(child: AppwriteIcon(size: 40)),
|
PlatformIcon(
|
||||||
|
size: context.isExtraWideScreen ? 142 : 100,
|
||||||
|
child: AppwriteIcon(size: context.isExtraWideScreen ? 56 : 40),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -51,7 +58,7 @@ class PlatformIcon extends StatelessWidget {
|
|||||||
height: size,
|
height: size,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFFAFAFD),
|
color: const Color(0xFFFAFAFD),
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(context.isExtraWideScreen ? size * 0.2 : 24),
|
||||||
border: Border.all(color: const Color(0x0A19191C), width: 1),
|
border: Border.all(color: const Color(0x0A19191C), width: 1),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@ -65,9 +72,10 @@ class PlatformIcon extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
width: size * 0.86,
|
width: size * 0.86,
|
||||||
height: size * 0.86,
|
height: size * 0.86,
|
||||||
|
margin: context.isExtraWideScreen ? EdgeInsets.all(8) : null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(context.isExtraWideScreen ? size * 0.2: 16),
|
||||||
border: Border.all(color: const Color(0xFFFAFAFB), width: 1),
|
border: Border.all(color: const Color(0xFFFAFAFB), width: 1),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
|
|||||||
@ -3,7 +3,11 @@ import 'dart:io';
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
|
|
||||||
|
import './extensions/http_overrides.dart';
|
||||||
|
|
||||||
/// A utility class for initializing the Flutter application.
|
/// A utility class for initializing the Flutter application.
|
||||||
///
|
///
|
||||||
@ -19,6 +23,11 @@ class AppInitializer {
|
|||||||
_ensureInitialized();
|
_ensureInitialized();
|
||||||
await _setupWindowDimensions();
|
await _setupWindowDimensions();
|
||||||
await _setupDeviceOrientation();
|
await _setupDeviceOrientation();
|
||||||
|
await _setupDateFormating();
|
||||||
|
//dotENV initial
|
||||||
|
await dotenv.load(fileName: '.env');
|
||||||
|
//Overrides initial
|
||||||
|
HttpOverrides.global = MyHttpOverrides();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures that Flutter bindings are initialized.
|
/// Ensures that Flutter bindings are initialized.
|
||||||
@ -51,4 +60,8 @@ class AppInitializer {
|
|||||||
overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top],
|
overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// Initialisiert die Lokalisierungsdaten für eine bestimmte Sprache
|
||||||
|
static Future<void> _setupDateFormating() async {
|
||||||
|
await initializeDateFormatting('de_DE', null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
lib/utils/extensions/constants.dart
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
var kTextStyle = TextStyle(fontSize: 20, color: Colors.grey.shade100);
|
||||||
|
var kTextStyleSub = TextStyle(fontSize: 16, color: Colors.grey.shade400);
|
||||||
|
var kColorEuroChart = Colors.red.shade500;
|
||||||
|
var kColorBenzinChart = Colors.yellow.shade500;
|
||||||
|
var kColorPerLiterChart = Colors.blueGrey.shade300;
|
||||||
|
var kChartDescriptionFontStyle = TextStyle(
|
||||||
|
color: Colors.grey.shade900,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
);
|
||||||
|
|
||||||
|
var kBarTitleStyle = TextStyle(
|
||||||
|
color: Colors.blue.shade900,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 14,
|
||||||
|
);
|
||||||
|
|
||||||
|
var kInputDecorationDropDownMenueYear = const InputDecoration(
|
||||||
|
prefixIcon: Icon(Icons.date_range),
|
||||||
|
hintText: 'Jahresauswahl',
|
||||||
|
filled: true,
|
||||||
|
fillColor: Colors.black,
|
||||||
|
errorStyle: TextStyle(color: Colors.yellow),
|
||||||
|
);
|
||||||
9
lib/utils/extensions/http_overrides.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
class MyHttpOverrides extends HttpOverrides{
|
||||||
|
@override
|
||||||
|
HttpClient createHttpClient(SecurityContext? context){
|
||||||
|
return super.createHttpClient(context)
|
||||||
|
..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
lib/utils/extensions/sample_bindings.dart
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import '../../pages/gaslist/gaslist_controller.dart';
|
||||||
|
import '../../pages/graph/graph_controller.dart';
|
||||||
|
import '../../pages/login/login_controller.dart';
|
||||||
|
import '../../pages/tank/tank_controller.dart';
|
||||||
|
import '../../pages/tanklist/tanklist_controller.dart';
|
||||||
|
import '../../pages/print/print_controller.dart';
|
||||||
|
|
||||||
|
class SampleBindings extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
// Define your dependencies here
|
||||||
|
Get.lazyPut<LoginController>(() => LoginController());
|
||||||
|
Get.lazyPut<TankController>(() => TankController());
|
||||||
|
Get.lazyPut<TanklistController>(() => TanklistController());
|
||||||
|
Get.lazyPut<GraphController>(() => GraphController());
|
||||||
|
Get.lazyPut<GaslistController>(() => GaslistController());
|
||||||
|
Get.lazyPut<PrintController>(() => PrintController());
|
||||||
|
}
|
||||||
|
}
|
||||||
53
lib/utils/extensions/sample_routes.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import '../../pages/graph/graph_view.dart';
|
||||||
|
import '../../pages/login/login_view.dart';
|
||||||
|
import '../../pages/print/print_view.dart';
|
||||||
|
import '../../pages/tank/tank_view.dart';
|
||||||
|
import '../../pages/tanklist/tanklist_view.dart';
|
||||||
|
import '../../pages/gaslist/gaslist_view.dart';
|
||||||
|
import './sample_bindings.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SampleRouts {
|
||||||
|
static final sampleBindings = SampleBindings();
|
||||||
|
static List<GetPage<dynamic>> samplePages = [
|
||||||
|
GetPage(
|
||||||
|
name: LoginPage.namedRoute,
|
||||||
|
page: () => const LoginPage(),
|
||||||
|
binding: sampleBindings,
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: TankPage.namedRoute,
|
||||||
|
page: () => const TankPage(),
|
||||||
|
binding: sampleBindings,
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: TanklistPage.namedRoute,
|
||||||
|
page: () => const TanklistPage(),
|
||||||
|
binding: sampleBindings,
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: GraphPage.namedRoute,
|
||||||
|
page: () => const GraphPage(),
|
||||||
|
binding: sampleBindings,
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: GaslistPage.namedRoute,
|
||||||
|
page: () => const GaslistPage(),
|
||||||
|
binding: sampleBindings,
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: PrintPage.namedRoute,
|
||||||
|
page: () => const PrintPage(),
|
||||||
|
binding: sampleBindings,
|
||||||
|
),
|
||||||
|
// GetPage(
|
||||||
|
// name: MapPage.namedRoute,
|
||||||
|
// page: () => const MapPage(),
|
||||||
|
// binding: SampleBindings(),
|
||||||
|
// ),
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
89
lib/utils/extensions/static_helper.dart
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:get_storage/get_storage.dart';
|
||||||
|
|
||||||
|
import '../../data/models/tank_model.dart';
|
||||||
|
|
||||||
|
class StaticHelper {
|
||||||
|
static double sumMonatEuro = 0.0;
|
||||||
|
static double sumMonatLiter = 0.0;
|
||||||
|
static List staticSearchList = [];
|
||||||
|
|
||||||
|
static GetStorage dataBox = GetStorage('MyUserStorage');
|
||||||
|
|
||||||
|
static List listMonth = [
|
||||||
|
{'month': 'Jänner', 'value': '01'},
|
||||||
|
{'month': 'Februar', 'value': '02'},
|
||||||
|
{'month': 'März', 'value': '03'},
|
||||||
|
{'month': 'April', 'value': '04'},
|
||||||
|
{'month': 'Mai', 'value': '05'},
|
||||||
|
{'month': 'Juni', 'value': '06'},
|
||||||
|
{'month': 'Juli', 'value': '07'},
|
||||||
|
{'month': 'August', 'value': '08'},
|
||||||
|
{'month': 'September', 'value': '09'},
|
||||||
|
{'month': 'Oktober', 'value': '10'},
|
||||||
|
{'month': 'November', 'value': '11'},
|
||||||
|
{'month': 'Dezember', 'value': '12'},
|
||||||
|
];
|
||||||
|
|
||||||
|
static List staticListGetDaysInBetween(
|
||||||
|
List<AppWriteTankModel> list,
|
||||||
|
String monthValue,
|
||||||
|
int year,
|
||||||
|
) {
|
||||||
|
List<AppWriteTankModel> searchList = [];
|
||||||
|
List<DateTime> days = [];
|
||||||
|
var dateYear = year;
|
||||||
|
var auswahlMonat = int.parse(monthValue);
|
||||||
|
var startDate = DateTime(dateYear, auswahlMonat, 1);
|
||||||
|
var endDate = DateTime(dateYear, auswahlMonat + 1, 0);
|
||||||
|
//List of Days
|
||||||
|
for (int i = 0; i <= endDate.difference(startDate).inDays; i++) {
|
||||||
|
days.add(DateTime(startDate.year, startDate.month, startDate.day + i));
|
||||||
|
}
|
||||||
|
sumMonatEuro = 0.0;
|
||||||
|
sumMonatLiter = 0.0;
|
||||||
|
int entryCounter = 0;
|
||||||
|
for (var day in days) {
|
||||||
|
for (AppWriteTankModel model in list) {
|
||||||
|
int milliSecDate = (DateTime.parse(model.date)).microsecondsSinceEpoch;
|
||||||
|
if (milliSecDate == day.microsecondsSinceEpoch) {
|
||||||
|
entryCounter = entryCounter + 1;
|
||||||
|
model.mnIndexCount = entryCounter;
|
||||||
|
searchList.add(model);
|
||||||
|
var mnEuroGesamt =
|
||||||
|
double.parse(model.liters) * double.parse(model.pricePerLiter);
|
||||||
|
sumMonatEuro += mnEuroGesamt;
|
||||||
|
sumMonatLiter += double.parse(model.liters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
searchList.sort((a, b) {
|
||||||
|
final DateTime dateA = DateTime.parse(a.date);
|
||||||
|
final DateTime dateB = DateTime.parse(b.date);
|
||||||
|
return dateB.compareTo(dateA);
|
||||||
|
});
|
||||||
|
return staticSearchList = searchList;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getMySnackeBar(String title, String message, Color backgroundColor) {
|
||||||
|
Get.snackbar(
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
snackPosition: SnackPosition.BOTTOM,
|
||||||
|
duration: const Duration(seconds: 4),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeFromStorage() {
|
||||||
|
dataBox.remove('sessionId');
|
||||||
|
dataBox.remove('userId');
|
||||||
|
dataBox.remove('userName');
|
||||||
|
dataBox.remove('userEmail');
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasData() {
|
||||||
|
return dataBox.hasData('sessionId');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
#include <desktop_webview_window/desktop_webview_window_plugin.h>
|
||||||
|
#include <printing/printing_plugin.h>
|
||||||
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
|
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
@ -16,6 +17,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
|||||||
g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
|
g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
|
||||||
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
|
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) printing_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin");
|
||||||
|
printing_plugin_register_with_registrar(printing_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
|
g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
|
||||||
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
|
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
desktop_webview_window
|
desktop_webview_window
|
||||||
|
printing
|
||||||
screen_retriever_linux
|
screen_retriever_linux
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
window_manager
|
window_manager
|
||||||
|
|||||||
@ -8,8 +8,10 @@ import Foundation
|
|||||||
import desktop_webview_window
|
import desktop_webview_window
|
||||||
import device_info_plus
|
import device_info_plus
|
||||||
import flutter_web_auth_2
|
import flutter_web_auth_2
|
||||||
|
import geolocator_apple
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
|
import printing
|
||||||
import screen_retriever_macos
|
import screen_retriever_macos
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
import window_manager
|
import window_manager
|
||||||
@ -19,8 +21,10 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
|
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
|
||||||
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin"))
|
FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin"))
|
||||||
|
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
||||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin"))
|
||||||
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
|
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||||
|
|||||||
@ -67,7 +67,7 @@
|
|||||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||||
33CC10ED2044A3C60003C045 /* AppwriteStarterKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = appwrite_flutter_starter_kit.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
33CC10ED2044A3C60003C045 /* AppwriteStarterKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = tankguru_flutter_app_appwrite.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||||
|
|||||||
@ -2,6 +2,14 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string>This app needs access to your location to find nearby gas stations and services.</string>
|
||||||
|
<key>NSLocationAlwaysUsageDescription</key>
|
||||||
|
<string>This app needs access to your location to track your position even when the app is in the background.</string>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>location</string>
|
||||||
|
</array>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
|
|||||||
192
pubspec.lock
@ -41,6 +41,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
version: "2.11.0"
|
||||||
|
barcode:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: barcode
|
||||||
|
sha256: "7b6729c37e3b7f34233e2318d866e8c48ddb46c1f7ad01ff7bb2a8de1da2b9f4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.9"
|
||||||
|
bidi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: bidi
|
||||||
|
sha256: "77f475165e94b261745cf1032c751e2032b8ed92ccb2bf5716036db79320637d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.13"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -137,6 +153,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.2"
|
version: "7.0.2"
|
||||||
|
equatable:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -153,13 +177,37 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
fl_chart:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fl_chart
|
||||||
|
sha256: "577aeac8ca414c25333334d7c4bb246775234c0e44b38b10a82b559dd4d764e7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_dotenv:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_dotenv
|
||||||
|
sha256: b7c7be5cd9f6ef7a78429cabd2774d3c4af50e79cb2b7593e3d5d763ef95c61b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.2.1"
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
dependency: "direct dev"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_launcher_icons
|
name: flutter_launcher_icons
|
||||||
sha256: bfa04787c85d80ecb3f8777bde5fc10c3de809240c48fa061a2c2bf15ea5211c
|
sha256: bfa04787c85d80ecb3f8777bde5fc10c3de809240c48fa061a2c2bf15ea5211c
|
||||||
@ -203,6 +251,70 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
geolocator:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: geolocator
|
||||||
|
sha256: f62bcd90459e63210bbf9c35deb6a51c521f992a78de19a1fe5c11704f9530e2
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "13.0.4"
|
||||||
|
geolocator_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_android
|
||||||
|
sha256: fcb1760a50d7500deca37c9a666785c047139b5f9ee15aa5469fae7dbbe3170d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.6.2"
|
||||||
|
geolocator_apple:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_apple
|
||||||
|
sha256: dbdd8789d5aaf14cf69f74d4925ad1336b4433a6efdf2fce91e8955dc921bf22
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.13"
|
||||||
|
geolocator_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_platform_interface
|
||||||
|
sha256: "30cb64f0b9adcc0fb36f628b4ebf4f731a2961a0ebd849f4b56200205056fe67"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.6"
|
||||||
|
geolocator_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_web
|
||||||
|
sha256: b1ae9bdfd90f861fde8fd4f209c37b953d65e92823cb73c7dee1fa021b06f172
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.3"
|
||||||
|
geolocator_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: geolocator_windows
|
||||||
|
sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.5"
|
||||||
|
get:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: get
|
||||||
|
sha256: c79eeb4339f1f3deffd9ec912f8a923834bec55f7b49c9e882b8fef2c139d425
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.7.2"
|
||||||
|
get_storage:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: get_storage
|
||||||
|
sha256: "39db1fffe779d0c22b3a744376e86febe4ade43bf65e06eab5af707dc84185a2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
html:
|
html:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -212,13 +324,13 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.15.5"
|
version: "0.15.5"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.5.0"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -251,6 +363,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.9.0"
|
version: "4.9.0"
|
||||||
|
latlong2:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: latlong2
|
||||||
|
sha256: "98227922caf49e6056f91b6c56945ea1c7b166f28ffcd5fb8e72fc0b453cc8fe"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.1"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -299,6 +419,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
|
path_parsing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_parsing
|
||||||
|
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -347,6 +475,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
pdf:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: pdf
|
||||||
|
sha256: "28eacad99bffcce2e05bba24e50153890ad0255294f4dd78a17075a2ba5c8416"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.11.3"
|
||||||
|
pdf_widget_wrapper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pdf_widget_wrapper
|
||||||
|
sha256: c930860d987213a3d58c7ec3b7ecf8085c3897f773e8dc23da9cae60a5d6d0f5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
petitparser:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -379,6 +523,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.1"
|
version: "6.0.1"
|
||||||
|
printing:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: printing
|
||||||
|
sha256: "482cd5a5196008f984bb43ed0e47cbfdca7373490b62f3b27b3299275bf22a93"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.14.2"
|
||||||
|
qr:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: qr
|
||||||
|
sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
screen_retriever:
|
screen_retriever:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -432,6 +592,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
|
sprintf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sprintf
|
||||||
|
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -536,14 +704,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.4"
|
version: "3.1.4"
|
||||||
|
uuid:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: uuid
|
||||||
|
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.5.1"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.2.0"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -625,5 +801,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.7.0-0 <4.0.0"
|
dart: ">=3.8.0-0 <4.0.0"
|
||||||
flutter: ">=3.27.0"
|
flutter: ">=3.27.4"
|
||||||
|
|||||||
29
pubspec.yaml
@ -1,6 +1,6 @@
|
|||||||
name: appwrite_flutter_starter_kit
|
name: tankguru_flutter_app_appwrite
|
||||||
description: "Appwrite StarterKit in Flutter"
|
description: "Appwrite StarterKit in Flutter"
|
||||||
publish_to: 'none'
|
publish_to: "none"
|
||||||
|
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
|
||||||
@ -8,14 +8,23 @@ environment:
|
|||||||
sdk: ^3.5.4
|
sdk: ^3.5.4
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
appwrite: ^14.0.0
|
||||||
|
cupertino_icons: ^1.0.8
|
||||||
|
fl_chart: ^1.0.0
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_dotenv: ^5.2.1
|
||||||
|
flutter_launcher_icons: ^0.14.3
|
||||||
|
geolocator: ^13.0.1
|
||||||
|
get: ^4.6.6
|
||||||
|
get_storage: ^2.1.1
|
||||||
|
http: ^1.4.0
|
||||||
intl: ^0.20.2
|
intl: ^0.20.2
|
||||||
appwrite: ^14.0.0
|
latlong2: ^0.9.1
|
||||||
|
pdf: ^3.11.3
|
||||||
|
printing: ^5.14.2
|
||||||
url_launcher: ^6.3.1
|
url_launcher: ^6.3.1
|
||||||
window_manager: ^0.4.3
|
window_manager: ^0.4.3
|
||||||
cupertino_icons: ^1.0.8
|
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
flutter_web_auth_2: 4.1.0
|
flutter_web_auth_2: 4.1.0
|
||||||
@ -23,7 +32,15 @@ dependency_overrides:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^5.0.0
|
flutter_lints: ^5.0.0
|
||||||
flutter_native_splash: ^2.4.4
|
flutter_native_splash: ^2.4.4
|
||||||
flutter_launcher_icons: ^0.14.3
|
|
||||||
|
flutter_icons:
|
||||||
|
android: true
|
||||||
|
ios: true
|
||||||
|
image_path: "lib/icons/gasolineCyberpunk.png"
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- .env
|
||||||
|
- lib/images/
|
||||||
|
- lib/icons/
|
||||||
|
|||||||