Compare commits
249 Commits
112eab3125
...
feature/no
Author | SHA1 | Date | |
---|---|---|---|
b6134c5506 | |||
6e0b54a925 | |||
5d9802d56e | |||
8a7515aaf6 | |||
f9934a0d5d | |||
09c82da57f | |||
75b42dd091 | |||
dfe21960be | |||
4a936cc267 | |||
bd5bfa94ca | |||
f30aa8f630 | |||
924794e8a0 | |||
7528e55dca | |||
02ba067615 | |||
9eafb30374 | |||
24cb90959d | |||
1620d84137 | |||
6e8ba642a2 | |||
0113fcc382 | |||
e4571cabe7 | |||
76db2f8254 | |||
0a62011b3a | |||
cbc75bbc7b | |||
7a418d82a7 | |||
e310197aa7 | |||
5d02f2b1fb | |||
119dfcbdfe | |||
9e50b6d6f6 | |||
dcc2ec25de | |||
2c2eedb7ce | |||
089aa58f4a | |||
b4b0199fc2 | |||
b48483c9f0 | |||
22d0581da3 | |||
337fab4a08 | |||
a99986813e | |||
199000035e | |||
f143036ca8 | |||
271c3ba118 | |||
f8b5c24efd | |||
1df36987d9 | |||
b4dc29aff6 | |||
79563e829c | |||
413807f039 | |||
26372368b2 | |||
f81a8c264c | |||
c5985f2954 | |||
1f8d18343c | |||
c3b8b0df14 | |||
ec8ce404ab | |||
1208982b15 | |||
479ab76fb7 | |||
1646a0b6e3 | |||
e21b03d13c | |||
45cdb253e4 | |||
75b443758a | |||
e2195e6500 | |||
4f41aff572 | |||
1c21f59420 | |||
7441af6f13 | |||
4f4b0b609c | |||
2e6814c33d | |||
909b321158 | |||
6641c7938c | |||
8904032265 | |||
9d35a59ba6 | |||
303447ff1e | |||
32532246eb | |||
452faf05d8 | |||
3a64f1ae36 | |||
afae3293c4 | |||
600bf8d2f4 | |||
ec7a286074 | |||
6c1c650fa1 | |||
7468c5f24c | |||
1bcc8372c6 | |||
510e366216 | |||
52580c6568 | |||
4f7c8f60d0 | |||
ce2a061bf0 | |||
04f4b7ce8e | |||
78ea5f0c10 | |||
395d3390cf | |||
daef20db66 | |||
9df499d198 | |||
e7afe8fddb | |||
5c2a58e484 | |||
e8bb2f6180 | |||
e4d8648fcc | |||
7f3240242d | |||
c936a02836 | |||
3352bab860 | |||
c31e275dae | |||
38e9908533 | |||
47c014cdda | |||
729e0ce1ca | |||
5c2fa27aa7 | |||
9846c614a1 | |||
d46f268c1b | |||
b0477c889c | |||
6fdb5cb2fe | |||
c1352b8eeb | |||
90e61cebbf | |||
6a3ec9a969 | |||
11c8cd74d2 | |||
39c60632f1 | |||
c5a280d91b | |||
280909deb9 | |||
b7fe1da681 | |||
56ae77137c | |||
06e26240ab | |||
908d94c269 | |||
642f35e73c | |||
524427a29f | |||
54c95a230b | |||
18d5c83181 | |||
b156cd084b | |||
3bb85a198a | |||
0f40c3e225 | |||
30b9be35ef | |||
db3cb20255 | |||
83c65be610 | |||
da3659e84a | |||
39b3efca33 | |||
82c31acf99 | |||
f84f513e67 | |||
3615c1f476 | |||
5419da7a98 | |||
ef8b8c96c0 | |||
ad19ea54d1 | |||
ef87a8bfe2 | |||
8adcd80306 | |||
43d77f778b | |||
938b677b6e | |||
6e5994e4dd | |||
b2e7080265 | |||
d8b3bf7ca2 | |||
0df538ef46 | |||
48c785c586 | |||
c5de20d64b | |||
43124d9cb9 | |||
03f3e1c55b | |||
ba8db9fb4c | |||
53a60a581a | |||
e4836ac4eb | |||
f267e3ede9 | |||
eadf07177b | |||
18fdc7d6c4 | |||
8d8c30f506 | |||
ea3d5ef8de | |||
578107c83c | |||
49a108905c | |||
32281233e0 | |||
63f6bd2af2 | |||
c58127342b | |||
0dc098554c | |||
b8e6adf2e8 | |||
ac566053b1 | |||
c12a957099 | |||
4b71867ee0 | |||
8c4c436241 | |||
1cc14277e8 | |||
cf100651eb | |||
76f1de27ec | |||
eef7e44999 | |||
3315cf1961 | |||
a4b747a273 | |||
6b53dc170d | |||
0977389695 | |||
559b35c7c2 | |||
edd2cd1581 | |||
d012ac409b | |||
4ec4b79f2c | |||
7f4f59b6c9 | |||
0e3d75e8a8 | |||
d93c10f319 | |||
a5533aa0d3 | |||
4b75abe6b6 | |||
8a46958e7c | |||
f85894416c | |||
dbba3304c2 | |||
6b3bf5004c | |||
bae64ebd35 | |||
431b8d78ad | |||
d4f8ee2182 | |||
627eb778ad | |||
23aec689f1 | |||
a0ece6f973 | |||
c6a0c2f4f1 | |||
f693ffa6b8 | |||
93beabe884 | |||
cdae84090f | |||
a7d8bebe1f | |||
4a313fc725 | |||
4d3533eb8a | |||
1f6d9bbcd0 | |||
5e0b541786 | |||
b7dc682e2f | |||
67b56b5764 | |||
de6a7f2399 | |||
c8223d9b7d | |||
7f5d59857c | |||
af65dc1cb0 | |||
4a04520800 | |||
f880ac1002 | |||
d4842b4d67 | |||
9146fd02e4 | |||
79f457eec1 | |||
74e55f3d6b | |||
7182e0e324 | |||
be8b0d3b66 | |||
2e1b25264a | |||
701f6dbacf | |||
8b1db195d7 | |||
c8bcd254dd | |||
76ab0aef48 | |||
a427fc5edf | |||
c1e85c255e | |||
1580d7a3cc | |||
00a9125eb2 | |||
44a0691e31 | |||
b22458021b | |||
5c0f4d345d | |||
3a0f24cc4c | |||
b796d0206f | |||
517652df98 | |||
951127d7bc | |||
3a350e33cc | |||
576d045cd8 | |||
4e0222d4bb | |||
c43eb789b1 | |||
6c806b1a39 | |||
b31e35504f | |||
f2de4a2faa | |||
df80137f46 | |||
3861a3c5e1 | |||
8b87f55bf9 | |||
519d20fd63 | |||
fb34b41385 | |||
f2cd070898 | |||
045209575e | |||
792ce0227e | |||
f0780c2a3e | |||
b3c4980b52 | |||
4d73360f74 | |||
b1ca67ed72 | |||
b1a8b932b9 | |||
7e6de0aa38 | |||
47d80791e5 |
@@ -1,9 +1,14 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.covas_mobile">
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<application
|
||||
android:label="covas_mobile"
|
||||
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"
|
||||
@@ -24,6 +29,7 @@
|
||||
<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 -->
|
||||
|
@@ -5,4 +5,16 @@
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
|
||||
<application
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:theme="@style/Theme.YourApp">
|
||||
<!-- Other configurations -->
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
@@ -1,3 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
flutter.compileSdkVersion=35
|
BIN
covas_mobile/images/marker-red.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
covas_mobile/images/marker.png
Normal file
After Width: | Height: | Size: 11 KiB |
@@ -45,5 +45,10 @@
|
||||
<false/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>Your location is needed for showing nearby events</string>
|
||||
<key>GADApplicationIdentifier</key>
|
||||
<string>ca-app-pub-4855855675386260~3438207239</string>
|
||||
|
||||
</dict>
|
||||
</plist>
|
||||
|
4
covas_mobile/l10n.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
arb-dir: lib/l10n
|
||||
template-arb-file: app_en.arb
|
||||
output-localization-file: app_localizations.dart
|
||||
output-class: AppLocalizations
|
209
covas_mobile/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:flutter_gen/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
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
27
covas_mobile/lib/classes/ad_helper.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
|
||||
class AdHelper {
|
||||
static Future<BannerAd> createBannerAd(Function setStateCallback) async {
|
||||
await dotenv.load(fileName: ".env");
|
||||
final adUnitId = dotenv.env['AD_UNIT_ID'] ?? '';
|
||||
|
||||
BannerAd bannerAd = BannerAd(
|
||||
adUnitId: adUnitId,
|
||||
size: AdSize.banner,
|
||||
request: AdRequest(),
|
||||
listener: BannerAdListener(
|
||||
onAdLoaded: (ad) {
|
||||
setStateCallback(() {});
|
||||
},
|
||||
onAdFailedToLoad: (ad, error) {
|
||||
print('Banner Ad failed to load: $error');
|
||||
ad.dispose();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
bannerAd.load();
|
||||
return bannerAd;
|
||||
}
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
mixin ShowErrorDialog<T extends StatefulWidget> on State<T> {
|
||||
void showErrorDialog(BuildContext context, String text) {
|
||||
mixin ShowAlertDialog {
|
||||
void showAlertDialog(BuildContext context, String title, String text) {
|
||||
// Create AlertDialog
|
||||
AlertDialog dialog = AlertDialog(
|
||||
title: Text("Error"),
|
||||
title: Text(title),
|
||||
content: Text(text),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
|
141
covas_mobile/lib/classes/auth_service.dart
Normal file
@@ -0,0 +1,141 @@
|
||||
import 'dart:convert';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../variable/globals.dart' as globals;
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:encrypt_shared_preferences/provider.dart';
|
||||
import '../pages/LoginDemo.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv
|
||||
|
||||
class AuthService {
|
||||
// Login with username and password
|
||||
Future<bool> login(String username, String password,
|
||||
{bool rememberMe = false}) async {
|
||||
final url = Uri.parse("${globals.api}/token");
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: {"username": username, "password": password},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (rememberMe) {
|
||||
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();
|
||||
sharedPref.setString("username", username);
|
||||
sharedPref.setString("password", password);
|
||||
}
|
||||
}
|
||||
final cookies = response.headers["set-cookie"]?.split(";") ?? [];
|
||||
|
||||
for (final cookie in cookies) {
|
||||
final cookieParts = cookie.split(",");
|
||||
for (final part in cookieParts) {
|
||||
final keyValue = part.split("=");
|
||||
if (keyValue.length == 2 && keyValue[0] == "access_token") {
|
||||
prefs.setString("access_token", keyValue[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
print("Login error: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Logout
|
||||
Future<void> logout() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove("access_token");
|
||||
}
|
||||
|
||||
Future<bool> isLoggedIn() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final accessToken = prefs.getString("access_token");
|
||||
|
||||
if (accessToken == null || accessToken.isEmpty) {
|
||||
print("No access token found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
print("Checking token validity...");
|
||||
var url = Uri.parse("${globals.api}/token");
|
||||
|
||||
try {
|
||||
final response = await http.get(
|
||||
url,
|
||||
headers: {
|
||||
HttpHeaders.cookieHeader: "access_token=$accessToken",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("Token is valid.");
|
||||
return true;
|
||||
} else {
|
||||
print("Token is invalid. Status code: ${response.statusCode}");
|
||||
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");
|
||||
await prefs.remove("access_token"); // Clear invalid token
|
||||
return false;
|
||||
} else {
|
||||
return login(username, password);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error while checking token: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkTokenStatus(context) async {
|
||||
bool loggedIn = await isLoggedIn();
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
if (!loggedIn) {
|
||||
await prefs.remove("access_token"); // Correctly remove the token
|
||||
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => LoginDemo()),
|
||||
(route) => false, // Remove all previous routes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Get stored access token
|
||||
Future<String?> getAccessToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString("access_token");
|
||||
}
|
||||
|
||||
// Login with Google
|
||||
}
|
@@ -5,21 +5,50 @@ class Events {
|
||||
String? startDate;
|
||||
String? endDate;
|
||||
String? description;
|
||||
String? zipCode;
|
||||
String? city;
|
||||
String? country;
|
||||
|
||||
Events({this.place, this.id, this.name, this.startDate});
|
||||
String? link;
|
||||
String? ticket;
|
||||
double? latitude;
|
||||
double? longitude;
|
||||
List<String>? tags;
|
||||
List<String>? organizers;
|
||||
String? imgUrl;
|
||||
int? interestedCount;
|
||||
bool? interested;
|
||||
Events(
|
||||
{this.place,
|
||||
this.id,
|
||||
this.name,
|
||||
this.startDate,
|
||||
this.description,
|
||||
this.endDate,
|
||||
this.tags,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.organizers,
|
||||
this.link,
|
||||
this.ticket,
|
||||
this.imgUrl,
|
||||
this.interestedCount,
|
||||
this.interested});
|
||||
|
||||
Events.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
name = json['name'];
|
||||
place = json['place'];
|
||||
startDate = json["start_date"];
|
||||
endDate = json['end_date'];
|
||||
description = json['description'];
|
||||
zipCode = json["zip_code"];
|
||||
city = json['city'];
|
||||
country = json['country'];
|
||||
id = json['id'] as String?;
|
||||
name = json['name'] as String?;
|
||||
place = json['place'] as String?;
|
||||
startDate = json['start_date'] as String?;
|
||||
endDate = json['end_date'] as String?;
|
||||
description = json['description'] as String?;
|
||||
latitude = (json['latitude'] as num?)?.toDouble(); // Safely cast to double
|
||||
longitude =
|
||||
(json['longitude'] as num?)?.toDouble(); // Safely cast to double
|
||||
tags = (json['tags'] as List<dynamic>?)
|
||||
?.cast<String>(); // Convert List<dynamic> to List<String>
|
||||
organizers = (json['organizers'] as List<dynamic>?)
|
||||
?.cast<String>(); // Convert List<dynamic> to List<String>
|
||||
imgUrl = json['imgUrl'] as String?;
|
||||
link = json['link'] as String?;
|
||||
ticket = json['ticket'] as String?;
|
||||
interested = json['interested'] as bool?;
|
||||
interestedCount = json['interested_count'] as int?;
|
||||
}
|
||||
}
|
||||
|
91
covas_mobile/lib/classes/notification_service.dart
Normal file
@@ -0,0 +1,91 @@
|
||||
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:flutter_gen/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
|
||||
uiLocalNotificationDateInterpretation:
|
||||
UILocalNotificationDateInterpretation.absoluteTime,
|
||||
matchDateTimeComponents: DateTimeComponents.dateAndTime,
|
||||
);
|
||||
}
|
||||
|
||||
/// Annule une notification planifiée
|
||||
static Future<void> cancel(String eventId) async {
|
||||
await _notificationsPlugin.cancel(eventId.hashCode);
|
||||
}
|
||||
}
|
143
covas_mobile/lib/l10n/app_de.arb
Normal file
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"@@locale": "de",
|
||||
"menu_list": "Veranstaltungsmenü",
|
||||
"language": "Sprache",
|
||||
"home": "Startseite",
|
||||
"settings": "Einstellungen",
|
||||
"update_profile": "Profil aktualisieren",
|
||||
"about": "Über",
|
||||
"log_out": "Abmelden",
|
||||
"french": "Französisch",
|
||||
"english": "Englisch",
|
||||
"german": "Deutsch",
|
||||
"select_language": "Sprache auswählen",
|
||||
"search_item": "Nach Element suchen",
|
||||
"search_tag": "Nach Schlagwörtern suchen",
|
||||
"search_geographical": "Nach geografischer Zone suchen",
|
||||
"show_date_field": "Datumsfelder anzeigen",
|
||||
"hide_date_field": "Datumsfelder ausblenden",
|
||||
"no_data": "Keine Daten verfügbar",
|
||||
"search": "Suchen",
|
||||
"no_events": "Keine Veranstaltungen für diesen Ort verfügbar.",
|
||||
"start_date": "Anfangsdatum",
|
||||
"end_date": "Enddatum",
|
||||
"failed_suggestions": "Vorschläge konnten nicht geladen werden",
|
||||
"error": "Fehler",
|
||||
"password_different": "Ein anderes Passwort eingeben",
|
||||
"create": "Erstellung",
|
||||
"user_create": "Benutzer wurde erstellt",
|
||||
"user_update": "Benutzer wurde aktualisiert",
|
||||
"request_error": "Fehlerhafte Anfrage",
|
||||
"incorrect_password": "Falsches Passwort",
|
||||
"unknown_user": "Unbekannter Benutzer",
|
||||
"disabled_user": "Benutzer deaktiviert",
|
||||
"invalid_token": "Ungültiger Token",
|
||||
"internal_error_server": "Interner Serverfehler",
|
||||
"unknown_error_auth": "Unbekannter Authentifizierungsfehler",
|
||||
"required_input": "Pflichtfeld",
|
||||
"create_profile": "Profil erstellen",
|
||||
"edit_pseudo": "Benutzernamen bearbeiten",
|
||||
"password": "Passwort",
|
||||
"enter_password": "Passwort eingeben",
|
||||
"password_confirmed": "Passwort bestätigt",
|
||||
"last_name": "Nachname",
|
||||
"first_name": "Vorname",
|
||||
"email": "E-Mail",
|
||||
"edit_last_name": "Nachnamen bearbeiten",
|
||||
"edit_first_name": "Vornamen bearbeiten",
|
||||
"edit_email": "E-Mail-Adresse bearbeiten",
|
||||
"birth_date": "Geburtsdatum",
|
||||
"edit_birth": "Geburtsdatum bearbeiten",
|
||||
"create_profile_button": "Profil erstellen",
|
||||
"take_picture": "Foto aufnehmen",
|
||||
"error_ia": "Google KI konnte das Bild nicht analysieren. Bitte ein anderes versuchen.",
|
||||
"no_data_geo": "Keine geografischen Daten",
|
||||
"response_status_update": "Statuscode-Antwort aktualisieren",
|
||||
"error_token": "Token-Fehler",
|
||||
"error_format": "Vom KI geliefertes Datenformat ist fehlerhaft",
|
||||
"display_picture": "Bild anzeigen",
|
||||
"analyze_image": "Bildanalyse läuft",
|
||||
"loading_progress": "Ladefortschritt",
|
||||
"error_event": "Veranstaltungsfehler",
|
||||
"no_future_event": "Keine zukünftigen Veranstaltungen",
|
||||
"error_user": "Benutzerfehler",
|
||||
"empty_input": "Eingabefeld leer",
|
||||
"info_event": "Veranstaltungsinfo",
|
||||
"event_already": "Veranstaltung existiert bereits",
|
||||
"picture_error": "Bildfehler",
|
||||
"no_picture_published": "Kein Bild veröffentlicht",
|
||||
"event_update": "Veranstaltung aktualisiert",
|
||||
"location": "Ort",
|
||||
"add_event": "Veranstaltung hinzufügen oder aktualisieren",
|
||||
"edit_image": "Bilder bearbeiten",
|
||||
"name": "Name",
|
||||
"edit_event_name": "Veranstaltungsname bearbeiten",
|
||||
"start_time": "Startzeit",
|
||||
"end_time": "Endzeit",
|
||||
"select_date": "Zum Auswählen eines Datums klicken",
|
||||
"select_time": "Zum Auswählen einer Uhrzeit klicken",
|
||||
"tag": "Schlagwörter",
|
||||
"already_tag": "Dieses Schlagwort ist bereits vorhanden",
|
||||
"enter_tag": "Ein Schlagwort eingeben",
|
||||
"organizer": "Veranstalter",
|
||||
"already_organiser": "Veranstalter bereits vorhanden",
|
||||
"enter_organizer": "Veranstalter eingeben",
|
||||
"description": "Beschreibung",
|
||||
"describe_event": "Veranstaltung beschreiben",
|
||||
"add": "Hinzufügen",
|
||||
"different_password_error": "Passwörter stimmen nicht überein",
|
||||
"update": "Aktualisieren",
|
||||
"updated": "Aktualisiert",
|
||||
"settings_updated": "Einstellungen aktualisiert",
|
||||
"define_kilometer": "Kilometer definieren",
|
||||
"email_sent": "E-Mail wurde gesendet",
|
||||
"forgot_password": "Passwort vergessen",
|
||||
"enter_email": "E-Mail eingeben",
|
||||
"send_email": "E-Mail senden",
|
||||
"invalid_cache": "Ungültiger Cache",
|
||||
"item_date": "Datum : ",
|
||||
"item_maps": "Karte : ",
|
||||
"item_organizer": "Veranstalter : ",
|
||||
"item_description": "Beschreibung : ",
|
||||
"item_tags": "Schlagwörter : ",
|
||||
"failed_auth": "Authentifizierung fehlgeschlagen",
|
||||
"login_page": "Anmeldeseite",
|
||||
"pseudo": "Benutzername",
|
||||
"enter_existing_pseudo": "Vorhandenen Benutzernamen eingeben",
|
||||
"remembr_me": "Angemeldet bleiben",
|
||||
"new_user": "Neuer Benutzer? Konto erstellen",
|
||||
"sign_in": "Anmelden",
|
||||
"map_token": "Mapbox-Zugangstoken ist nicht verfügbar",
|
||||
"geo_disabled": "Standortdienste sind deaktiviert.",
|
||||
"permission_denied": "Standortberechtigungen wurden verweigert.",
|
||||
"enable_permission": "Standortberechtigungen dauerhaft verweigert. Bitte in den Einstellungen aktivieren.",
|
||||
"no_last_position": "Keine letzte bekannte Position verfügbar.",
|
||||
"failed_location": "Standort konnte nicht ermittelt werden",
|
||||
"failed_fetch": "Route konnte nicht abgerufen werden",
|
||||
"invalid_coordinates_symbol": "Ungültige Koordinaten, Symbol kann nicht hinzugefügt werden.",
|
||||
"error_symbol": "Fehler beim Hinzufügen des Symbols.",
|
||||
"position_not_init": "Benutzerposition noch nicht initialisiert. Erneut versuchen.",
|
||||
"invalid_coordinates": "Ungültige Koordinaten.",
|
||||
"walking": "Zu Fuß",
|
||||
"cycling": "Mit dem Fahrrad",
|
||||
"driving": "Mit dem Auto",
|
||||
"get_direction": "Wegbeschreibung und Markierungen anzeigen",
|
||||
"missing_token": "Zugangstoken fehlt",
|
||||
"geocoding_error": "Fehler bei der Geokodierung",
|
||||
"no_found_place": "Kein Ort gefunden",
|
||||
"upload_error": "Fehler beim Hochladen des Bildes",
|
||||
"event_added": "Veranstaltung hinzugefügt",
|
||||
"unknown_error": "Unbekannter Fehler",
|
||||
"app_error": "Anwendungsfehler",
|
||||
"at": "um",
|
||||
"to_date": "bis",
|
||||
"item_link": "Link : ",
|
||||
"item_ticket": "Abendkarte : ",
|
||||
"link": "Link",
|
||||
"edit_link": "Linkereignis bearbeiten",
|
||||
"ticket": "Abendkarte",
|
||||
"edit_ticket": "Ticketlink bearbeiten",
|
||||
"toogle_interest": "Fehler beim Umschalten des Interesses",
|
||||
"error_update": "Fehler beim Update",
|
||||
"count_interested": "Anzahl der Interessenten"
|
||||
}
|
145
covas_mobile/lib/l10n/app_en.arb
Normal file
@@ -0,0 +1,145 @@
|
||||
{
|
||||
"@@locale": "en",
|
||||
"menu_list": "Event list menu",
|
||||
"language": "Language",
|
||||
"home": "Home",
|
||||
"settings": "Settings",
|
||||
"update_profile": "Update profile",
|
||||
"about": "About",
|
||||
"log_out": "Log out",
|
||||
"french": "French",
|
||||
"english": "English",
|
||||
"german": "German",
|
||||
"select_language": "Select language",
|
||||
"search_item": "Search by item",
|
||||
"search_tag": "Search by tags",
|
||||
"search_geographical": "Search by geographical zone",
|
||||
"show_date_field": "Show Date Fields",
|
||||
"hide_date_field": "Hide Date Fields",
|
||||
"no_data": "No data available",
|
||||
"search": "Search",
|
||||
"no_events": "No events available for this location.",
|
||||
"start_date": "Start date",
|
||||
"end_date": "End date",
|
||||
"failed_suggestions": "Failed to load suggestions",
|
||||
"error":"Error",
|
||||
"password_different":"Must write a different password",
|
||||
"create": "Creation",
|
||||
"user_create": "Your user created",
|
||||
"user_update": "Your user updated",
|
||||
"request_error": "Poorly constructed query",
|
||||
"incorrect_password": "Incorrect password",
|
||||
"unknown_user": "Unknown user",
|
||||
"disabled_user": "User disabled",
|
||||
"invalid_token": "Invalid token",
|
||||
"internal_error_server": "Internal error server",
|
||||
"unknown_error_auth": "Unknown error authentification",
|
||||
"required_input": "Required input",
|
||||
"create_profile": "Create profile",
|
||||
"edit_pseudo": "Edit pseudo",
|
||||
"password":"Password",
|
||||
"enter_password": "Enter the passord",
|
||||
"password_confirmed": "Password confirmed",
|
||||
"last_name": "Last name",
|
||||
"first_name": "First name",
|
||||
"email": "Mail",
|
||||
"edit_last_name": "Edit name",
|
||||
"edit_first_name": "Edit first name",
|
||||
"edit_email": "Edit email address",
|
||||
"birth_date": "Birth date",
|
||||
"edit_birth": "Edit birth date",
|
||||
"create_profile_button": "Create profile",
|
||||
"take_picture": "Take a picture",
|
||||
"error_ia": "Google AI failed to analyze picture. Retry with another one",
|
||||
"no_data_geo": "No geographical data",
|
||||
"response_status_update": "response status code update",
|
||||
"error_token": "Token error",
|
||||
"error_format": "Data format error given by AI",
|
||||
"display_picture": "Display the Picture",
|
||||
"analyze_image": "Image Analyze in progress",
|
||||
"loading_progress": "Loading progress",
|
||||
"error_event": "Event error",
|
||||
"no_future_event": "No future event",
|
||||
"error_user": "Error user",
|
||||
"empty_input": "Empty input",
|
||||
"info_event": "Event info",
|
||||
"event_already": "Event already exists",
|
||||
"picture_error": "Picture error",
|
||||
"no_picture_published": "No picture published",
|
||||
"event_update": "Event updated",
|
||||
"location": "Location",
|
||||
"add_event": "Add or Update a event",
|
||||
"edit_image": "Edit pictures",
|
||||
"name": "Name",
|
||||
"edit_event_name": "Edit event name",
|
||||
"start_time": "Start time",
|
||||
"end_time": "End time",
|
||||
"select_date": "Click to select a date",
|
||||
"select_time": "Click to select a time",
|
||||
"tag": "Tags",
|
||||
"already_tag": "You have already this tags",
|
||||
"enter_tag": "Enter a tag",
|
||||
"organizer": "Organizer",
|
||||
"already_organiser": "You have already a organizer",
|
||||
"enter_organizer": "Enter a organizer",
|
||||
"description": "Description",
|
||||
"describe_event": "Describe event",
|
||||
"add": "Add",
|
||||
"update_profile": "Update profile",
|
||||
"different_password_error": "Different password",
|
||||
"update": "Update",
|
||||
"updated": "Updated",
|
||||
"settings_updated": "Settings updated",
|
||||
"define_kilometer": "Define Kilometer",
|
||||
"settings": "Settings",
|
||||
"email_sent": "Email has been sent",
|
||||
"forgot_password": "Forgot password",
|
||||
"enter_email": "Enter the email",
|
||||
"send_email": "Send email",
|
||||
"invalid_cache": "Invalid cache",
|
||||
"item_date": "Date : ",
|
||||
"item_maps": "Maps : ",
|
||||
"item_organizer": "Organizer : ",
|
||||
"item_description": "Description : ",
|
||||
"item_tags": "Tags : ",
|
||||
"failed_auth": "Authentification failed",
|
||||
"login_page": "Login page",
|
||||
"pseudo": "Pseudo",
|
||||
"enter_existing_pseudo": "Enter a existing pseudo",
|
||||
"remembr_me": "Remember me",
|
||||
"new_user": "New User? Create Account",
|
||||
"sign_in": "Sign in",
|
||||
"map_token": "Mapbox Access Token is not available",
|
||||
"geo_disabled": "Location services are disabled.",
|
||||
"permission_denied":"Location permissions are denied.",
|
||||
"enable_permission": "Location permissions are permanently denied. Enable them in settings.",
|
||||
"no_last_position": "No last known position available.",
|
||||
"failed_location": "Failed to get user location",
|
||||
"failed_fetch": "Failed to fetch the route",
|
||||
"invalid_coordinates_symbol": "Invalid coordinates, cannot add symbol.",
|
||||
"error_symbol": "Error when adding symbol.",
|
||||
"position_not_init": "User position is not yet initialized. Try again.",
|
||||
"invalid_coordinates": "Invalid coordinates.",
|
||||
"walking": "Walking",
|
||||
"cycling": "Cycling",
|
||||
"driving": "Driving",
|
||||
"get_direction": "Get Directions and Markers",
|
||||
"missing_token": "Missing access token",
|
||||
"geocoding_error": "Error when geocoding",
|
||||
"no_found_place": "No found place",
|
||||
"upload_error": "Error when image uploading",
|
||||
"event_added": "Event added",
|
||||
"unknown_error": "Unknown error",
|
||||
"app_error": "Application error",
|
||||
"at": "at",
|
||||
"to_date": "to",
|
||||
"item_link": "Link : ",
|
||||
"item_ticket": "Ticket : ",
|
||||
"link": "Link",
|
||||
"edit_link": "Edit link name",
|
||||
"ticket": "Ticket",
|
||||
"edit_ticket": "Edit ticket link",
|
||||
"toogle_interest": "Error toggle interest",
|
||||
"error_update": "Error when updating",
|
||||
"count_interested": "Interested people number"
|
||||
}
|
146
covas_mobile/lib/l10n/app_fr.arb
Normal file
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"@@locale": "fr",
|
||||
"menu_list": "Liste d'évènement",
|
||||
"language": "Langue",
|
||||
"home": "Accueil",
|
||||
"settings": "Paramètres",
|
||||
"update_profile": "Modifier profil",
|
||||
"about": "À propos",
|
||||
"log_out": "Se déconnecter",
|
||||
"french": "Français",
|
||||
"english": "Anglais",
|
||||
"german": "Allemand",
|
||||
"select_language": "Selectionne la langue",
|
||||
"search_item": "Recherche par item",
|
||||
"search_tag": "Recherche par tags",
|
||||
"search_geographical": "Recherche par zone géographique",
|
||||
"show_date_field": "Afficher champ date",
|
||||
"hide_date_field": "Cacher Date Fields",
|
||||
"no_data": "Aucune donnée disponible",
|
||||
"search": "Recherche",
|
||||
"no_events": "Pas d'évènements dans cette localisation",
|
||||
"start_date": "Date de début",
|
||||
"end_date": "Date de fin",
|
||||
"failed_suggestions": "Echec de chargement des suggestions",
|
||||
"error":"Erreur",
|
||||
"password_different":"Tu dois écrire un mot de passe different",
|
||||
"create": "Création",
|
||||
"user_create": "Votre utilisateur a été créé",
|
||||
"user_update": "Votre utilisateur a été modifié",
|
||||
"request_error": "Requête mal construite",
|
||||
"incorrect_password": "Mot de passe incorrect",
|
||||
"unknown_user": "Utilisateur inconnu",
|
||||
"disabled_user": "Utilisateur désactivé",
|
||||
"invalid_token": "Token invalide",
|
||||
"internal_error_server": "Erreur interne de serveur",
|
||||
"unknown_error_auth": "Problème d'authentification inconnu",
|
||||
"required_input": "Champ requis",
|
||||
"create_profile": "Creation profil",
|
||||
"edit_pseudo": "Modifier le pseudo",
|
||||
"password":"Mot de passe",
|
||||
"enter_password": "Entrez le password",
|
||||
"password_confirmed": "Confirmez le mot de passe",
|
||||
"last_name": "Nom",
|
||||
"first_name": "Prénom",
|
||||
"email": "Email",
|
||||
"edit_last_name": "Modifier le nom",
|
||||
"edit_first_name": "Modifier le prénom",
|
||||
"edit_email": "Modifier l'email",
|
||||
"birth_date": "Date de naissance",
|
||||
"edit_birth": "Modifier la date de naissance",
|
||||
"create_profile_button": "Créer le profil",
|
||||
"take_picture": "Take a picture",
|
||||
"error_ia": "L'IA de Google n'a pas su analyser l'image. Recommencer avec une autre",
|
||||
"no_data_geo": "Aucune donnée géographique",
|
||||
"response_status_update": "Code du statut de réponse de la modification",
|
||||
"error_token": "Erreur de token",
|
||||
"error_format": "Erreur de format de donnée fourni par l'IA",
|
||||
"display_picture": "Display the Picture",
|
||||
"analyze_image": "Analyse de l'image en cours",
|
||||
"loading_progress": "Chargement en cours",
|
||||
"error_event": "Erreur de l'évènement",
|
||||
"no_future_event": "Évènement non futur",
|
||||
"error_user": "Erreur de l'utilisateur",
|
||||
"empty_input": "Champ vide",
|
||||
"info_event": "Event info",
|
||||
"event_already": "Event already exists",
|
||||
"picture_error": "Erreur image",
|
||||
"no_picture_published": "Image non publiée",
|
||||
"event_update": "Évènement modifié",
|
||||
"location": "Lieu",
|
||||
"add_event": "Ajouter ou modifier un évènement",
|
||||
"edit_image": "Changer la photo",
|
||||
"name": "Nom",
|
||||
"edit_event_name": "Changer le nom de l'évènement",
|
||||
"start_time": "Heure de début",
|
||||
"end_time": "Heure de fin",
|
||||
"select_date": "Cliquer pour selectionner une date",
|
||||
"select_time": "Cliquer pour selectionner une heure",
|
||||
"tag": "Tags",
|
||||
"already_tag": "Tu as déjà entré ce tag",
|
||||
"enter_tag": "Entrer un tag",
|
||||
"organizer": "Organisateur",
|
||||
"already_organiser": "Tu as déjà rentré cet organisateur",
|
||||
"enter_organizer": "Entrer un organisateur",
|
||||
"description": "Description",
|
||||
"describe_event": "Décrire l'évènement",
|
||||
"add": "Ajouter",
|
||||
"update_profile": "Modifier le profil",
|
||||
"different_password_error": "Mot de passe différent",
|
||||
"update": "Mettre à jour",
|
||||
"updated": "Mis à jour",
|
||||
"settings_updated": "Paramètre mis à jour",
|
||||
"define_kilometer": "Definir un kilomètre",
|
||||
"settings": "Paramètres",
|
||||
"email_sent": "Email a été envoyé",
|
||||
"forgot_password": "Mot de passe oublié",
|
||||
"enter_email": "Entrez l'email",
|
||||
"send_email": "Send email",
|
||||
"invalid_cache": "Cache invalide",
|
||||
"item_date": "Date : ",
|
||||
"item_maps": "Carte : ",
|
||||
"item_organizer": "Organisateurs : ",
|
||||
"item_description": "Description : ",
|
||||
"item_tags": "Tags : ",
|
||||
"failed_auth": "Échec de l'authenticaton",
|
||||
"login_page": "Page d'authentification",
|
||||
"pseudo": "Pseudo",
|
||||
"enter_existing_pseudo": "Entrez un pseudo existant",
|
||||
"remembr_me": "Se souvenir de moi",
|
||||
"new_user": "Nouvel utilisateur ? Créer un compte",
|
||||
"sign_in": "Se connecter",
|
||||
"map_token": "Token d'accès de Mapbox n'est pas disponible",
|
||||
"geo_disabled": "Les services de localisation sont désactivés.",
|
||||
"permission_denied":"Les permissions de localisation sont refusées.",
|
||||
"enable_permission": "Les permissions de localisation sont toujours désactivés. Il faut les désactiver",
|
||||
"no_last_position": "Aucune position n'est pas disponible.",
|
||||
"failed_location": "Échec de récupération des données geographique",
|
||||
"failed_fetch": "Échec de récupération des routes",
|
||||
"invalid_coordinates_symbol": "Coordonnées invalides. On ne peut pas ajouter le symbole",
|
||||
"error_symbol": "Erreur lors de l'ajout du symbole",
|
||||
"position_not_init": "Coordonnées non initialisées. Essaye encore.",
|
||||
"invalid_coordinates": "Coordonnées invalides",
|
||||
"walking": "Marche",
|
||||
"cycling": "Vélo",
|
||||
"driving": "Voiture",
|
||||
"get_direction": "Get Directions and Markers",
|
||||
"missing_token": "Token d'accès manquant",
|
||||
"geocoding_error": "Erreur lors du geocodage",
|
||||
"no_found_place": "Lieu introuvable",
|
||||
"upload_error": "Erreur lors de l'upload d'image",
|
||||
"event_added": "Évènement ajouté",
|
||||
"unknown_error": "Erreur inconnue",
|
||||
"app_error": "Erreur d'application",
|
||||
"at": "à",
|
||||
"to_date": "jusqu'à",
|
||||
"item_link": "Lien : ",
|
||||
"item_ticket": "Billet : ",
|
||||
"link": "Lien",
|
||||
"edit_link": "Editer le lien",
|
||||
"ticket": "Billet",
|
||||
"edit_ticket": "Editer le lien du billet",
|
||||
"toogle_interest": "Erreur de bouton de changement",
|
||||
"error_update": "Erreur lors de la mise à jour",
|
||||
"count_interested": "Nombre de personne interessé"
|
||||
|
||||
}
|
41
covas_mobile/lib/locale_provider.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class LocaleProvider with ChangeNotifier {
|
||||
static const _localeKey = 'locale_code';
|
||||
|
||||
Locale _locale = const Locale('en');
|
||||
|
||||
Locale get locale => _locale;
|
||||
|
||||
LocaleProvider() {
|
||||
_loadLocale();
|
||||
}
|
||||
|
||||
Future<void> _loadLocale() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final code = prefs.getString(_localeKey);
|
||||
if (code != null && L10n.all.contains(Locale(code))) {
|
||||
_locale = Locale(code);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void setLocale(Locale locale) async {
|
||||
if (!L10n.all.contains(locale)) return;
|
||||
|
||||
_locale = locale;
|
||||
notifyListeners();
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_localeKey, locale.languageCode);
|
||||
}
|
||||
}
|
||||
|
||||
class L10n {
|
||||
static final all = [
|
||||
const Locale('en'),
|
||||
const Locale('fr'),
|
||||
const Locale('de')
|
||||
];
|
||||
}
|
@@ -1,241 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'pages/LoginDemo.dart';
|
||||
import 'locale_provider.dart'; // <-- à adapter selon ton arborescence
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'classes/notification_service.dart';
|
||||
|
||||
//import 'MyHomePage.dart';
|
||||
import 'pages/ListItemMenu.dart';
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await MobileAds.instance.initialize();
|
||||
await NotificationService.initialize();
|
||||
|
||||
import 'classes/alert.dart';
|
||||
|
||||
import 'variable/globals.dart' as globals;
|
||||
|
||||
void main() {
|
||||
runApp(MyApp());
|
||||
runApp(
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => LocaleProvider(),
|
||||
child: MyApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@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: LoginDemo(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoginDemo extends StatefulWidget {
|
||||
@override
|
||||
_LoginDemoState createState() => _LoginDemoState();
|
||||
}
|
||||
|
||||
class _LoginDemoState extends State<LoginDemo> with ShowErrorDialog {
|
||||
TextEditingController inputPseudo = TextEditingController();
|
||||
TextEditingController inputPassword = TextEditingController();
|
||||
Future<void> _login(BuildContext context) async {
|
||||
var url = Uri.parse("${globals.api}/token");
|
||||
var pseudo = inputPseudo.text;
|
||||
var password = inputPassword.text;
|
||||
print("get login");
|
||||
print(pseudo.isNotEmpty);
|
||||
print(password.isNotEmpty);
|
||||
if ((pseudo.isNotEmpty) && (password.isNotEmpty)) {
|
||||
print(url);
|
||||
try {
|
||||
//String credentials = "${pseudo}:${password}";
|
||||
//Codec<String, String> stringToBase64 = utf8.fuse(base64);
|
||||
//String encoded = stringToBase64.encode(credentials);
|
||||
var response = await http.post(url,
|
||||
// headers: {
|
||||
// HttpHeaders.authorizationHeader: 'Basic $encoded',
|
||||
//}
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: {
|
||||
"username": "${pseudo}",
|
||||
"password": "${password}"
|
||||
});
|
||||
print(response.statusCode);
|
||||
if ((response.statusCode == 200) || (response.statusCode == 201)) {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
var cookies = response.headers["set-cookie"].toString().split(";");
|
||||
for (var cookie in cookies) {
|
||||
var cookiesMany = cookie.split(",");
|
||||
for (var cookie2 in cookiesMany) {
|
||||
switch (cookie2.split("=")[0]) {
|
||||
case "access_token":
|
||||
{
|
||||
prefs.setString("access_token", cookie2.split("=")[1]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Navigator.push(
|
||||
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
|
||||
} else {
|
||||
var text = "";
|
||||
switch (response.statusCode) {
|
||||
case 400:
|
||||
{
|
||||
text = "Requête mal construite";
|
||||
}
|
||||
break;
|
||||
case 406:
|
||||
{
|
||||
text = "Mot de passe incorrect";
|
||||
}
|
||||
break;
|
||||
case 404:
|
||||
{
|
||||
text = "Utilisateur inconnu";
|
||||
}
|
||||
break;
|
||||
case 403:
|
||||
{
|
||||
text = "Utilisateur desactive";
|
||||
}
|
||||
break;
|
||||
case 410:
|
||||
{
|
||||
text = "Token invalide";
|
||||
}
|
||||
break;
|
||||
case 500:
|
||||
{
|
||||
text = "Probleme interne du serveur";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
text = "Probleme d'authentification inconnu";
|
||||
}
|
||||
break;
|
||||
}
|
||||
showErrorDialog(context, text);
|
||||
}
|
||||
} catch (e) {
|
||||
showErrorDialog(context, "${e}");
|
||||
}
|
||||
} else {
|
||||
showErrorDialog(context, "Champ vide");
|
||||
}
|
||||
}
|
||||
|
||||
void start() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var access_token = prefs.getString("access_token") ?? "";
|
||||
print("Get access token");
|
||||
|
||||
if (access_token.isNotEmpty) {
|
||||
print("Appel HTTP");
|
||||
var urlToken = Uri.parse("${globals.api}/token");
|
||||
|
||||
var responseToken = await http.get(urlToken,
|
||||
headers: {HttpHeaders.cookieHeader: 'access_token: ${access_token}'});
|
||||
print(responseToken.statusCode);
|
||||
if (responseToken.statusCode == 200) {
|
||||
print("route to item list");
|
||||
Navigator.push(
|
||||
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
|
||||
} else {
|
||||
prefs.remove("access_token");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
start();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
title: Text("Login Page"),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 60.0),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 200,
|
||||
height: 150,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red,
|
||||
borderRadius: BorderRadius.circular(50.0)),
|
||||
child: Image.asset('./images/flutter.png')),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
//padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0),
|
||||
padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextField(
|
||||
controller: inputPseudo,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Pseudo',
|
||||
hintText: 'Enter pseudo existent'),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15.0, right: 15.0, top: 15, bottom: 0),
|
||||
//padding: EdgeInsets.symmetric(horizontal: 15),
|
||||
child: TextField(
|
||||
controller: inputPassword,
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'Password',
|
||||
hintText: 'Enter secure password'),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
//TODO FORGOT PASSWORD SCREEN GOES HERE
|
||||
},
|
||||
child: Text(
|
||||
'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(
|
||||
'Login',
|
||||
style: TextStyle(color: Colors.white, fontSize: 25),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 130,
|
||||
),
|
||||
Text('New User? Create Account')
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
335
covas_mobile/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:flutter_gen/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),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
@@ -1,11 +1,16 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
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:flutter_gen/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()`
|
||||
@@ -45,12 +50,14 @@ class Camera extends StatefulWidget {
|
||||
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.
|
||||
@@ -63,8 +70,6 @@ class CameraState extends State<Camera> {
|
||||
_initializeControllerFuture = _controller.initialize();
|
||||
}
|
||||
|
||||
Future<void> getCamera() async {}
|
||||
|
||||
Future<void> pickImage() async {
|
||||
final imagePicker = ImagePicker();
|
||||
final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);
|
||||
@@ -91,10 +96,13 @@ class CameraState extends State<Camera> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Take a picture')),
|
||||
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) {
|
||||
|
166
covas_mobile/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:flutter_gen/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),
|
||||
)
|
||||
],
|
||||
)));
|
||||
}
|
||||
}
|
@@ -12,8 +12,19 @@ import 'UpdateEventImage.dart';
|
||||
import 'dart:convert';
|
||||
|
||||
import '../variable/globals.dart' as globals;
|
||||
import '../classes/MyDrawer.dart';
|
||||
|
||||
void main() {
|
||||
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:flutter_gen/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());
|
||||
}
|
||||
|
||||
@@ -54,10 +65,22 @@ class DisplayPictureScreen extends StatefulWidget {
|
||||
|
||||
// A widget that displays the picture taken by the user.
|
||||
class DisplayPictureScreenState extends State<DisplayPictureScreen>
|
||||
with ShowDescImageAdd, ShowErrorDialog, TickerProviderStateMixin {
|
||||
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].
|
||||
@@ -67,7 +90,6 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
|
||||
setState(() {});
|
||||
});
|
||||
controller.repeat(reverse: false);
|
||||
super.initState();
|
||||
|
||||
_getEventInfosFromImage();
|
||||
}
|
||||
@@ -80,42 +102,112 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
|
||||
|
||||
Future<void> displayError(String e) async {
|
||||
print("problem gemini : ${e}");
|
||||
showErrorDialog(context,
|
||||
"L'IA de Google n'a pas su analyser l'image. Recommecer avec une autre");
|
||||
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);
|
||||
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") ?? "";
|
||||
|
||||
Map<String, dynamic> jsonData = jsonDecode(json);
|
||||
var name = jsonData["name"];
|
||||
var place = jsonData["place"];
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
|
||||
if (accessToken.isNotEmpty) {
|
||||
var urlGet = Uri.parse("${globals.api}/events?name=${name}");
|
||||
|
||||
var responseGet = await http.get(urlGet,
|
||||
headers: {HttpHeaders.cookieHeader: 'access_token=${accessToken}'});
|
||||
if (responseGet.statusCode == 200) {
|
||||
var events = jsonDecode(utf8.decode(responseGet.bodyBytes));
|
||||
print("reponse http : ${events.length}");
|
||||
if (events.length == 0) {
|
||||
Navigator.push(
|
||||
if (accessToken.isNotEmpty) {
|
||||
final location = await _fetchGeolocation(place);
|
||||
if (location == null) {
|
||||
_showErrorDialog(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => UpdateeventImage(
|
||||
events: jsonData, imagePath: imagePath)));
|
||||
} else {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => ItemMenu(title: events[0]["id"])));
|
||||
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");
|
||||
}
|
||||
} else {
|
||||
showErrorDialog(context, "Erreur de token");
|
||||
} catch (e) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.error_format ??
|
||||
"Data format error given by AI");
|
||||
}
|
||||
|
||||
//showDescImageAddDialog(context, message);
|
||||
@@ -132,7 +224,7 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
|
||||
gemini
|
||||
.textAndImage(
|
||||
text:
|
||||
"Peux-tu donner le nom, la date avec l'année actuelle ou d'une année future proche et le lieu de l'évènement sous format JSON avec les valeurs suivantes : name, address, city, zip_code, country, description, start_date et end_date sous le format en YYYY-MM-DD HH:mm:ssZ, et sans la présence du mot json dans la chaîne de caractère",
|
||||
"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",
|
||||
images: [file.readAsBytesSync()],
|
||||
modelName: "models/gemini-1.5-pro-latest")
|
||||
.then((value) => searchEvents(
|
||||
@@ -143,21 +235,33 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Display the Picture')),
|
||||
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(
|
||||
'Analyse de l\'image en cours',
|
||||
AppLocalizations.of(context)?.analyze_image ??
|
||||
'Image analyze in progress',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
CircularProgressIndicator(
|
||||
value: controller.value,
|
||||
semanticsLabel: 'Loading progress',
|
||||
semanticsLabel:
|
||||
AppLocalizations.of(context)?.loading_progress ??
|
||||
'Loading progress',
|
||||
),
|
||||
])));
|
||||
}
|
||||
|
921
covas_mobile/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:flutter_gen/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/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:flutter_gen/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/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:flutter_gen/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/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:flutter_gen/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),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ 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';
|
||||
@@ -13,10 +14,25 @@ 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';
|
||||
|
||||
void main() {
|
||||
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:flutter_gen/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()));
|
||||
}
|
||||
|
||||
@@ -63,125 +79,110 @@ class ItemMenu extends StatefulWidget {
|
||||
State<ItemMenu> createState() => _ItemMenuState();
|
||||
}
|
||||
|
||||
class _ItemMenuState extends State<ItemMenu> with ShowErrorDialog {
|
||||
class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
|
||||
BannerAd? _bannerAd;
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
String listUser = "";
|
||||
String eventName = "";
|
||||
String eventStartDate = "";
|
||||
String eventDescription = "";
|
||||
String organizers = "";
|
||||
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 {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
String formerName = "";
|
||||
String formerDate = "";
|
||||
String formerOrga = "";
|
||||
String formerMap = "";
|
||||
String formerImage = "";
|
||||
String formerDesc = "";
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final 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}'});
|
||||
stderr.writeln('Response Get status: ${responseGet.statusCode}');
|
||||
if (responseGet.statusCode == 200) {
|
||||
stderr.writeln('Username : ${responseGet.body}');
|
||||
var events = jsonDecode(utf8.decode(responseGet.bodyBytes));
|
||||
formerName = events["name"];
|
||||
formerMap =
|
||||
"${events["place"]} - ${events["zip_code"]} ${events["city"]} - ${events["country"]}";
|
||||
formerDesc = events["description"];
|
||||
final startDate = DateTime.parse(events["start_date"]);
|
||||
final date = DateFormat.yMd().format(startDate);
|
||||
final time = DateFormat.Hm().format(startDate);
|
||||
|
||||
final endDate = DateTime.parse(events["end_date"]);
|
||||
|
||||
final dateE = DateFormat.yMd().format(endDate);
|
||||
final timeE = DateFormat.Hm().format(endDate);
|
||||
if (events["imgUrl"] != null) {
|
||||
formerImage = events["imgUrl"];
|
||||
}
|
||||
|
||||
formerDate = "${date} ${time} à ${dateE} ${timeE}";
|
||||
if (events["organizers"].length > 1) {
|
||||
formerOrga = "${events['organizers'][0]}";
|
||||
for (var i = 1; i < events["organizers"].length; i++) {
|
||||
formerOrga = "${formerOrga}, ${events['organizers'][i]}";
|
||||
}
|
||||
} else {
|
||||
formerOrga = "${events['organizers'][0]}";
|
||||
}
|
||||
} else {
|
||||
var text = "";
|
||||
switch (responseGet.statusCode) {
|
||||
case 400:
|
||||
{
|
||||
text = "Requête mal construite";
|
||||
}
|
||||
break;
|
||||
case 406:
|
||||
{
|
||||
text = "Mot de passe incorrect";
|
||||
}
|
||||
break;
|
||||
case 404:
|
||||
{
|
||||
text = "Utilisateur inconnu";
|
||||
}
|
||||
break;
|
||||
case 403:
|
||||
{
|
||||
text = "Vous n'avez pas l'autorisation de faire cette action";
|
||||
}
|
||||
break;
|
||||
case 410:
|
||||
{
|
||||
text = "Token invalide";
|
||||
}
|
||||
break;
|
||||
case 500:
|
||||
{
|
||||
text = "Probleme interne du serveur";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
text = "Probleme d'authentification inconnu";
|
||||
}
|
||||
break;
|
||||
}
|
||||
showErrorDialog(context, text);
|
||||
}
|
||||
} else {
|
||||
showErrorDialog(context, "Cache invalide");
|
||||
if (accessToken.isEmpty) {
|
||||
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.invalid_cache ?? "Invalid cache");
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
// This call to setState tells the Flutter framework that something has
|
||||
// changed in this State, which causes it to rerun the build method below
|
||||
// so that the display can reflect the updated values. If we changed
|
||||
// _counter without calling setState(), then the build method would not be
|
||||
// called again, and so nothing would appear to happen.
|
||||
eventName = formerName;
|
||||
eventStartDate = formerDate;
|
||||
organizers = formerOrga;
|
||||
place = formerMap;
|
||||
imgUrl = formerImage;
|
||||
eventDescription = formerDesc;
|
||||
});
|
||||
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>();
|
||||
@@ -195,32 +196,57 @@ class _ItemMenuState extends State<ItemMenu> with ShowErrorDialog {
|
||||
// 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}"),
|
||||
backgroundColor: Colors.blue,
|
||||
foregroundColor: Colors.white,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
|
||||
},
|
||||
)),
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
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: Center(
|
||||
child: Container(height: 250, child: Image.network(imgUrl)),
|
||||
),
|
||||
),
|
||||
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(
|
||||
"Date : ",
|
||||
AppLocalizations.of(context)?.item_date ?? "Date : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
|
||||
)
|
||||
]),
|
||||
@@ -234,36 +260,113 @@ class _ItemMenuState extends State<ItemMenu> with ShowErrorDialog {
|
||||
Row(children: [
|
||||
Icon(Icons.explore),
|
||||
Text(
|
||||
"Carte : ",
|
||||
AppLocalizations.of(context)?.item_maps ?? "Maps : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
|
||||
)
|
||||
]),
|
||||
Row(children: [
|
||||
Flexible(
|
||||
child: Text("${place}",
|
||||
style: TextStyle(fontSize: 15.0),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis))
|
||||
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(
|
||||
"Organisateurs : ",
|
||||
AppLocalizations.of(context)?.item_organizer ?? "Organizers : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
|
||||
)
|
||||
]),
|
||||
Row(children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
"${organizers}",
|
||||
style: TextStyle(fontSize: 15.0),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
))
|
||||
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("Description : ",
|
||||
Text(
|
||||
AppLocalizations.of(context)?.item_description ??
|
||||
"Description : ",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold))
|
||||
]),
|
||||
Row(children: [
|
||||
@@ -272,8 +375,81 @@ class _ItemMenuState extends State<ItemMenu> with ShowErrorDialog {
|
||||
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/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:flutter_gen/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/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:flutter_gen/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();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
169
covas_mobile/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:flutter_gen/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()));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
452
covas_mobile/lib/pages/MapboxPages.dart
Normal file
@@ -0,0 +1,452 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart'; // For environment variables
|
||||
import 'package:flutter/services.dart'; // For loading assets
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mapbox_gl/mapbox_gl.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:geolocator/geolocator.dart'; // For getting the user's location
|
||||
|
||||
import '../classes/alert.dart'; // Assuming this contains your error dialog code.
|
||||
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_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../locale_provider.dart'; //
|
||||
|
||||
void main() async {
|
||||
await dotenv.load(fileName: ".env"); // Load .env file
|
||||
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();
|
||||
|
||||
late MapboxMapController mapController;
|
||||
late String mapboxAccessToken;
|
||||
List<LatLng> routeCoordinates = [];
|
||||
String selectedMode = 'driving';
|
||||
double longitude = 0.0;
|
||||
double latitude = 0.0;
|
||||
bool isLoading = true;
|
||||
late LatLng userPosition;
|
||||
bool isUserPositionInitialized = false;
|
||||
Line? currentRouteLine;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_authService.checkTokenStatus(context);
|
||||
|
||||
_getUserLocation();
|
||||
}
|
||||
|
||||
void _initToken() {
|
||||
mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '';
|
||||
if (mapboxAccessToken.isEmpty) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.map_token ??
|
||||
"Map Access Token is not available.");
|
||||
}
|
||||
}
|
||||
|
||||
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, AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.invalid_cache ?? "Invalid cache.");
|
||||
}
|
||||
}
|
||||
|
||||
void _handleErrorResponse(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"
|
||||
};
|
||||
|
||||
final errorMessage = messages[statusCode] ??
|
||||
AppLocalizations.of(context)?.unknown_error_auth ??
|
||||
"Unknown error auth";
|
||||
showAlertDialog(
|
||||
context, AppLocalizations.of(context)?.error ?? "Error", errorMessage);
|
||||
}
|
||||
|
||||
Future<void> _getUserLocation() async {
|
||||
try {
|
||||
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.geo_disabled ??
|
||||
"Location services are disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
LocationPermission permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.permission_denied ??
|
||||
"Location permissions are denied.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.enable_permission ??
|
||||
"Location permissions are permanently denied. Enable them in settings.");
|
||||
return;
|
||||
}
|
||||
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
|
||||
position = await Geolocator.getLastKnownPosition();
|
||||
if (position == null) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.no_last_position ??
|
||||
"No last known position available..");
|
||||
}
|
||||
} catch (e) {
|
||||
// Handle other errors
|
||||
position = await Geolocator.getLastKnownPosition();
|
||||
if (position == null) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.no_last_position ??
|
||||
"No last known position available");
|
||||
}
|
||||
}
|
||||
if (position != null) {
|
||||
setState(() {
|
||||
userPosition = LatLng(position!.latitude, position!.longitude);
|
||||
isUserPositionInitialized = true;
|
||||
});
|
||||
}
|
||||
_initToken();
|
||||
_getEventInfo();
|
||||
} catch (e) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.failed_location ??
|
||||
"Failed to get user location");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchRoute(
|
||||
LatLng origin, LatLng destination, String mode) async {
|
||||
final url = Uri.parse(
|
||||
'https://api.mapbox.com/directions/v5/mapbox/$mode/${origin.longitude},${origin.latitude};${destination.longitude},${destination.latitude}?geometries=geojson&access_token=$mapboxAccessToken',
|
||||
);
|
||||
|
||||
final response = await http.get(url);
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
final geometry = data['routes'][0]['geometry']['coordinates'];
|
||||
setState(() {
|
||||
routeCoordinates = geometry.map<LatLng>((coord) {
|
||||
return LatLng(coord[1], coord[0]);
|
||||
}).toList();
|
||||
});
|
||||
} else {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.failed_fetch ??
|
||||
"Failed to fetch the route");
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the map is created
|
||||
void _onStyleLoaded() async {
|
||||
// Log the map controller and coordinates
|
||||
|
||||
// Check if the mapController is really initialized
|
||||
if (mapController != null) {
|
||||
try {
|
||||
// Ensure the coordinates are valid
|
||||
if (latitude != 0.0 && longitude != 0.0) {
|
||||
// Load marker image as Uint8List
|
||||
final userMarkerImage = await _loadMarkerImage('images/marker.png');
|
||||
|
||||
// Register the image with Mapbox
|
||||
await mapController.addImage('event-marker', userMarkerImage);
|
||||
|
||||
final symbolOptions = SymbolOptions(
|
||||
geometry: LatLng(latitude, longitude),
|
||||
iconImage: "event-marker", // Use the registered custom marker
|
||||
iconSize: 0.4, // Optional: Adjust size
|
||||
);
|
||||
|
||||
// Debugging symbol options
|
||||
|
||||
// Add symbol to map
|
||||
mapController!.addSymbol(symbolOptions);
|
||||
} else {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.invalid_coordinates_symbol ??
|
||||
"Error: Invalid coordinates, cannot add symbol.");
|
||||
}
|
||||
} catch (e) {
|
||||
// Handle any exception that occurs when adding the symbol
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.error_symbol ??
|
||||
"Error when adding symbol.");
|
||||
}
|
||||
} else {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.error_symbol ??
|
||||
"Error when adding symbol.");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _drawRouteAndMarkers() async {
|
||||
// Remove previous route line if it exists
|
||||
if (currentRouteLine != null) {
|
||||
await mapController.removeLine(currentRouteLine!);
|
||||
currentRouteLine = null;
|
||||
}
|
||||
if (!isUserPositionInitialized) {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.position_not_init ??
|
||||
"User position is not yet initialized. Try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mapController != null &&
|
||||
userPosition != null &&
|
||||
latitude != 0.0 &&
|
||||
longitude != 0.0) {
|
||||
final destination = LatLng(latitude, longitude);
|
||||
|
||||
// Register the custom images
|
||||
// Add event marker
|
||||
|
||||
final eventMarkerImage = await _loadMarkerImage('images/marker-red.png');
|
||||
|
||||
// Register the image with Mapbox
|
||||
await mapController.addImage('user-marker', eventMarkerImage);
|
||||
await mapController.addSymbol(SymbolOptions(
|
||||
geometry: userPosition,
|
||||
iconImage: 'user-marker', // Custom icon for event
|
||||
iconSize: 0.2,
|
||||
));
|
||||
|
||||
// Fetch and draw route
|
||||
await _fetchRoute(userPosition, destination, selectedMode);
|
||||
|
||||
if (routeCoordinates.isNotEmpty) {
|
||||
currentRouteLine = await mapController.addLine(
|
||||
LineOptions(
|
||||
geometry: routeCoordinates,
|
||||
lineColor: '#3b9ddd',
|
||||
lineWidth: 5.0,
|
||||
lineOpacity: 0.8,
|
||||
),
|
||||
);
|
||||
|
||||
_zoomToFitRoute(routeCoordinates);
|
||||
}
|
||||
} else {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.invalid_coordinates ??
|
||||
"Invalid coordinates or user position.");
|
||||
}
|
||||
}
|
||||
|
||||
void _zoomToFitRoute(List<LatLng> coordinates) {
|
||||
// Calculate the bounding box
|
||||
double minLat = coordinates.first.latitude;
|
||||
double maxLat = coordinates.first.latitude;
|
||||
double minLng = coordinates.first.longitude;
|
||||
double maxLng = coordinates.first.longitude;
|
||||
|
||||
for (LatLng coord in coordinates) {
|
||||
if (coord.latitude < minLat) minLat = coord.latitude;
|
||||
if (coord.latitude > maxLat) maxLat = coord.latitude;
|
||||
if (coord.longitude < minLng) minLng = coord.longitude;
|
||||
if (coord.longitude > maxLng) maxLng = coord.longitude;
|
||||
}
|
||||
|
||||
// Define the bounds
|
||||
LatLng southwest = LatLng(minLat, minLng);
|
||||
LatLng northeast = LatLng(maxLat, maxLng);
|
||||
|
||||
mapController.moveCamera(
|
||||
CameraUpdate.newLatLngBounds(
|
||||
LatLngBounds(southwest: southwest, northeast: northeast),
|
||||
left: 50, // Padding on the left
|
||||
top: 50, // Padding on the top
|
||||
right: 50, // Padding on the right
|
||||
bottom: 50, // Padding on the bottom
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Load image from assets
|
||||
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),
|
||||
actions: [
|
||||
DropdownButton<String>(
|
||||
value: selectedMode,
|
||||
items: [
|
||||
DropdownMenuItem(
|
||||
value: 'walking',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.directions_walk, color: Colors.blue),
|
||||
SizedBox(width: 8),
|
||||
Text(AppLocalizations.of(context)?.walking ?? 'Walking'),
|
||||
],
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 'cycling',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.directions_bike, color: Colors.green),
|
||||
SizedBox(width: 8),
|
||||
Text(AppLocalizations.of(context)?.cycling ?? 'Cycling'),
|
||||
],
|
||||
),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: 'driving',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.directions_car, color: Colors.red),
|
||||
SizedBox(width: 8),
|
||||
Text(AppLocalizations.of(context)?.driving ?? 'Driving'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
onChanged: (mode) {
|
||||
setState(() {
|
||||
selectedMode = mode!;
|
||||
});
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
drawer: MyDrawer(),
|
||||
body: Stack(
|
||||
children: [
|
||||
isLoading
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: MapboxMap(
|
||||
accessToken: mapboxAccessToken,
|
||||
onMapCreated: (controller) {
|
||||
mapController = controller;
|
||||
},
|
||||
onStyleLoadedCallback: _onStyleLoaded,
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: LatLng(latitude, longitude),
|
||||
zoom: 14.0,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 20,
|
||||
right: 20,
|
||||
child: FloatingActionButton(
|
||||
onPressed: _drawRouteAndMarkers,
|
||||
child: Icon(Icons.directions),
|
||||
tooltip: AppLocalizations.of(context)?.get_direction ??
|
||||
'Get Directions and Markers',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@@ -7,9 +7,13 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
|
||||
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_linux
|
||||
url_launcher_linux
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
@@ -6,11 +6,19 @@ import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import file_selector_macos
|
||||
import flutter_local_notifications
|
||||
import geolocator_apple
|
||||
import path_provider_foundation
|
||||
import shared_preferences_foundation
|
||||
import url_launcher_macos
|
||||
import webview_flutter_wkwebview
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
|
||||
}
|
||||
|
@@ -1,6 +1,30 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
asn1lib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: asn1lib
|
||||
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.8"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -29,18 +53,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_android_camerax
|
||||
sha256: "8bd9cab67551642eb33ceb33ece7acc0890014fc90ddfae637c7e2b683657e65"
|
||||
sha256: e3627fdc2132d89212b8a8676679f5b07008c7e3d8ae00cea775c3397f9e742b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7+2"
|
||||
version: "0.6.10"
|
||||
camera_avfoundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: camera_avfoundation
|
||||
sha256: "7c28969a975a7eb2349bc2cb2dfe3ad218a33dba9968ecfb181ce08c87486655"
|
||||
sha256: "2e4c568f70e406ccb87376bc06b53d2f5bebaab71e2fbcc1a950e31449381bcf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.17+3"
|
||||
version: "0.9.17+5"
|
||||
camera_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -81,6 +105,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -89,6 +121,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.4+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -105,22 +145,46 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.0"
|
||||
dbus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dbus
|
||||
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.11"
|
||||
dio:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio
|
||||
sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714
|
||||
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.0+1"
|
||||
version: "5.7.0"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio_web_adapter
|
||||
sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac"
|
||||
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
version: "2.0.0"
|
||||
encrypt:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: encrypt
|
||||
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.3"
|
||||
encrypt_shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: encrypt_shared_preferences
|
||||
sha256: "35cd218e5e9d12fe4a63a545f46f2144d861909e4c4f2c4606fc75ffb53d9a46"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.8"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -133,34 +197,34 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
||||
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.3"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
version: "7.0.1"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_linux
|
||||
sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492"
|
||||
sha256: b2b91daf8a68ecfa4a01b778a6f52edef9b14ecd506e771488ea0f2e0784198b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.2+1"
|
||||
version: "0.9.3+1"
|
||||
file_selector_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_macos
|
||||
sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385
|
||||
sha256: "271ab9986df0c135d45c3cdb6bd0faa5db6f4976d3e4b437cf7d0f258d941bfc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.4"
|
||||
version: "0.9.4+2"
|
||||
file_selector_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -173,10 +237,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_selector_windows
|
||||
sha256: "2ad726953f6e8affbc4df8dc78b77c3b4a060967a291e528ef72ae846c60fb69"
|
||||
sha256: "8f5d2f6590d51ecd9179ba39c64f722edc15226cc93dcc8698466ad36a4a85a4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.3+2"
|
||||
version: "0.9.3+3"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@@ -186,10 +258,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_dotenv
|
||||
sha256: "9357883bdd153ab78cbf9ffa07656e336b8bbb2b5a3ca596b0b27e119f7c7d77"
|
||||
sha256: b7c7be5cd9f6ef7a78429cabd2774d3c4af50e79cb2b7593e3d5d763ef95c61b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.0"
|
||||
version: "5.2.1"
|
||||
flutter_gemini:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -206,14 +278,43 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
flutter_local_notifications:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_local_notifications
|
||||
sha256: "674173fd3c9eda9d4c8528da2ce0ea69f161577495a9cc835a2a4ecd7eadeb35"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "17.2.4"
|
||||
flutter_local_notifications_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_linux
|
||||
sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.1"
|
||||
flutter_local_notifications_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_platform_interface
|
||||
sha256: "85f8d07fe708c1bdcf45037f2c0109753b26ae077e9d9e899d55971711a4ea66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.0"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de"
|
||||
sha256: "9b78450b89f059e96c9ebb355fa6b3df1d6b330436e0b885fb49594c41721398"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.21"
|
||||
version: "2.0.23"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -232,6 +333,62 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.4"
|
||||
geolocator:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: geolocator
|
||||
sha256: "0ec58b731776bc43097fcf751f79681b6a8f6d3bc737c94779fe9f1ad73c1a81"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "13.0.1"
|
||||
geolocator_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: geolocator_android
|
||||
sha256: "7aefc530db47d90d0580b552df3242440a10fe60814496a979aa67aa98b1fd47"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.6.1"
|
||||
geolocator_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: geolocator_apple
|
||||
sha256: bc2aca02423ad429cb0556121f56e60360a2b7d694c8570301d06ea0c00732fd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.7"
|
||||
geolocator_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: geolocator_platform_interface
|
||||
sha256: "386ce3d9cce47838355000070b1d0b13efb5bc430f8ecda7e9238c8409ace012"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.4"
|
||||
geolocator_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: geolocator_web
|
||||
sha256: "2ed69328e05cd94e7eb48bb0535f5fc0c0c44d1c4fa1e9737267484d05c29b5e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.1"
|
||||
geolocator_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: geolocator_windows
|
||||
sha256: "53da08937d07c24b0d9952eb57a3b474e29aae2abf9dd717f7e1230995f13f0e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.3"
|
||||
google_mobile_ads:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_mobile_ads
|
||||
sha256: "0d4a3744b5e8ed1b8be6a1b452d309f811688855a497c6113fc4400f922db603"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.1"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -248,6 +405,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
image_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -260,26 +425,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_android
|
||||
sha256: c0e72ecd170b00a5590bb71238d57dc8ad22ee14c60c6b0d1a4e05cafbc5db4b
|
||||
sha256: "8faba09ba361d4b246dc0a17cb4289b3324c2b9f6db7b3d457ee69106a86bd32"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.12+11"
|
||||
version: "0.8.12+17"
|
||||
image_picker_for_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_for_web
|
||||
sha256: "65d94623e15372c5c51bebbcb820848d7bcb323836e12dfdba60b5d3a8b39e50"
|
||||
sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
version: "3.0.6"
|
||||
image_picker_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image_picker_ios
|
||||
sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447"
|
||||
sha256: "4f0568120c6fcc0aaa04511cb9f9f4d29fc3d0139884b1d06be88dcec7641d6b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.12"
|
||||
version: "0.8.12+1"
|
||||
image_picker_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -320,6 +485,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -360,6 +533,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
mapbox_gl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mapbox_gl
|
||||
sha256: d78907338ff232e3cf6c1d6dba45e6a8814069496fd352e49bb1967d498f09af
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.16.0"
|
||||
mapbox_gl_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mapbox_gl_dart
|
||||
sha256: de6d03718e5eb05c9eb1ddaae7f0383b28acb5afa16405e1deed7ff04dd34f3d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
mapbox_gl_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mapbox_gl_platform_interface
|
||||
sha256: b7c1490b022e650afd20412bdf8ae45a1897118b7ce6049ef6c42df09193d4b2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.16.0"
|
||||
mapbox_gl_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mapbox_gl_web
|
||||
sha256: e77113bf95a4f321ff44938232517e0f2725aae991f0b283af1afaa7e7a58aca
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.16.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -388,10 +593,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
|
||||
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
version: "1.0.6"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
path:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -404,18 +617,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb"
|
||||
sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.9"
|
||||
version: "2.2.12"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -448,14 +661,70 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.3.1"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.13"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.4.5"
|
||||
permission_handler_html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_html
|
||||
sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3+2"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.3"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -464,30 +733,46 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.9.1"
|
||||
provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: c272f9cabca5a81adc9b0894381e9c1def363e980f960fa903c604c471b22f68
|
||||
sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.3.3"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: a7e8467e9181cef109f601e3f65765685786c1a738a83d7fbbde377589c0d974
|
||||
sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.3.3"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "776786cff96324851b656777648f36ac772d88bc4c669acff97b7fce5de3c849"
|
||||
sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.1"
|
||||
version: "2.5.3"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -533,6 +818,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sprintf
|
||||
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -581,14 +874,102 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
textfield_tags:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: textfield_tags
|
||||
sha256: d1f2204114157a1296bb97c20d7f8c8c7fd036212812afb2e19de7bb34acc55b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
timezone:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: timezone
|
||||
sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.4"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
version: "1.4.0"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.1"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.14"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.1"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.1"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -609,18 +990,58 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062
|
||||
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.1.0"
|
||||
webview_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: "889a0a678e7c793c308c68739996227c9661590605e70b1f6cf6b9a6634f7aec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.0"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: "512c26ccc5b8a571fd5d13ec994b7509f142ff6faf85835e243dde3538fdc713"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.3.2"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_platform_interface
|
||||
sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.10.0"
|
||||
webview_flutter_wkwebview:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: d183aa3d0fbc1f4d0715ce06c5a44cad636598c3340ae8d359fbc61b4016fb60
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.18.3"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "1.1.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.5.0"
|
||||
sdks:
|
||||
dart: ">=3.4.0 <4.0.0"
|
||||
flutter: ">=3.22.0"
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
|
@@ -29,10 +29,15 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
flutter_local_notifications: ^17.2.0
|
||||
timezone: ^0.9.4
|
||||
cupertino_icons: ^1.0.2
|
||||
http: ^1.2.1
|
||||
shared_preferences: ^2.2.3
|
||||
@@ -45,6 +50,14 @@ dependencies:
|
||||
flutter_dotenv: ^5.1.0
|
||||
image_picker: ^1.1.2
|
||||
date_format_field: ^0.1.0
|
||||
textfield_tags: ^3.0.1
|
||||
geolocator: ^13.0.1
|
||||
permission_handler: ^11.3.1
|
||||
url_launcher: ^6.3.1
|
||||
mapbox_gl: ^0.16.0
|
||||
google_mobile_ads: ^5.3.1
|
||||
encrypt_shared_preferences: ^0.8.8
|
||||
provider: ^6.1.2 # ou la dernière version
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -62,6 +75,7 @@ dev_dependencies:
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
generate: true
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
@@ -73,6 +87,8 @@ flutter:
|
||||
- images/flutter.png
|
||||
- .env
|
||||
- images/search.png
|
||||
- images/marker.png
|
||||
- images/marker-red.png
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
|
@@ -7,8 +7,17 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
#include <geolocator_windows/geolocator_windows.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FileSelectorWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||
GeolocatorWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("GeolocatorWindows"));
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
@@ -4,6 +4,9 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
file_selector_windows
|
||||
geolocator_windows
|
||||
permission_handler_windows
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
|
45
covas_mobile_new/.gitignore
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.build/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
.swiftpm/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins-dependencies
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
/coverage/
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
45
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
|
44
covas_mobile_new/android/app/build.gradle.kts
Normal file
@@ -0,0 +1,44 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
id("dev.flutter.flutter-gradle-plugin")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.covas_mobile_new"
|
||||
compileSdk = flutter.compileSdkVersion
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
45
covas_mobile_new/android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application
|
||||
android:label="covas_mobile_new"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||
|
||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
@@ -0,0 +1,5 @@
|
||||
package com.example.covas_mobile_new
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity : FlutterActivity()
|
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
After Width: | Height: | Size: 544 B |
After Width: | Height: | Size: 442 B |
After Width: | Height: | Size: 721 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
18
covas_mobile_new/android/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
@@ -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")
|
34
covas_mobile_new/ios/.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
**/dgph
|
||||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.moved-aside
|
||||
*.pbxuser
|
||||
*.perspectivev3
|
||||
**/*sync/
|
||||
.sconsign.dblite
|
||||
.tags*
|
||||
**/.vagrant/
|
||||
**/DerivedData/
|
||||
Icon?
|
||||
**/Pods/
|
||||
**/.symlinks/
|
||||
profile
|
||||
xcuserdata
|
||||
**/.generated/
|
||||
Flutter/App.framework
|
||||
Flutter/Flutter.framework
|
||||
Flutter/Flutter.podspec
|
||||
Flutter/Generated.xcconfig
|
||||
Flutter/ephemeral/
|
||||
Flutter/app.flx
|
||||
Flutter/app.zip
|
||||
Flutter/flutter_assets/
|
||||
Flutter/flutter_export_environment.sh
|
||||
ServiceDefinitions.json
|
||||
Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
!default.pbxuser
|
||||
!default.perspectivev3
|
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>
|
1
covas_mobile_new/ios/Flutter/Debug.xcconfig
Normal file
@@ -0,0 +1 @@
|
||||
#include "Generated.xcconfig"
|
1
covas_mobile_new/ios/Flutter/Release.xcconfig
Normal file
@@ -0,0 +1 @@
|
||||
#include "Generated.xcconfig"
|
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 */;
|
||||
}
|
7
covas_mobile_new/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
@@ -0,0 +1,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>
|
7
covas_mobile_new/ios/Runner.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
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)
|
||||
}
|
||||
}
|
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "Icon-App-1024x1024@1x.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
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 |
23
covas_mobile_new/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "LaunchImage@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
BIN
covas_mobile_new/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
vendored
Normal file
After Width: | Height: | Size: 68 B |
BIN
covas_mobile_new/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
vendored
Normal file
After Width: | Height: | Size: 68 B |
BIN
covas_mobile_new/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
vendored
Normal file
After Width: | Height: | Size: 68 B |
5
covas_mobile_new/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Launch Screen Assets
|
||||
|
||||
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
|
||||
|
||||
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
|
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
|
||||
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="LaunchImage" width="168" height="185"/>
|
||||
</resources>
|
||||
</document>
|
26
covas_mobile_new/ios/Runner/Base.lproj/Main.storyboard
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Flutter View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|