Merge pull request 'hotfix/fix-upgrade' (#50) from hotfix/fix-upgrade into main
Reviewed-on: #50
45
covas_mobile_new/.metadata
Normal file
@@ -0,0 +1,45 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "05db9689081f091050f01aed79f04dce0c750154"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
base_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
- platform: android
|
||||
create_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
base_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
- platform: ios
|
||||
create_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
base_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
- platform: linux
|
||||
create_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
base_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
- platform: macos
|
||||
create_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
base_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
- platform: web
|
||||
create_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
base_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
- platform: windows
|
||||
create_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
base_revision: 05db9689081f091050f01aed79f04dce0c750154
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
16
covas_mobile_new/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# covas_mobile_new
|
||||
|
||||
A new Flutter project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
This project is a starting point for a Flutter application.
|
||||
|
||||
A few resources to get you started if this is your first Flutter project:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||
|
||||
For help getting started with Flutter development, view the
|
||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||
samples, guidance on mobile development, and a full API reference.
|
28
covas_mobile_new/analysis_options.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at https://dart.dev/lints.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
14
covas_mobile_new/android/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
GeneratedPluginRegistrant.java
|
||||
.cxx/
|
||||
|
||||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/to/reference-keystore
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
49
covas_mobile_new/android/app/build.gradle.kts
Normal file
@@ -0,0 +1,49 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
id("dev.flutter.flutter-gradle-plugin")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.covas_mobile_new"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "com.example.covas_mobile_new"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
minSdk = flutter.minSdkVersion
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
53
covas_mobile_new/android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
|
||||
<application
|
||||
android:label="covas_mobile_new"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
||||
android:value="ca-app-pub-4855855675386260~3438207239"/>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||
|
||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
@@ -0,0 +1,5 @@
|
||||
package com.example.covas_mobile_new
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity : FlutterActivity()
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 544 B |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 721 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
24
covas_mobile_new/android/build.gradle.kts
Normal file
@@ -0,0 +1,24 @@
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
val newBuildDir: Directory =
|
||||
rootProject.layout.buildDirectory
|
||||
.dir("../../build")
|
||||
.get()
|
||||
rootProject.layout.buildDirectory.value(newBuildDir)
|
||||
|
||||
subprojects {
|
||||
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
|
||||
project.layout.buildDirectory.value(newSubprojectBuildDir)
|
||||
}
|
||||
subprojects {
|
||||
project.evaluationDependsOn(":app")
|
||||
}
|
||||
|
||||
tasks.register<Delete>("clean") {
|
||||
delete(rootProject.layout.buildDirectory)
|
||||
}
|
3
covas_mobile_new/android/gradle.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
5
covas_mobile_new/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
26
covas_mobile_new/android/settings.gradle.kts
Normal file
@@ -0,0 +1,26 @@
|
||||
pluginManagement {
|
||||
val flutterSdkPath =
|
||||
run {
|
||||
val properties = java.util.Properties()
|
||||
file("local.properties").inputStream().use { properties.load(it) }
|
||||
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
||||
flutterSdkPath
|
||||
}
|
||||
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||
id("com.android.application") version "8.9.1" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
|
||||
}
|
||||
|
||||
include(":app")
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
26
covas_mobile_new/ios/Flutter/AppFrameworkInfo.plist
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>App</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.flutter.flutter.app</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>App</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>13.0</string>
|
||||
</dict>
|
||||
</plist>
|
616
covas_mobile_new/ios/Runner.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,616 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
|
||||
remoteInfo = Runner;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
331C807B294A618700263BE5 /* RunnerTests.swift */,
|
||||
);
|
||||
path = RunnerTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||
);
|
||||
name = Flutter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146E51CF9000F007C117D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146EF1CF9000F007C117D /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146EE1CF9000F007C117D /* Runner.app */,
|
||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146F01CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
97C147021CF9000F007C117D /* Info.plist */,
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
331C8080294A63A400263BE5 /* RunnerTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||
buildPhases = (
|
||||
331C807D294A63A400263BE5 /* Sources */,
|
||||
331C807F294A63A400263BE5 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
331C8086294A63A400263BE5 /* PBXTargetDependency */,
|
||||
);
|
||||
name = RunnerTests;
|
||||
productName = RunnerTests;
|
||||
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
97C146ED1CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
97C146E61CF9000F007C117D /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1510;
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
331C8080294A63A400263BE5 = {
|
||||
CreatedOnToolsVersion = 14.0;
|
||||
TestTargetID = 97C146ED1CF9000F007C117D;
|
||||
};
|
||||
97C146ED1CF9000F007C117D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
LastSwiftMigration = 1100;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 97C146E51CF9000F007C117D;
|
||||
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
97C146ED1CF9000F007C117D /* Runner */,
|
||||
331C8080294A63A400263BE5 /* RunnerTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
331C807F294A63A400263BE5 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
97C146EC1CF9000F007C117D /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
|
||||
);
|
||||
name = "Thin Binary";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||
};
|
||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
331C807D294A63A400263BE5 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
97C146EA1CF9000F007C117D /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 97C146ED1CF9000F007C117D /* Runner */;
|
||||
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C146FB1CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
97C147001CF9000F007C117D /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.covasMobileNew;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
331C8088294A63A400263BE5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.covasMobileNew.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
331C8089294A63A400263BE5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.covasMobileNew.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
331C808A294A63A400263BE5 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.covasMobileNew.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
97C147031CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147041CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
97C147061CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.covasMobileNew;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147071CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.covasMobileNew;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
331C8088294A63A400263BE5 /* Debug */,
|
||||
331C8089294A63A400263BE5 /* Release */,
|
||||
331C808A294A63A400263BE5 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147031CF9000F007C117D /* Debug */,
|
||||
97C147041CF9000F007C117D /* Release */,
|
||||
249021D3217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
97C147061CF9000F007C117D /* Debug */,
|
||||
97C147071CF9000F007C117D /* Release */,
|
||||
249021D4217E4FDB00AE95B9 /* Profile */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
}
|
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "331C8080294A63A400263BE5"
|
||||
BuildableName = "RunnerTests.xctest"
|
||||
BlueprintName = "RunnerTests"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUValidationMode = "1"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
||||
BuildableName = "Runner.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
13
covas_mobile_new/ios/Runner/AppDelegate.swift
Normal file
@@ -0,0 +1,13 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 295 B |
After Width: | Height: | Size: 406 B |
After Width: | Height: | Size: 450 B |
After Width: | Height: | Size: 282 B |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 704 B |
After Width: | Height: | Size: 406 B |
After Width: | Height: | Size: 586 B |
After Width: | Height: | Size: 862 B |
After Width: | Height: | Size: 862 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 762 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 68 B |
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 68 B |
Before Width: | Height: | Size: 68 B After Width: | Height: | Size: 68 B |
49
covas_mobile_new/ios/Runner/Info.plist
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Covas Mobile New</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>covas_mobile_new</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
12
covas_mobile_new/ios/RunnerTests/RunnerTests.swift
Normal file
@@ -0,0 +1,12 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
import XCTest
|
||||
|
||||
class RunnerTests: XCTestCase {
|
||||
|
||||
func testExample() {
|
||||
// If you add code to the Runner application, consider adding tests here.
|
||||
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
|
||||
}
|
||||
|
||||
}
|
5
covas_mobile_new/l10n.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
arb-dir: lib/l10n
|
||||
template-arb-file: app_en.arb
|
||||
output-localization-file: app_localizations.dart
|
||||
output-class: AppLocalizations
|
||||
output-dir: lib/gen_l10n
|
209
covas_mobile_new/lib/classes/MyDrawer.dart
Normal file
@@ -0,0 +1,209 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:io';
|
||||
import '../pages/EditProfile.dart';
|
||||
import '../pages/EditSettings.dart';
|
||||
import '../pages/ListItemMenu.dart';
|
||||
import 'alert.dart';
|
||||
|
||||
import '../variable/globals.dart' as globals;
|
||||
|
||||
import '../pages/LoginDemo.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv
|
||||
import 'package:encrypt_shared_preferences/provider.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
|
||||
class MyDrawer extends StatelessWidget with ShowAlertDialog {
|
||||
Future<void> logout(BuildContext context) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
|
||||
if (accessToken.isNotEmpty) {
|
||||
var url = Uri.parse("${globals.api}/token");
|
||||
|
||||
try {
|
||||
var response = await http.delete(url, headers: {
|
||||
"Content-Type": "application/json",
|
||||
HttpHeaders.cookieHeader: "access_token=${accessToken}"
|
||||
});
|
||||
|
||||
print("Status code logout ${response.statusCode}");
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
await prefs.remove("access_token");
|
||||
await dotenv.load(fileName: ".env"); // Load .env file
|
||||
|
||||
final keyEncrypt = dotenv.env['KEY_ENCRYPT'] ?? '';
|
||||
if (keyEncrypt.isNotEmpty) {
|
||||
await EncryptedSharedPreferences.initialize(keyEncrypt);
|
||||
var sharedPref = EncryptedSharedPreferences.getInstance();
|
||||
String username = sharedPref.getString("username") ?? "";
|
||||
String password = sharedPref.getString("password") ?? "";
|
||||
if ((username.isEmpty) || (password.isEmpty)) {
|
||||
sharedPref.remove("username");
|
||||
sharedPref.remove("password");
|
||||
}
|
||||
}
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => LoginDemo()),
|
||||
(route) => false, // Remove all previous routes
|
||||
);
|
||||
} else {
|
||||
String errorMessage;
|
||||
switch (response.statusCode) {
|
||||
case 400:
|
||||
errorMessage = "Bad Request: Please check your input.";
|
||||
break;
|
||||
case 401:
|
||||
errorMessage = "Unauthorized: Invalid credentials.";
|
||||
break;
|
||||
case 403:
|
||||
errorMessage = "Forbidden: You don't have permission.";
|
||||
break;
|
||||
case 404:
|
||||
errorMessage = "Not Found: The resource was not found.";
|
||||
break;
|
||||
case 500:
|
||||
errorMessage =
|
||||
"Server Error: Something went wrong on the server.";
|
||||
break;
|
||||
default:
|
||||
errorMessage = "Unexpected Error: ${response.statusCode}";
|
||||
break;
|
||||
}
|
||||
print(errorMessage);
|
||||
showAlertDialog(context, "Error", errorMessage);
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error: $e");
|
||||
showAlertDialog(
|
||||
context, "Error", "An error occurred. Please try again.");
|
||||
}
|
||||
} else {
|
||||
showAlertDialog(context, "Error", "Invalid token.");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
final localeProvider = Provider.of<LocaleProvider>(context);
|
||||
return Drawer(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.zero,
|
||||
children: [
|
||||
// Drawer Header
|
||||
DrawerHeader(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
),
|
||||
child: Text(
|
||||
'Menu',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
// Drawer Items
|
||||
ListTile(
|
||||
leading: Icon(Icons.home),
|
||||
title: Text(loc?.home ?? "Home"),
|
||||
onTap: () {
|
||||
Navigator.pushReplacement(
|
||||
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
|
||||
|
||||
/// Close the drawer
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.settings),
|
||||
title: Text(loc?.settings ?? 'Settings'),
|
||||
onTap: () {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => EditSettings())); // Close the drawer
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.account_circle),
|
||||
title: Text(loc?.update_profile ?? 'Update profile'),
|
||||
onTap: () {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => EditProfile())); // Close the drawer
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.language),
|
||||
title: Text(loc?.language ?? 'Language'),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AlertDialog(
|
||||
title: Text(loc?.select_language ?? 'Select Language'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: Icon(Icons.flag),
|
||||
title: Text(loc?.french ?? 'Français'),
|
||||
onTap: () {
|
||||
Provider.of<LocaleProvider>(context, listen: false)
|
||||
.setLocale(const Locale('fr'));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.flag_outlined),
|
||||
title: Text(loc?.english ?? 'English'),
|
||||
onTap: () {
|
||||
Provider.of<LocaleProvider>(context, listen: false)
|
||||
.setLocale(const Locale('en'));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.flag_outlined),
|
||||
title: Text(loc?.german ?? 'German'),
|
||||
onTap: () {
|
||||
Provider.of<LocaleProvider>(context, listen: false)
|
||||
.setLocale(const Locale('de'));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
ListTile(
|
||||
leading: Icon(Icons.info),
|
||||
title: Text(loc?.about ?? 'About'),
|
||||
onTap: () {
|
||||
showAlertDialog(context, loc?.about ?? 'About',
|
||||
"Version 0.0.1"); // Close the drawer
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.logout),
|
||||
title: Text(loc?.log_out ?? 'Log out'),
|
||||
onTap: () async {
|
||||
logout(context);
|
||||
// Close the drawer
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
89
covas_mobile_new/lib/classes/notification_service.dart
Normal file
@@ -0,0 +1,89 @@
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
import 'package:timezone/data/latest_all.dart' as tz;
|
||||
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class NotificationService {
|
||||
static final FlutterLocalNotificationsPlugin _notificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
|
||||
/// Initialisation (à appeler dans main())
|
||||
static Future<void> initialize() async {
|
||||
tz.initializeTimeZones();
|
||||
|
||||
const AndroidInitializationSettings androidInitSettings =
|
||||
AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
|
||||
const InitializationSettings initSettings = InitializationSettings(
|
||||
android: androidInitSettings,
|
||||
iOS: DarwinInitializationSettings(),
|
||||
);
|
||||
|
||||
await _notificationsPlugin.initialize(initSettings);
|
||||
|
||||
// Demande les permissions au lancement
|
||||
await requestPermissions();
|
||||
}
|
||||
|
||||
/// Demander les permissions (Android 13+ et iOS)
|
||||
static Future<void> requestPermissions() async {
|
||||
// Android 13+
|
||||
final androidImplementation =
|
||||
_notificationsPlugin.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>();
|
||||
await androidImplementation?.requestNotificationsPermission();
|
||||
|
||||
// iOS
|
||||
final iosImplementation =
|
||||
_notificationsPlugin.resolvePlatformSpecificImplementation<
|
||||
IOSFlutterLocalNotificationsPlugin>();
|
||||
await iosImplementation?.requestPermissions(
|
||||
alert: true,
|
||||
badge: true,
|
||||
sound: true,
|
||||
);
|
||||
}
|
||||
|
||||
/// Planifie une notification 1h avant l’évènement
|
||||
static Future<void> scheduleEventNotification({
|
||||
required String eventId,
|
||||
required String title,
|
||||
required String body,
|
||||
required DateTime eventDate,
|
||||
}) async {
|
||||
final scheduledDate = eventDate.subtract(const Duration(hours: 1));
|
||||
|
||||
if (scheduledDate.isBefore(DateTime.now())) {
|
||||
// Trop tard pour notifier
|
||||
return;
|
||||
}
|
||||
|
||||
await _notificationsPlugin.zonedSchedule(
|
||||
eventId.hashCode, // identifiant unique pour l’évènement
|
||||
title,
|
||||
body,
|
||||
tz.TZDateTime.from(scheduledDate, tz.local),
|
||||
const NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
'events_channel',
|
||||
'Events',
|
||||
channelDescription: 'Favorite event notifications ',
|
||||
importance: Importance.high,
|
||||
priority: Priority.high,
|
||||
),
|
||||
iOS: DarwinNotificationDetails(),
|
||||
),
|
||||
androidScheduleMode: AndroidScheduleMode
|
||||
.inexactAllowWhileIdle, // évite l'erreur Exact Alarm
|
||||
matchDateTimeComponents: DateTimeComponents.dateAndTime,
|
||||
);
|
||||
}
|
||||
|
||||
/// Annule une notification planifiée
|
||||
static Future<void> cancel(String eventId) async {
|
||||
await _notificationsPlugin.cancel(eventId.hashCode);
|
||||
}
|
||||
}
|
977
covas_mobile_new/lib/gen_l10n/app_localizations.dart
Normal file
@@ -0,0 +1,977 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
|
||||
import 'app_localizations_de.dart';
|
||||
import 'app_localizations_en.dart';
|
||||
import 'app_localizations_fr.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// Callers can lookup localized strings with an instance of AppLocalizations
|
||||
/// returned by `AppLocalizations.of(context)`.
|
||||
///
|
||||
/// Applications need to include `AppLocalizations.delegate()` in their app's
|
||||
/// `localizationDelegates` list, and the locales they support in the app's
|
||||
/// `supportedLocales` list. For example:
|
||||
///
|
||||
/// ```dart
|
||||
/// import 'gen_l10n/app_localizations.dart';
|
||||
///
|
||||
/// return MaterialApp(
|
||||
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
/// supportedLocales: AppLocalizations.supportedLocales,
|
||||
/// home: MyApplicationHome(),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// ## Update pubspec.yaml
|
||||
///
|
||||
/// Please make sure to update your pubspec.yaml to include the following
|
||||
/// packages:
|
||||
///
|
||||
/// ```yaml
|
||||
/// dependencies:
|
||||
/// # Internationalization support.
|
||||
/// flutter_localizations:
|
||||
/// sdk: flutter
|
||||
/// intl: any # Use the pinned version from flutter_localizations
|
||||
///
|
||||
/// # Rest of dependencies
|
||||
/// ```
|
||||
///
|
||||
/// ## iOS Applications
|
||||
///
|
||||
/// iOS applications define key application metadata, including supported
|
||||
/// locales, in an Info.plist file that is built into the application bundle.
|
||||
/// To configure the locales supported by your app, you’ll need to edit this
|
||||
/// file.
|
||||
///
|
||||
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
|
||||
/// Then, in the Project Navigator, open the Info.plist file under the Runner
|
||||
/// project’s Runner folder.
|
||||
///
|
||||
/// Next, select the Information Property List item, select Add Item from the
|
||||
/// Editor menu, then select Localizations from the pop-up menu.
|
||||
///
|
||||
/// Select and expand the newly-created Localizations item then, for each
|
||||
/// locale your application supports, add a new item and select the locale
|
||||
/// you wish to add from the pop-up menu in the Value field. This list should
|
||||
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
|
||||
/// property.
|
||||
abstract class AppLocalizations {
|
||||
AppLocalizations(String locale)
|
||||
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
|
||||
|
||||
final String localeName;
|
||||
|
||||
static AppLocalizations? of(BuildContext context) {
|
||||
return Localizations.of<AppLocalizations>(context, AppLocalizations);
|
||||
}
|
||||
|
||||
static const LocalizationsDelegate<AppLocalizations> delegate =
|
||||
_AppLocalizationsDelegate();
|
||||
|
||||
/// A list of this localizations delegate along with the default localizations
|
||||
/// delegates.
|
||||
///
|
||||
/// Returns a list of localizations delegates containing this delegate along with
|
||||
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
|
||||
/// and GlobalWidgetsLocalizations.delegate.
|
||||
///
|
||||
/// Additional delegates can be added by appending to this list in
|
||||
/// MaterialApp. This list does not have to be used at all if a custom list
|
||||
/// of delegates is preferred or required.
|
||||
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
|
||||
<LocalizationsDelegate<dynamic>>[
|
||||
delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
];
|
||||
|
||||
/// A list of this localizations delegate's supported locales.
|
||||
static const List<Locale> supportedLocales = <Locale>[
|
||||
Locale('de'),
|
||||
Locale('en'),
|
||||
Locale('fr')
|
||||
];
|
||||
|
||||
/// No description provided for @menu_list.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Event list menu'**
|
||||
String get menu_list;
|
||||
|
||||
/// No description provided for @language.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Language'**
|
||||
String get language;
|
||||
|
||||
/// No description provided for @home.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Home'**
|
||||
String get home;
|
||||
|
||||
/// No description provided for @settings.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Settings'**
|
||||
String get settings;
|
||||
|
||||
/// No description provided for @update_profile.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Update profile'**
|
||||
String get update_profile;
|
||||
|
||||
/// No description provided for @about.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'About'**
|
||||
String get about;
|
||||
|
||||
/// No description provided for @log_out.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Log out'**
|
||||
String get log_out;
|
||||
|
||||
/// No description provided for @french.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'French'**
|
||||
String get french;
|
||||
|
||||
/// No description provided for @english.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'English'**
|
||||
String get english;
|
||||
|
||||
/// No description provided for @german.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'German'**
|
||||
String get german;
|
||||
|
||||
/// No description provided for @select_language.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select language'**
|
||||
String get select_language;
|
||||
|
||||
/// No description provided for @search_item.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Search by item'**
|
||||
String get search_item;
|
||||
|
||||
/// No description provided for @search_tag.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Search by tags'**
|
||||
String get search_tag;
|
||||
|
||||
/// No description provided for @search_geographical.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Search by geographical zone'**
|
||||
String get search_geographical;
|
||||
|
||||
/// No description provided for @show_date_field.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Show Date Fields'**
|
||||
String get show_date_field;
|
||||
|
||||
/// No description provided for @hide_date_field.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Hide Date Fields'**
|
||||
String get hide_date_field;
|
||||
|
||||
/// No description provided for @no_data.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No data available'**
|
||||
String get no_data;
|
||||
|
||||
/// No description provided for @search.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Search'**
|
||||
String get search;
|
||||
|
||||
/// No description provided for @no_events.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No events available for this location.'**
|
||||
String get no_events;
|
||||
|
||||
/// No description provided for @start_date.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Start date'**
|
||||
String get start_date;
|
||||
|
||||
/// No description provided for @end_date.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'End date'**
|
||||
String get end_date;
|
||||
|
||||
/// No description provided for @failed_suggestions.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Failed to load suggestions'**
|
||||
String get failed_suggestions;
|
||||
|
||||
/// No description provided for @error.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error'**
|
||||
String get error;
|
||||
|
||||
/// No description provided for @password_different.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Must write a different password'**
|
||||
String get password_different;
|
||||
|
||||
/// No description provided for @create.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Creation'**
|
||||
String get create;
|
||||
|
||||
/// No description provided for @user_create.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Your user created'**
|
||||
String get user_create;
|
||||
|
||||
/// No description provided for @user_update.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Your user updated'**
|
||||
String get user_update;
|
||||
|
||||
/// No description provided for @request_error.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Poorly constructed query'**
|
||||
String get request_error;
|
||||
|
||||
/// No description provided for @incorrect_password.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Incorrect password'**
|
||||
String get incorrect_password;
|
||||
|
||||
/// No description provided for @unknown_user.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unknown user'**
|
||||
String get unknown_user;
|
||||
|
||||
/// No description provided for @disabled_user.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'User disabled'**
|
||||
String get disabled_user;
|
||||
|
||||
/// No description provided for @invalid_token.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Invalid token'**
|
||||
String get invalid_token;
|
||||
|
||||
/// No description provided for @internal_error_server.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Internal error server'**
|
||||
String get internal_error_server;
|
||||
|
||||
/// No description provided for @unknown_error_auth.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unknown error authentification'**
|
||||
String get unknown_error_auth;
|
||||
|
||||
/// No description provided for @required_input.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Required input'**
|
||||
String get required_input;
|
||||
|
||||
/// No description provided for @create_profile.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create profile'**
|
||||
String get create_profile;
|
||||
|
||||
/// No description provided for @edit_pseudo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit pseudo'**
|
||||
String get edit_pseudo;
|
||||
|
||||
/// No description provided for @password.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Password'**
|
||||
String get password;
|
||||
|
||||
/// No description provided for @enter_password.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enter the passord'**
|
||||
String get enter_password;
|
||||
|
||||
/// No description provided for @password_confirmed.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Password confirmed'**
|
||||
String get password_confirmed;
|
||||
|
||||
/// No description provided for @last_name.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Last name'**
|
||||
String get last_name;
|
||||
|
||||
/// No description provided for @first_name.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'First name'**
|
||||
String get first_name;
|
||||
|
||||
/// No description provided for @email.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Mail'**
|
||||
String get email;
|
||||
|
||||
/// No description provided for @edit_last_name.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit name'**
|
||||
String get edit_last_name;
|
||||
|
||||
/// No description provided for @edit_first_name.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit first name'**
|
||||
String get edit_first_name;
|
||||
|
||||
/// No description provided for @edit_email.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit email address'**
|
||||
String get edit_email;
|
||||
|
||||
/// No description provided for @birth_date.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Birth date'**
|
||||
String get birth_date;
|
||||
|
||||
/// No description provided for @edit_birth.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit birth date'**
|
||||
String get edit_birth;
|
||||
|
||||
/// No description provided for @create_profile_button.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Create profile'**
|
||||
String get create_profile_button;
|
||||
|
||||
/// No description provided for @take_picture.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Take a picture'**
|
||||
String get take_picture;
|
||||
|
||||
/// No description provided for @error_ia.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Google AI failed to analyze picture. Retry with another one'**
|
||||
String get error_ia;
|
||||
|
||||
/// No description provided for @no_data_geo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No geographical data'**
|
||||
String get no_data_geo;
|
||||
|
||||
/// No description provided for @response_status_update.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'response status code update'**
|
||||
String get response_status_update;
|
||||
|
||||
/// No description provided for @error_token.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Token error'**
|
||||
String get error_token;
|
||||
|
||||
/// No description provided for @error_format.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Data format error given by AI'**
|
||||
String get error_format;
|
||||
|
||||
/// No description provided for @display_picture.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Display the Picture'**
|
||||
String get display_picture;
|
||||
|
||||
/// No description provided for @analyze_image.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Image Analyze in progress'**
|
||||
String get analyze_image;
|
||||
|
||||
/// No description provided for @loading_progress.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Loading progress'**
|
||||
String get loading_progress;
|
||||
|
||||
/// No description provided for @error_event.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Event error'**
|
||||
String get error_event;
|
||||
|
||||
/// No description provided for @no_future_event.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No future event'**
|
||||
String get no_future_event;
|
||||
|
||||
/// No description provided for @error_user.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error user'**
|
||||
String get error_user;
|
||||
|
||||
/// No description provided for @empty_input.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Empty input'**
|
||||
String get empty_input;
|
||||
|
||||
/// No description provided for @info_event.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Event info'**
|
||||
String get info_event;
|
||||
|
||||
/// No description provided for @event_already.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Event already exists'**
|
||||
String get event_already;
|
||||
|
||||
/// No description provided for @picture_error.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Picture error'**
|
||||
String get picture_error;
|
||||
|
||||
/// No description provided for @no_picture_published.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No picture published'**
|
||||
String get no_picture_published;
|
||||
|
||||
/// No description provided for @event_update.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Event updated'**
|
||||
String get event_update;
|
||||
|
||||
/// No description provided for @location.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Location'**
|
||||
String get location;
|
||||
|
||||
/// No description provided for @add_event.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add or Update a event'**
|
||||
String get add_event;
|
||||
|
||||
/// No description provided for @edit_image.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit pictures'**
|
||||
String get edit_image;
|
||||
|
||||
/// No description provided for @name.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Name'**
|
||||
String get name;
|
||||
|
||||
/// No description provided for @edit_event_name.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit event name'**
|
||||
String get edit_event_name;
|
||||
|
||||
/// No description provided for @start_time.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Start time'**
|
||||
String get start_time;
|
||||
|
||||
/// No description provided for @end_time.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'End time'**
|
||||
String get end_time;
|
||||
|
||||
/// No description provided for @select_date.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Click to select a date'**
|
||||
String get select_date;
|
||||
|
||||
/// No description provided for @select_time.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Click to select a time'**
|
||||
String get select_time;
|
||||
|
||||
/// No description provided for @tag.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Tags'**
|
||||
String get tag;
|
||||
|
||||
/// No description provided for @already_tag.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'You have already this tags'**
|
||||
String get already_tag;
|
||||
|
||||
/// No description provided for @enter_tag.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enter a tag'**
|
||||
String get enter_tag;
|
||||
|
||||
/// No description provided for @organizer.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Organizer'**
|
||||
String get organizer;
|
||||
|
||||
/// No description provided for @already_organiser.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'You have already a organizer'**
|
||||
String get already_organiser;
|
||||
|
||||
/// No description provided for @enter_organizer.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enter a organizer'**
|
||||
String get enter_organizer;
|
||||
|
||||
/// No description provided for @description.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Description'**
|
||||
String get description;
|
||||
|
||||
/// No description provided for @describe_event.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Describe event'**
|
||||
String get describe_event;
|
||||
|
||||
/// No description provided for @add.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Add'**
|
||||
String get add;
|
||||
|
||||
/// No description provided for @different_password_error.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Different password'**
|
||||
String get different_password_error;
|
||||
|
||||
/// No description provided for @update.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Update'**
|
||||
String get update;
|
||||
|
||||
/// No description provided for @updated.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Updated'**
|
||||
String get updated;
|
||||
|
||||
/// No description provided for @settings_updated.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Settings updated'**
|
||||
String get settings_updated;
|
||||
|
||||
/// No description provided for @define_kilometer.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Define Kilometer'**
|
||||
String get define_kilometer;
|
||||
|
||||
/// No description provided for @email_sent.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Email has been sent'**
|
||||
String get email_sent;
|
||||
|
||||
/// No description provided for @forgot_password.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Forgot password'**
|
||||
String get forgot_password;
|
||||
|
||||
/// No description provided for @enter_email.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enter the email'**
|
||||
String get enter_email;
|
||||
|
||||
/// No description provided for @send_email.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Send email'**
|
||||
String get send_email;
|
||||
|
||||
/// No description provided for @invalid_cache.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Invalid cache'**
|
||||
String get invalid_cache;
|
||||
|
||||
/// No description provided for @item_date.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Date : '**
|
||||
String get item_date;
|
||||
|
||||
/// No description provided for @item_maps.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Maps : '**
|
||||
String get item_maps;
|
||||
|
||||
/// No description provided for @item_organizer.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Organizer : '**
|
||||
String get item_organizer;
|
||||
|
||||
/// No description provided for @item_description.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Description : '**
|
||||
String get item_description;
|
||||
|
||||
/// No description provided for @item_tags.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Tags : '**
|
||||
String get item_tags;
|
||||
|
||||
/// No description provided for @failed_auth.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Authentification failed'**
|
||||
String get failed_auth;
|
||||
|
||||
/// No description provided for @login_page.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Login page'**
|
||||
String get login_page;
|
||||
|
||||
/// No description provided for @pseudo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pseudo'**
|
||||
String get pseudo;
|
||||
|
||||
/// No description provided for @enter_existing_pseudo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Enter a existing pseudo'**
|
||||
String get enter_existing_pseudo;
|
||||
|
||||
/// No description provided for @remembr_me.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remember me'**
|
||||
String get remembr_me;
|
||||
|
||||
/// No description provided for @new_user.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'New User? Create Account'**
|
||||
String get new_user;
|
||||
|
||||
/// No description provided for @sign_in.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Sign in'**
|
||||
String get sign_in;
|
||||
|
||||
/// No description provided for @map_token.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Mapbox Access Token is not available'**
|
||||
String get map_token;
|
||||
|
||||
/// No description provided for @geo_disabled.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Location services are disabled.'**
|
||||
String get geo_disabled;
|
||||
|
||||
/// No description provided for @permission_denied.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Location permissions are denied.'**
|
||||
String get permission_denied;
|
||||
|
||||
/// No description provided for @enable_permission.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Location permissions are permanently denied. Enable them in settings.'**
|
||||
String get enable_permission;
|
||||
|
||||
/// No description provided for @no_last_position.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No last known position available.'**
|
||||
String get no_last_position;
|
||||
|
||||
/// No description provided for @failed_location.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Failed to get user location'**
|
||||
String get failed_location;
|
||||
|
||||
/// No description provided for @failed_fetch.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Failed to fetch the route'**
|
||||
String get failed_fetch;
|
||||
|
||||
/// No description provided for @invalid_coordinates_symbol.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Invalid coordinates, cannot add symbol.'**
|
||||
String get invalid_coordinates_symbol;
|
||||
|
||||
/// No description provided for @error_symbol.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error when adding symbol.'**
|
||||
String get error_symbol;
|
||||
|
||||
/// No description provided for @position_not_init.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'User position is not yet initialized. Try again.'**
|
||||
String get position_not_init;
|
||||
|
||||
/// No description provided for @invalid_coordinates.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Invalid coordinates.'**
|
||||
String get invalid_coordinates;
|
||||
|
||||
/// No description provided for @walking.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Walking'**
|
||||
String get walking;
|
||||
|
||||
/// No description provided for @cycling.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Cycling'**
|
||||
String get cycling;
|
||||
|
||||
/// No description provided for @driving.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Driving'**
|
||||
String get driving;
|
||||
|
||||
/// No description provided for @get_direction.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Get Directions and Markers'**
|
||||
String get get_direction;
|
||||
|
||||
/// No description provided for @missing_token.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Missing access token'**
|
||||
String get missing_token;
|
||||
|
||||
/// No description provided for @geocoding_error.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error when geocoding'**
|
||||
String get geocoding_error;
|
||||
|
||||
/// No description provided for @no_found_place.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No found place'**
|
||||
String get no_found_place;
|
||||
|
||||
/// No description provided for @upload_error.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error when image uploading'**
|
||||
String get upload_error;
|
||||
|
||||
/// No description provided for @event_added.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Event added'**
|
||||
String get event_added;
|
||||
|
||||
/// No description provided for @unknown_error.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Unknown error'**
|
||||
String get unknown_error;
|
||||
|
||||
/// No description provided for @app_error.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Application error'**
|
||||
String get app_error;
|
||||
|
||||
/// No description provided for @at.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'at'**
|
||||
String get at;
|
||||
|
||||
/// No description provided for @to_date.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'to'**
|
||||
String get to_date;
|
||||
|
||||
/// No description provided for @item_link.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Link : '**
|
||||
String get item_link;
|
||||
|
||||
/// No description provided for @item_ticket.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Ticket : '**
|
||||
String get item_ticket;
|
||||
|
||||
/// No description provided for @link.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Link'**
|
||||
String get link;
|
||||
|
||||
/// No description provided for @edit_link.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit link name'**
|
||||
String get edit_link;
|
||||
|
||||
/// No description provided for @ticket.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Ticket'**
|
||||
String get ticket;
|
||||
|
||||
/// No description provided for @edit_ticket.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit ticket link'**
|
||||
String get edit_ticket;
|
||||
|
||||
/// No description provided for @toogle_interest.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error toggle interest'**
|
||||
String get toogle_interest;
|
||||
|
||||
/// No description provided for @error_update.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Error when updating'**
|
||||
String get error_update;
|
||||
|
||||
/// No description provided for @count_interested.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Interested people number'**
|
||||
String get count_interested;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
extends LocalizationsDelegate<AppLocalizations> {
|
||||
const _AppLocalizationsDelegate();
|
||||
|
||||
@override
|
||||
Future<AppLocalizations> load(Locale locale) {
|
||||
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) =>
|
||||
<String>['de', 'en', 'fr'].contains(locale.languageCode);
|
||||
|
||||
@override
|
||||
bool shouldReload(_AppLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
||||
AppLocalizations lookupAppLocalizations(Locale locale) {
|
||||
// Lookup logic when only language code is specified.
|
||||
switch (locale.languageCode) {
|
||||
case 'de':
|
||||
return AppLocalizationsDe();
|
||||
case 'en':
|
||||
return AppLocalizationsEn();
|
||||
case 'fr':
|
||||
return AppLocalizationsFr();
|
||||
}
|
||||
|
||||
throw FlutterError(
|
||||
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
|
||||
'an issue with the localizations generation tool. Please file an issue '
|
||||
'on GitHub with a reproducible sample app and the gen-l10n configuration '
|
||||
'that was used.');
|
||||
}
|
434
covas_mobile_new/lib/gen_l10n/app_localizations_de.dart
Normal file
@@ -0,0 +1,434 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for German (`de`).
|
||||
class AppLocalizationsDe extends AppLocalizations {
|
||||
AppLocalizationsDe([String locale = 'de']) : super(locale);
|
||||
|
||||
@override
|
||||
String get menu_list => 'Veranstaltungsmenü';
|
||||
|
||||
@override
|
||||
String get language => 'Sprache';
|
||||
|
||||
@override
|
||||
String get home => 'Startseite';
|
||||
|
||||
@override
|
||||
String get settings => 'Einstellungen';
|
||||
|
||||
@override
|
||||
String get update_profile => 'Profil aktualisieren';
|
||||
|
||||
@override
|
||||
String get about => 'Über';
|
||||
|
||||
@override
|
||||
String get log_out => 'Abmelden';
|
||||
|
||||
@override
|
||||
String get french => 'Französisch';
|
||||
|
||||
@override
|
||||
String get english => 'Englisch';
|
||||
|
||||
@override
|
||||
String get german => 'Deutsch';
|
||||
|
||||
@override
|
||||
String get select_language => 'Sprache auswählen';
|
||||
|
||||
@override
|
||||
String get search_item => 'Nach Element suchen';
|
||||
|
||||
@override
|
||||
String get search_tag => 'Nach Schlagwörtern suchen';
|
||||
|
||||
@override
|
||||
String get search_geographical => 'Nach geografischer Zone suchen';
|
||||
|
||||
@override
|
||||
String get show_date_field => 'Datumsfelder anzeigen';
|
||||
|
||||
@override
|
||||
String get hide_date_field => 'Datumsfelder ausblenden';
|
||||
|
||||
@override
|
||||
String get no_data => 'Keine Daten verfügbar';
|
||||
|
||||
@override
|
||||
String get search => 'Suchen';
|
||||
|
||||
@override
|
||||
String get no_events => 'Keine Veranstaltungen für diesen Ort verfügbar.';
|
||||
|
||||
@override
|
||||
String get start_date => 'Anfangsdatum';
|
||||
|
||||
@override
|
||||
String get end_date => 'Enddatum';
|
||||
|
||||
@override
|
||||
String get failed_suggestions => 'Vorschläge konnten nicht geladen werden';
|
||||
|
||||
@override
|
||||
String get error => 'Fehler';
|
||||
|
||||
@override
|
||||
String get password_different => 'Ein anderes Passwort eingeben';
|
||||
|
||||
@override
|
||||
String get create => 'Erstellung';
|
||||
|
||||
@override
|
||||
String get user_create => 'Benutzer wurde erstellt';
|
||||
|
||||
@override
|
||||
String get user_update => 'Benutzer wurde aktualisiert';
|
||||
|
||||
@override
|
||||
String get request_error => 'Fehlerhafte Anfrage';
|
||||
|
||||
@override
|
||||
String get incorrect_password => 'Falsches Passwort';
|
||||
|
||||
@override
|
||||
String get unknown_user => 'Unbekannter Benutzer';
|
||||
|
||||
@override
|
||||
String get disabled_user => 'Benutzer deaktiviert';
|
||||
|
||||
@override
|
||||
String get invalid_token => 'Ungültiger Token';
|
||||
|
||||
@override
|
||||
String get internal_error_server => 'Interner Serverfehler';
|
||||
|
||||
@override
|
||||
String get unknown_error_auth => 'Unbekannter Authentifizierungsfehler';
|
||||
|
||||
@override
|
||||
String get required_input => 'Pflichtfeld';
|
||||
|
||||
@override
|
||||
String get create_profile => 'Profil erstellen';
|
||||
|
||||
@override
|
||||
String get edit_pseudo => 'Benutzernamen bearbeiten';
|
||||
|
||||
@override
|
||||
String get password => 'Passwort';
|
||||
|
||||
@override
|
||||
String get enter_password => 'Passwort eingeben';
|
||||
|
||||
@override
|
||||
String get password_confirmed => 'Passwort bestätigt';
|
||||
|
||||
@override
|
||||
String get last_name => 'Nachname';
|
||||
|
||||
@override
|
||||
String get first_name => 'Vorname';
|
||||
|
||||
@override
|
||||
String get email => 'E-Mail';
|
||||
|
||||
@override
|
||||
String get edit_last_name => 'Nachnamen bearbeiten';
|
||||
|
||||
@override
|
||||
String get edit_first_name => 'Vornamen bearbeiten';
|
||||
|
||||
@override
|
||||
String get edit_email => 'E-Mail-Adresse bearbeiten';
|
||||
|
||||
@override
|
||||
String get birth_date => 'Geburtsdatum';
|
||||
|
||||
@override
|
||||
String get edit_birth => 'Geburtsdatum bearbeiten';
|
||||
|
||||
@override
|
||||
String get create_profile_button => 'Profil erstellen';
|
||||
|
||||
@override
|
||||
String get take_picture => 'Foto aufnehmen';
|
||||
|
||||
@override
|
||||
String get error_ia =>
|
||||
'Google KI konnte das Bild nicht analysieren. Bitte ein anderes versuchen.';
|
||||
|
||||
@override
|
||||
String get no_data_geo => 'Keine geografischen Daten';
|
||||
|
||||
@override
|
||||
String get response_status_update => 'Statuscode-Antwort aktualisieren';
|
||||
|
||||
@override
|
||||
String get error_token => 'Token-Fehler';
|
||||
|
||||
@override
|
||||
String get error_format => 'Vom KI geliefertes Datenformat ist fehlerhaft';
|
||||
|
||||
@override
|
||||
String get display_picture => 'Bild anzeigen';
|
||||
|
||||
@override
|
||||
String get analyze_image => 'Bildanalyse läuft';
|
||||
|
||||
@override
|
||||
String get loading_progress => 'Ladefortschritt';
|
||||
|
||||
@override
|
||||
String get error_event => 'Veranstaltungsfehler';
|
||||
|
||||
@override
|
||||
String get no_future_event => 'Keine zukünftigen Veranstaltungen';
|
||||
|
||||
@override
|
||||
String get error_user => 'Benutzerfehler';
|
||||
|
||||
@override
|
||||
String get empty_input => 'Eingabefeld leer';
|
||||
|
||||
@override
|
||||
String get info_event => 'Veranstaltungsinfo';
|
||||
|
||||
@override
|
||||
String get event_already => 'Veranstaltung existiert bereits';
|
||||
|
||||
@override
|
||||
String get picture_error => 'Bildfehler';
|
||||
|
||||
@override
|
||||
String get no_picture_published => 'Kein Bild veröffentlicht';
|
||||
|
||||
@override
|
||||
String get event_update => 'Veranstaltung aktualisiert';
|
||||
|
||||
@override
|
||||
String get location => 'Ort';
|
||||
|
||||
@override
|
||||
String get add_event => 'Veranstaltung hinzufügen oder aktualisieren';
|
||||
|
||||
@override
|
||||
String get edit_image => 'Bilder bearbeiten';
|
||||
|
||||
@override
|
||||
String get name => 'Name';
|
||||
|
||||
@override
|
||||
String get edit_event_name => 'Veranstaltungsname bearbeiten';
|
||||
|
||||
@override
|
||||
String get start_time => 'Startzeit';
|
||||
|
||||
@override
|
||||
String get end_time => 'Endzeit';
|
||||
|
||||
@override
|
||||
String get select_date => 'Zum Auswählen eines Datums klicken';
|
||||
|
||||
@override
|
||||
String get select_time => 'Zum Auswählen einer Uhrzeit klicken';
|
||||
|
||||
@override
|
||||
String get tag => 'Schlagwörter';
|
||||
|
||||
@override
|
||||
String get already_tag => 'Dieses Schlagwort ist bereits vorhanden';
|
||||
|
||||
@override
|
||||
String get enter_tag => 'Ein Schlagwort eingeben';
|
||||
|
||||
@override
|
||||
String get organizer => 'Veranstalter';
|
||||
|
||||
@override
|
||||
String get already_organiser => 'Veranstalter bereits vorhanden';
|
||||
|
||||
@override
|
||||
String get enter_organizer => 'Veranstalter eingeben';
|
||||
|
||||
@override
|
||||
String get description => 'Beschreibung';
|
||||
|
||||
@override
|
||||
String get describe_event => 'Veranstaltung beschreiben';
|
||||
|
||||
@override
|
||||
String get add => 'Hinzufügen';
|
||||
|
||||
@override
|
||||
String get different_password_error => 'Passwörter stimmen nicht überein';
|
||||
|
||||
@override
|
||||
String get update => 'Aktualisieren';
|
||||
|
||||
@override
|
||||
String get updated => 'Aktualisiert';
|
||||
|
||||
@override
|
||||
String get settings_updated => 'Einstellungen aktualisiert';
|
||||
|
||||
@override
|
||||
String get define_kilometer => 'Kilometer definieren';
|
||||
|
||||
@override
|
||||
String get email_sent => 'E-Mail wurde gesendet';
|
||||
|
||||
@override
|
||||
String get forgot_password => 'Passwort vergessen';
|
||||
|
||||
@override
|
||||
String get enter_email => 'E-Mail eingeben';
|
||||
|
||||
@override
|
||||
String get send_email => 'E-Mail senden';
|
||||
|
||||
@override
|
||||
String get invalid_cache => 'Ungültiger Cache';
|
||||
|
||||
@override
|
||||
String get item_date => 'Datum : ';
|
||||
|
||||
@override
|
||||
String get item_maps => 'Karte : ';
|
||||
|
||||
@override
|
||||
String get item_organizer => 'Veranstalter : ';
|
||||
|
||||
@override
|
||||
String get item_description => 'Beschreibung : ';
|
||||
|
||||
@override
|
||||
String get item_tags => 'Schlagwörter : ';
|
||||
|
||||
@override
|
||||
String get failed_auth => 'Authentifizierung fehlgeschlagen';
|
||||
|
||||
@override
|
||||
String get login_page => 'Anmeldeseite';
|
||||
|
||||
@override
|
||||
String get pseudo => 'Benutzername';
|
||||
|
||||
@override
|
||||
String get enter_existing_pseudo => 'Vorhandenen Benutzernamen eingeben';
|
||||
|
||||
@override
|
||||
String get remembr_me => 'Angemeldet bleiben';
|
||||
|
||||
@override
|
||||
String get new_user => 'Neuer Benutzer? Konto erstellen';
|
||||
|
||||
@override
|
||||
String get sign_in => 'Anmelden';
|
||||
|
||||
@override
|
||||
String get map_token => 'Mapbox-Zugangstoken ist nicht verfügbar';
|
||||
|
||||
@override
|
||||
String get geo_disabled => 'Standortdienste sind deaktiviert.';
|
||||
|
||||
@override
|
||||
String get permission_denied => 'Standortberechtigungen wurden verweigert.';
|
||||
|
||||
@override
|
||||
String get enable_permission =>
|
||||
'Standortberechtigungen dauerhaft verweigert. Bitte in den Einstellungen aktivieren.';
|
||||
|
||||
@override
|
||||
String get no_last_position => 'Keine letzte bekannte Position verfügbar.';
|
||||
|
||||
@override
|
||||
String get failed_location => 'Standort konnte nicht ermittelt werden';
|
||||
|
||||
@override
|
||||
String get failed_fetch => 'Route konnte nicht abgerufen werden';
|
||||
|
||||
@override
|
||||
String get invalid_coordinates_symbol =>
|
||||
'Ungültige Koordinaten, Symbol kann nicht hinzugefügt werden.';
|
||||
|
||||
@override
|
||||
String get error_symbol => 'Fehler beim Hinzufügen des Symbols.';
|
||||
|
||||
@override
|
||||
String get position_not_init =>
|
||||
'Benutzerposition noch nicht initialisiert. Erneut versuchen.';
|
||||
|
||||
@override
|
||||
String get invalid_coordinates => 'Ungültige Koordinaten.';
|
||||
|
||||
@override
|
||||
String get walking => 'Zu Fuß';
|
||||
|
||||
@override
|
||||
String get cycling => 'Mit dem Fahrrad';
|
||||
|
||||
@override
|
||||
String get driving => 'Mit dem Auto';
|
||||
|
||||
@override
|
||||
String get get_direction => 'Wegbeschreibung und Markierungen anzeigen';
|
||||
|
||||
@override
|
||||
String get missing_token => 'Zugangstoken fehlt';
|
||||
|
||||
@override
|
||||
String get geocoding_error => 'Fehler bei der Geokodierung';
|
||||
|
||||
@override
|
||||
String get no_found_place => 'Kein Ort gefunden';
|
||||
|
||||
@override
|
||||
String get upload_error => 'Fehler beim Hochladen des Bildes';
|
||||
|
||||
@override
|
||||
String get event_added => 'Veranstaltung hinzugefügt';
|
||||
|
||||
@override
|
||||
String get unknown_error => 'Unbekannter Fehler';
|
||||
|
||||
@override
|
||||
String get app_error => 'Anwendungsfehler';
|
||||
|
||||
@override
|
||||
String get at => 'um';
|
||||
|
||||
@override
|
||||
String get to_date => 'bis';
|
||||
|
||||
@override
|
||||
String get item_link => 'Link : ';
|
||||
|
||||
@override
|
||||
String get item_ticket => 'Abendkarte : ';
|
||||
|
||||
@override
|
||||
String get link => 'Link';
|
||||
|
||||
@override
|
||||
String get edit_link => 'Linkereignis bearbeiten';
|
||||
|
||||
@override
|
||||
String get ticket => 'Abendkarte';
|
||||
|
||||
@override
|
||||
String get edit_ticket => 'Ticketlink bearbeiten';
|
||||
|
||||
@override
|
||||
String get toogle_interest => 'Fehler beim Umschalten des Interesses';
|
||||
|
||||
@override
|
||||
String get error_update => 'Fehler beim Update';
|
||||
|
||||
@override
|
||||
String get count_interested => 'Anzahl der Interessenten';
|
||||
}
|
434
covas_mobile_new/lib/gen_l10n/app_localizations_en.dart
Normal file
@@ -0,0 +1,434 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for English (`en`).
|
||||
class AppLocalizationsEn extends AppLocalizations {
|
||||
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
||||
|
||||
@override
|
||||
String get menu_list => 'Event list menu';
|
||||
|
||||
@override
|
||||
String get language => 'Language';
|
||||
|
||||
@override
|
||||
String get home => 'Home';
|
||||
|
||||
@override
|
||||
String get settings => 'Settings';
|
||||
|
||||
@override
|
||||
String get update_profile => 'Update profile';
|
||||
|
||||
@override
|
||||
String get about => 'About';
|
||||
|
||||
@override
|
||||
String get log_out => 'Log out';
|
||||
|
||||
@override
|
||||
String get french => 'French';
|
||||
|
||||
@override
|
||||
String get english => 'English';
|
||||
|
||||
@override
|
||||
String get german => 'German';
|
||||
|
||||
@override
|
||||
String get select_language => 'Select language';
|
||||
|
||||
@override
|
||||
String get search_item => 'Search by item';
|
||||
|
||||
@override
|
||||
String get search_tag => 'Search by tags';
|
||||
|
||||
@override
|
||||
String get search_geographical => 'Search by geographical zone';
|
||||
|
||||
@override
|
||||
String get show_date_field => 'Show Date Fields';
|
||||
|
||||
@override
|
||||
String get hide_date_field => 'Hide Date Fields';
|
||||
|
||||
@override
|
||||
String get no_data => 'No data available';
|
||||
|
||||
@override
|
||||
String get search => 'Search';
|
||||
|
||||
@override
|
||||
String get no_events => 'No events available for this location.';
|
||||
|
||||
@override
|
||||
String get start_date => 'Start date';
|
||||
|
||||
@override
|
||||
String get end_date => 'End date';
|
||||
|
||||
@override
|
||||
String get failed_suggestions => 'Failed to load suggestions';
|
||||
|
||||
@override
|
||||
String get error => 'Error';
|
||||
|
||||
@override
|
||||
String get password_different => 'Must write a different password';
|
||||
|
||||
@override
|
||||
String get create => 'Creation';
|
||||
|
||||
@override
|
||||
String get user_create => 'Your user created';
|
||||
|
||||
@override
|
||||
String get user_update => 'Your user updated';
|
||||
|
||||
@override
|
||||
String get request_error => 'Poorly constructed query';
|
||||
|
||||
@override
|
||||
String get incorrect_password => 'Incorrect password';
|
||||
|
||||
@override
|
||||
String get unknown_user => 'Unknown user';
|
||||
|
||||
@override
|
||||
String get disabled_user => 'User disabled';
|
||||
|
||||
@override
|
||||
String get invalid_token => 'Invalid token';
|
||||
|
||||
@override
|
||||
String get internal_error_server => 'Internal error server';
|
||||
|
||||
@override
|
||||
String get unknown_error_auth => 'Unknown error authentification';
|
||||
|
||||
@override
|
||||
String get required_input => 'Required input';
|
||||
|
||||
@override
|
||||
String get create_profile => 'Create profile';
|
||||
|
||||
@override
|
||||
String get edit_pseudo => 'Edit pseudo';
|
||||
|
||||
@override
|
||||
String get password => 'Password';
|
||||
|
||||
@override
|
||||
String get enter_password => 'Enter the passord';
|
||||
|
||||
@override
|
||||
String get password_confirmed => 'Password confirmed';
|
||||
|
||||
@override
|
||||
String get last_name => 'Last name';
|
||||
|
||||
@override
|
||||
String get first_name => 'First name';
|
||||
|
||||
@override
|
||||
String get email => 'Mail';
|
||||
|
||||
@override
|
||||
String get edit_last_name => 'Edit name';
|
||||
|
||||
@override
|
||||
String get edit_first_name => 'Edit first name';
|
||||
|
||||
@override
|
||||
String get edit_email => 'Edit email address';
|
||||
|
||||
@override
|
||||
String get birth_date => 'Birth date';
|
||||
|
||||
@override
|
||||
String get edit_birth => 'Edit birth date';
|
||||
|
||||
@override
|
||||
String get create_profile_button => 'Create profile';
|
||||
|
||||
@override
|
||||
String get take_picture => 'Take a picture';
|
||||
|
||||
@override
|
||||
String get error_ia =>
|
||||
'Google AI failed to analyze picture. Retry with another one';
|
||||
|
||||
@override
|
||||
String get no_data_geo => 'No geographical data';
|
||||
|
||||
@override
|
||||
String get response_status_update => 'response status code update';
|
||||
|
||||
@override
|
||||
String get error_token => 'Token error';
|
||||
|
||||
@override
|
||||
String get error_format => 'Data format error given by AI';
|
||||
|
||||
@override
|
||||
String get display_picture => 'Display the Picture';
|
||||
|
||||
@override
|
||||
String get analyze_image => 'Image Analyze in progress';
|
||||
|
||||
@override
|
||||
String get loading_progress => 'Loading progress';
|
||||
|
||||
@override
|
||||
String get error_event => 'Event error';
|
||||
|
||||
@override
|
||||
String get no_future_event => 'No future event';
|
||||
|
||||
@override
|
||||
String get error_user => 'Error user';
|
||||
|
||||
@override
|
||||
String get empty_input => 'Empty input';
|
||||
|
||||
@override
|
||||
String get info_event => 'Event info';
|
||||
|
||||
@override
|
||||
String get event_already => 'Event already exists';
|
||||
|
||||
@override
|
||||
String get picture_error => 'Picture error';
|
||||
|
||||
@override
|
||||
String get no_picture_published => 'No picture published';
|
||||
|
||||
@override
|
||||
String get event_update => 'Event updated';
|
||||
|
||||
@override
|
||||
String get location => 'Location';
|
||||
|
||||
@override
|
||||
String get add_event => 'Add or Update a event';
|
||||
|
||||
@override
|
||||
String get edit_image => 'Edit pictures';
|
||||
|
||||
@override
|
||||
String get name => 'Name';
|
||||
|
||||
@override
|
||||
String get edit_event_name => 'Edit event name';
|
||||
|
||||
@override
|
||||
String get start_time => 'Start time';
|
||||
|
||||
@override
|
||||
String get end_time => 'End time';
|
||||
|
||||
@override
|
||||
String get select_date => 'Click to select a date';
|
||||
|
||||
@override
|
||||
String get select_time => 'Click to select a time';
|
||||
|
||||
@override
|
||||
String get tag => 'Tags';
|
||||
|
||||
@override
|
||||
String get already_tag => 'You have already this tags';
|
||||
|
||||
@override
|
||||
String get enter_tag => 'Enter a tag';
|
||||
|
||||
@override
|
||||
String get organizer => 'Organizer';
|
||||
|
||||
@override
|
||||
String get already_organiser => 'You have already a organizer';
|
||||
|
||||
@override
|
||||
String get enter_organizer => 'Enter a organizer';
|
||||
|
||||
@override
|
||||
String get description => 'Description';
|
||||
|
||||
@override
|
||||
String get describe_event => 'Describe event';
|
||||
|
||||
@override
|
||||
String get add => 'Add';
|
||||
|
||||
@override
|
||||
String get different_password_error => 'Different password';
|
||||
|
||||
@override
|
||||
String get update => 'Update';
|
||||
|
||||
@override
|
||||
String get updated => 'Updated';
|
||||
|
||||
@override
|
||||
String get settings_updated => 'Settings updated';
|
||||
|
||||
@override
|
||||
String get define_kilometer => 'Define Kilometer';
|
||||
|
||||
@override
|
||||
String get email_sent => 'Email has been sent';
|
||||
|
||||
@override
|
||||
String get forgot_password => 'Forgot password';
|
||||
|
||||
@override
|
||||
String get enter_email => 'Enter the email';
|
||||
|
||||
@override
|
||||
String get send_email => 'Send email';
|
||||
|
||||
@override
|
||||
String get invalid_cache => 'Invalid cache';
|
||||
|
||||
@override
|
||||
String get item_date => 'Date : ';
|
||||
|
||||
@override
|
||||
String get item_maps => 'Maps : ';
|
||||
|
||||
@override
|
||||
String get item_organizer => 'Organizer : ';
|
||||
|
||||
@override
|
||||
String get item_description => 'Description : ';
|
||||
|
||||
@override
|
||||
String get item_tags => 'Tags : ';
|
||||
|
||||
@override
|
||||
String get failed_auth => 'Authentification failed';
|
||||
|
||||
@override
|
||||
String get login_page => 'Login page';
|
||||
|
||||
@override
|
||||
String get pseudo => 'Pseudo';
|
||||
|
||||
@override
|
||||
String get enter_existing_pseudo => 'Enter a existing pseudo';
|
||||
|
||||
@override
|
||||
String get remembr_me => 'Remember me';
|
||||
|
||||
@override
|
||||
String get new_user => 'New User? Create Account';
|
||||
|
||||
@override
|
||||
String get sign_in => 'Sign in';
|
||||
|
||||
@override
|
||||
String get map_token => 'Mapbox Access Token is not available';
|
||||
|
||||
@override
|
||||
String get geo_disabled => 'Location services are disabled.';
|
||||
|
||||
@override
|
||||
String get permission_denied => 'Location permissions are denied.';
|
||||
|
||||
@override
|
||||
String get enable_permission =>
|
||||
'Location permissions are permanently denied. Enable them in settings.';
|
||||
|
||||
@override
|
||||
String get no_last_position => 'No last known position available.';
|
||||
|
||||
@override
|
||||
String get failed_location => 'Failed to get user location';
|
||||
|
||||
@override
|
||||
String get failed_fetch => 'Failed to fetch the route';
|
||||
|
||||
@override
|
||||
String get invalid_coordinates_symbol =>
|
||||
'Invalid coordinates, cannot add symbol.';
|
||||
|
||||
@override
|
||||
String get error_symbol => 'Error when adding symbol.';
|
||||
|
||||
@override
|
||||
String get position_not_init =>
|
||||
'User position is not yet initialized. Try again.';
|
||||
|
||||
@override
|
||||
String get invalid_coordinates => 'Invalid coordinates.';
|
||||
|
||||
@override
|
||||
String get walking => 'Walking';
|
||||
|
||||
@override
|
||||
String get cycling => 'Cycling';
|
||||
|
||||
@override
|
||||
String get driving => 'Driving';
|
||||
|
||||
@override
|
||||
String get get_direction => 'Get Directions and Markers';
|
||||
|
||||
@override
|
||||
String get missing_token => 'Missing access token';
|
||||
|
||||
@override
|
||||
String get geocoding_error => 'Error when geocoding';
|
||||
|
||||
@override
|
||||
String get no_found_place => 'No found place';
|
||||
|
||||
@override
|
||||
String get upload_error => 'Error when image uploading';
|
||||
|
||||
@override
|
||||
String get event_added => 'Event added';
|
||||
|
||||
@override
|
||||
String get unknown_error => 'Unknown error';
|
||||
|
||||
@override
|
||||
String get app_error => 'Application error';
|
||||
|
||||
@override
|
||||
String get at => 'at';
|
||||
|
||||
@override
|
||||
String get to_date => 'to';
|
||||
|
||||
@override
|
||||
String get item_link => 'Link : ';
|
||||
|
||||
@override
|
||||
String get item_ticket => 'Ticket : ';
|
||||
|
||||
@override
|
||||
String get link => 'Link';
|
||||
|
||||
@override
|
||||
String get edit_link => 'Edit link name';
|
||||
|
||||
@override
|
||||
String get ticket => 'Ticket';
|
||||
|
||||
@override
|
||||
String get edit_ticket => 'Edit ticket link';
|
||||
|
||||
@override
|
||||
String get toogle_interest => 'Error toggle interest';
|
||||
|
||||
@override
|
||||
String get error_update => 'Error when updating';
|
||||
|
||||
@override
|
||||
String get count_interested => 'Interested people number';
|
||||
}
|
437
covas_mobile_new/lib/gen_l10n/app_localizations_fr.dart
Normal file
@@ -0,0 +1,437 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for French (`fr`).
|
||||
class AppLocalizationsFr extends AppLocalizations {
|
||||
AppLocalizationsFr([String locale = 'fr']) : super(locale);
|
||||
|
||||
@override
|
||||
String get menu_list => 'Liste d\'évènement';
|
||||
|
||||
@override
|
||||
String get language => 'Langue';
|
||||
|
||||
@override
|
||||
String get home => 'Accueil';
|
||||
|
||||
@override
|
||||
String get settings => 'Paramètres';
|
||||
|
||||
@override
|
||||
String get update_profile => 'Modifier le profil';
|
||||
|
||||
@override
|
||||
String get about => 'À propos';
|
||||
|
||||
@override
|
||||
String get log_out => 'Se déconnecter';
|
||||
|
||||
@override
|
||||
String get french => 'Français';
|
||||
|
||||
@override
|
||||
String get english => 'Anglais';
|
||||
|
||||
@override
|
||||
String get german => 'Allemand';
|
||||
|
||||
@override
|
||||
String get select_language => 'Selectionne la langue';
|
||||
|
||||
@override
|
||||
String get search_item => 'Recherche par item';
|
||||
|
||||
@override
|
||||
String get search_tag => 'Recherche par tags';
|
||||
|
||||
@override
|
||||
String get search_geographical => 'Recherche par zone géographique';
|
||||
|
||||
@override
|
||||
String get show_date_field => 'Afficher champ date';
|
||||
|
||||
@override
|
||||
String get hide_date_field => 'Cacher Date Fields';
|
||||
|
||||
@override
|
||||
String get no_data => 'Aucune donnée disponible';
|
||||
|
||||
@override
|
||||
String get search => 'Recherche';
|
||||
|
||||
@override
|
||||
String get no_events => 'Pas d\'évènements dans cette localisation';
|
||||
|
||||
@override
|
||||
String get start_date => 'Date de début';
|
||||
|
||||
@override
|
||||
String get end_date => 'Date de fin';
|
||||
|
||||
@override
|
||||
String get failed_suggestions => 'Echec de chargement des suggestions';
|
||||
|
||||
@override
|
||||
String get error => 'Erreur';
|
||||
|
||||
@override
|
||||
String get password_different => 'Tu dois écrire un mot de passe different';
|
||||
|
||||
@override
|
||||
String get create => 'Création';
|
||||
|
||||
@override
|
||||
String get user_create => 'Votre utilisateur a été créé';
|
||||
|
||||
@override
|
||||
String get user_update => 'Votre utilisateur a été modifié';
|
||||
|
||||
@override
|
||||
String get request_error => 'Requête mal construite';
|
||||
|
||||
@override
|
||||
String get incorrect_password => 'Mot de passe incorrect';
|
||||
|
||||
@override
|
||||
String get unknown_user => 'Utilisateur inconnu';
|
||||
|
||||
@override
|
||||
String get disabled_user => 'Utilisateur désactivé';
|
||||
|
||||
@override
|
||||
String get invalid_token => 'Token invalide';
|
||||
|
||||
@override
|
||||
String get internal_error_server => 'Erreur interne de serveur';
|
||||
|
||||
@override
|
||||
String get unknown_error_auth => 'Problème d\'authentification inconnu';
|
||||
|
||||
@override
|
||||
String get required_input => 'Champ requis';
|
||||
|
||||
@override
|
||||
String get create_profile => 'Creation profil';
|
||||
|
||||
@override
|
||||
String get edit_pseudo => 'Modifier le pseudo';
|
||||
|
||||
@override
|
||||
String get password => 'Mot de passe';
|
||||
|
||||
@override
|
||||
String get enter_password => 'Entrez le password';
|
||||
|
||||
@override
|
||||
String get password_confirmed => 'Confirmez le mot de passe';
|
||||
|
||||
@override
|
||||
String get last_name => 'Nom';
|
||||
|
||||
@override
|
||||
String get first_name => 'Prénom';
|
||||
|
||||
@override
|
||||
String get email => 'Email';
|
||||
|
||||
@override
|
||||
String get edit_last_name => 'Modifier le nom';
|
||||
|
||||
@override
|
||||
String get edit_first_name => 'Modifier le prénom';
|
||||
|
||||
@override
|
||||
String get edit_email => 'Modifier l\'email';
|
||||
|
||||
@override
|
||||
String get birth_date => 'Date de naissance';
|
||||
|
||||
@override
|
||||
String get edit_birth => 'Modifier la date de naissance';
|
||||
|
||||
@override
|
||||
String get create_profile_button => 'Créer le profil';
|
||||
|
||||
@override
|
||||
String get take_picture => 'Take a picture';
|
||||
|
||||
@override
|
||||
String get error_ia =>
|
||||
'L\'IA de Google n\'a pas su analyser l\'image. Recommencer avec une autre';
|
||||
|
||||
@override
|
||||
String get no_data_geo => 'Aucune donnée géographique';
|
||||
|
||||
@override
|
||||
String get response_status_update =>
|
||||
'Code du statut de réponse de la modification';
|
||||
|
||||
@override
|
||||
String get error_token => 'Erreur de token';
|
||||
|
||||
@override
|
||||
String get error_format => 'Erreur de format de donnée fourni par l\'IA';
|
||||
|
||||
@override
|
||||
String get display_picture => 'Display the Picture';
|
||||
|
||||
@override
|
||||
String get analyze_image => 'Analyse de l\'image en cours';
|
||||
|
||||
@override
|
||||
String get loading_progress => 'Chargement en cours';
|
||||
|
||||
@override
|
||||
String get error_event => 'Erreur de l\'évènement';
|
||||
|
||||
@override
|
||||
String get no_future_event => 'Évènement non futur';
|
||||
|
||||
@override
|
||||
String get error_user => 'Erreur de l\'utilisateur';
|
||||
|
||||
@override
|
||||
String get empty_input => 'Champ vide';
|
||||
|
||||
@override
|
||||
String get info_event => 'Event info';
|
||||
|
||||
@override
|
||||
String get event_already => 'Event already exists';
|
||||
|
||||
@override
|
||||
String get picture_error => 'Erreur image';
|
||||
|
||||
@override
|
||||
String get no_picture_published => 'Image non publiée';
|
||||
|
||||
@override
|
||||
String get event_update => 'Évènement modifié';
|
||||
|
||||
@override
|
||||
String get location => 'Lieu';
|
||||
|
||||
@override
|
||||
String get add_event => 'Ajouter ou modifier un évènement';
|
||||
|
||||
@override
|
||||
String get edit_image => 'Changer la photo';
|
||||
|
||||
@override
|
||||
String get name => 'Nom';
|
||||
|
||||
@override
|
||||
String get edit_event_name => 'Changer le nom de l\'évènement';
|
||||
|
||||
@override
|
||||
String get start_time => 'Heure de début';
|
||||
|
||||
@override
|
||||
String get end_time => 'Heure de fin';
|
||||
|
||||
@override
|
||||
String get select_date => 'Cliquer pour selectionner une date';
|
||||
|
||||
@override
|
||||
String get select_time => 'Cliquer pour selectionner une heure';
|
||||
|
||||
@override
|
||||
String get tag => 'Tags';
|
||||
|
||||
@override
|
||||
String get already_tag => 'Tu as déjà entré ce tag';
|
||||
|
||||
@override
|
||||
String get enter_tag => 'Entrer un tag';
|
||||
|
||||
@override
|
||||
String get organizer => 'Organisateur';
|
||||
|
||||
@override
|
||||
String get already_organiser => 'Tu as déjà rentré cet organisateur';
|
||||
|
||||
@override
|
||||
String get enter_organizer => 'Entrer un organisateur';
|
||||
|
||||
@override
|
||||
String get description => 'Description';
|
||||
|
||||
@override
|
||||
String get describe_event => 'Décrire l\'évènement';
|
||||
|
||||
@override
|
||||
String get add => 'Ajouter';
|
||||
|
||||
@override
|
||||
String get different_password_error => 'Mot de passe différent';
|
||||
|
||||
@override
|
||||
String get update => 'Mettre à jour';
|
||||
|
||||
@override
|
||||
String get updated => 'Mis à jour';
|
||||
|
||||
@override
|
||||
String get settings_updated => 'Paramètre mis à jour';
|
||||
|
||||
@override
|
||||
String get define_kilometer => 'Definir un kilomètre';
|
||||
|
||||
@override
|
||||
String get email_sent => 'Email a été envoyé';
|
||||
|
||||
@override
|
||||
String get forgot_password => 'Mot de passe oublié';
|
||||
|
||||
@override
|
||||
String get enter_email => 'Entrez l\'email';
|
||||
|
||||
@override
|
||||
String get send_email => 'Send email';
|
||||
|
||||
@override
|
||||
String get invalid_cache => 'Cache invalide';
|
||||
|
||||
@override
|
||||
String get item_date => 'Date : ';
|
||||
|
||||
@override
|
||||
String get item_maps => 'Carte : ';
|
||||
|
||||
@override
|
||||
String get item_organizer => 'Organisateurs : ';
|
||||
|
||||
@override
|
||||
String get item_description => 'Description : ';
|
||||
|
||||
@override
|
||||
String get item_tags => 'Tags : ';
|
||||
|
||||
@override
|
||||
String get failed_auth => 'Échec de l\'authenticaton';
|
||||
|
||||
@override
|
||||
String get login_page => 'Page d\'authentification';
|
||||
|
||||
@override
|
||||
String get pseudo => 'Pseudo';
|
||||
|
||||
@override
|
||||
String get enter_existing_pseudo => 'Entrez un pseudo existant';
|
||||
|
||||
@override
|
||||
String get remembr_me => 'Se souvenir de moi';
|
||||
|
||||
@override
|
||||
String get new_user => 'Nouvel utilisateur ? Créer un compte';
|
||||
|
||||
@override
|
||||
String get sign_in => 'Se connecter';
|
||||
|
||||
@override
|
||||
String get map_token => 'Token d\'accès de Mapbox n\'est pas disponible';
|
||||
|
||||
@override
|
||||
String get geo_disabled => 'Les services de localisation sont désactivés.';
|
||||
|
||||
@override
|
||||
String get permission_denied =>
|
||||
'Les permissions de localisation sont refusées.';
|
||||
|
||||
@override
|
||||
String get enable_permission =>
|
||||
'Les permissions de localisation sont toujours désactivés. Il faut les désactiver';
|
||||
|
||||
@override
|
||||
String get no_last_position => 'Aucune position n\'est pas disponible.';
|
||||
|
||||
@override
|
||||
String get failed_location =>
|
||||
'Échec de récupération des données geographique';
|
||||
|
||||
@override
|
||||
String get failed_fetch => 'Échec de récupération des routes';
|
||||
|
||||
@override
|
||||
String get invalid_coordinates_symbol =>
|
||||
'Coordonnées invalides. On ne peut pas ajouter le symbole';
|
||||
|
||||
@override
|
||||
String get error_symbol => 'Erreur lors de l\'ajout du symbole';
|
||||
|
||||
@override
|
||||
String get position_not_init =>
|
||||
'Coordonnées non initialisées. Essaye encore.';
|
||||
|
||||
@override
|
||||
String get invalid_coordinates => 'Coordonnées invalides';
|
||||
|
||||
@override
|
||||
String get walking => 'Marche';
|
||||
|
||||
@override
|
||||
String get cycling => 'Vélo';
|
||||
|
||||
@override
|
||||
String get driving => 'Voiture';
|
||||
|
||||
@override
|
||||
String get get_direction => 'Get Directions and Markers';
|
||||
|
||||
@override
|
||||
String get missing_token => 'Token d\'accès manquant';
|
||||
|
||||
@override
|
||||
String get geocoding_error => 'Erreur lors du geocodage';
|
||||
|
||||
@override
|
||||
String get no_found_place => 'Lieu introuvable';
|
||||
|
||||
@override
|
||||
String get upload_error => 'Erreur lors de l\'upload d\'image';
|
||||
|
||||
@override
|
||||
String get event_added => 'Évènement ajouté';
|
||||
|
||||
@override
|
||||
String get unknown_error => 'Erreur inconnue';
|
||||
|
||||
@override
|
||||
String get app_error => 'Erreur d\'application';
|
||||
|
||||
@override
|
||||
String get at => 'à';
|
||||
|
||||
@override
|
||||
String get to_date => 'jusqu\'à';
|
||||
|
||||
@override
|
||||
String get item_link => 'Lien : ';
|
||||
|
||||
@override
|
||||
String get item_ticket => 'Billet : ';
|
||||
|
||||
@override
|
||||
String get link => 'Lien';
|
||||
|
||||
@override
|
||||
String get edit_link => 'Editer le lien';
|
||||
|
||||
@override
|
||||
String get ticket => 'Billet';
|
||||
|
||||
@override
|
||||
String get edit_ticket => 'Editer le lien du billet';
|
||||
|
||||
@override
|
||||
String get toogle_interest => 'Erreur de bouton de changement';
|
||||
|
||||
@override
|
||||
String get error_update => 'Erreur lors de la mise à jour';
|
||||
|
||||
@override
|
||||
String get count_interested => 'Nombre de personne interessé';
|
||||
}
|
43
covas_mobile_new/lib/main.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'pages/LoginDemo.dart';
|
||||
import 'locale_provider.dart'; // <-- à adapter selon ton arborescence
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'classes/notification_service.dart';
|
||||
import 'classes/auth_service.dart';
|
||||
import 'pages/ListItemMenu.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await MobileAds.instance.initialize();
|
||||
await NotificationService.initialize();
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
final loggedIn = await _authService.isLoggedIn();
|
||||
|
||||
runApp(
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => LocaleProvider(),
|
||||
child: MyApp(isLoggedIn: loggedIn),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
final bool isLoggedIn;
|
||||
const MyApp({Key? key, required this.isLoggedIn}) : super(key: key);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localeProvider = Provider.of<LocaleProvider>(
|
||||
context); // écoute les changements de langue
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
locale: localeProvider.locale, // <-- utilise la locale courante
|
||||
supportedLocales: L10n.all,
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
home: isLoggedIn ? ListItemMenu() : LoginDemo(),
|
||||
);
|
||||
}
|
||||
}
|
335
covas_mobile_new/lib/pages/AddProfile.dart
Normal file
@@ -0,0 +1,335 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import '../pages/LoginDemo.dart';
|
||||
|
||||
import '../classes/alert.dart';
|
||||
|
||||
import '../variable/globals.dart' as globals;
|
||||
|
||||
import '../classes/ad_helper.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; // Créé plus loin
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await MobileAds.instance.initialize();
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: AddProfile(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AddProfile extends StatefulWidget {
|
||||
const AddProfile({super.key});
|
||||
|
||||
@override
|
||||
_AddProfileState createState() => _AddProfileState();
|
||||
}
|
||||
|
||||
class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
|
||||
BannerAd? _bannerAd;
|
||||
|
||||
TextEditingController inputUserName = TextEditingController();
|
||||
|
||||
TextEditingController inputName = TextEditingController();
|
||||
|
||||
TextEditingController inputFirstName = TextEditingController();
|
||||
TextEditingController inputEmail = TextEditingController();
|
||||
TextEditingController inputBirth = TextEditingController();
|
||||
TextEditingController inputPassword = TextEditingController();
|
||||
TextEditingController inputPasswordConfirmed = TextEditingController();
|
||||
|
||||
onTapFunctionDatePicker({required BuildContext context}) async {
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
firstDate: DateTime(1900),
|
||||
initialDate: DateTime.now(),
|
||||
lastDate: DateTime(2104));
|
||||
if (pickedDate == null) return;
|
||||
inputBirth.text = DateFormat("dd/MM/yyyy").format(pickedDate);
|
||||
}
|
||||
|
||||
convertNulltoEmptyString(var check) {
|
||||
if (check == null) {
|
||||
return "";
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
convertNulltoArray(List<String> check) {
|
||||
if (check == null) {
|
||||
return [];
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
String formatDate(String date) {
|
||||
var splitedDate = date.split("/");
|
||||
|
||||
var day = splitedDate[0];
|
||||
var month = splitedDate[1];
|
||||
var year = splitedDate[2];
|
||||
|
||||
return "${year}-${month}-${day}";
|
||||
}
|
||||
|
||||
Future<void> _createProfile(BuildContext context) async {
|
||||
var username = inputUserName.text;
|
||||
var firstName = inputFirstName.text;
|
||||
var name = inputName.text;
|
||||
var email = inputEmail.text;
|
||||
var password = inputPassword.text;
|
||||
var confirmedPassword = inputPasswordConfirmed.text;
|
||||
var birth = DateTime.parse(formatDate(inputBirth.text));
|
||||
|
||||
if ((password.isNotEmpty) && (confirmedPassword.isNotEmpty)) {
|
||||
if (password != confirmedPassword) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.password_different ??
|
||||
"Must write a different password");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var urlPut = Uri.parse("${globals.api}/mail");
|
||||
|
||||
var responsePost = await http.post(urlPut,
|
||||
headers: {
|
||||
HttpHeaders.acceptHeader: 'application/json, text/plain, */*',
|
||||
HttpHeaders.contentTypeHeader: 'application/json'
|
||||
},
|
||||
body: jsonEncode({
|
||||
'name': name,
|
||||
'username': username,
|
||||
'firstName': firstName,
|
||||
'password': password,
|
||||
'email': email,
|
||||
'birth': birth.toString()
|
||||
}));
|
||||
print(responsePost.statusCode);
|
||||
if (responsePost.statusCode == 200) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.create ?? "Creation",
|
||||
AppLocalizations.of(context)?.user_create ?? "Your user created");
|
||||
Navigator.pushReplacement(
|
||||
context, MaterialPageRoute(builder: (_) => LoginDemo()));
|
||||
return;
|
||||
}
|
||||
|
||||
final errorMessages = {
|
||||
400: AppLocalizations.of(context)?.request_error ??
|
||||
"Poorly constructed query",
|
||||
406: AppLocalizations.of(context)?.incorrect_password ??
|
||||
"Incorrect password",
|
||||
404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user",
|
||||
403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user",
|
||||
410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token",
|
||||
500: AppLocalizations.of(context)?.internal_error_server ??
|
||||
"Internal error server"
|
||||
};
|
||||
|
||||
final text = errorMessages[responsePost.statusCode] ??
|
||||
AppLocalizations.of(context)?.unknown_error_auth ??
|
||||
"Unknown error auth";
|
||||
showAlertDialog(
|
||||
context, AppLocalizations.of(context)?.error ?? "Error", text);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
|
||||
setState(() {
|
||||
_bannerAd = ad;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
String? _validateField(String? value) {
|
||||
return value!.isEmpty
|
||||
? AppLocalizations.of(context)?.required_input ?? 'Required input'
|
||||
: null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)?.create_profile ?? "Create profile"),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_bannerAd == null
|
||||
? SizedBox.shrink()
|
||||
: SizedBox(
|
||||
height: _bannerAd!.size.height.toDouble(),
|
||||
width: _bannerAd!.size.width.toDouble(),
|
||||
child: AdWidget(ad: _bannerAd!)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputUserName,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Pseudo',
|
||||
hintText: AppLocalizations.of(context)?.edit_pseudo ??
|
||||
'Edit pseudo'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputPassword,
|
||||
validator: (value) => _validateField(value),
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.password ??
|
||||
'Password',
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.enter_password ??
|
||||
'Enter the password'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputPasswordConfirmed,
|
||||
validator: (value) => _validateField(value),
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
AppLocalizations.of(context)?.password_confirmed ??
|
||||
'Password confirmed',
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.password_confirmed ??
|
||||
'Password confirmed',
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputName,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.last_name ??
|
||||
'Last name',
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.edit_last_name ??
|
||||
'Edit last name'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputFirstName,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.first_name ??
|
||||
'First name',
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.edit_first_name ??
|
||||
'Edit first name'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputEmail,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
AppLocalizations.of(context)?.email ?? 'Email',
|
||||
hintText: AppLocalizations.of(context)?.edit_email ??
|
||||
'Edit email'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputBirth,
|
||||
readOnly: true,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.birth_date ??
|
||||
'Birth date',
|
||||
hintText: AppLocalizations.of(context)?.edit_birth ??
|
||||
'Edit birth date'),
|
||||
onTap: () => onTapFunctionDatePicker(context: context)),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: 250,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(20)),
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_createProfile(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
AppLocalizations.of(context)?.create_profile_button ??
|
||||
"Create profile",
|
||||
style: TextStyle(color: Colors.white, fontSize: 25),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
163
covas_mobile_new/lib/pages/Camera.dart
Normal file
@@ -0,0 +1,163 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import '../classes/MyDrawer.dart';
|
||||
|
||||
import 'DisplayPictureScreen.dart';
|
||||
import 'package:camera/camera.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; // Créé plus loin
|
||||
|
||||
Future<void> main() async {
|
||||
// Ensure that plugin services are initialized so that `availableCameras()`
|
||||
// can be called before `runApp()`
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Obtain a list of the available cameras on the device.
|
||||
final cameras = await availableCameras();
|
||||
|
||||
// Get a specific camera from the list of available cameras.
|
||||
final firstCamera = cameras.first;
|
||||
|
||||
runApp(
|
||||
MaterialApp(
|
||||
theme: ThemeData.dark(),
|
||||
home: Camera(
|
||||
// Pass the appropriate camera to the TakePictureScreen widget.
|
||||
camera: firstCamera,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// A screen that allows users to take a picture using a given camera.
|
||||
class Camera extends StatefulWidget {
|
||||
const Camera({
|
||||
super.key,
|
||||
required this.camera,
|
||||
});
|
||||
|
||||
final CameraDescription camera;
|
||||
|
||||
@override
|
||||
CameraState createState() => CameraState();
|
||||
}
|
||||
|
||||
class CameraState extends State<Camera> {
|
||||
late CameraController _controller;
|
||||
late Future<void> _initializeControllerFuture;
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// To display the current output from the Camera,
|
||||
// create a CameraController.
|
||||
_authService.checkTokenStatus(context);
|
||||
|
||||
_controller = CameraController(
|
||||
// Get a specific camera from the list of available cameras.
|
||||
widget.camera,
|
||||
// Define the resolution to use.
|
||||
ResolutionPreset.medium,
|
||||
);
|
||||
|
||||
// Next, initialize the controller. This returns a Future.
|
||||
_initializeControllerFuture = _controller.initialize();
|
||||
}
|
||||
|
||||
Future<void> pickImage() async {
|
||||
final imagePicker = ImagePicker();
|
||||
final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);
|
||||
if (pickedFile != null) {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DisplayPictureScreen(
|
||||
// Pass the automatically generated path to
|
||||
// the DisplayPictureScreen widget.
|
||||
imagePath: pickedFile.path,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Dispose of the controller when the widget is disposed.
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)?.take_picture ??
|
||||
"Take a picture")),
|
||||
// You must wait until the controller is initialized before displaying the
|
||||
// camera preview. Use a FutureBuilder to display a loading spinner until the
|
||||
// controller has finished initializing.
|
||||
drawer: MyDrawer(),
|
||||
body: FutureBuilder<void>(
|
||||
future: _initializeControllerFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
// If the Future is complete, display the preview.
|
||||
return CameraPreview(_controller);
|
||||
} else {
|
||||
// Otherwise, display a loading indicator.
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
floatingActionButton: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
FloatingActionButton(
|
||||
onPressed: pickImage,
|
||||
child: Icon(Icons.photo_library),
|
||||
),
|
||||
SizedBox(width: 40),
|
||||
FloatingActionButton(
|
||||
// Provide an onPressed callback.
|
||||
onPressed: () async {
|
||||
// Take the Picture in a try / catch block. If anything goes wrong,
|
||||
// catch the error.
|
||||
try {
|
||||
// Ensure that the camera is initialized.
|
||||
await _initializeControllerFuture;
|
||||
|
||||
// Attempt to take a picture and get the file `image`
|
||||
// where it was saved.
|
||||
final image = await _controller.takePicture();
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
// If the picture was taken, display it on a new screen.
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => DisplayPictureScreen(
|
||||
// Pass the automatically generated path to
|
||||
// the DisplayPictureScreen widget.
|
||||
imagePath: image.path,
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
// If an error occurs, log the error to the console.
|
||||
print(e);
|
||||
}
|
||||
},
|
||||
child: const Icon(Icons.camera_alt),
|
||||
)
|
||||
],
|
||||
)));
|
||||
}
|
||||
}
|
166
covas_mobile_new/lib/pages/CameraEdit.dart
Normal file
@@ -0,0 +1,166 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import '../classes/events.dart';
|
||||
import '../classes/MyDrawer.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'EditEvent.dart';
|
||||
import 'package:camera/camera.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; // Créé
|
||||
|
||||
Future<void> main() async {
|
||||
// Ensure that plugin services are initialized so that `availableCameras()`
|
||||
// can be called before `runApp()`
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Obtain a list of the available cameras on the device.
|
||||
final cameras = await availableCameras();
|
||||
|
||||
// Get a specific camera from the list of available cameras.
|
||||
final firstCamera = cameras.first;
|
||||
Events? events;
|
||||
|
||||
runApp(
|
||||
MaterialApp(
|
||||
theme: ThemeData.dark(),
|
||||
home: CameraEdit(
|
||||
// Pass the appropriate camera to the TakePictureScreen widget.
|
||||
camera: firstCamera,
|
||||
events: events),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// A screen that allows users to take a picture using a given camera.
|
||||
class CameraEdit extends StatefulWidget {
|
||||
const CameraEdit({super.key, required this.camera, required this.events});
|
||||
final Events? events;
|
||||
|
||||
final CameraDescription camera;
|
||||
|
||||
@override
|
||||
CameraEditState createState() => CameraEditState();
|
||||
}
|
||||
|
||||
class CameraEditState extends State<CameraEdit> {
|
||||
late CameraController _controller;
|
||||
late Future<void> _initializeControllerFuture;
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
|
||||
// To display the current output from the Camera,
|
||||
// create a CameraController.
|
||||
|
||||
_controller = CameraController(
|
||||
// Get a specific camera from the list of available cameras.
|
||||
widget.camera,
|
||||
// Define the resolution to use.
|
||||
ResolutionPreset.medium,
|
||||
);
|
||||
|
||||
// Next, initialize the controller. This returns a Future.
|
||||
_initializeControllerFuture = _controller.initialize();
|
||||
}
|
||||
|
||||
Future<void> pickImage() async {
|
||||
final imagePicker = ImagePicker();
|
||||
final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);
|
||||
if (pickedFile != null) {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => EditEvent(
|
||||
// Pass the automatically generated path to
|
||||
// the DisplayPictureScreen widget.
|
||||
events: widget.events,
|
||||
imgPath: pickedFile.path,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Dispose of the controller when the widget is disposed.
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)?.take_picture ??
|
||||
'Take a picture')),
|
||||
// You must wait until the controller is initialized before displaying the
|
||||
// camera preview. Use a FutureBuilder to display a loading spinner until the
|
||||
// controller has finished initializing.
|
||||
drawer: MyDrawer(),
|
||||
body: FutureBuilder<void>(
|
||||
future: _initializeControllerFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
// If the Future is complete, display the preview.
|
||||
return CameraPreview(_controller);
|
||||
} else {
|
||||
// Otherwise, display a loading indicator.
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
floatingActionButton: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
FloatingActionButton(
|
||||
onPressed: pickImage,
|
||||
child: Icon(Icons.photo_library),
|
||||
),
|
||||
SizedBox(width: 40),
|
||||
FloatingActionButton(
|
||||
// Provide an onPressed callback.
|
||||
onPressed: () async {
|
||||
// Take the Picture in a try / catch block. If anything goes wrong,
|
||||
// catch the error.
|
||||
try {
|
||||
// Ensure that the camera is initialized.
|
||||
await _initializeControllerFuture;
|
||||
|
||||
// Attempt to take a picture and get the file `image`
|
||||
// where it was saved.
|
||||
final image = await _controller.takePicture();
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
// If the picture was taken, display it on a new screen.
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => EditEvent(
|
||||
// Pass the automatically generated path to
|
||||
// the DisplayPictureScreen widget.
|
||||
events: widget.events,
|
||||
imgPath: image.path,
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
// If an error occurs, log the error to the console.
|
||||
print(e);
|
||||
}
|
||||
},
|
||||
child: const Icon(Icons.camera_alt),
|
||||
)
|
||||
],
|
||||
)));
|
||||
}
|
||||
}
|
266
covas_mobile_new/lib/pages/DisplayPictureScreen.dart
Normal file
@@ -0,0 +1,266 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io';
|
||||
import '../classes/addEventImage.dart';
|
||||
import '../classes/alert.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:flutter_gemini/flutter_gemini.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import "ItemMenu.dart";
|
||||
import 'UpdateEventImage.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import '../variable/globals.dart' as globals;
|
||||
import '../classes/MyDrawer.dart';
|
||||
|
||||
import '../classes/ad_helper.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; // Créé
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await MobileAds.instance.initialize();
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
// This widget is the root of your application.
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// Try running your application with "flutter run". You'll see the
|
||||
// application has a blue toolbar. Then, without quitting the app, try
|
||||
// changing the primarySwatch below to Colors.green and then invoke
|
||||
// "hot reload" (press "r" in the console where you ran "flutter run",
|
||||
// or simply save your changes to "hot reload" in a Flutter IDE).
|
||||
// Notice that the counter didn't reset back to zero; the application
|
||||
// is not restarted.
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// A screen that allows users to take a picture using a given camera.
|
||||
class DisplayPictureScreen extends StatefulWidget {
|
||||
final String imagePath;
|
||||
|
||||
const DisplayPictureScreen({super.key, required this.imagePath});
|
||||
|
||||
@override
|
||||
DisplayPictureScreenState createState() => DisplayPictureScreenState();
|
||||
}
|
||||
|
||||
// A widget that displays the picture taken by the user.
|
||||
class DisplayPictureScreenState extends State<DisplayPictureScreen>
|
||||
with ShowDescImageAdd, ShowAlertDialog, TickerProviderStateMixin {
|
||||
BannerAd? _bannerAd;
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
late AnimationController controller;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
|
||||
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
|
||||
setState(() {
|
||||
_bannerAd = ad;
|
||||
});
|
||||
});
|
||||
|
||||
controller = AnimationController(
|
||||
/// [AnimationController]s can be created with `vsync: this` because of
|
||||
/// [TickerProviderStateMixin].
|
||||
vsync: this,
|
||||
duration: const Duration(seconds: 5),
|
||||
)..addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
controller.repeat(reverse: false);
|
||||
|
||||
_getEventInfosFromImage();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> displayError(String e) async {
|
||||
print("problem gemini : ${e}");
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? 'Error',
|
||||
AppLocalizations.of(context)?.error_ia ??
|
||||
'Google AI failed to analyze picture. Retry with another one');
|
||||
}
|
||||
|
||||
void _showErrorDialog(BuildContext context, String title, String message) {
|
||||
showAlertDialog(context, title, message);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> _fetchGeolocation(String place) async {
|
||||
final apiKey = dotenv.env['PLACE_API_KEY'] ?? '';
|
||||
final response = await http.get(Uri.parse(
|
||||
'https://maps.googleapis.com/maps/api/place/textsearch/json?query=${place}}&key=$apiKey'));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
if (data['results'].isNotEmpty) {
|
||||
return data['results'][0]['geometry']['location'];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<bool> _isDuplicateEvent(String accessToken,
|
||||
Map<String, dynamic> jsonData, Map<String, dynamic> location) async {
|
||||
final url = Uri.parse(
|
||||
"${globals.api}/events/search?item=${jsonData["name"]}&date_event=${jsonData["start_date"]}"
|
||||
"&min_lat=${location['lat']}&max_lat=${location['lat']}"
|
||||
"&min_lon=${location['lng']}&max_lon=${location['lng']}");
|
||||
|
||||
final response = await http.get(url,
|
||||
headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'});
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final events = jsonDecode(utf8.decode(response.bodyBytes));
|
||||
return events.isNotEmpty;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> searchEvents(String json, String imagePath) async {
|
||||
print(json.replaceAll("'''json", '').replaceAll("'''", ""));
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
try {
|
||||
Map<String, dynamic> jsonData =
|
||||
jsonDecode(json.replaceAll("```json", '').replaceAll("```", ""));
|
||||
print("json : ${jsonData}");
|
||||
var name = jsonData["name"];
|
||||
print("name : ${name}");
|
||||
var place = jsonData["place"];
|
||||
var date = jsonData["start_date"];
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
|
||||
if (accessToken.isNotEmpty) {
|
||||
final location = await _fetchGeolocation(place);
|
||||
if (location == null) {
|
||||
_showErrorDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? 'Error',
|
||||
AppLocalizations.of(context)?.no_data_geo ??
|
||||
'No geographical data');
|
||||
return;
|
||||
}
|
||||
|
||||
final url = Uri.parse(
|
||||
"${globals.api}/events/search?item=${name}&date_event=${date}"
|
||||
"&min_lat=${location['lat']}&max_lat=${location['lat']}"
|
||||
"&min_lon=${location['lng']}&max_lon=${location['lng']}");
|
||||
|
||||
final response = await http.get(url,
|
||||
headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'});
|
||||
if (response.statusCode == 200) {
|
||||
var events = jsonDecode(utf8.decode(response.bodyBytes));
|
||||
print("reponse http : ${events.length}");
|
||||
if (events.length == 0) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => UpdateeventImage(
|
||||
events: jsonData, imagePath: imagePath)));
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => ItemMenu(title: events[0]["id"])));
|
||||
}
|
||||
} else {
|
||||
String error = AppLocalizations.of(context)?.response_status_update ??
|
||||
'Response status update : ${response.statusCode}';
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? 'Error',
|
||||
"${error} : ${response.statusCode}");
|
||||
}
|
||||
} else {
|
||||
showAlertDialog(context, AppLocalizations.of(context)?.error ?? 'Error',
|
||||
AppLocalizations.of(context)?.error_token ?? "Token error");
|
||||
}
|
||||
} catch (e) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.error_format ??
|
||||
"Data format error given by AI");
|
||||
}
|
||||
|
||||
//showDescImageAddDialog(context, message);
|
||||
}
|
||||
|
||||
Future<void> _getEventInfosFromImage() async {
|
||||
await dotenv.load();
|
||||
|
||||
final gemini = Gemini.init(
|
||||
apiKey: dotenv.env['GEMINI_API_KEY']!, enableDebugging: true);
|
||||
|
||||
final file = File(widget.imagePath);
|
||||
|
||||
gemini.prompt(parts: [
|
||||
Part.text(
|
||||
"Peux-tu donner le nom, la date (si l'année n'est pas précisé, mettez l'année actuelle ou future) et le lieu de l'évènement sous format JSON (sans le caratère json au début de la chaine de caractère) avec les valeurs suivantes : name, place, description, tags (tableau sans espace), organizers (tableau), start_date et end_date (si le end_date est vide, alors donnez une valeur de six de plus par rapport à start_date) sous le format en YYYY-MM-DD HH:mm:ssZ"),
|
||||
Part.bytes(file.readAsBytesSync())
|
||||
], model: "models/gemini-1.5-pro-latest").then((value) {
|
||||
searchEvents(value?.output ?? '', widget.imagePath);
|
||||
}).catchError((e) => displayError);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)?.display_picture ??
|
||||
"Display The Picture")),
|
||||
// The image is stored as a file on the device. Use the `Image.file`
|
||||
// constructor with the given path to display the image.
|
||||
drawer: MyDrawer(),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
_bannerAd == null
|
||||
? SizedBox.shrink()
|
||||
: SizedBox(
|
||||
height: _bannerAd!.size.height.toDouble(),
|
||||
width: _bannerAd!.size.width.toDouble(),
|
||||
child: AdWidget(ad: _bannerAd!)),
|
||||
Text(
|
||||
AppLocalizations.of(context)?.analyze_image ??
|
||||
'Image analyze in progress',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
CircularProgressIndicator(
|
||||
value: controller.value,
|
||||
semanticsLabel:
|
||||
AppLocalizations.of(context)?.loading_progress ??
|
||||
'Loading progress',
|
||||
),
|
||||
])));
|
||||
}
|
||||
}
|
921
covas_mobile_new/lib/pages/EditEvent.dart
Normal file
@@ -0,0 +1,921 @@
|
||||
import 'package:covas_mobile/pages/CameraEdit.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:textfield_tags/textfield_tags.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import '../classes/events.dart';
|
||||
import '../classes/MyDrawer.dart';
|
||||
|
||||
import 'package:camera/camera.dart';
|
||||
|
||||
import '../classes/alert.dart';
|
||||
import '../classes/eventAdded.dart';
|
||||
|
||||
import '../variable/globals.dart' as globals;
|
||||
|
||||
import '../classes/ad_helper.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; // Créé
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await MobileAds.instance.initialize();
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
Events? events;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: EditEvent(
|
||||
events: events,
|
||||
imgPath: "",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EditEvent extends StatefulWidget {
|
||||
const EditEvent({Key? key, required this.events, required this.imgPath})
|
||||
: super(key: key);
|
||||
final Events? events;
|
||||
|
||||
final String imgPath;
|
||||
|
||||
@override
|
||||
_EditEventState createState() => _EditEventState();
|
||||
}
|
||||
|
||||
class _EditEventState extends State<EditEvent>
|
||||
with ShowAlertDialog, ShowEventDialog {
|
||||
BannerAd? _bannerAd;
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
TextEditingController inputName = TextEditingController();
|
||||
|
||||
TextEditingController inputTicket = TextEditingController();
|
||||
TextEditingController inputLink = TextEditingController();
|
||||
TextEditingController inputDate = TextEditingController();
|
||||
TextEditingController inputDesc = TextEditingController();
|
||||
|
||||
TextEditingController inputGeo = TextEditingController();
|
||||
|
||||
TextEditingController startDatepicker = TextEditingController();
|
||||
TextEditingController startTimepicker = TextEditingController();
|
||||
TextEditingController endDatepicker = TextEditingController();
|
||||
TextEditingController endTimepicker = TextEditingController();
|
||||
final _stringTagController = StringTagController();
|
||||
|
||||
DateTime startDate = DateTime.now();
|
||||
DateTime endDate = DateTime.now();
|
||||
List<Map<String, dynamic>> suggestions = [];
|
||||
String geographicalZone = "";
|
||||
String imgUrl = "";
|
||||
|
||||
List<String> initialTags = [];
|
||||
|
||||
final _stringOrgaController = StringTagController();
|
||||
List<String> initialOrga = [];
|
||||
|
||||
onTapFunctionDatePicker(
|
||||
{required BuildContext context, required String position}) async {
|
||||
DateTime date;
|
||||
if ((startDatepicker.text.isEmpty) || (endDatepicker.text.isEmpty)) {
|
||||
date = DateTime.now();
|
||||
} else {
|
||||
date = DateTime.parse(formatDate(startDatepicker.text));
|
||||
if (position == "end") {
|
||||
date = DateTime.parse(formatDate(endDatepicker.text));
|
||||
}
|
||||
}
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
firstDate: date,
|
||||
initialDate: date,
|
||||
lastDate: DateTime(2104));
|
||||
if (pickedDate == null) return;
|
||||
if (position == "start") {
|
||||
startDatepicker.text = DateFormat("dd/MM/yyyy").format(pickedDate);
|
||||
}
|
||||
if (position == "end") {
|
||||
endDatepicker.text = DateFormat("dd/MM/yyyy").format(pickedDate);
|
||||
}
|
||||
}
|
||||
|
||||
onTapFunctionTimePicker(
|
||||
{required BuildContext context, required String position}) async {
|
||||
TimeOfDay time;
|
||||
|
||||
if ((startTimepicker.text.isEmpty) || (endTimepicker.text.isEmpty)) {
|
||||
time = TimeOfDay.now();
|
||||
} else {
|
||||
DateTime date = new DateTime.now();
|
||||
date = date.copyWith(
|
||||
hour: int.parse(startTimepicker.text.split(":")[0]),
|
||||
minute: int.parse(startTimepicker.text.split(":")[1]));
|
||||
time = TimeOfDay.fromDateTime(date);
|
||||
if (position == "end") {
|
||||
date = date.copyWith(
|
||||
hour: int.parse(endTimepicker.text.split(":")[0]),
|
||||
minute: int.parse(endTimepicker.text.split(":")[1]));
|
||||
time = TimeOfDay.fromDateTime(date);
|
||||
}
|
||||
}
|
||||
|
||||
TimeOfDay? pickedDate =
|
||||
await showTimePicker(context: context, initialTime: time);
|
||||
if (pickedDate == null) return;
|
||||
if (position == "start") {
|
||||
startTimepicker.text = pickedDate.format(context);
|
||||
}
|
||||
if (position == "end") {
|
||||
endTimepicker.text = pickedDate.format(context);
|
||||
}
|
||||
}
|
||||
|
||||
convertNulltoEmptyString(var check) {
|
||||
if (check == null) {
|
||||
return "";
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
convertNulltoArray(List<String> check) {
|
||||
if (check == null) {
|
||||
return [];
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
String formatDate(String date) {
|
||||
var splitedDate = date.split("/");
|
||||
|
||||
var day = splitedDate[0];
|
||||
var month = splitedDate[1];
|
||||
var year = splitedDate[2];
|
||||
|
||||
return "${year}-${month}-${day}";
|
||||
}
|
||||
|
||||
Future<void> _updateEvent(BuildContext context) async {
|
||||
if (!_isEventInFuture()) {
|
||||
_showErrorDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error_event ?? "Event error",
|
||||
AppLocalizations.of(context)?.no_future_event ?? "No future event");
|
||||
return;
|
||||
}
|
||||
|
||||
final accessToken = await _getAccessToken();
|
||||
if (accessToken.isEmpty) {
|
||||
_showErrorDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error_user ?? "User error",
|
||||
AppLocalizations.of(context)?.empty_input ?? "Empty input");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await dotenv.load();
|
||||
final geolocation = await _fetchGeolocation();
|
||||
if (geolocation == null) {
|
||||
_showErrorDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.no_data_geo ??
|
||||
"No geographical data");
|
||||
return;
|
||||
}
|
||||
|
||||
if (await _isDuplicateEvent(accessToken, geolocation)) {
|
||||
_showErrorDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.info_event ?? "Event info",
|
||||
AppLocalizations.of(context)?.event_already ??
|
||||
"Event already exists");
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget.imgPath.isNotEmpty) {
|
||||
imgUrl = await _uploadImage(widget.imgPath);
|
||||
if (imgUrl.isEmpty) {
|
||||
_showErrorDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.picture_error ?? "Error picture",
|
||||
AppLocalizations.of(context)?.no_picture_published ??
|
||||
"No picture published");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _updateEventData(accessToken, geolocation);
|
||||
String message =
|
||||
AppLocalizations.of(context)?.event_update ?? "Event updated";
|
||||
showEventDialog(context, "${message} : ${inputName.text}");
|
||||
} catch (e) {
|
||||
_showErrorDialog(
|
||||
context, AppLocalizations.of(context)?.error ?? "Error", "$e");
|
||||
}
|
||||
}
|
||||
|
||||
bool _isEventInFuture() {
|
||||
DateTime startDateCompare = DateTime.parse(
|
||||
"${formatDate(startDatepicker.text)}T${startTimepicker.text.replaceAll('-', ':')}");
|
||||
return startDateCompare.isAfter(DateTime.now());
|
||||
}
|
||||
|
||||
Future<String> _getAccessToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString("access_token") ?? "";
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> _fetchGeolocation() async {
|
||||
final apiKey = dotenv.env['PLACE_API_KEY'] ?? '';
|
||||
final response = await http.get(Uri.parse(
|
||||
'https://maps.googleapis.com/maps/api/place/textsearch/json?query=${inputGeo.text}&key=$apiKey'));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
if (data['results'].isNotEmpty) {
|
||||
return data['results'][0]['geometry']['location'];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<bool> _isDuplicateEvent(
|
||||
String accessToken, Map<String, dynamic> location) async {
|
||||
final url = Uri.parse(
|
||||
"${globals.api}/events/search?item=${inputName.text}&date_event=${formatDate(startDatepicker.text)}"
|
||||
"&min_lat=${location['lat']}&max_lat=${location['lat']}"
|
||||
"&min_lon=${location['lng']}&max_lon=${location['lng']}");
|
||||
|
||||
final response = await http.get(url,
|
||||
headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'});
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final events = jsonDecode(utf8.decode(response.bodyBytes));
|
||||
return events.isNotEmpty && events[0]["id"] != widget.events!.id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<String> _uploadImage(String imagePath) async {
|
||||
final params = {
|
||||
'expiration': '15552000',
|
||||
'key': dotenv.env["IMGBB_API_KEY"]
|
||||
};
|
||||
final url = Uri.parse('https://api.imgbb.com/1/upload')
|
||||
.replace(queryParameters: params);
|
||||
|
||||
final image = File(imagePath);
|
||||
final req = http.MultipartRequest('POST', url)
|
||||
..fields['image'] = base64.encode(await image.readAsBytes());
|
||||
|
||||
final response = await http.Response.fromStream(await req.send());
|
||||
return response.statusCode == 200
|
||||
? json.decode(response.body)["data"]["url"]
|
||||
: "";
|
||||
}
|
||||
|
||||
Future<void> _updateEventData(
|
||||
String accessToken, Map<String, dynamic> location) async {
|
||||
final url = Uri.parse("${globals.api}/events/${widget.events!.id}");
|
||||
final response = await http.put(url,
|
||||
headers: {
|
||||
HttpHeaders.cookieHeader: 'access_token=$accessToken',
|
||||
HttpHeaders.acceptHeader: 'application/json, text/plain, */*',
|
||||
HttpHeaders.contentTypeHeader: 'application/json'
|
||||
},
|
||||
body: jsonEncode({
|
||||
'name': inputName.text,
|
||||
'place': inputGeo.text,
|
||||
'start_date':
|
||||
"${formatDate(startDatepicker.text)}T${startTimepicker.text.replaceAll('-', ':')}",
|
||||
'end_date':
|
||||
"${formatDate(endDatepicker.text)}T${endTimepicker.text.replaceAll('-', ':')}",
|
||||
'organizers':
|
||||
List<String>.from(_stringOrgaController.getTags as List),
|
||||
'latitude': location['lat'],
|
||||
'longitude': location['lng'],
|
||||
'description': inputDesc.text,
|
||||
"imgUrl": imgUrl,
|
||||
'link': inputLink.text,
|
||||
'ticket': inputTicket.text,
|
||||
"tags": List<String>.from(_stringTagController.getTags as List)
|
||||
}));
|
||||
|
||||
if (response.statusCode != 200 && response.statusCode != 201) {
|
||||
_handleErrorResponse(context, response.statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
void _handleErrorResponse(BuildContext context, int statusCode) {
|
||||
final messages = {
|
||||
400: AppLocalizations.of(context)?.request_error ??
|
||||
"Poorly constructed query",
|
||||
406: AppLocalizations.of(context)?.incorrect_password ??
|
||||
"Incorrect password",
|
||||
404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user",
|
||||
403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user",
|
||||
410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token",
|
||||
500: AppLocalizations.of(context)?.internal_error_server ??
|
||||
"Internal error server"
|
||||
};
|
||||
_showErrorDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
messages[statusCode] ??
|
||||
AppLocalizations.of(context)?.unknown_error_auth ??
|
||||
"Unknown error auth");
|
||||
}
|
||||
|
||||
void _showErrorDialog(BuildContext context, String title, String message) {
|
||||
showAlertDialog(context, title, message);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
|
||||
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
|
||||
setState(() {
|
||||
_bannerAd = ad;
|
||||
});
|
||||
});
|
||||
inputName.text = widget.events!.name ?? "";
|
||||
inputTicket.text = widget.events!.ticket ?? "";
|
||||
inputLink.text = widget.events!.link ?? "";
|
||||
startDatepicker.text = DateFormat("dd/MM/yyyy")
|
||||
.format(DateTime.parse(
|
||||
widget.events!.startDate ?? DateTime.now().toString()))
|
||||
.toString();
|
||||
startTimepicker.text = DateFormat("HH:mm")
|
||||
.format(DateTime.parse(
|
||||
widget.events!.startDate ?? DateTime.now().toString()))
|
||||
.toString();
|
||||
endDatepicker.text = DateFormat("dd/MM/yyyy")
|
||||
.format(
|
||||
DateTime.parse(widget.events!.endDate ?? DateTime.now().toString()))
|
||||
.toString();
|
||||
endTimepicker.text = DateFormat("HH:mm")
|
||||
.format(
|
||||
DateTime.parse(widget.events!.endDate ?? DateTime.now().toString()))
|
||||
.toString();
|
||||
inputGeo.text = widget.events!.place ?? "";
|
||||
|
||||
imgUrl = widget.events!.imgUrl ?? "";
|
||||
inputDesc.text = widget.events!.description ?? "";
|
||||
initialTags = List<String>.from((widget.events?.tags as List?) ?? []);
|
||||
initialOrga = List<String>.from((widget.events?.organizers as List?) ?? []);
|
||||
}
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
String? _validateField(String? value) {
|
||||
return value!.isEmpty
|
||||
? AppLocalizations.of(context)?.required_input ?? "Required input"
|
||||
: null;
|
||||
}
|
||||
|
||||
Future<void> searchSuggestions(String input) async {
|
||||
await dotenv.load(fileName: ".env"); // Load .env file
|
||||
|
||||
final mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '';
|
||||
final url =
|
||||
'https://api.mapbox.com/geocoding/v5/mapbox.places/${input}.json?access_token=${mapboxAccessToken}&types=poi,address,place';
|
||||
var encoded = Uri.encodeFull(url);
|
||||
final response = await http.get(Uri.parse(encoded));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
setState(() {
|
||||
suggestions = (data['features'] as List)
|
||||
.map((feature) => {
|
||||
'place_name': feature['place_name'],
|
||||
'text': feature['text'],
|
||||
'geometry': feature[
|
||||
'geometry'], // Include geometry for latitude/longitude
|
||||
})
|
||||
.toList();
|
||||
});
|
||||
} else {
|
||||
throw Exception('Failed to load suggestions');
|
||||
}
|
||||
}
|
||||
|
||||
Padding _buildGeographicalZoneSearchField() {
|
||||
return Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
child: Column(
|
||||
children: [
|
||||
TextField(
|
||||
controller: inputGeo,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context)?.location ?? "Location",
|
||||
border: OutlineInputBorder(),
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
inputGeo.clear(); // Clear the text field
|
||||
geographicalZone = ''; // Reset the geographical zone state
|
||||
suggestions.clear(); // Optionally clear suggestions
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
geographicalZone = value;
|
||||
searchSuggestions(value);
|
||||
});
|
||||
},
|
||||
),
|
||||
if (suggestions.isNotEmpty)
|
||||
Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.blue),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: suggestions.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ListTile(
|
||||
title: Text(suggestions[index]['text']),
|
||||
subtitle: Text(suggestions[index]['place_name']),
|
||||
onTap: () async {
|
||||
print("suggestion tapped : ${suggestions[index]}");
|
||||
final latitude =
|
||||
suggestions[index]['geometry']['coordinates'][1];
|
||||
final longitude =
|
||||
suggestions[index]['geometry']['coordinates'][0];
|
||||
|
||||
setState(() {
|
||||
geographicalZone = suggestions[index]['text'];
|
||||
inputGeo.text = geographicalZone;
|
||||
suggestions.clear();
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> popCamera() async {
|
||||
await availableCameras().then((value) => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) =>
|
||||
CameraEdit(camera: value.first, events: widget.events))));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
drawer: MyDrawer(),
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)?.add_event ??
|
||||
"Add or Update a event"),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_bannerAd == null
|
||||
? SizedBox.shrink()
|
||||
: SizedBox(
|
||||
height: _bannerAd!.size.height.toDouble(),
|
||||
width: _bannerAd!.size.width.toDouble(),
|
||||
child: AdWidget(ad: _bannerAd!)),
|
||||
if (widget.imgPath.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 60.0),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 150,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(100.0)),
|
||||
child: Image.file(File(widget.imgPath))),
|
||||
),
|
||||
),
|
||||
if (widget.imgPath.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 60.0),
|
||||
child: Image.network(
|
||||
imgUrl,
|
||||
width: MediaQuery.of(context).size.width *
|
||||
0.5, // 50% of screen width
|
||||
height: MediaQuery.of(context).size.height * 0.5,
|
||||
loadingBuilder: (BuildContext context, Widget child,
|
||||
ImageChunkEvent? loadingProgress) {
|
||||
if (loadingProgress == null) {
|
||||
return child; // The image has finished loading
|
||||
}
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
errorBuilder: (BuildContext context, Object error,
|
||||
StackTrace? stackTrace) {
|
||||
return Center(
|
||||
child: Icon(Icons.error,
|
||||
size: MediaQuery.of(context).size.width * 0.1),
|
||||
);
|
||||
},
|
||||
)),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: popCamera,
|
||||
icon: Icon(Icons.edit, size: 16), // Edit icon
|
||||
label: Text(AppLocalizations.of(context)?.edit_image ??
|
||||
"Edit pictures"), // Button text
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Button color
|
||||
foregroundColor: Colors.white, // Text color
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
//padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0),
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputName,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.name ?? "Name",
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.edit_event_name ??
|
||||
"Edit event name"),
|
||||
),
|
||||
),
|
||||
_buildGeographicalZoneSearchField(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: startDatepicker,
|
||||
readOnly: true,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.start_date ??
|
||||
"Start date",
|
||||
hintText: AppLocalizations.of(context)?.select_date ??
|
||||
"Click to select a date"),
|
||||
onTap: () => onTapFunctionDatePicker(
|
||||
context: context, position: "start")),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: startTimepicker,
|
||||
readOnly: true,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.start_time ??
|
||||
"Start time",
|
||||
hintText: AppLocalizations.of(context)?.select_time ??
|
||||
"Click to select a time"),
|
||||
onTap: () => onTapFunctionTimePicker(
|
||||
context: context, position: "start")),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: endDatepicker,
|
||||
readOnly: true,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.end_date ??
|
||||
"End date",
|
||||
hintText: AppLocalizations.of(context)?.select_time ??
|
||||
"Click to select a date"),
|
||||
onTap: () => onTapFunctionDatePicker(
|
||||
context: context, position: "end")),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: endTimepicker,
|
||||
readOnly: true,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.end_time ??
|
||||
"End time",
|
||||
hintText: AppLocalizations.of(context)?.select_time ??
|
||||
"Click to select a time"),
|
||||
onTap: () => onTapFunctionTimePicker(
|
||||
context: context, position: "end")),
|
||||
),
|
||||
Padding(
|
||||
//padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
child: TextFormField(
|
||||
controller: inputLink,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.link ?? "Link",
|
||||
hintText: AppLocalizations.of(context)?.edit_link ??
|
||||
"Edit link event"),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
//padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
child: TextFormField(
|
||||
controller: inputTicket,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
AppLocalizations.of(context)?.ticket ?? "Ticket",
|
||||
hintText: AppLocalizations.of(context)?.edit_ticket ??
|
||||
"Edit ticket link"),
|
||||
),
|
||||
),
|
||||
TextFieldTags<String>(
|
||||
textfieldTagsController: _stringTagController,
|
||||
initialTags: initialTags,
|
||||
textSeparators: const [' ', ','],
|
||||
validator: (String tag) {
|
||||
if (_stringTagController.getTags!.contains(tag)) {
|
||||
return AppLocalizations.of(context)?.already_tag ??
|
||||
"You have already entered this tag";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
inputFieldBuilder: (context, inputFieldValues) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
child: TextField(
|
||||
controller: inputFieldValues.textEditingController,
|
||||
focusNode: inputFieldValues.focusNode,
|
||||
onChanged: inputFieldValues.onTagChanged,
|
||||
onSubmitted: inputFieldValues.onTagSubmitted,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
AppLocalizations.of(context)?.tag ?? 'Tags',
|
||||
hintText: inputFieldValues.tags.isNotEmpty
|
||||
? ''
|
||||
: AppLocalizations.of(context)?.enter_tag ??
|
||||
"Enter tag...",
|
||||
errorText: inputFieldValues.error,
|
||||
prefixIcon: inputFieldValues.tags.isNotEmpty
|
||||
? SingleChildScrollView(
|
||||
controller:
|
||||
inputFieldValues.tagScrollController,
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
bottom: 8,
|
||||
left: 8,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 4.0,
|
||||
spacing: 4.0,
|
||||
children: inputFieldValues.tags
|
||||
.map((String tag) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(20.0),
|
||||
),
|
||||
color: Colors.blue,
|
||||
),
|
||||
margin:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 5.0),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 10.0,
|
||||
vertical: 5.0),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
child: Text(
|
||||
'$tag',
|
||||
style: const TextStyle(
|
||||
color: Colors.white),
|
||||
),
|
||||
onTap: () {
|
||||
//print("$tag selected");
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 4.0),
|
||||
InkWell(
|
||||
child: const Icon(
|
||||
Icons.cancel,
|
||||
size: 14.0,
|
||||
color: Color.fromARGB(
|
||||
255, 233, 233, 233),
|
||||
),
|
||||
onTap: () {
|
||||
inputFieldValues
|
||||
.onTagRemoved(tag);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList()),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
TextFieldTags<String>(
|
||||
textfieldTagsController: _stringOrgaController,
|
||||
initialTags: initialOrga,
|
||||
textSeparators: const [','],
|
||||
validator: (String tag) {
|
||||
if (_stringOrgaController.getTags!.contains(tag)) {
|
||||
return AppLocalizations.of(context)
|
||||
?.already_organiser ??
|
||||
"You have already entered this organizer";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
inputFieldBuilder: (context, inputFieldValues) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
child: TextField(
|
||||
controller: inputFieldValues.textEditingController,
|
||||
focusNode: inputFieldValues.focusNode,
|
||||
onChanged: inputFieldValues.onTagChanged,
|
||||
onSubmitted: inputFieldValues.onTagSubmitted,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
AppLocalizations.of(context)?.organizer ??
|
||||
"Organizer",
|
||||
hintText: inputFieldValues.tags.isNotEmpty
|
||||
? ''
|
||||
: AppLocalizations.of(context)
|
||||
?.enter_organizer ??
|
||||
"Enter a organizer",
|
||||
errorText: inputFieldValues.error,
|
||||
prefixIcon: inputFieldValues.tags.isNotEmpty
|
||||
? SingleChildScrollView(
|
||||
controller:
|
||||
inputFieldValues.tagScrollController,
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
bottom: 8,
|
||||
left: 8,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 4.0,
|
||||
spacing: 4.0,
|
||||
children: inputFieldValues.tags
|
||||
.map((String tag) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(20.0),
|
||||
),
|
||||
color: Colors.blue,
|
||||
),
|
||||
margin:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 5.0),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: 10.0,
|
||||
vertical: 5.0),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
child: Text(
|
||||
'$tag',
|
||||
style: const TextStyle(
|
||||
color: Colors.white),
|
||||
),
|
||||
onTap: () {
|
||||
//print("$tag selected");
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 4.0),
|
||||
InkWell(
|
||||
child: const Icon(
|
||||
Icons.cancel,
|
||||
size: 14.0,
|
||||
color: Color.fromARGB(
|
||||
255, 233, 233, 233),
|
||||
),
|
||||
onTap: () {
|
||||
inputFieldValues
|
||||
.onTagRemoved(tag);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList()),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextField(
|
||||
controller: inputDesc,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: 10,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.description ??
|
||||
'Description',
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.describe_event ??
|
||||
'Describe event'),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: 250,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(20)),
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_updateEvent(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
AppLocalizations.of(context)?.add ?? 'Add',
|
||||
style: TextStyle(color: Colors.white, fontSize: 25),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
396
covas_mobile_new/lib/pages/EditProfile.dart
Normal file
@@ -0,0 +1,396 @@
|
||||
import 'package:covas_mobile/classes/MyDrawer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import '../classes/MyDrawer.dart';
|
||||
import '../pages/LoginDemo.dart';
|
||||
|
||||
import '../classes/alert.dart';
|
||||
import '../classes/eventAdded.dart';
|
||||
|
||||
import '../variable/globals.dart' as globals;
|
||||
|
||||
import '../classes/ad_helper.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; // Créé
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await MobileAds.instance.initialize();
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: EditProfile(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EditProfile extends StatefulWidget {
|
||||
const EditProfile({super.key});
|
||||
|
||||
@override
|
||||
_EditProfileState createState() => _EditProfileState();
|
||||
}
|
||||
|
||||
class _EditProfileState extends State<EditProfile>
|
||||
with ShowAlertDialog, ShowEventDialog {
|
||||
BannerAd? _bannerAd;
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
TextEditingController inputUserName = TextEditingController();
|
||||
|
||||
TextEditingController inputName = TextEditingController();
|
||||
|
||||
TextEditingController inputFirstName = TextEditingController();
|
||||
TextEditingController inputEmail = TextEditingController();
|
||||
TextEditingController inputBirth = TextEditingController();
|
||||
TextEditingController inputPassword = TextEditingController();
|
||||
TextEditingController inputPasswordConfirmed = TextEditingController();
|
||||
|
||||
onTapFunctionDatePicker({required BuildContext context}) async {
|
||||
DateTime initialDate = DateTime.parse(formatDate(inputBirth.text));
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
firstDate: DateTime(1900),
|
||||
initialDate: initialDate,
|
||||
lastDate: DateTime(2104));
|
||||
if (pickedDate == null) return;
|
||||
inputBirth.text = DateFormat("dd/MM/yyyy").format(pickedDate);
|
||||
}
|
||||
|
||||
convertNulltoEmptyString(var check) {
|
||||
if (check == null) {
|
||||
return "";
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
convertNulltoArray(List<String> check) {
|
||||
if (check == null) {
|
||||
return [];
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
String formatDate(String date) {
|
||||
var splitedDate = date.split("/");
|
||||
|
||||
var day = splitedDate[0];
|
||||
var month = splitedDate[1];
|
||||
var year = splitedDate[2];
|
||||
|
||||
return "${year}-${month}-${day}";
|
||||
}
|
||||
|
||||
Future<void> _updateProfile(BuildContext context) async {
|
||||
var username = inputUserName.text;
|
||||
var firstName = inputFirstName.text;
|
||||
var name = inputName.text;
|
||||
var email = inputEmail.text;
|
||||
var password = inputPassword.text;
|
||||
var confirmedPassword = inputPasswordConfirmed.text;
|
||||
var birth = DateTime.parse(formatDate(inputBirth.text));
|
||||
|
||||
if ((password.isNotEmpty) && (confirmedPassword.isNotEmpty)) {
|
||||
if (password != confirmedPassword) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.different_password_error ??
|
||||
"Different password");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var urlPut = Uri.parse("${globals.api}/users/me");
|
||||
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
if (accessToken.isNotEmpty) {
|
||||
var responsePut = await http.put(urlPut,
|
||||
headers: {
|
||||
HttpHeaders.cookieHeader: 'access_token=${accessToken}',
|
||||
HttpHeaders.acceptHeader: 'application/json, text/plain, */*',
|
||||
HttpHeaders.contentTypeHeader: 'application/json'
|
||||
},
|
||||
body: jsonEncode({
|
||||
'name': name,
|
||||
'username': username,
|
||||
'firstName': firstName,
|
||||
'password': password,
|
||||
'email': email,
|
||||
'roles': '',
|
||||
'birth': birth.toString()
|
||||
}));
|
||||
print(responsePut.statusCode);
|
||||
if (responsePut.statusCode == 200) {
|
||||
showEventDialog(context,
|
||||
AppLocalizations.of(context)?.user_update ?? "Your user updated");
|
||||
Navigator.pushReplacement(
|
||||
context, MaterialPageRoute(builder: (_) => EditProfile()));
|
||||
return;
|
||||
}
|
||||
|
||||
final messages = {
|
||||
400: AppLocalizations.of(context)?.request_error ??
|
||||
"Poorly constructed query",
|
||||
406: AppLocalizations.of(context)?.incorrect_password ??
|
||||
"Incorrect password",
|
||||
404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user",
|
||||
403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user",
|
||||
410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token",
|
||||
500: AppLocalizations.of(context)?.internal_error_server ??
|
||||
"Internal error server"
|
||||
};
|
||||
final text = messages[responsePut.statusCode] ??
|
||||
AppLocalizations.of(context)?.unknown_error_auth ??
|
||||
"Unknown error auth";
|
||||
showAlertDialog(
|
||||
context, AppLocalizations.of(context)?.error ?? "Error", text);
|
||||
} else {
|
||||
Navigator.pushReplacement(
|
||||
context, MaterialPageRoute(builder: (_) => LoginDemo()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _getInfoProfile() async {
|
||||
var urlGet = Uri.parse("${globals.api}/users/me");
|
||||
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
if (accessToken.isNotEmpty) {
|
||||
var responseGet = await http.get(urlGet, headers: {
|
||||
HttpHeaders.cookieHeader: 'access_token=${accessToken}',
|
||||
HttpHeaders.acceptHeader: 'application/json, text/plain, */*',
|
||||
HttpHeaders.contentTypeHeader: 'application/json'
|
||||
});
|
||||
print(responseGet.statusCode);
|
||||
if (responseGet.statusCode == 200) {
|
||||
var body = json.decode(utf8.decode(responseGet.bodyBytes));
|
||||
setState(() {
|
||||
inputName.text = body["name"];
|
||||
inputFirstName.text = body["firstName"];
|
||||
inputUserName.text = body["username"];
|
||||
inputEmail.text = body["email"];
|
||||
inputBirth.text =
|
||||
DateFormat("dd/MM/yyyy").format(DateTime.parse(body["birth"]));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
final messages = {
|
||||
400: AppLocalizations.of(context)?.request_error ??
|
||||
"Poorly constructed query",
|
||||
406: AppLocalizations.of(context)?.incorrect_password ??
|
||||
"Incorrect password",
|
||||
404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user",
|
||||
403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user",
|
||||
410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token",
|
||||
500: AppLocalizations.of(context)?.internal_error_server ??
|
||||
"Internal error server"
|
||||
};
|
||||
final text = messages[responseGet.statusCode] ??
|
||||
AppLocalizations.of(context)?.unknown_error_auth ??
|
||||
"Unknown error auth";
|
||||
showAlertDialog(
|
||||
context, AppLocalizations.of(context)?.error ?? "Error", text);
|
||||
} else {
|
||||
Navigator.pushReplacement(
|
||||
context, MaterialPageRoute(builder: (_) => LoginDemo()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
|
||||
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
|
||||
setState(() {
|
||||
_bannerAd = ad;
|
||||
});
|
||||
});
|
||||
_getInfoProfile();
|
||||
}
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
String? _validateField(String? value) {
|
||||
return value!.isEmpty
|
||||
? AppLocalizations.of(context)?.required_input ?? "Required input"
|
||||
: null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)?.update_profile ?? "Update profile"),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
drawer: MyDrawer(),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_bannerAd == null
|
||||
? SizedBox.shrink()
|
||||
: SizedBox(
|
||||
height: _bannerAd!.size.height.toDouble(),
|
||||
width: _bannerAd!.size.width.toDouble(),
|
||||
child: AdWidget(ad: _bannerAd!)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputUserName,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.name,
|
||||
hintText: AppLocalizations.of(context)?.edit_pseudo ??
|
||||
"Edit pseudo"),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputPassword,
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.password ??
|
||||
"Password",
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.enter_password ??
|
||||
"Enter a password"),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputPasswordConfirmed,
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
AppLocalizations.of(context)?.password_confirmed ??
|
||||
"Must confirm password",
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.password_confirmed ??
|
||||
"Must confirm password"),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputName,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.last_name ??
|
||||
"Last name",
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.edit_last_name ??
|
||||
"Edit last name"),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputFirstName,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.first_name ??
|
||||
"First name",
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.edit_first_name ??
|
||||
"Edit first name"),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputEmail,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
AppLocalizations.of(context)?.email ?? "Email",
|
||||
hintText: AppLocalizations.of(context)?.edit_email ??
|
||||
"Edit email"),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputBirth,
|
||||
readOnly: true,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.birth_date,
|
||||
hintText: AppLocalizations.of(context)?.edit_birth ??
|
||||
"Click to select a birth date"),
|
||||
onTap: () => onTapFunctionDatePicker(context: context)),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: 250,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(20)),
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_updateProfile(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
AppLocalizations.of(context)?.update_profile ??
|
||||
"Update profile ",
|
||||
style: TextStyle(color: Colors.white, fontSize: 25),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
170
covas_mobile_new/lib/pages/EditSettings.dart
Normal file
@@ -0,0 +1,170 @@
|
||||
import 'package:covas_mobile/classes/MyDrawer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import '../classes/MyDrawer.dart';
|
||||
|
||||
import '../classes/alert.dart';
|
||||
import '../classes/eventAdded.dart';
|
||||
|
||||
import '../classes/ad_helper.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; // Créé
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await MobileAds.instance.initialize();
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: EditSettings(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EditSettings extends StatefulWidget {
|
||||
const EditSettings({super.key});
|
||||
|
||||
@override
|
||||
_EditProfileState createState() => _EditProfileState();
|
||||
}
|
||||
|
||||
class _EditProfileState extends State<EditSettings>
|
||||
with ShowAlertDialog, ShowEventDialog {
|
||||
BannerAd? _bannerAd;
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
TextEditingController inputUserName = TextEditingController();
|
||||
int? kilometer;
|
||||
|
||||
Future<void> getParameter() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
setState(() {
|
||||
var kilometer = prefs.getDouble("kilometer")?.toInt() ?? null;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> setParameter() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
if (kilometer != null) {
|
||||
prefs.setDouble("kilometer", kilometer?.toDouble() ?? 50);
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.updated ?? "Updated",
|
||||
AppLocalizations.of(context)?.settings_updated ?? "Settings updated");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
|
||||
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
|
||||
setState(() {
|
||||
_bannerAd = ad;
|
||||
});
|
||||
});
|
||||
getParameter();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)?.settings ?? "Settings"),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
drawer: MyDrawer(),
|
||||
body: Form(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_bannerAd == null
|
||||
? SizedBox.shrink()
|
||||
: SizedBox(
|
||||
height: _bannerAd!.size.height.toDouble(),
|
||||
width: _bannerAd!.size.width.toDouble(),
|
||||
child: AdWidget(ad: _bannerAd!)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0,
|
||||
right: 15.0,
|
||||
top: 15.0,
|
||||
bottom: 0.0,
|
||||
),
|
||||
child: DropdownButtonFormField<int>(
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
AppLocalizations.of(context)?.define_kilometer ??
|
||||
'Define kilometer',
|
||||
),
|
||||
value:
|
||||
kilometer, // Set the initial selected value here, or leave as `null` if unselected.
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
value: 5,
|
||||
child: Text('5km'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 25,
|
||||
child: Text('25km'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 50,
|
||||
child: Text('50km'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 75,
|
||||
child: Text('75km'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 100,
|
||||
child: Text('100km'),
|
||||
),
|
||||
],
|
||||
onChanged: (int? newValue) {
|
||||
// Handle selection
|
||||
kilometer = newValue;
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: 250,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(20)),
|
||||
child: TextButton(
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
AppLocalizations.of(context)?.update ?? "Update",
|
||||
style: TextStyle(color: Colors.white, fontSize: 25),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
176
covas_mobile_new/lib/pages/ForgotPassword.dart
Normal file
@@ -0,0 +1,176 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import '../main.dart';
|
||||
|
||||
import '../classes/alert.dart';
|
||||
|
||||
import '../variable/globals.dart' as globals;
|
||||
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; //
|
||||
|
||||
void main() {
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: PasswordForgot(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PasswordForgot extends StatefulWidget {
|
||||
const PasswordForgot({super.key});
|
||||
|
||||
@override
|
||||
_PasswordForgotState createState() => _PasswordForgotState();
|
||||
}
|
||||
|
||||
class _PasswordForgotState extends State<PasswordForgot> with ShowAlertDialog {
|
||||
TextEditingController inputEmail = TextEditingController();
|
||||
|
||||
convertNulltoEmptyString(var check) {
|
||||
if (check == null) {
|
||||
return "";
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
convertNulltoArray(List<String> check) {
|
||||
if (check == null) {
|
||||
return [];
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
String formatDate(String date) {
|
||||
var splitedDate = date.split("/");
|
||||
|
||||
var day = splitedDate[0];
|
||||
var month = splitedDate[1];
|
||||
var year = splitedDate[2];
|
||||
|
||||
return "${year}-${month}-${day}";
|
||||
}
|
||||
|
||||
Future<void> _forgotPassword(BuildContext context) async {
|
||||
var email = inputEmail.text;
|
||||
|
||||
var urlPut = Uri.parse("${globals.api}/password/forgot");
|
||||
|
||||
var responsePost = await http.post(urlPut,
|
||||
headers: {
|
||||
HttpHeaders.acceptHeader: 'application/json, text/plain, */*',
|
||||
HttpHeaders.contentTypeHeader: 'application/json'
|
||||
},
|
||||
body: jsonEncode({
|
||||
'email': email,
|
||||
}));
|
||||
print(responsePost.statusCode);
|
||||
if (responsePost.statusCode == 200) {
|
||||
String message =
|
||||
AppLocalizations.of(context)?.email_sent ?? "Email has been sent";
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.create ?? "Creation",
|
||||
"${message} : ${email}");
|
||||
return;
|
||||
}
|
||||
|
||||
final messages = {
|
||||
400: AppLocalizations.of(context)?.request_error ??
|
||||
"Poorly constructed query",
|
||||
406: AppLocalizations.of(context)?.incorrect_password ??
|
||||
"Incorrect password",
|
||||
404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user",
|
||||
403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user",
|
||||
410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token",
|
||||
500: AppLocalizations.of(context)?.internal_error_server ??
|
||||
"Internal error server"
|
||||
};
|
||||
|
||||
final text = messages[responsePost.statusCode] ??
|
||||
AppLocalizations.of(context)?.unknown_error_auth ??
|
||||
"Unknown error auth";
|
||||
showAlertDialog(
|
||||
context, AppLocalizations.of(context)?.error ?? "Error", text);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
String? _validateField(String? value) {
|
||||
return value!.isEmpty ? 'Champ requis' : null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)?.forgot_password ??
|
||||
"Forgot password"),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: inputEmail,
|
||||
validator: (value) => _validateField(value),
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
AppLocalizations.of(context)?.email ?? 'Email',
|
||||
hintText: AppLocalizations.of(context)?.enter_email ??
|
||||
'Enter the email'),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: 250,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(20)),
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_forgotPassword(context);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
AppLocalizations.of(context)?.send_email ?? 'Send email',
|
||||
style: TextStyle(color: Colors.white, fontSize: 25),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
455
covas_mobile_new/lib/pages/ItemMenu.dart
Normal file
@@ -0,0 +1,455 @@
|
||||
// ignore_for_file: unnecessary_brace_in_string_interps
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:covas_mobile/classes/alert.dart';
|
||||
import 'package:covas_mobile/pages/ListItemByTags.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
|
||||
import '../variable/globals.dart' as globals;
|
||||
|
||||
import '../classes/events.dart';
|
||||
import '../classes/MyDrawer.dart';
|
||||
|
||||
import 'ListItemMenu.dart';
|
||||
import 'MapboxPages.dart';
|
||||
import 'ListItemByOrganizers.dart';
|
||||
import 'EditEvent.dart';
|
||||
|
||||
import '../classes/ad_helper.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; //
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await MobileAds.instance.initialize();
|
||||
initializeDateFormatting("fr_FR", null).then((_) => (const MyApp()));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
// This widget is the root of your application.
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// Try running your application with "flutter run". You'll see the
|
||||
// application has a blue toolbar. Then, without quitting the app, try
|
||||
// changing the primarySwatch below to Colors.green and then invoke
|
||||
// "hot reload" (press "r" in the console where you ran "flutter run",
|
||||
// or simply save your changes to "hot reload" in a Flutter IDE).
|
||||
// Notice that the counter didn't reset back to zero; the application
|
||||
// is not restarted.
|
||||
primarySwatch: Colors.blue,
|
||||
),
|
||||
home: const ItemMenu(title: 'Flutter Demo Home Page'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ItemMenu extends StatefulWidget {
|
||||
const ItemMenu({Key? key, required this.title}) : super(key: key);
|
||||
|
||||
// This widget is the home page of your application. It is stateful, meaning
|
||||
// that it has a State object (defined below) that contains fields that affect
|
||||
// how it looks.
|
||||
|
||||
// This class is the configuration for the state. It holds the values (in this
|
||||
// case the title) provided by the parent (in this case the App widget) and
|
||||
// used by the build method of the State. Fields in a Widget subclass are
|
||||
// always marked "final".
|
||||
|
||||
final String title;
|
||||
@override
|
||||
State<ItemMenu> createState() => _ItemMenuState();
|
||||
}
|
||||
|
||||
class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
|
||||
BannerAd? _bannerAd;
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
String listUser = "";
|
||||
String eventName = "";
|
||||
String eventStartDate = "";
|
||||
String eventDescription = "";
|
||||
String eventTicket = "";
|
||||
String eventLink = "";
|
||||
String place = "";
|
||||
String imgUrl = "";
|
||||
List<String> tags = [];
|
||||
List<String> organizers = [];
|
||||
|
||||
String id = "";
|
||||
|
||||
Events? events;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
|
||||
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
|
||||
setState(() {
|
||||
_bannerAd = ad;
|
||||
});
|
||||
});
|
||||
|
||||
_getEventInfos();
|
||||
}
|
||||
|
||||
Future<void> _getEventInfos() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final accessToken = prefs.getString("access_token") ?? "";
|
||||
|
||||
if (accessToken.isEmpty) {
|
||||
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.invalid_cache ?? "Invalid cache");
|
||||
return;
|
||||
}
|
||||
|
||||
final urlGet = Uri.parse("${globals.api}/events/${widget.title}");
|
||||
final responseGet = await http.get(
|
||||
urlGet,
|
||||
headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'},
|
||||
);
|
||||
|
||||
if (responseGet.statusCode == 200) {
|
||||
final responseBody = utf8.decode(responseGet.bodyBytes);
|
||||
|
||||
events = Events.fromJson(jsonDecode(responseBody));
|
||||
final locale = Provider.of<LocaleProvider>(context, listen: false)
|
||||
.locale
|
||||
?.toString() ??
|
||||
'en_US';
|
||||
final startDate =
|
||||
DateTime.parse(events?.startDate ?? DateTime.now().toString());
|
||||
//final date = DateFormat.yMd().format(startDate);
|
||||
//final time = DateFormat.Hm().format(startDate);
|
||||
final endDate =
|
||||
DateTime.parse(events?.endDate ?? DateTime.now().toString());
|
||||
String separator = AppLocalizations.of(context)?.at ?? "at";
|
||||
final formattedStartDate =
|
||||
DateFormat("EEEE d MMMM y '${separator}' HH:mm", locale)
|
||||
.format(startDate);
|
||||
|
||||
final formattedEndDate =
|
||||
DateFormat("EEEE d MMMM y '${separator}' HH:mm", locale)
|
||||
.format(endDate);
|
||||
|
||||
String link = AppLocalizations.of(context)?.to_date ?? "to";
|
||||
|
||||
setState(() {
|
||||
eventName = events?.name ?? "";
|
||||
|
||||
eventStartDate = "$formattedStartDate ${link} $formattedEndDate";
|
||||
organizers = List<String>.from(events?.organizers ?? []);
|
||||
place = events?.place ?? "";
|
||||
imgUrl = events?.imgUrl ?? "";
|
||||
eventLink = events?.link ?? "";
|
||||
eventTicket = events?.ticket ?? "";
|
||||
eventDescription = events?.description ?? "";
|
||||
tags = List<String>.from(events?.tags ?? []);
|
||||
});
|
||||
} else {
|
||||
final messages = {
|
||||
400: AppLocalizations.of(context)?.request_error ??
|
||||
"Poorly constructed query",
|
||||
406: AppLocalizations.of(context)?.incorrect_password ??
|
||||
"Incorrect password",
|
||||
404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user",
|
||||
403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user",
|
||||
410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token",
|
||||
500: AppLocalizations.of(context)?.internal_error_server ??
|
||||
"Internal error server"
|
||||
};
|
||||
|
||||
final errorMessage = messages[responseGet.statusCode] ??
|
||||
AppLocalizations.of(context)?.unknown_error_auth ??
|
||||
"Unknown error auth";
|
||||
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
|
||||
errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// This method is rerun every time setState is called, for instance as done
|
||||
// by the _incrementCounter method above.
|
||||
//
|
||||
// The Flutter framework has been optimized to make rerunning build methods
|
||||
// fast, so that you can just rebuild anything that needs updating rather
|
||||
// than having to individually change instances of widgets.
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
// Here we take the value from the MyHomePage object that was created by
|
||||
// the App.build method, and use it to set our appbar title.
|
||||
title: Text("${eventName}", overflow: TextOverflow.ellipsis),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
|
||||
},
|
||||
)),
|
||||
drawer: MyDrawer(),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_bannerAd == null
|
||||
? SizedBox.shrink()
|
||||
: SizedBox(
|
||||
height: _bannerAd!.size.height.toDouble(),
|
||||
width: _bannerAd!.size.width.toDouble(),
|
||||
child: AdWidget(ad: _bannerAd!)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 60.0),
|
||||
child: Image.network(
|
||||
imgUrl,
|
||||
width: MediaQuery.of(context).size.width *
|
||||
0.5, // 50% of screen width
|
||||
height: MediaQuery.of(context).size.height * 0.5,
|
||||
loadingBuilder: (BuildContext context, Widget child,
|
||||
ImageChunkEvent? loadingProgress) {
|
||||
if (loadingProgress == null) {
|
||||
return child; // The image has finished loading
|
||||
}
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
errorBuilder: (BuildContext context, Object error,
|
||||
StackTrace? stackTrace) {
|
||||
return Center(
|
||||
child: Icon(Icons.error,
|
||||
size: MediaQuery.of(context).size.width * 0.1),
|
||||
);
|
||||
},
|
||||
)),
|
||||
Row(children: [
|
||||
Icon(Icons.event),
|
||||
Text(
|
||||
AppLocalizations.of(context)?.item_date ?? "Date : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
|
||||
)
|
||||
]),
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text("${eventStartDate}",
|
||||
style: TextStyle(fontSize: 15.0)))
|
||||
],
|
||||
),
|
||||
Row(children: [
|
||||
Icon(Icons.explore),
|
||||
Text(
|
||||
AppLocalizations.of(context)?.item_maps ?? "Maps : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
|
||||
)
|
||||
]),
|
||||
Row(children: [
|
||||
Flexible(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => MapboxPages(
|
||||
title: '${widget.title}',
|
||||
place: '${place}')));
|
||||
},
|
||||
child: Text("${place}",
|
||||
style: TextStyle(fontSize: 15.0),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis)))
|
||||
]),
|
||||
Row(children: [
|
||||
Icon(Icons.link),
|
||||
Text(
|
||||
AppLocalizations.of(context)?.item_link ?? "Link : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
|
||||
)
|
||||
]),
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child:
|
||||
Text("${eventLink}", style: TextStyle(fontSize: 15.0)))
|
||||
],
|
||||
),
|
||||
Row(children: [
|
||||
Icon(Icons.add_shopping_cart),
|
||||
Text(
|
||||
AppLocalizations.of(context)?.item_ticket ?? "Ticket : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
|
||||
)
|
||||
]),
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text("${eventTicket}",
|
||||
style: TextStyle(fontSize: 15.0)))
|
||||
],
|
||||
),
|
||||
Row(children: [
|
||||
Icon(Icons.group),
|
||||
Text(
|
||||
AppLocalizations.of(context)?.item_organizer ?? "Organizers : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
|
||||
)
|
||||
]),
|
||||
Row(children: [
|
||||
Flexible(
|
||||
flex: 3,
|
||||
fit: FlexFit.tight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
bottom: 8,
|
||||
left: 8,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 2.0,
|
||||
spacing: 2.0,
|
||||
children: organizers.map((String tag) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(20.0),
|
||||
),
|
||||
color: Colors.blue,
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5.0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10.0, vertical: 5.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => ListItemOrganizers(
|
||||
organizer: '$tag')));
|
||||
},
|
||||
child: Text(
|
||||
'$tag',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList()),
|
||||
)),
|
||||
]),
|
||||
Row(children: [
|
||||
Icon(Icons.description),
|
||||
Text(
|
||||
AppLocalizations.of(context)?.item_description ??
|
||||
"Description : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold))
|
||||
]),
|
||||
Row(children: [
|
||||
Flexible(
|
||||
child: Text("${eventDescription}",
|
||||
style: TextStyle(fontSize: 15.0),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis))
|
||||
]),
|
||||
Row(children: [
|
||||
Icon(Icons.category),
|
||||
Text(AppLocalizations.of(context)?.item_tags ?? "Tags : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold))
|
||||
]),
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
flex: 3,
|
||||
fit: FlexFit.tight,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8,
|
||||
bottom: 8,
|
||||
left: 8,
|
||||
),
|
||||
child: Wrap(
|
||||
runSpacing: 2.0,
|
||||
spacing: 2.0,
|
||||
children: tags.map((String tag) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(20.0),
|
||||
),
|
||||
color: Colors.blue,
|
||||
),
|
||||
margin:
|
||||
const EdgeInsets.symmetric(horizontal: 5.0),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10.0, vertical: 5.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) =>
|
||||
ListItemTags(tags: '$tag')));
|
||||
},
|
||||
child: Text(
|
||||
'$tag',
|
||||
style:
|
||||
const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList()),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => EditEvent(
|
||||
events: events,
|
||||
imgPath: "",
|
||||
)),
|
||||
);
|
||||
},
|
||||
backgroundColor: Colors.blue,
|
||||
tooltip: AppLocalizations.of(context)?.search ?? 'Search',
|
||||
child: const Icon(Icons.edit, color: Colors.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
275
covas_mobile_new/lib/pages/ListItemByOrganizers.dart
Normal file
@@ -0,0 +1,275 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import "ItemMenu.dart";
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/material.dart';
|
||||
import '../classes/events.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
|
||||
import '../variable/globals.dart' as globals;
|
||||
import '../classes/MyDrawer.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; //
|
||||
import '../classes/notification_service.dart';
|
||||
|
||||
// app starting point
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await initializeDateFormatting("fr_FR", null);
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
localizationsDelegates: [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: [
|
||||
const Locale('fr', 'FR'),
|
||||
],
|
||||
home: const ListItemOrganizers(organizer: "default"),
|
||||
debugShowCheckedModeBanner: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// homepage class
|
||||
class ListItemOrganizers extends StatefulWidget {
|
||||
const ListItemOrganizers({Key? key, required this.organizer})
|
||||
: super(key: key);
|
||||
|
||||
final String organizer;
|
||||
@override
|
||||
State<ListItemOrganizers> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
// homepage state
|
||||
class _MyHomePageState extends State<ListItemOrganizers> {
|
||||
int _fetchCount = 0;
|
||||
bool _isLoading = false;
|
||||
late ScrollController _scrollController;
|
||||
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
void _incrementFetchCount() {
|
||||
setState(() {
|
||||
_fetchCount++;
|
||||
});
|
||||
}
|
||||
|
||||
void _scrollListener() {
|
||||
if (_scrollController.position.pixels ==
|
||||
_scrollController.position.maxScrollExtent) {
|
||||
_incrementFetchCount();
|
||||
_fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchData() async {
|
||||
if (_isLoading) return;
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
getPosts(widget.organizer, count: _fetchCount);
|
||||
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> toggleInterested(String eventId) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
final url = Uri.parse("${globals.api}/events/${eventId}/interest");
|
||||
if (accessToken.isNotEmpty) {
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
HttpHeaders.cookieHeader: "access_token=$accessToken"
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw (AppLocalizations.of(context)?.toogle_interest ??
|
||||
"Error toogle interest: ${response.statusCode}");
|
||||
}
|
||||
|
||||
var event = json.decode(response.body);
|
||||
return event;
|
||||
}
|
||||
return {"interested": false, "interested_count": 0};
|
||||
}
|
||||
|
||||
// variable to call and store future list of posts
|
||||
|
||||
// function to fetch data from api and return future list of posts
|
||||
static Future<List<Events>> getPosts(organizer, {count = 0}) async {
|
||||
await initializeDateFormatting("fr_FR", null);
|
||||
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
final List<Events> body = [];
|
||||
if (accessToken.isNotEmpty) {
|
||||
DateTime currentDatetime = DateTime.now();
|
||||
num limit = 20 * (count + 1);
|
||||
var url = Uri.parse(
|
||||
"${globals.api}/events?organizers=${organizer}&limit=${limit}¤t_datetime=${currentDatetime.toString()}");
|
||||
final response = await http.get(url, headers: {
|
||||
"Content-Type": "application/json",
|
||||
HttpHeaders.cookieHeader: "access_token=${accessToken}"
|
||||
});
|
||||
final List body = json.decode(utf8.decode(response.bodyBytes));
|
||||
return body.map((e) => Events.fromJson(e)).toList();
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
_scrollController = ScrollController();
|
||||
_scrollController.addListener(_scrollListener);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// build function
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
drawer: MyDrawer(),
|
||||
body: Center(
|
||||
// FutureBuilder
|
||||
child: FutureBuilder<List<Events>>(
|
||||
future: getPosts(widget.organizer),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
// until data is fetched, show loader
|
||||
return const CircularProgressIndicator();
|
||||
} else if (snapshot.hasData) {
|
||||
// once data is fetched, display it on screen (call buildPosts())
|
||||
final posts = snapshot.data!;
|
||||
return buildPosts(posts);
|
||||
} else {
|
||||
// if no data, show simple Text
|
||||
return Text(
|
||||
AppLocalizations.of(context)?.no_data ?? "No data available");
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// function to display fetched data on screen
|
||||
Widget buildPosts(List<Events> posts) {
|
||||
String organizer =
|
||||
AppLocalizations.of(context)?.item_organizer ?? "Organizer : ";
|
||||
// ListView Builder to show data in a list
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
// Here we take the value from the MyHomePage object that was created by
|
||||
// the App.build method, and use it to set our appbar title.
|
||||
title: Text("${organizer}${widget.organizer}",
|
||||
overflow: TextOverflow.ellipsis),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
body: ListView.separated(
|
||||
controller: _scrollController,
|
||||
itemCount: posts.isNotEmpty
|
||||
? posts.length + (_isLoading ? 1 : 0) // Add 1 only if loading
|
||||
: 0,
|
||||
itemBuilder: (context, index) {
|
||||
final post = posts[index];
|
||||
final startDate = DateTime.parse(post.startDate!);
|
||||
|
||||
final locale = Provider.of<LocaleProvider>(context, listen: false)
|
||||
.locale
|
||||
?.toString() ??
|
||||
'en_US';
|
||||
|
||||
final dateLongue =
|
||||
DateFormat('EEEE d MMMM y', locale).format(startDate);
|
||||
final countInterestedString =
|
||||
AppLocalizations.of(context)?.count_interested ??
|
||||
"Interested people number";
|
||||
final countInterested =
|
||||
"${countInterestedString} : ${post.interestedCount}";
|
||||
|
||||
return ListTile(
|
||||
title: Text('${post.name!}'),
|
||||
subtitle:
|
||||
Text('${post.place!}\n${dateLongue}\n${countInterested}'),
|
||||
trailing: IconButton(
|
||||
onPressed: () async {
|
||||
try {
|
||||
final result = await toggleInterested(post.id!);
|
||||
setState(() {
|
||||
post.interested = result["interested"];
|
||||
post.interestedCount = result["interested_count"];
|
||||
});
|
||||
if (result["interested"] == true) {
|
||||
NotificationService.scheduleEventNotification(
|
||||
eventId: post.id!,
|
||||
title: "Rappel évènement",
|
||||
body:
|
||||
"Ton évènement '${post.name}' commence dans 1 heure !",
|
||||
eventDate: DateTime.parse(post.startDate!),
|
||||
);
|
||||
} else {
|
||||
NotificationService.cancel(post.id!);
|
||||
}
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(context)?.error_update ??
|
||||
"Error when updating")),
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: Icon(
|
||||
post.interested ?? false
|
||||
? Icons.favorite
|
||||
: Icons.favorite_border,
|
||||
color:
|
||||
post.interested ?? false ? Colors.red : Colors.grey)),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => ItemMenu(title: post.id!)));
|
||||
});
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return Divider();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
277
covas_mobile_new/lib/pages/ListItemByTags.dart
Normal file
@@ -0,0 +1,277 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import "ItemMenu.dart";
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/material.dart';
|
||||
import '../classes/events.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
|
||||
import '../variable/globals.dart' as globals;
|
||||
|
||||
import '../classes/MyDrawer.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; //
|
||||
import '../classes/notification_service.dart';
|
||||
|
||||
// app starting point
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await initializeDateFormatting("fr_FR", null);
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
localizationsDelegates: [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: [
|
||||
const Locale('fr', 'FR'),
|
||||
],
|
||||
home: const ListItemTags(tags: "default"),
|
||||
debugShowCheckedModeBanner: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// homepage class
|
||||
class ListItemTags extends StatefulWidget {
|
||||
const ListItemTags({Key? key, required this.tags}) : super(key: key);
|
||||
|
||||
final String tags;
|
||||
@override
|
||||
State<ListItemTags> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
// homepage state
|
||||
class _MyHomePageState extends State<ListItemTags> {
|
||||
// variable to call and store future list of posts
|
||||
|
||||
int _fetchCount = 0;
|
||||
bool _isLoading = false;
|
||||
late ScrollController _scrollController;
|
||||
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
void _incrementFetchCount() {
|
||||
setState(() {
|
||||
_fetchCount++;
|
||||
});
|
||||
}
|
||||
|
||||
void _scrollListener() {
|
||||
if (_scrollController.position.pixels ==
|
||||
_scrollController.position.maxScrollExtent) {
|
||||
_incrementFetchCount();
|
||||
|
||||
_fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchData() async {
|
||||
print("Counter : ${_fetchCount}");
|
||||
if (_isLoading) return;
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
getPosts(widget.tags, count: _fetchCount);
|
||||
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
// function to fetch data from api and return future list of posts
|
||||
static Future<List<Events>> getPosts(tags, {count = 0}) async {
|
||||
await initializeDateFormatting("fr_FR", null);
|
||||
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
final List<Events> body = [];
|
||||
if (accessToken.isNotEmpty) {
|
||||
DateTime currentDatetime = DateTime.now();
|
||||
num limit = 20 * (count + 1);
|
||||
|
||||
var url = Uri.parse(
|
||||
"${globals.api}/events?tags=${tags}&limit=${limit}¤t_datetime=${currentDatetime.toString()}");
|
||||
final response = await http.get(url, headers: {
|
||||
"Content-Type": "application/json",
|
||||
HttpHeaders.cookieHeader: "access_token=${accessToken}"
|
||||
});
|
||||
final List body = json.decode(utf8.decode(response.bodyBytes));
|
||||
return body.map((e) => Events.fromJson(e)).toList();
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> toggleInterested(String eventId) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
final url = Uri.parse("${globals.api}/events/${eventId}/interest");
|
||||
if (accessToken.isNotEmpty) {
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
HttpHeaders.cookieHeader: "access_token=$accessToken"
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw (AppLocalizations.of(context)?.toogle_interest ??
|
||||
"Error toogle interest: ${response.statusCode}");
|
||||
}
|
||||
|
||||
var event = json.decode(response.body);
|
||||
return event;
|
||||
}
|
||||
return {"interested": false, "interested_count": 0};
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
_authService.checkTokenStatus(context);
|
||||
_scrollController = ScrollController();
|
||||
_scrollController.addListener(_scrollListener);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// build function
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
drawer: MyDrawer(),
|
||||
body: Center(
|
||||
// FutureBuilder
|
||||
child: FutureBuilder<List<Events>>(
|
||||
future: getPosts(widget.tags),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
// until data is fetched, show loader
|
||||
return const CircularProgressIndicator();
|
||||
} else if (snapshot.hasData) {
|
||||
// once data is fetched, display it on screen (call buildPosts())
|
||||
final posts = snapshot.data!;
|
||||
return buildPosts(posts);
|
||||
} else {
|
||||
// if no data, show simple Text
|
||||
return Text(
|
||||
AppLocalizations.of(context)?.no_data ?? "No data available");
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// function to display fetched data on screen
|
||||
Widget buildPosts(List<Events> posts) {
|
||||
// ListView Builder to show data in a list
|
||||
String tag = AppLocalizations.of(context)?.item_tags ?? "Tags : ";
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
// Here we take the value from the MyHomePage object that was created by
|
||||
// the App.build method, and use it to set our appbar title.
|
||||
title: Text("${tag}${widget.tags}", overflow: TextOverflow.ellipsis),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
body: ListView.separated(
|
||||
controller: _scrollController,
|
||||
itemCount: posts.isNotEmpty
|
||||
? posts.length + (_isLoading ? 1 : 0) // Add 1 only if loading
|
||||
: 0,
|
||||
itemBuilder: (context, index) {
|
||||
final post = posts[index];
|
||||
final startDate = DateTime.parse(post.startDate!);
|
||||
final locale = Provider.of<LocaleProvider>(context, listen: false)
|
||||
.locale
|
||||
?.toString() ??
|
||||
'en_US';
|
||||
final dateLongue =
|
||||
DateFormat('EEEE d MMMM y', locale).format(startDate);
|
||||
|
||||
final countInterestedString =
|
||||
AppLocalizations.of(context)?.count_interested ??
|
||||
"Interested people number";
|
||||
final countInterested =
|
||||
"${countInterestedString} : ${post.interestedCount}";
|
||||
|
||||
return ListTile(
|
||||
title: Text('${post.name!}'),
|
||||
subtitle:
|
||||
Text('${post.place!}\n${dateLongue}\n${countInterested}'),
|
||||
trailing: IconButton(
|
||||
onPressed: () async {
|
||||
try {
|
||||
final result = await toggleInterested(post.id!);
|
||||
setState(() {
|
||||
post.interested = result["interested"];
|
||||
post.interestedCount = result["interested_count"];
|
||||
});
|
||||
if (result["interested"] == true) {
|
||||
NotificationService.scheduleEventNotification(
|
||||
eventId: post.id!,
|
||||
title: "Rappel évènement",
|
||||
body:
|
||||
"Ton évènement '${post.name}' commence dans 1 heure !",
|
||||
eventDate: DateTime.parse(post.startDate!),
|
||||
);
|
||||
} else {
|
||||
NotificationService.cancel(post.id!);
|
||||
}
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(context)?.error_update ??
|
||||
"Error when updating")),
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: Icon(
|
||||
post.interested ?? false
|
||||
? Icons.favorite
|
||||
: Icons.favorite_border,
|
||||
color:
|
||||
post.interested ?? false ? Colors.red : Colors.grey)),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => ItemMenu(title: post.id!)));
|
||||
});
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return Divider();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
954
covas_mobile_new/lib/pages/ListItemMenu.dart
Normal file
@@ -0,0 +1,954 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'ItemMenu.dart';
|
||||
import '../classes/events.dart';
|
||||
import '../classes/MyDrawer.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
import 'dart:math';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import '../variable/globals.dart' as globals;
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import "Camera.dart";
|
||||
import 'package:camera/camera.dart';
|
||||
|
||||
import '../classes/ad_helper.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; // Créé plus loin
|
||||
import '../classes/notification_service.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await MobileAds.instance.initialize();
|
||||
await initializeDateFormatting("fr_FR", null);
|
||||
|
||||
runApp(ChangeNotifierProvider(
|
||||
create: (_) => LocaleProvider(),
|
||||
child: const MyApp(),
|
||||
));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localeProvider = Provider.of<LocaleProvider>(context);
|
||||
return MaterialApp(
|
||||
localizationsDelegates: [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: [const Locale('fr', 'FR'), const Locale('en')],
|
||||
locale: localeProvider.locale,
|
||||
home: Builder(builder: (context) => ListItemMenu()),
|
||||
debugShowCheckedModeBanner: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ListItemMenu extends StatefulWidget {
|
||||
const ListItemMenu({super.key});
|
||||
|
||||
@override
|
||||
State<ListItemMenu> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<ListItemMenu> {
|
||||
BannerAd? _bannerAd;
|
||||
final AuthService _authService = AuthService();
|
||||
late ScrollController _scrollController;
|
||||
int _fetchCount = 0;
|
||||
bool _isLoading = false;
|
||||
|
||||
Future<List<Events>> postsFuture = getPosts();
|
||||
List<Events> filteredPosts = [];
|
||||
String geographicalZone = '';
|
||||
String itemName = '';
|
||||
String itemTags = '';
|
||||
String query = '';
|
||||
List<Map<String, dynamic>> suggestionsGeo = [];
|
||||
List<Map<String, dynamic>> suggestionsItem = [];
|
||||
List<Map<String, dynamic>> suggestionsTags = [];
|
||||
TextEditingController inputGeo = TextEditingController();
|
||||
TextEditingController startDatepicker = TextEditingController();
|
||||
TextEditingController endDatepicker = TextEditingController();
|
||||
TextEditingController inputItem = TextEditingController();
|
||||
TextEditingController inputTags = TextEditingController();
|
||||
|
||||
bool showDateFields = false; // State to toggle date fields
|
||||
bool showArrow = true;
|
||||
bool showInputSearch = true;
|
||||
bool showInputGeo = true;
|
||||
bool showInputTag = true;
|
||||
// Fetching events from API
|
||||
static Future<List<Events>> getPosts() async {
|
||||
await initializeDateFormatting("fr_FR");
|
||||
PermissionStatus status = await Permission.location.status;
|
||||
final List<Events> body = [];
|
||||
var url = Uri.parse("${globals.api}/events");
|
||||
if (status.isGranted) {
|
||||
print("Location permission granted");
|
||||
|
||||
// Get the current position with high accuracy
|
||||
|
||||
const LocationSettings locationSettings = LocationSettings(
|
||||
accuracy: LocationAccuracy.medium,
|
||||
timeLimit: Duration(seconds: 5),
|
||||
);
|
||||
Position? position;
|
||||
try {
|
||||
position = await Geolocator.getCurrentPosition(
|
||||
locationSettings: locationSettings);
|
||||
} on LocationServiceDisabledException {
|
||||
// Handle location services disabled
|
||||
print('Location services are disabled.');
|
||||
position = await Geolocator.getLastKnownPosition();
|
||||
if (position == null) {
|
||||
print('No last known position available.');
|
||||
}
|
||||
} catch (e) {
|
||||
// Handle other errors
|
||||
print('Failed to get location: $e');
|
||||
position = await Geolocator.getLastKnownPosition();
|
||||
if (position == null) {
|
||||
print('No last known position available.');
|
||||
}
|
||||
}
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
if (position != null) {
|
||||
// Calculate the boundaries
|
||||
double radiusInKm = prefs.getDouble("kilometer") ?? 50.0;
|
||||
double latDistance = radiusInKm / 111.0;
|
||||
double lonDistance =
|
||||
radiusInKm / (111.0 * cos(position.latitude * pi / 180));
|
||||
|
||||
double minLat = position.latitude - latDistance;
|
||||
double maxLat = position.latitude + latDistance;
|
||||
double minLon = position.longitude - lonDistance;
|
||||
double maxLon = position.longitude + lonDistance;
|
||||
DateTime currentDatetime = DateTime.now();
|
||||
url = Uri.parse("${globals.api}/events/search"
|
||||
"?min_lat=$minLat&max_lat=$maxLat"
|
||||
"&min_lon=$minLon&max_lon=$maxLon¤t_datetime=${currentDatetime.toString()}");
|
||||
}
|
||||
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
|
||||
if (accessToken.isNotEmpty) {
|
||||
final response = await http.get(url, headers: {
|
||||
"Content-Type": "application/json",
|
||||
HttpHeaders.cookieHeader: "access_token=${accessToken}"
|
||||
});
|
||||
final List body = json.decode(utf8.decode(response.bodyBytes));
|
||||
return body.map((e) => Events.fromJson(e)).toList();
|
||||
}
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
String formatDate(String date) {
|
||||
var splitedDate = date.split("-");
|
||||
var day = splitedDate[0];
|
||||
var month = splitedDate[1];
|
||||
var year = splitedDate[2];
|
||||
|
||||
return "${year}-${month}-${day}";
|
||||
}
|
||||
|
||||
void _incrementFetchCount() {
|
||||
setState(() {
|
||||
_fetchCount++;
|
||||
});
|
||||
_fetchData();
|
||||
}
|
||||
|
||||
void _scrollListener() {
|
||||
if (_scrollController.position.pixels ==
|
||||
_scrollController.position.maxScrollExtent) {
|
||||
_incrementFetchCount();
|
||||
}
|
||||
|
||||
// Scroll to top
|
||||
}
|
||||
|
||||
Future<void> _fetchData() async {
|
||||
print("Counter : ${_fetchCount}");
|
||||
if (_isLoading) return;
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
fetchPostsByLocation();
|
||||
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
|
||||
setState(() {
|
||||
_bannerAd = ad;
|
||||
});
|
||||
});
|
||||
_scrollController = ScrollController();
|
||||
_scrollController.addListener(_scrollListener);
|
||||
|
||||
// Initialize data fetch when the page loads
|
||||
_getCurrentLocation();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// Get the device's current location
|
||||
Future<void> _getCurrentLocation() async {
|
||||
PermissionStatus status = await Permission.location.status;
|
||||
|
||||
if (status.isGranted) {
|
||||
print("Location permission granted");
|
||||
|
||||
// Get the current position with high accuracy
|
||||
const LocationSettings locationSettings = LocationSettings(
|
||||
accuracy: LocationAccuracy.medium, timeLimit: Duration(seconds: 5));
|
||||
Position? position;
|
||||
try {
|
||||
position = await Geolocator.getCurrentPosition(
|
||||
locationSettings: locationSettings);
|
||||
} on LocationServiceDisabledException {
|
||||
// Handle location services disabled
|
||||
print('Location services are disabled.');
|
||||
position = await Geolocator.getLastKnownPosition();
|
||||
if (position == null) {
|
||||
print('No last known position available.');
|
||||
}
|
||||
} catch (e) {
|
||||
// Handle other errors
|
||||
print('Failed to get location: $e');
|
||||
position = await Geolocator.getLastKnownPosition();
|
||||
if (position == null) {
|
||||
print('No last known position available.');
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse geocode: Get city and country from latitude and longitude using Mapbox Search API
|
||||
if (position != null) {
|
||||
_getCityAndCountry(position!.latitude, position!.longitude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method to get city and country from latitude and longitude using Mapbox API
|
||||
Future<void> _getCityAndCountry(double latitude, double longitude) async {
|
||||
await dotenv.load(fileName: ".env"); // Load .env file
|
||||
|
||||
final mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '';
|
||||
final url = Uri.parse(
|
||||
'https://api.mapbox.com/geocoding/v5/mapbox.places/$longitude,$latitude.json?access_token=$mapboxAccessToken',
|
||||
);
|
||||
|
||||
try {
|
||||
// Send GET request to Mapbox API
|
||||
final response = await http.get(url);
|
||||
|
||||
// If the request is successful (HTTP status 200)
|
||||
print("status mapbox : ${response.statusCode}");
|
||||
if (response.statusCode == 200) {
|
||||
// Parse the response body
|
||||
final data = json.decode(response.body);
|
||||
|
||||
// Extract the city and country from the response
|
||||
final features = data['features'];
|
||||
|
||||
if (features.isNotEmpty) {
|
||||
String city = _getCityFromFeatures(features);
|
||||
String country = _getCountryFromFeatures(features);
|
||||
print("city : ${city} ${country}");
|
||||
if (city.isNotEmpty && country.isNotEmpty) {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
prefs.setDouble("city_lat", latitude);
|
||||
prefs.setDouble("city_long", longitude);
|
||||
fetchPostsByLocation();
|
||||
setState(() {
|
||||
inputGeo.text = "${city}, ${country}";
|
||||
});
|
||||
} else {
|
||||
fetchPostsByLocation();
|
||||
}
|
||||
} else {
|
||||
fetchPostsByLocation();
|
||||
}
|
||||
} else {
|
||||
fetchPostsByLocation();
|
||||
throw Exception('Failed to load location data');
|
||||
}
|
||||
} catch (e) {
|
||||
fetchPostsByLocation();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to extract the city from the Mapbox features array
|
||||
String _getCityFromFeatures(List<dynamic> features) {
|
||||
for (var feature in features) {
|
||||
if (feature['place_type'] != null &&
|
||||
feature['place_type'].contains('place')) {
|
||||
return feature['text'] ?? '';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// Helper function to extract the country from the Mapbox features array
|
||||
String _getCountryFromFeatures(List<dynamic> features) {
|
||||
for (var feature in features) {
|
||||
if (feature['place_type'] != null &&
|
||||
feature['place_type'].contains('country')) {
|
||||
return feature['text'] ?? '';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
Future<void> searchSuggestionsGeo(String input) async {
|
||||
await dotenv.load(fileName: ".env"); // Load .env file
|
||||
|
||||
final mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '';
|
||||
final url =
|
||||
'https://api.mapbox.com/geocoding/v5/mapbox.places/${input}.json?access_token=${mapboxAccessToken}&proximity=ip';
|
||||
final response = await http.get(Uri.parse(url));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
setState(() {
|
||||
suggestionsGeo = (data['features'] as List)
|
||||
.map((feature) => {
|
||||
'place_name': feature['place_name'],
|
||||
'geometry': feature[
|
||||
'geometry'], // Include geometry for latitude/longitude
|
||||
})
|
||||
.toList();
|
||||
if (suggestionsGeo.isNotEmpty) {
|
||||
showArrow = false;
|
||||
showInputSearch = false;
|
||||
showInputTag = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw Exception(AppLocalizations.of(context)?.failed_suggestions ??
|
||||
'Failed to load suggestions');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uri> getUrlForEvents() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final latitude = prefs.getDouble("city_lat") ?? 0.0;
|
||||
final longitude = prefs.getDouble("city_long") ?? 0.0;
|
||||
|
||||
final radiusInKm = prefs.getDouble("kilometer") ?? 50.0;
|
||||
String endpoint = "events";
|
||||
String queryParameters = "";
|
||||
|
||||
if (latitude != 0.0 && longitude != 0.0) {
|
||||
final latDistance = radiusInKm / 111.0;
|
||||
final lonDistance = radiusInKm / (111.0 * cos(latitude * pi / 180));
|
||||
|
||||
final minLat = latitude - latDistance;
|
||||
final maxLat = latitude + latDistance;
|
||||
final minLon = longitude - lonDistance;
|
||||
final maxLon = longitude + lonDistance;
|
||||
|
||||
endpoint = "events/search";
|
||||
queryParameters =
|
||||
"min_lat=$minLat&max_lat=$maxLat&min_lon=$minLon&max_lon=$maxLon";
|
||||
}
|
||||
|
||||
final currentDate = DateTime.now();
|
||||
String dateParameter = "current_datetime=${currentDate.toIso8601String()}";
|
||||
|
||||
if (startDatepicker.text.isNotEmpty || endDatepicker.text.isNotEmpty) {
|
||||
endpoint = "events/search";
|
||||
if (startDatepicker.text.isNotEmpty) {
|
||||
final startDate = DateTime.parse(formatDate(startDatepicker.text));
|
||||
dateParameter = "start_date=${startDate.toIso8601String()}";
|
||||
}
|
||||
if (endDatepicker.text.isNotEmpty) {
|
||||
final endDate = DateTime.parse(formatDate(endDatepicker.text));
|
||||
dateParameter += "&end_date=${endDate.toIso8601String()}";
|
||||
}
|
||||
}
|
||||
|
||||
if (inputItem.text.isNotEmpty) {
|
||||
queryParameters += "&item=${inputItem.text}";
|
||||
}
|
||||
|
||||
if (inputTags.text.isNotEmpty) {
|
||||
queryParameters += "&tags=${inputTags.text}";
|
||||
}
|
||||
|
||||
if (queryParameters.isNotEmpty) {
|
||||
queryParameters = "$queryParameters&$dateParameter";
|
||||
} else {
|
||||
queryParameters = dateParameter;
|
||||
}
|
||||
int limit = 20 * (_fetchCount + 1);
|
||||
|
||||
return Uri.parse(
|
||||
"${globals.api}/$endpoint?$queryParameters&limit=${limit}");
|
||||
}
|
||||
|
||||
Future<void> searchSuggestionsByItem(String input) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
|
||||
if (accessToken.isNotEmpty) {
|
||||
var url = await getUrlForEvents();
|
||||
final response = await http.get(url, headers: {
|
||||
"Content-Type": "application/json",
|
||||
HttpHeaders.cookieHeader: "access_token=$accessToken"
|
||||
});
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(utf8.decode(response.bodyBytes));
|
||||
setState(() {
|
||||
suggestionsItem = (data as List)
|
||||
.map((feature) => {'name': feature['name']})
|
||||
.toList();
|
||||
if (suggestionsItem.isNotEmpty) {
|
||||
showDateFields = false;
|
||||
showArrow = false;
|
||||
}
|
||||
});
|
||||
print("status code : ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> searchSuggestionsByTag(String input) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
|
||||
if (accessToken.isNotEmpty) {
|
||||
var url = Uri.parse("${globals.api}/tags?name=${input}");
|
||||
final response = await http.get(url, headers: {
|
||||
"Content-Type": "application/json",
|
||||
HttpHeaders.cookieHeader: "access_token=$accessToken"
|
||||
});
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(utf8.decode(response.bodyBytes));
|
||||
setState(() {
|
||||
suggestionsTags = (data as List)
|
||||
.map((feature) => {'name': feature['name']})
|
||||
.toList();
|
||||
if (suggestionsTags.isNotEmpty) {
|
||||
showInputGeo = false;
|
||||
showInputSearch = false;
|
||||
showArrow = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> toggleInterested(String eventId) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
final url = Uri.parse("${globals.api}/events/${eventId}/interest");
|
||||
if (accessToken.isNotEmpty) {
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
HttpHeaders.cookieHeader: "access_token=$accessToken"
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw (AppLocalizations.of(context)?.toogle_interest ??
|
||||
"Error toogle interest: ${response.statusCode}");
|
||||
}
|
||||
|
||||
var event = json.decode(response.body);
|
||||
return event;
|
||||
}
|
||||
return {"interested": false, "interested_count": 0};
|
||||
}
|
||||
|
||||
Future<void> fetchPostsByLocation() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
|
||||
if (accessToken.isNotEmpty) {
|
||||
var url = await getUrlForEvents();
|
||||
final response = await http.get(url, headers: {
|
||||
"Content-Type": "application/json",
|
||||
HttpHeaders.cookieHeader: "access_token=$accessToken"
|
||||
});
|
||||
|
||||
print("status code : ${response.statusCode}");
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> body = json.decode(utf8.decode(response.bodyBytes));
|
||||
print("results fetch : ${body}");
|
||||
print("fetch count : ${_fetchCount}");
|
||||
// Update state after getting the response
|
||||
|
||||
setState(() {
|
||||
int counter = filteredPosts.length;
|
||||
// If we have results, map them to Events
|
||||
filteredPosts = body
|
||||
.map((e) => Events.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
if (counter == filteredPosts.length) {
|
||||
_fetchCount--;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw Exception('Failed to load posts');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onTapFunctionDatePicker(
|
||||
{required BuildContext context, String position = ""}) async {
|
||||
DateTime dateEvent = DateTime.now();
|
||||
if (startDatepicker.text.isNotEmpty) {
|
||||
dateEvent = DateTime.parse(formatDate(startDatepicker.text));
|
||||
}
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context, firstDate: dateEvent, lastDate: DateTime(2104));
|
||||
if (pickedDate == null) return;
|
||||
if (position == "start") {
|
||||
startDatepicker.text = DateFormat("dd-MM-yyyy").format(pickedDate);
|
||||
} else if (position == "end") {
|
||||
endDatepicker.text = DateFormat("dd-MM-yyyy").format(pickedDate);
|
||||
}
|
||||
|
||||
fetchPostsByLocation();
|
||||
}
|
||||
|
||||
Padding _buildDateField(String position) {
|
||||
TextEditingController datePicker = startDatepicker;
|
||||
String hintText = AppLocalizations.of(context)?.start_date ?? "Start date";
|
||||
if (position == "end") {
|
||||
datePicker = endDatepicker;
|
||||
hintText = AppLocalizations.of(context)?.end_date ?? "End date";
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextFormField(
|
||||
controller: datePicker,
|
||||
readOnly: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
suffixIcon: datePicker.text.isEmpty
|
||||
? null
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
datePicker.text = '';
|
||||
});
|
||||
fetchPostsByLocation();
|
||||
},
|
||||
),
|
||||
hintText: hintText),
|
||||
onTap: () =>
|
||||
onTapFunctionDatePicker(context: context, position: position)),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSearchField({
|
||||
required TextEditingController controller,
|
||||
required String labelText,
|
||||
required Function(String) onChanged,
|
||||
required Function() onClear,
|
||||
required List<Map<String, dynamic>> suggestions,
|
||||
required Function(Map<String, dynamic>) onSuggestionTap,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
TextField(
|
||||
controller: controller,
|
||||
decoration: InputDecoration(
|
||||
labelText: labelText,
|
||||
border: OutlineInputBorder(),
|
||||
suffixIcon: controller.text.isEmpty
|
||||
? null
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.clear),
|
||||
onPressed: () => onClear(),
|
||||
),
|
||||
),
|
||||
onChanged: onChanged,
|
||||
),
|
||||
if (suggestions.isNotEmpty)
|
||||
Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.blue),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: suggestions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final suggestion = suggestions[index];
|
||||
return ListTile(
|
||||
title: Text(
|
||||
suggestion['name'] ?? suggestion['place_name'] ?? ''),
|
||||
onTap: () => onSuggestionTap(suggestion),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> popCamera() async {
|
||||
await availableCameras().then((value) => Navigator.push(context,
|
||||
MaterialPageRoute(builder: (_) => Camera(camera: value.first))));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final loc = AppLocalizations.of(context);
|
||||
final localeProvider = Provider.of<LocaleProvider>(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(loc?.menu_list ?? "Item list menu"),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
drawer: MyDrawer(),
|
||||
body: Column(
|
||||
children: [
|
||||
_bannerAd == null
|
||||
? SizedBox.shrink()
|
||||
: SizedBox(
|
||||
height: _bannerAd!.size.height.toDouble(),
|
||||
width: _bannerAd!.size.width.toDouble(),
|
||||
child: AdWidget(ad: _bannerAd!),
|
||||
),
|
||||
if (showInputSearch)
|
||||
_buildSearchField(
|
||||
controller: inputItem,
|
||||
labelText: loc?.search_item ?? "Search by item",
|
||||
onChanged: (value) {
|
||||
_fetchCount = 0;
|
||||
if (value.isNotEmpty) {
|
||||
setState(() {
|
||||
itemName = value;
|
||||
searchSuggestionsByItem(value);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
inputItem.clear();
|
||||
itemName = '';
|
||||
suggestionsItem.clear();
|
||||
showDateFields = true;
|
||||
showArrow = true;
|
||||
});
|
||||
fetchPostsByLocation();
|
||||
}
|
||||
},
|
||||
onClear: () {
|
||||
_fetchCount = 0;
|
||||
setState(() {
|
||||
inputItem.clear();
|
||||
itemName = '';
|
||||
suggestionsItem.clear();
|
||||
showDateFields = true;
|
||||
showArrow = true;
|
||||
});
|
||||
fetchPostsByLocation();
|
||||
},
|
||||
suggestions: suggestionsItem,
|
||||
onSuggestionTap: (suggestion) async {
|
||||
_fetchCount = 0;
|
||||
setState(() {
|
||||
itemName = suggestion['name'];
|
||||
inputItem.text = itemName;
|
||||
suggestionsItem.clear();
|
||||
showDateFields = true;
|
||||
showArrow = true;
|
||||
});
|
||||
await fetchPostsByLocation();
|
||||
},
|
||||
),
|
||||
if ((showDateFields) && (showInputTag))
|
||||
_buildSearchField(
|
||||
controller: inputTags,
|
||||
labelText: loc?.search_tag ?? "Search by tags",
|
||||
onChanged: (value) {
|
||||
_fetchCount = 0;
|
||||
if (value.isNotEmpty) {
|
||||
setState(() {
|
||||
itemTags = value;
|
||||
searchSuggestionsByTag(value);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
inputTags.clear();
|
||||
showArrow = true;
|
||||
showInputSearch = true;
|
||||
showInputGeo = true;
|
||||
itemTags = '';
|
||||
});
|
||||
fetchPostsByLocation();
|
||||
}
|
||||
},
|
||||
onClear: () {
|
||||
_fetchCount = 0;
|
||||
setState(() {
|
||||
inputTags.clear();
|
||||
});
|
||||
fetchPostsByLocation();
|
||||
},
|
||||
suggestions: suggestionsTags,
|
||||
onSuggestionTap: (suggestion) async {
|
||||
_fetchCount = 0;
|
||||
|
||||
setState(() {
|
||||
itemTags = suggestion['name'];
|
||||
inputTags.text = itemTags;
|
||||
suggestionsTags.clear();
|
||||
showArrow = true;
|
||||
showInputSearch = true;
|
||||
showInputGeo = true;
|
||||
});
|
||||
await fetchPostsByLocation();
|
||||
},
|
||||
),
|
||||
if ((showDateFields) && (showArrow))
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Flexible(child: _buildDateField("start")),
|
||||
Flexible(child: _buildDateField("end"))
|
||||
]),
|
||||
if ((showDateFields) && (showInputGeo))
|
||||
_buildSearchField(
|
||||
controller: inputGeo,
|
||||
labelText:
|
||||
loc?.search_geographical ?? 'Search by geographical zone',
|
||||
onChanged: (value) async {
|
||||
_fetchCount = 0;
|
||||
|
||||
if (value.isNotEmpty) {
|
||||
setState(() {
|
||||
geographicalZone = value;
|
||||
searchSuggestionsGeo(value);
|
||||
});
|
||||
} else {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
prefs.remove("city_lat");
|
||||
prefs.remove("city_long");
|
||||
setState(() {
|
||||
inputGeo.clear();
|
||||
geographicalZone = '';
|
||||
suggestionsGeo.clear();
|
||||
showArrow = true;
|
||||
showInputSearch = true;
|
||||
showInputTag = true;
|
||||
});
|
||||
fetchPostsByLocation();
|
||||
}
|
||||
},
|
||||
onClear: () async {
|
||||
_fetchCount = 0;
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
prefs.remove("city_lat");
|
||||
prefs.remove("city_long");
|
||||
setState(() {
|
||||
inputGeo.clear();
|
||||
geographicalZone = '';
|
||||
suggestionsGeo.clear();
|
||||
showArrow = true;
|
||||
showInputSearch = true;
|
||||
showInputTag = true;
|
||||
});
|
||||
fetchPostsByLocation();
|
||||
},
|
||||
suggestions: suggestionsGeo,
|
||||
onSuggestionTap: (suggestion) async {
|
||||
_fetchCount = 0;
|
||||
final latitude = suggestion['geometry']['coordinates'][1];
|
||||
final longitude = suggestion['geometry']['coordinates'][0];
|
||||
setState(() {
|
||||
geographicalZone = suggestion['place_name'];
|
||||
inputGeo.text = geographicalZone;
|
||||
suggestionsGeo.clear();
|
||||
showArrow = true;
|
||||
showInputSearch = true;
|
||||
showInputTag = true;
|
||||
});
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
prefs.setDouble("city_lat", latitude);
|
||||
prefs.setDouble("city_long", longitude);
|
||||
await fetchPostsByLocation();
|
||||
},
|
||||
),
|
||||
if (showArrow)
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
showDateFields = !showDateFields; // Toggle visibility
|
||||
});
|
||||
},
|
||||
icon: Icon(
|
||||
showDateFields
|
||||
? Icons.keyboard_arrow_up
|
||||
: Icons.keyboard_arrow_down,
|
||||
color: Colors.blue,
|
||||
),
|
||||
tooltip: showDateFields
|
||||
? loc?.show_date_field ?? 'Show Date Fields'
|
||||
: loc?.hide_date_field ?? 'Hide Date Fields',
|
||||
),
|
||||
Expanded(
|
||||
child: FutureBuilder<List<Events>>(
|
||||
future: postsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasData) {
|
||||
final posts = snapshot.data!;
|
||||
final displayedPosts =
|
||||
filteredPosts.isEmpty ? posts : filteredPosts;
|
||||
return buildPosts(displayedPosts);
|
||||
} else {
|
||||
return Center(
|
||||
child: Text(AppLocalizations.of(context)?.no_data ??
|
||||
"No data available"),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: popCamera,
|
||||
backgroundColor: Colors.blue,
|
||||
tooltip: loc?.search ?? 'Recherche',
|
||||
child: const Icon(Icons.photo_camera, color: Colors.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Function to display fetched data on screen
|
||||
Widget buildPosts(List<Events> posts) {
|
||||
final displayedPosts = filteredPosts;
|
||||
// If filteredPosts is empty, show a message saying no data is available
|
||||
if (displayedPosts.isEmpty) {
|
||||
return Center(
|
||||
child: Text(
|
||||
AppLocalizations.of(context)?.no_events ??
|
||||
'No events available for this location.',
|
||||
style: TextStyle(fontSize: 18, color: Colors.grey)),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
controller: _scrollController,
|
||||
itemCount: displayedPosts.isNotEmpty
|
||||
? displayedPosts.length +
|
||||
(_isLoading ? 1 : 0) // Add 1 only if loading
|
||||
: 0,
|
||||
itemBuilder: (context, index) {
|
||||
if (index >= displayedPosts.length) {
|
||||
return _isLoading
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: SizedBox.shrink();
|
||||
}
|
||||
final post = displayedPosts[index];
|
||||
final startDate = DateTime.parse(post.startDate!);
|
||||
//final date = DateFormat.yMd().format(startDate);
|
||||
//final time = DateFormat.Hm().format(startDate);
|
||||
final locale =
|
||||
Provider.of<LocaleProvider>(context).locale?.toString() ??
|
||||
'en_US';
|
||||
final dateLongue =
|
||||
DateFormat('EEEE d MMMM y', locale).format(startDate);
|
||||
final countInterestedString =
|
||||
AppLocalizations.of(context)?.count_interested ??
|
||||
"Interested people number";
|
||||
final countInterested =
|
||||
"${countInterestedString} : ${post.interestedCount}";
|
||||
return ListTile(
|
||||
title: Text('${post.name!}'),
|
||||
subtitle: Text('${post.place!}\n${dateLongue}\n${countInterested}'),
|
||||
trailing: IconButton(
|
||||
onPressed: () async {
|
||||
try {
|
||||
final result = await toggleInterested(post.id!);
|
||||
setState(() {
|
||||
post.interested = result["interested"];
|
||||
post.interestedCount = result["interested_count"];
|
||||
});
|
||||
|
||||
if (result["interested"] == true) {
|
||||
NotificationService.scheduleEventNotification(
|
||||
eventId: post.id!,
|
||||
title: "Rappel évènement",
|
||||
body:
|
||||
"Ton évènement '${post.name}' commence dans 1 heure !",
|
||||
eventDate: DateTime.parse(post.startDate!),
|
||||
);
|
||||
} else {
|
||||
NotificationService.cancel(post.id!);
|
||||
}
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(context)?.error_update ??
|
||||
"Error when updating")),
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: Icon(
|
||||
post.interested ?? false
|
||||
? Icons.favorite
|
||||
: Icons.favorite_border,
|
||||
color:
|
||||
post.interested ?? false ? Colors.red : Colors.grey)),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => ItemMenu(title: post.id!)),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return Divider();
|
||||
});
|
||||
}
|
||||
}
|
169
covas_mobile_new/lib/pages/LoginDemo.dart
Normal file
@@ -0,0 +1,169 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
import '../pages/ListItemMenu.dart';
|
||||
import '../pages/AddProfile.dart';
|
||||
import '../pages/ForgotPassword.dart';
|
||||
import '../classes/alert.dart';
|
||||
import '../classes/ad_helper.dart';
|
||||
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; //
|
||||
|
||||
class LoginDemo extends StatefulWidget {
|
||||
@override
|
||||
_LoginDemoState createState() => _LoginDemoState();
|
||||
}
|
||||
|
||||
class _LoginDemoState extends State<LoginDemo> with ShowAlertDialog {
|
||||
BannerAd? _bannerAd;
|
||||
TextEditingController inputPseudo = TextEditingController();
|
||||
TextEditingController inputPassword = TextEditingController();
|
||||
final AuthService _authService = AuthService();
|
||||
bool _rememberMe = false;
|
||||
|
||||
Future<void> _login(BuildContext context) async {
|
||||
final pseudo = inputPseudo.text;
|
||||
final password = inputPassword.text;
|
||||
|
||||
if (pseudo.isEmpty || password.isEmpty) {
|
||||
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.empty_input ?? "Empty input");
|
||||
return;
|
||||
}
|
||||
|
||||
bool success =
|
||||
await _authService.login(pseudo, password, rememberMe: _rememberMe);
|
||||
|
||||
if (success) {
|
||||
Navigator.push(
|
||||
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
|
||||
} else {
|
||||
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.failed_auth ?? "Authentication failed");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
AdHelper.createBannerAd(() => setState(() {})).then((ad) {
|
||||
setState(() {
|
||||
_bannerAd = ad;
|
||||
});
|
||||
});
|
||||
|
||||
_checkLocationPermission();
|
||||
_checkLoginStatus();
|
||||
}
|
||||
|
||||
Future<void> _checkLoginStatus() async {
|
||||
bool loggedIn = await _authService.isLoggedIn();
|
||||
if (loggedIn) {
|
||||
Navigator.push(
|
||||
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _checkLocationPermission() async {
|
||||
PermissionStatus status = await Permission.location.status;
|
||||
if (!status.isGranted) {
|
||||
await Permission.location.request();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)?.login_page ?? "Login Page"),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_bannerAd == null
|
||||
? SizedBox.shrink()
|
||||
: SizedBox(
|
||||
height: _bannerAd!.size.height.toDouble(),
|
||||
width: _bannerAd!.size.width.toDouble(),
|
||||
child: AdWidget(ad: _bannerAd!),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextField(
|
||||
controller: inputPseudo,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: AppLocalizations.of(context)?.pseudo ?? 'Pseudo',
|
||||
hintText:
|
||||
AppLocalizations.of(context)?.enter_existing_pseudo ??
|
||||
'Enter a existing pseudo',
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 15),
|
||||
child: TextField(
|
||||
controller: inputPassword,
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText:
|
||||
AppLocalizations.of(context)?.password ?? "Password",
|
||||
hintText: AppLocalizations.of(context)?.enter_password ??
|
||||
"Enter the password",
|
||||
),
|
||||
),
|
||||
),
|
||||
CheckboxListTile(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)?.remembr_me ?? "Remember me"),
|
||||
value: _rememberMe,
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
_rememberMe = newValue ?? false;
|
||||
});
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.push(context,
|
||||
MaterialPageRoute(builder: (_) => PasswordForgot()));
|
||||
},
|
||||
child: Text(
|
||||
AppLocalizations.of(context)?.forgot_password ??
|
||||
'Forgot Password',
|
||||
style: TextStyle(color: Colors.blue, fontSize: 15)),
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: 250,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue, borderRadius: BorderRadius.circular(20)),
|
||||
child: TextButton(
|
||||
onPressed: () => _login(context),
|
||||
child: Text(AppLocalizations.of(context)?.sign_in ?? 'Sign in',
|
||||
style: TextStyle(color: Colors.white, fontSize: 25)),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 130),
|
||||
InkWell(
|
||||
child: Text(AppLocalizations.of(context)?.new_user ??
|
||||
'New User? Create Account'),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context, MaterialPageRoute(builder: (_) => AddProfile()));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
330
covas_mobile_new/lib/pages/MapboxPages.dart
Normal file
@@ -0,0 +1,330 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart' as mapbox;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:geolocator/geolocator.dart' as geo;
|
||||
|
||||
import '../classes/alert.dart';
|
||||
import '../variable/globals.dart' as globals;
|
||||
import '../classes/MyDrawer.dart';
|
||||
import '../classes/auth_service.dart';
|
||||
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await dotenv.load(fileName: ".env");
|
||||
|
||||
// Set the access token globally
|
||||
mapbox.MapboxOptions.setAccessToken(dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '');
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Directions Example',
|
||||
theme: ThemeData(primarySwatch: Colors.blue),
|
||||
home: const MapboxPages(title: 'Event Location', place: "Flutter"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MapboxPages extends StatefulWidget {
|
||||
const MapboxPages({Key? key, required this.title, required this.place})
|
||||
: super(key: key);
|
||||
|
||||
final String title;
|
||||
final String place;
|
||||
|
||||
@override
|
||||
State<MapboxPages> createState() => _MapboxPagesState();
|
||||
}
|
||||
|
||||
class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
mapbox.MapboxMap? mapboxMap;
|
||||
mapbox.PointAnnotationManager? pointAnnotationManager;
|
||||
mapbox.PolylineAnnotationManager? polylineAnnotationManager;
|
||||
|
||||
double longitude = 0.0;
|
||||
double latitude = 0.0;
|
||||
bool isLoading = true;
|
||||
mapbox.Point? userPosition;
|
||||
bool isUserPositionInitialized = false;
|
||||
|
||||
String selectedMode = 'driving';
|
||||
List<List<double>> routeCoordinates = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
_getUserLocation();
|
||||
}
|
||||
|
||||
Future<void> _getEventInfo() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
|
||||
if (accessToken.isNotEmpty) {
|
||||
var urlGet = Uri.parse("${globals.api}/events/${widget.title}");
|
||||
|
||||
var responseGet = await http.get(urlGet,
|
||||
headers: {HttpHeaders.cookieHeader: 'access_token=${accessToken}'});
|
||||
if (responseGet.statusCode == 200) {
|
||||
var events = jsonDecode(utf8.decode(responseGet.bodyBytes));
|
||||
latitude = events["latitude"];
|
||||
longitude = events["longitude"];
|
||||
|
||||
setState(() => isLoading = false);
|
||||
} else {
|
||||
_handleErrorResponse(responseGet.statusCode);
|
||||
}
|
||||
} else {
|
||||
showAlertDialog(context, "Error", "Invalid cache.");
|
||||
}
|
||||
}
|
||||
|
||||
void _handleErrorResponse(int statusCode) {
|
||||
final errorMessage = "Error $statusCode fetching event";
|
||||
showAlertDialog(context, "Error", errorMessage);
|
||||
}
|
||||
|
||||
Future<void> _getUserLocation() async {
|
||||
await dotenv.load(fileName: ".env");
|
||||
|
||||
// Set the access token globally
|
||||
mapbox.MapboxOptions.setAccessToken(
|
||||
dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '');
|
||||
try {
|
||||
bool serviceEnabled = await geo.Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) return;
|
||||
|
||||
geo.LocationPermission permission =
|
||||
await geo.Geolocator.checkPermission();
|
||||
if (permission == geo.LocationPermission.denied) {
|
||||
permission = await geo.Geolocator.requestPermission();
|
||||
}
|
||||
|
||||
if (permission == geo.LocationPermission.deniedForever) return;
|
||||
|
||||
geo.Position position = await geo.Geolocator.getCurrentPosition();
|
||||
setState(() {
|
||||
userPosition = mapbox.Point(
|
||||
coordinates:
|
||||
mapbox.Position(position.longitude, position.latitude));
|
||||
isUserPositionInitialized = true;
|
||||
});
|
||||
|
||||
_getEventInfo();
|
||||
} catch (e) {
|
||||
showAlertDialog(context, "Error", "Failed to get location");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchRoute(
|
||||
mapbox.Point origin, mapbox.Point destination, String mode) async {
|
||||
final url = Uri.parse(
|
||||
'https://api.mapbox.com/directions/v5/mapbox/$mode/'
|
||||
'${origin.coordinates.lng},${origin.coordinates.lat};'
|
||||
'${destination.coordinates.lng},${destination.coordinates.lat}'
|
||||
'?geometries=geojson&access_token=${dotenv.env['MAPBOX_ACCESS_TOKEN']}',
|
||||
);
|
||||
|
||||
final response = await http.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
|
||||
// Vérifie si 'routes' existe et contient au moins 1 élément
|
||||
if (data['routes'] != null && (data['routes'] as List).isNotEmpty) {
|
||||
final geometry = data['routes'][0]['geometry']['coordinates'];
|
||||
|
||||
setState(() {
|
||||
routeCoordinates = (geometry as List)
|
||||
.map<List<double>>((coord) => [coord[0], coord[1]])
|
||||
.toList();
|
||||
});
|
||||
} else {
|
||||
debugPrint("⚠️ Aucune route trouvée entre ${origin} et $destination.");
|
||||
// Optionnel : afficher un snackbar/toast à l’utilisateur
|
||||
}
|
||||
} else {
|
||||
debugPrint("❌ Erreur API Mapbox: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _zoomToFitRoute(List<List<double>> coordinates) async {
|
||||
if (mapboxMap == null || coordinates.isEmpty) return;
|
||||
|
||||
double minLat = coordinates.first[1];
|
||||
double maxLat = coordinates.first[1];
|
||||
double minLng = coordinates.first[0];
|
||||
double maxLng = coordinates.first[0];
|
||||
|
||||
for (var coord in coordinates) {
|
||||
if (coord[1] < minLat) minLat = coord[1];
|
||||
if (coord[1] > maxLat) maxLat = coord[1];
|
||||
if (coord[0] < minLng) minLng = coord[0];
|
||||
if (coord[0] > maxLng) maxLng = coord[0];
|
||||
}
|
||||
|
||||
final bounds = mapbox.CoordinateBounds(
|
||||
southwest: mapbox.Point(coordinates: mapbox.Position(minLng, minLat)),
|
||||
northeast: mapbox.Point(coordinates: mapbox.Position(maxLng, maxLat)),
|
||||
infiniteBounds: true);
|
||||
|
||||
// Calculer une CameraOptions automatiquement à partir des bounds
|
||||
final cameraOptions = await mapboxMap!.cameraForCoordinateBounds(
|
||||
bounds,
|
||||
mapbox.MbxEdgeInsets(
|
||||
top: 50, left: 50, right: 50, bottom: 50), // marges
|
||||
0.0,
|
||||
0.0,
|
||||
null,
|
||||
null);
|
||||
|
||||
// Appliquer la caméra avec animation
|
||||
await mapboxMap!.flyTo(
|
||||
cameraOptions,
|
||||
mapbox.MapAnimationOptions(duration: 1000),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _drawRouteAndMarkers() async {
|
||||
if (mapboxMap == null || !isUserPositionInitialized) return;
|
||||
|
||||
// Managers
|
||||
pointAnnotationManager ??=
|
||||
await mapboxMap!.annotations.createPointAnnotationManager();
|
||||
polylineAnnotationManager ??=
|
||||
await mapboxMap!.annotations.createPolylineAnnotationManager();
|
||||
|
||||
// Clear old annotations
|
||||
await pointAnnotationManager!.deleteAll();
|
||||
await polylineAnnotationManager!.deleteAll();
|
||||
|
||||
final destination =
|
||||
mapbox.Point(coordinates: mapbox.Position(longitude, latitude));
|
||||
|
||||
// Add user marker
|
||||
final userIcon = await _loadMarkerImage('images/marker.png');
|
||||
await pointAnnotationManager!.create(mapbox.PointAnnotationOptions(
|
||||
geometry: mapbox.Point(
|
||||
coordinates: mapbox.Position(
|
||||
userPosition!.coordinates.lng, userPosition!.coordinates.lat)),
|
||||
image: userIcon,
|
||||
iconSize: 0.4,
|
||||
));
|
||||
|
||||
// Ajoute directement la flèche rouge à la position de l’événement
|
||||
final eventIcon = await _loadMarkerImage('images/marker-red.png');
|
||||
await pointAnnotationManager!.create(mapbox.PointAnnotationOptions(
|
||||
geometry: mapbox.Point(coordinates: mapbox.Position(longitude, latitude)),
|
||||
image: eventIcon,
|
||||
iconSize: 0.2,
|
||||
));
|
||||
|
||||
// Fetch and draw route
|
||||
await _fetchRoute(userPosition!, destination, selectedMode);
|
||||
if (routeCoordinates.isNotEmpty) {
|
||||
await polylineAnnotationManager!.create(mapbox.PolylineAnnotationOptions(
|
||||
geometry: mapbox.LineString(
|
||||
coordinates:
|
||||
routeCoordinates.map((c) => mapbox.Position(c[0], c[1])).toList(),
|
||||
),
|
||||
lineColor: Colors.blue.value,
|
||||
lineWidth: 4.0,
|
||||
));
|
||||
await _zoomToFitRoute(routeCoordinates);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uint8List> _loadMarkerImage(String assetPath) async {
|
||||
final ByteData data = await rootBundle.load(assetPath);
|
||||
return data.buffer.asUint8List();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(widget.place)),
|
||||
drawer: MyDrawer(),
|
||||
body: isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text("Mode : "),
|
||||
DropdownButton<String>(
|
||||
value: selectedMode,
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: 'driving', child: Text("🚗 Voiture")),
|
||||
DropdownMenuItem(
|
||||
value: 'walking', child: Text("🚶 Marche")),
|
||||
DropdownMenuItem(
|
||||
value: 'cycling', child: Text("🚴 Vélo")),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
selectedMode = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: mapbox.MapWidget(
|
||||
onMapCreated: (controller) async {
|
||||
mapboxMap = controller;
|
||||
|
||||
// Crée un manager si nécessaire
|
||||
pointAnnotationManager ??= await mapboxMap!.annotations
|
||||
.createPointAnnotationManager();
|
||||
|
||||
// Ajoute directement la flèche rouge à la position de l’événement
|
||||
final eventIcon =
|
||||
await _loadMarkerImage('images/marker-red.png');
|
||||
await pointAnnotationManager!
|
||||
.create(mapbox.PointAnnotationOptions(
|
||||
geometry: mapbox.Point(
|
||||
coordinates: mapbox.Position(longitude, latitude)),
|
||||
image: eventIcon,
|
||||
iconSize: 0.2,
|
||||
));
|
||||
},
|
||||
cameraOptions: mapbox.CameraOptions(
|
||||
center: mapbox.Point(
|
||||
coordinates: mapbox.Position(longitude, latitude)),
|
||||
zoom: 14.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _drawRouteAndMarkers,
|
||||
child: const Icon(Icons.directions),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|