refactoring
This commit is contained in:
@@ -2,7 +2,8 @@
|
|||||||
<application
|
<application
|
||||||
android:label="afterwork"
|
android:label="afterwork"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:enableOnBackInvokedCallback="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
@@ -5,18 +5,44 @@ import 'package:afterwork/presentation/screens/event/event_screen.dart';
|
|||||||
import 'package:afterwork/presentation/screens/story/story_screen.dart';
|
import 'package:afterwork/presentation/screens/story/story_screen.dart';
|
||||||
import 'package:afterwork/presentation/screens/profile/profile_screen.dart';
|
import 'package:afterwork/presentation/screens/profile/profile_screen.dart';
|
||||||
import 'package:afterwork/presentation/screens/settings/settings_screen.dart';
|
import 'package:afterwork/presentation/screens/settings/settings_screen.dart';
|
||||||
|
import 'package:afterwork/data/datasources/event_remote_data_source.dart';
|
||||||
import '../presentation/reservations/reservations_screen.dart';
|
import '../presentation/reservations/reservations_screen.dart';
|
||||||
|
|
||||||
class AppRouter {
|
class AppRouter {
|
||||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
final EventRemoteDataSource eventRemoteDataSource;
|
||||||
|
final String userId;
|
||||||
|
final String userName;
|
||||||
|
final String userLastName;
|
||||||
|
|
||||||
|
AppRouter({
|
||||||
|
required this.eventRemoteDataSource,
|
||||||
|
required this.userId,
|
||||||
|
required this.userName,
|
||||||
|
required this.userLastName,
|
||||||
|
});
|
||||||
|
|
||||||
|
Route<dynamic> generateRoute(RouteSettings settings) {
|
||||||
switch (settings.name) {
|
switch (settings.name) {
|
||||||
case '/':
|
case '/':
|
||||||
return MaterialPageRoute(builder: (_) => const LoginScreen());
|
return MaterialPageRoute(builder: (_) => const LoginScreen());
|
||||||
case '/home':
|
case '/home':
|
||||||
return MaterialPageRoute(builder: (_) => const HomeScreen());
|
return MaterialPageRoute(
|
||||||
|
builder: (_) => HomeScreen(
|
||||||
|
eventRemoteDataSource: eventRemoteDataSource,
|
||||||
|
userId: userId,
|
||||||
|
userName: userName,
|
||||||
|
userLastName: userLastName,
|
||||||
|
),
|
||||||
|
);
|
||||||
case '/event':
|
case '/event':
|
||||||
return MaterialPageRoute(builder: (_) => const EventScreen());
|
return MaterialPageRoute(
|
||||||
|
builder: (_) => EventScreen(
|
||||||
|
eventRemoteDataSource: eventRemoteDataSource,
|
||||||
|
userId: userId,
|
||||||
|
userName: userName,
|
||||||
|
userLastName: userLastName,
|
||||||
|
),
|
||||||
|
);
|
||||||
case '/story':
|
case '/story':
|
||||||
return MaterialPageRoute(builder: (_) => const StoryScreen());
|
return MaterialPageRoute(builder: (_) => const StoryScreen());
|
||||||
case '/profile':
|
case '/profile':
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
class Urls {
|
class Urls {
|
||||||
static const String baseUrl = 'http://192.168.1.145:8085';
|
static const String baseUrl = 'http://192.168.1.14:8085';
|
||||||
// static const String login = baseUrl + 'auth/login';
|
// static const String login = baseUrl + 'auth/login';
|
||||||
// static const String events = baseUrl + 'events';
|
static const String eventsUrl = '$baseUrl/events';
|
||||||
// Ajoute d'autres URLs ici
|
// Ajoute d'autres URLs ici
|
||||||
}
|
}
|
||||||
|
|||||||
145
lib/data/datasources/event_remote_data_source.dart
Normal file
145
lib/data/datasources/event_remote_data_source.dart
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:afterwork/core/constants/urls.dart';
|
||||||
|
import 'package:afterwork/data/models/event_model.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import '../../core/errors/exceptions.dart';
|
||||||
|
|
||||||
|
class EventRemoteDataSource {
|
||||||
|
final http.Client client;
|
||||||
|
|
||||||
|
EventRemoteDataSource(this.client);
|
||||||
|
|
||||||
|
/// Récupérer tous les événements depuis l'API.
|
||||||
|
Future<List<EventModel>> getAllEvents() async {
|
||||||
|
print('Récupération de tous les événements depuis ${Urls.baseUrl}/events');
|
||||||
|
|
||||||
|
final response = await client.get(Uri.parse('${Urls.baseUrl}/events'));
|
||||||
|
|
||||||
|
print('Statut de la réponse: ${response.statusCode}');
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final List<dynamic> jsonResponse = json.decode(response.body);
|
||||||
|
print('Réponse JSON reçue: $jsonResponse');
|
||||||
|
return jsonResponse.map((event) => EventModel.fromJson(event)).toList();
|
||||||
|
} else {
|
||||||
|
print('Erreur lors de la récupération des événements: ${response.body}');
|
||||||
|
throw ServerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Créer un nouvel événement via l'API.
|
||||||
|
Future<EventModel> createEvent(EventModel event) async {
|
||||||
|
print('Création d\'un nouvel événement avec les données: ${event.toJson()}');
|
||||||
|
|
||||||
|
final response = await client.post(
|
||||||
|
Uri.parse(Urls.eventsUrl),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode(event.toJson()),
|
||||||
|
);
|
||||||
|
|
||||||
|
print('Statut de la réponse: ${response.statusCode}');
|
||||||
|
|
||||||
|
if (response.statusCode == 201) {
|
||||||
|
print('Événement créé avec succès');
|
||||||
|
return EventModel.fromJson(json.decode(response.body));
|
||||||
|
} else {
|
||||||
|
print('Erreur lors de la création de l\'événement: ${response.body}');
|
||||||
|
throw ServerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Récupérer un événement spécifique par son ID.
|
||||||
|
Future<EventModel> getEventById(String id) async {
|
||||||
|
print('Récupération de l\'événement avec l\'ID: $id');
|
||||||
|
|
||||||
|
final response = await client.get(Uri.parse('${Urls.eventsUrl}/$id'));
|
||||||
|
|
||||||
|
print('Statut de la réponse: ${response.statusCode}');
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print('Événement récupéré avec succès');
|
||||||
|
return EventModel.fromJson(json.decode(response.body));
|
||||||
|
} else {
|
||||||
|
print('Erreur lors de la récupération de l\'événement: ${response.body}');
|
||||||
|
throw ServerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mettre à jour un événement existant.
|
||||||
|
Future<EventModel> updateEvent(String id, EventModel event) async {
|
||||||
|
print('Mise à jour de l\'événement avec l\'ID: $id, données: ${event.toJson()}');
|
||||||
|
|
||||||
|
final response = await client.put(
|
||||||
|
Uri.parse('${Urls.eventsUrl}/$id'),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode(event.toJson()),
|
||||||
|
);
|
||||||
|
|
||||||
|
print('Statut de la réponse: ${response.statusCode}');
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print('Événement mis à jour avec succès');
|
||||||
|
return EventModel.fromJson(json.decode(response.body));
|
||||||
|
} else {
|
||||||
|
print('Erreur lors de la mise à jour de l\'événement: ${response.body}');
|
||||||
|
throw ServerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supprimer un événement par son ID.
|
||||||
|
Future<void> deleteEvent(String id) async {
|
||||||
|
print('Suppression de l\'événement avec l\'ID: $id');
|
||||||
|
|
||||||
|
final response = await client.delete(Uri.parse('${Urls.eventsUrl}/$id'));
|
||||||
|
|
||||||
|
print('Statut de la réponse: ${response.statusCode}');
|
||||||
|
|
||||||
|
if (response.statusCode != 204) {
|
||||||
|
print('Erreur lors de la suppression de l\'événement: ${response.body}');
|
||||||
|
throw ServerException();
|
||||||
|
} else {
|
||||||
|
print('Événement supprimé avec succès');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Participer à un événement.
|
||||||
|
Future<EventModel> participateInEvent(String eventId, String userId) async {
|
||||||
|
print('Participation à l\'événement avec l\'ID: $eventId, utilisateur: $userId');
|
||||||
|
|
||||||
|
final response = await client.post(
|
||||||
|
Uri.parse('${Urls.eventsUrl}/$eventId/participate'),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode({'userId': userId}),
|
||||||
|
);
|
||||||
|
|
||||||
|
print('Statut de la réponse: ${response.statusCode}');
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print('Participation réussie');
|
||||||
|
return EventModel.fromJson(json.decode(response.body));
|
||||||
|
} else {
|
||||||
|
print('Erreur lors de la participation à l\'événement: ${response.body}');
|
||||||
|
throw ServerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Réagir à un événement.
|
||||||
|
Future<void> reactToEvent(String eventId, String userId) async {
|
||||||
|
print('Réaction à l\'événement avec l\'ID: $eventId, utilisateur: $userId');
|
||||||
|
|
||||||
|
final response = await client.post(
|
||||||
|
Uri.parse('${Urls.eventsUrl}/$eventId/react'),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode({'userId': userId}),
|
||||||
|
);
|
||||||
|
|
||||||
|
print('Statut de la réponse: ${response.statusCode}');
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
print('Erreur lors de la réaction à l\'événement: ${response.body}');
|
||||||
|
throw ServerException();
|
||||||
|
} else {
|
||||||
|
print('Réaction réussie');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:afterwork/data/models/user_model.dart';
|
|
||||||
import 'package:afterwork/core/constants/urls.dart';
|
import 'package:afterwork/core/constants/urls.dart';
|
||||||
|
import 'package:afterwork/data/models/user_model.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
import '../../core/errors/exceptions.dart';
|
import '../../core/errors/exceptions.dart';
|
||||||
@@ -10,7 +11,8 @@ class UserRemoteDataSource {
|
|||||||
|
|
||||||
UserRemoteDataSource(this.client);
|
UserRemoteDataSource(this.client);
|
||||||
|
|
||||||
Future<UserModel> authenticateUser(String email, String password) async {
|
// Authentifier l'utilisateur
|
||||||
|
Future<UserModel> authenticateUser(String email, String password, String userId) async {
|
||||||
if (email.isEmpty || password.isEmpty) {
|
if (email.isEmpty || password.isEmpty) {
|
||||||
throw Exception('Email ou mot de passe vide');
|
throw Exception('Email ou mot de passe vide');
|
||||||
}
|
}
|
||||||
@@ -34,8 +36,9 @@ class UserRemoteDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Récupérer un utilisateur par ID
|
||||||
Future<UserModel> getUser(String id) async {
|
Future<UserModel> getUser(String id) async {
|
||||||
final response = await client.get(Uri.parse('${Urls.baseUrl}/user/$id'));
|
final response = await client.get(Uri.parse('${Urls.baseUrl}/users/$id'));
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
return UserModel.fromJson(json.decode(response.body));
|
return UserModel.fromJson(json.decode(response.body));
|
||||||
@@ -43,4 +46,45 @@ class UserRemoteDataSource {
|
|||||||
throw ServerException();
|
throw ServerException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Créer un nouvel utilisateur
|
||||||
|
Future<UserModel> createUser(UserModel user) async {
|
||||||
|
final response = await client.post(
|
||||||
|
Uri.parse('${Urls.baseUrl}/users'),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode(user.toJson()),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 201) {
|
||||||
|
return UserModel.fromJson(json.decode(response.body));
|
||||||
|
} else {
|
||||||
|
throw ServerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour un utilisateur
|
||||||
|
Future<UserModel> updateUser(UserModel user) async {
|
||||||
|
final response = await client.put(
|
||||||
|
Uri.parse('${Urls.baseUrl}/users/${user.userId}'),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode(user.toJson()),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
return UserModel.fromJson(json.decode(response.body));
|
||||||
|
} else {
|
||||||
|
throw ServerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supprimer un utilisateur par ID
|
||||||
|
Future<void> deleteUser(String id) async {
|
||||||
|
final response = await client.delete(
|
||||||
|
Uri.parse('${Urls.baseUrl}/users/$id'),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode != 204) {
|
||||||
|
throw ServerException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
lib/data/models/creator_model.dart
Normal file
34
lib/data/models/creator_model.dart
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import 'package:afterwork/data/models/user_model.dart';
|
||||||
|
|
||||||
|
/// Modèle représentant le créateur d'un événement.
|
||||||
|
class CreatorModel extends UserModel {
|
||||||
|
CreatorModel({
|
||||||
|
required String id,
|
||||||
|
required String nom,
|
||||||
|
required String prenoms,
|
||||||
|
|
||||||
|
}) : super(
|
||||||
|
userId: id,
|
||||||
|
nom: nom,
|
||||||
|
prenoms: prenoms,
|
||||||
|
email: '', // Valeur par défaut vide
|
||||||
|
motDePasse: '', // Valeur par défaut vide
|
||||||
|
);
|
||||||
|
|
||||||
|
factory CreatorModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return CreatorModel(
|
||||||
|
id: json['id'],
|
||||||
|
nom: json['nom'],
|
||||||
|
prenoms: json['prenoms'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'id': userId,
|
||||||
|
'nom': nom,
|
||||||
|
'prenoms': prenoms,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
65
lib/data/models/event_model.dart
Normal file
65
lib/data/models/event_model.dart
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import 'package:afterwork/data/models/user_model.dart';
|
||||||
|
|
||||||
|
/// Modèle de données représentant un événement.
|
||||||
|
class EventModel {
|
||||||
|
final String id;
|
||||||
|
final String title;
|
||||||
|
final String description;
|
||||||
|
final String date;
|
||||||
|
final String location;
|
||||||
|
final String category;
|
||||||
|
final String link;
|
||||||
|
final String? imageUrl;
|
||||||
|
final UserModel creator;
|
||||||
|
final List<UserModel> participants;
|
||||||
|
|
||||||
|
/// Constructeur pour initialiser toutes les propriétés de l'événement.
|
||||||
|
EventModel({
|
||||||
|
required this.id,
|
||||||
|
required this.title,
|
||||||
|
required this.description,
|
||||||
|
required this.date,
|
||||||
|
required this.location,
|
||||||
|
required this.category,
|
||||||
|
required this.link,
|
||||||
|
required this.imageUrl,
|
||||||
|
required this.creator,
|
||||||
|
required this.participants,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Convertit un objet JSON en `EventModel`.
|
||||||
|
factory EventModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return EventModel(
|
||||||
|
id: json['id'],
|
||||||
|
title: json['title'],
|
||||||
|
description: json['description'],
|
||||||
|
date: json['date'],
|
||||||
|
location: json['location'],
|
||||||
|
category: json['category'],
|
||||||
|
link: json['link'],
|
||||||
|
imageUrl: json['imageUrl'],
|
||||||
|
creator: UserModel.fromJson(json['creator']),
|
||||||
|
participants: json['participants'] != null
|
||||||
|
? (json['participants'] as List)
|
||||||
|
.map((user) => UserModel.fromJson(user))
|
||||||
|
.toList()
|
||||||
|
: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convertit un `EventModel` en objet JSON.
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'date': date,
|
||||||
|
'location': location,
|
||||||
|
'category': category,
|
||||||
|
'link': link,
|
||||||
|
'imageUrl': imageUrl,
|
||||||
|
'creator': creator.toJson(),
|
||||||
|
'participants': participants.map((user) => user.toJson()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
34
lib/data/models/participant_model.dart
Normal file
34
lib/data/models/participant_model.dart
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import 'package:afterwork/data/models/user_model.dart';
|
||||||
|
|
||||||
|
/// Modèle représentant un participant à un événement.
|
||||||
|
class ParticipantModel extends UserModel {
|
||||||
|
ParticipantModel({
|
||||||
|
required String id,
|
||||||
|
required String nom,
|
||||||
|
required String prenoms,
|
||||||
|
}) : super(
|
||||||
|
userId: id,
|
||||||
|
nom: nom,
|
||||||
|
prenoms: prenoms,
|
||||||
|
email: '', // Valeur par défaut vide
|
||||||
|
motDePasse: '', // Valeur par défaut vide
|
||||||
|
);
|
||||||
|
|
||||||
|
factory ParticipantModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ParticipantModel(
|
||||||
|
id: json['id'],
|
||||||
|
nom: json['nom'],
|
||||||
|
prenoms: json['prenoms'],
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'id': userId,
|
||||||
|
'nom': nom,
|
||||||
|
'prenoms': prenoms,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
import 'package:afterwork/domain/entities/user.dart';
|
import 'package:afterwork/domain/entities/user.dart';
|
||||||
|
|
||||||
class UserModel extends User {
|
class UserModel extends User {
|
||||||
const UserModel({
|
const UserModel({
|
||||||
required String id,
|
required String userId, // Utilisez `id` pour correspondre à l'entité User
|
||||||
required String nom,
|
required String nom,
|
||||||
required String prenoms,
|
required String prenoms,
|
||||||
required String email,
|
required String email,
|
||||||
required String motDePasse,
|
required String motDePasse,
|
||||||
}) : super(
|
}) : super(
|
||||||
id: id,
|
userId: userId,
|
||||||
nom: nom,
|
nom: nom,
|
||||||
prenoms: prenoms,
|
prenoms: prenoms,
|
||||||
email: email,
|
email: email,
|
||||||
@@ -17,7 +17,7 @@ class UserModel extends User {
|
|||||||
|
|
||||||
factory UserModel.fromJson(Map<String, dynamic> json) {
|
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||||
return UserModel(
|
return UserModel(
|
||||||
id: json['id'] ?? '',
|
userId: json['id'] ?? '',
|
||||||
nom: json['nom'] ?? '',
|
nom: json['nom'] ?? '',
|
||||||
prenoms: json['prenoms'] ?? '',
|
prenoms: json['prenoms'] ?? '',
|
||||||
email: json['email'] ?? '',
|
email: json['email'] ?? '',
|
||||||
@@ -27,7 +27,7 @@ class UserModel extends User {
|
|||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'id': id,
|
'id': userId, // Utilisez `id` pour correspondre à l'entité User
|
||||||
'nom': nom,
|
'nom': nom,
|
||||||
'prenoms': prenoms,
|
'prenoms': prenoms,
|
||||||
'email': email,
|
'email': email,
|
||||||
|
|||||||
18
lib/data/providers/user_provider.dart
Normal file
18
lib/data/providers/user_provider.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class UserProvider with ChangeNotifier {
|
||||||
|
String _userId = '';
|
||||||
|
String _userName = '';
|
||||||
|
String _userLastName = '';
|
||||||
|
|
||||||
|
String get userId => _userId;
|
||||||
|
String get userName => _userName;
|
||||||
|
String get userLastName => _userLastName;
|
||||||
|
|
||||||
|
void setUser(String id, String name, String lastName) {
|
||||||
|
_userId = id;
|
||||||
|
_userName = name;
|
||||||
|
_userLastName = lastName;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,8 +14,8 @@ class UserRepositoryImpl implements UserRepository {
|
|||||||
return userModel; // Retourne un UserModel qui est un sous-type de User
|
return userModel; // Retourne un UserModel qui est un sous-type de User
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<User> authenticateUser(String email, String password) async {
|
Future<User> authenticateUser(String email, String password, String userId) async {
|
||||||
UserModel userModel = await remoteDataSource.authenticateUser(email, password);
|
UserModel userModel = await remoteDataSource.authenticateUser(email, password, userId);
|
||||||
return userModel; // Retourne un UserModel qui est un sous-type de User
|
return userModel; // Retourne un UserModel qui est un sous-type de User
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
lib/data/services/hash_password.dart
Normal file
8
lib/data/services/hash_password.dart
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
|
||||||
|
String hashPassword(String password) {
|
||||||
|
var bytes = utf8.encode(password); // Convertir en bytes
|
||||||
|
var digest = sha256.convert(bytes); // Hachage SHA-256
|
||||||
|
return digest.toString(); // Retourner le hash sous forme de chaîne
|
||||||
|
}
|
||||||
50
lib/data/services/preferences_helper.dart
Normal file
50
lib/data/services/preferences_helper.dart
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
class PreferencesHelper {
|
||||||
|
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
Future<void> setString(String key, String value) async {
|
||||||
|
final prefs = await _prefs;
|
||||||
|
await prefs.setString(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getString(String key) async {
|
||||||
|
final prefs = await _prefs;
|
||||||
|
return prefs.getString(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> remove(String key) async {
|
||||||
|
final prefs = await _prefs;
|
||||||
|
await prefs.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveUserId(String userId) async {
|
||||||
|
await setString('user_id', userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getUserId() async {
|
||||||
|
return await getString('user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveUserName(String userName) async {
|
||||||
|
await setString('user_name', userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getUserName() async {
|
||||||
|
return await getString('user_name');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveUserLastName(String userLastName) async {
|
||||||
|
await setString('user_last_name', userLastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getUserLastName() async {
|
||||||
|
return await getString('user_last_name');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> clearUserInfo() async {
|
||||||
|
await remove('user_id');
|
||||||
|
await remove('user_name');
|
||||||
|
await remove('user_last_name');
|
||||||
|
}
|
||||||
|
}
|
||||||
47
lib/data/services/secure_storage.dart
Normal file
47
lib/data/services/secure_storage.dart
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
|
||||||
|
class SecureStorage {
|
||||||
|
final FlutterSecureStorage _storage = const FlutterSecureStorage();
|
||||||
|
|
||||||
|
Future<void> write(String key, String value) async {
|
||||||
|
await _storage.write(key: key, value: value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> read(String key) async {
|
||||||
|
return await _storage.read(key: key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> delete(String key) async {
|
||||||
|
await _storage.delete(key: key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveUserId(String userId) async {
|
||||||
|
await write('user_id', userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getUserId() async {
|
||||||
|
return await read('user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveUserName(String userName) async {
|
||||||
|
await write('user_name', userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getUserName() async {
|
||||||
|
return await read('user_name');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveUserLastName(String userLastName) async {
|
||||||
|
await write('user_last_name', userLastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> getUserLastName() async {
|
||||||
|
return await read('user_last_name');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteUserInfo() async {
|
||||||
|
await delete('user_id');
|
||||||
|
await delete('user_name');
|
||||||
|
await delete('user_last_name');
|
||||||
|
}
|
||||||
|
}
|
||||||
64
lib/domain/entities/event.dart
Normal file
64
lib/domain/entities/event.dart
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
class EventModel {
|
||||||
|
final String eventId;
|
||||||
|
final String title;
|
||||||
|
final String description;
|
||||||
|
final String eventDate;
|
||||||
|
final String location;
|
||||||
|
final String category;
|
||||||
|
final String? link;
|
||||||
|
final String? imageUrl;
|
||||||
|
final String creatorId;
|
||||||
|
|
||||||
|
EventModel({
|
||||||
|
required this.eventId,
|
||||||
|
required this.title,
|
||||||
|
required this.description,
|
||||||
|
required this.eventDate,
|
||||||
|
required this.location,
|
||||||
|
required this.category,
|
||||||
|
this.link,
|
||||||
|
this.imageUrl,
|
||||||
|
required this.creatorId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Méthode pour créer un EventModel à partir d'un JSON
|
||||||
|
factory EventModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return EventModel(
|
||||||
|
eventId: json['id'],
|
||||||
|
title: json['title'],
|
||||||
|
description: json['description'],
|
||||||
|
eventDate: json['event_date'],
|
||||||
|
location: json['location'],
|
||||||
|
category: json['category'],
|
||||||
|
link: json['link'],
|
||||||
|
imageUrl: json['imageUrl'],
|
||||||
|
creatorId: json['creator']['id'], // Assurez-vous que le JSON a ce format
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Méthode pour convertir un EventModel en JSON
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'id': eventId,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'event_date': eventDate,
|
||||||
|
'location': location,
|
||||||
|
'category': category,
|
||||||
|
'link': link,
|
||||||
|
'imageUrl': imageUrl,
|
||||||
|
'creator': {'id': creatorId}, // Structure du JSON pour l'API
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convertir une liste d'EventModel à partir d'une liste JSON
|
||||||
|
static List<EventModel> fromJsonList(List<dynamic> jsonList) {
|
||||||
|
return jsonList.map((json) => EventModel.fromJson(json)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convertir une liste d'EventModel en JSON
|
||||||
|
static List<Map<String, dynamic>> toJsonList(List<EventModel> events) {
|
||||||
|
return events.map((event) => event.toJson()).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
class User extends Equatable {
|
class User extends Equatable {
|
||||||
final String id;
|
final String userId;
|
||||||
final String nom;
|
final String nom;
|
||||||
final String prenoms;
|
final String prenoms;
|
||||||
final String email;
|
final String email;
|
||||||
final String motDePasse;
|
final String motDePasse;
|
||||||
|
|
||||||
const User({
|
const User({
|
||||||
required this.id,
|
required this.userId,
|
||||||
required this.nom,
|
required this.nom,
|
||||||
required this.prenoms,
|
required this.prenoms,
|
||||||
required this.email,
|
required this.email,
|
||||||
@@ -16,5 +16,5 @@ class User extends Equatable {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [id, nom, prenoms, email, motDePasse];
|
List<Object?> get props => [userId, nom, prenoms, email, motDePasse];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,74 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'config/router.dart';
|
import 'package:afterwork/config/router.dart';
|
||||||
import 'core/theme/app_theme.dart';
|
import 'package:afterwork/data/datasources/event_remote_data_source.dart';
|
||||||
|
import 'package:afterwork/data/providers/user_provider.dart';
|
||||||
|
import 'package:afterwork/data/services/secure_storage.dart';
|
||||||
|
import 'package:afterwork/data/services/preferences_helper.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
void main() {
|
void main() async {
|
||||||
runApp(const AfterWorkApp());
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
final eventRemoteDataSource = EventRemoteDataSource(http.Client());
|
||||||
|
|
||||||
|
// Remplacez ici par l'utilisation du stockage sécurisé ou des préférences
|
||||||
|
final SecureStorage secureStorage = SecureStorage();
|
||||||
|
final PreferencesHelper preferencesHelper = PreferencesHelper();
|
||||||
|
|
||||||
|
String? userId = await secureStorage.getUserId();
|
||||||
|
String? userName = await preferencesHelper.getUserName();
|
||||||
|
String? userLastName = await preferencesHelper.getUserLastName();
|
||||||
|
|
||||||
|
// Si les valeurs sont nulles, vous pouvez définir des valeurs par défaut ou gérer autrement
|
||||||
|
userId ??= 'default_user_id'; // Remplacer par une valeur par défaut si nécessaire
|
||||||
|
userName ??= 'Default';
|
||||||
|
userLastName ??= 'User';
|
||||||
|
|
||||||
|
runApp(MyApp(
|
||||||
|
eventRemoteDataSource: eventRemoteDataSource,
|
||||||
|
userId: userId,
|
||||||
|
userName: userName,
|
||||||
|
userLastName: userLastName,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
class AfterWorkApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const AfterWorkApp({super.key});
|
final EventRemoteDataSource eventRemoteDataSource;
|
||||||
|
final String userId;
|
||||||
|
final String userName;
|
||||||
|
final String userLastName;
|
||||||
|
|
||||||
|
const MyApp({
|
||||||
|
super.key,
|
||||||
|
required this.eventRemoteDataSource,
|
||||||
|
required this.userId,
|
||||||
|
required this.userName,
|
||||||
|
required this.userLastName,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MultiProvider(
|
||||||
title: 'AfterWork',
|
providers: [
|
||||||
theme: AppTheme.lightTheme,
|
ChangeNotifierProvider(
|
||||||
darkTheme: AppTheme.darkTheme, // Ajout du thème sombre
|
create: (_) => UserProvider()..setUser(userId, userName, userLastName),
|
||||||
themeMode: ThemeMode.system, // Choix automatique du thème en fonction du système
|
),
|
||||||
onGenerateRoute: AppRouter.generateRoute,
|
// Ajouter d'autres providers ici si nécessaire
|
||||||
initialRoute: '/',
|
],
|
||||||
|
child: MaterialApp(
|
||||||
|
title: 'AfterWork',
|
||||||
|
theme: ThemeData(
|
||||||
|
primarySwatch: Colors.blue,
|
||||||
|
),
|
||||||
|
onGenerateRoute: AppRouter(
|
||||||
|
eventRemoteDataSource: eventRemoteDataSource,
|
||||||
|
userId: userId,
|
||||||
|
userName: userName,
|
||||||
|
userLastName: userLastName,
|
||||||
|
).generateRoute,
|
||||||
|
initialRoute: '/',
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,26 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:afterwork/data/models/creator_model.dart';
|
||||||
|
import 'package:afterwork/data/models/event_model.dart';
|
||||||
|
import 'package:afterwork/data/models/participant_model.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:afterwork/data/providers/user_provider.dart';
|
||||||
|
import 'package:afterwork/core/constants/urls.dart';
|
||||||
|
import '../location/location_picker_screen.dart';
|
||||||
|
|
||||||
class AddEventDialog extends StatefulWidget {
|
class AddEventDialog extends StatefulWidget {
|
||||||
const AddEventDialog({super.key});
|
final String userId;
|
||||||
|
final String userName;
|
||||||
|
final String userLastName;
|
||||||
|
|
||||||
|
const AddEventDialog({
|
||||||
|
super.key,
|
||||||
|
required this.userId,
|
||||||
|
required this.userName,
|
||||||
|
required this.userLastName,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_AddEventDialogState createState() => _AddEventDialogState();
|
_AddEventDialogState createState() => _AddEventDialogState();
|
||||||
@@ -14,10 +32,10 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
String _description = '';
|
String _description = '';
|
||||||
DateTime? _selectedDate;
|
DateTime? _selectedDate;
|
||||||
String? _imagePath;
|
String? _imagePath;
|
||||||
String _location = '';
|
String _location = 'Abidjan'; // Par défaut à Cocody, Abidjan
|
||||||
String _category = '';
|
String _category = '';
|
||||||
String _link = '';
|
String _link = '';
|
||||||
LatLng? _selectedLatLng;
|
LatLng? _selectedLatLng = const LatLng(5.348722, -3.985038); // Par défaut à Cocody, Abidjan
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -26,29 +44,31 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
borderRadius: BorderRadius.circular(15.0),
|
borderRadius: BorderRadius.circular(15.0),
|
||||||
),
|
),
|
||||||
backgroundColor: const Color(0xFF2C2C3E),
|
backgroundColor: const Color(0xFF2C2C3E),
|
||||||
child: Padding(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(16.0),
|
child: Padding(
|
||||||
child: Form(
|
padding: const EdgeInsets.all(16.0),
|
||||||
key: _formKey,
|
child: Form(
|
||||||
child: Column(
|
key: _formKey,
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
_buildTitleField(),
|
children: [
|
||||||
const SizedBox(height: 10),
|
_buildTitleField(),
|
||||||
_buildDescriptionField(),
|
const SizedBox(height: 10),
|
||||||
const SizedBox(height: 10),
|
_buildDescriptionField(),
|
||||||
_buildDatePicker(),
|
const SizedBox(height: 10),
|
||||||
const SizedBox(height: 10),
|
_buildDatePicker(),
|
||||||
_buildLocationField(context),
|
const SizedBox(height: 10),
|
||||||
const SizedBox(height: 10),
|
_buildLocationField(context),
|
||||||
_buildCategoryField(),
|
const SizedBox(height: 10),
|
||||||
const SizedBox(height: 10),
|
_buildCategoryField(),
|
||||||
_buildImagePicker(),
|
const SizedBox(height: 10),
|
||||||
const SizedBox(height: 10),
|
_buildImagePicker(),
|
||||||
_buildLinkField(),
|
const SizedBox(height: 10),
|
||||||
const SizedBox(height: 20),
|
_buildLinkField(),
|
||||||
_buildSubmitButton(),
|
const SizedBox(height: 20),
|
||||||
],
|
_buildSubmitButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -71,12 +91,14 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
|
print('Erreur: Titre est vide');
|
||||||
return 'Veuillez entrer un titre';
|
return 'Veuillez entrer un titre';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onSaved: (value) {
|
onSaved: (value) {
|
||||||
_title = value ?? '';
|
_title = value ?? '';
|
||||||
|
print('Titre sauvegardé: $_title');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -98,12 +120,14 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
|
print('Erreur: Description est vide');
|
||||||
return 'Veuillez entrer une description';
|
return 'Veuillez entrer une description';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onSaved: (value) {
|
onSaved: (value) {
|
||||||
_description = value ?? '';
|
_description = value ?? '';
|
||||||
|
print('Description sauvegardée: $_description');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -120,7 +144,10 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
if (picked != null && picked != _selectedDate) {
|
if (picked != null && picked != _selectedDate) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedDate = picked;
|
_selectedDate = picked;
|
||||||
|
print('Date sélectionnée: $_selectedDate');
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
print('Date non sélectionnée ou égale à la précédente');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -158,7 +185,10 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_selectedLatLng = pickedLocation;
|
_selectedLatLng = pickedLocation;
|
||||||
_location = '${pickedLocation.latitude}, ${pickedLocation.longitude}';
|
_location = '${pickedLocation.latitude}, ${pickedLocation.longitude}';
|
||||||
|
print('Localisation sélectionnée: $_location');
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
print('Localisation non sélectionnée');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -199,6 +229,7 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
onSaved: (value) {
|
onSaved: (value) {
|
||||||
_category = value ?? '';
|
_category = value ?? '';
|
||||||
|
print('Catégorie sauvegardée: $_category');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -207,6 +238,7 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// Logique pour sélectionner une image
|
// Logique pour sélectionner une image
|
||||||
|
print('Image Picker activé');
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
|
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
|
||||||
@@ -218,7 +250,9 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
_imagePath == null ? 'Sélectionnez une image' : 'Image sélectionnée',
|
_imagePath == null
|
||||||
|
? 'Sélectionnez une image'
|
||||||
|
: 'Image sélectionnée: $_imagePath',
|
||||||
style: const TextStyle(color: Colors.white70),
|
style: const TextStyle(color: Colors.white70),
|
||||||
),
|
),
|
||||||
const Icon(Icons.image, color: Colors.white70),
|
const Icon(Icons.image, color: Colors.white70),
|
||||||
@@ -244,17 +278,69 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
onSaved: (value) {
|
onSaved: (value) {
|
||||||
_link = value ?? '';
|
_link = value ?? '';
|
||||||
|
print('Lien sauvegardé: $_link');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSubmitButton() {
|
Widget _buildSubmitButton() {
|
||||||
return ElevatedButton(
|
return ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
_formKey.currentState!.save();
|
_formKey.currentState!.save();
|
||||||
// Logique pour soumettre les données
|
print('Formulaire validé');
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
// Créer l'événement en utilisant l'ID réel de l'utilisateur pour le créateur et les participants
|
||||||
|
EventModel newEvent = EventModel(
|
||||||
|
title: _title,
|
||||||
|
description: _description,
|
||||||
|
date: _selectedDate?.toIso8601String() ?? '',
|
||||||
|
location: _location,
|
||||||
|
category: _category,
|
||||||
|
link: _link,
|
||||||
|
imageUrl: _imagePath ?? '',
|
||||||
|
creator: CreatorModel(
|
||||||
|
id: widget.userId,
|
||||||
|
nom: widget.userName,
|
||||||
|
prenoms: widget.userLastName,
|
||||||
|
),
|
||||||
|
participants: [
|
||||||
|
ParticipantModel(
|
||||||
|
id: widget.userId,
|
||||||
|
nom: widget.userName,
|
||||||
|
prenoms: widget.userLastName,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
id: '',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Convertir l'événement en JSON
|
||||||
|
Map<String, dynamic> eventData = newEvent.toJson();
|
||||||
|
print('Données JSON de l\'événement: $eventData');
|
||||||
|
|
||||||
|
// Envoyer la requête POST à l'API
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse('${Urls.baseUrl}/events'),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: jsonEncode(eventData),
|
||||||
|
);
|
||||||
|
|
||||||
|
print('Statut de la réponse: ${response.statusCode}');
|
||||||
|
print('Réponse brute: ${response.body}');
|
||||||
|
|
||||||
|
if (response.statusCode == 201) {
|
||||||
|
// Création réussie
|
||||||
|
print('Événement créé avec succès');
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
|
} else {
|
||||||
|
// Gérer l'erreur
|
||||||
|
print('Erreur lors de la création de l\'événement: ${response.reasonPhrase}');
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Erreur: ${response.reasonPhrase}')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print('Le formulaire n\'est pas valide');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
@@ -265,40 +351,8 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
|||||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||||
minimumSize: const Size(double.infinity, 40),
|
minimumSize: const Size(double.infinity, 40),
|
||||||
),
|
),
|
||||||
child: const Text('Ajouter l\'événement', style: TextStyle(color: Colors.white)),
|
child: const Text('Ajouter l\'événement',
|
||||||
);
|
style: TextStyle(color: Colors.white)),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LocationPickerScreen extends StatelessWidget {
|
|
||||||
const LocationPickerScreen({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Sélectionnez une localisation'),
|
|
||||||
backgroundColor: const Color(0xFF1E1E2C),
|
|
||||||
),
|
|
||||||
body: GoogleMap(
|
|
||||||
initialCameraPosition: const CameraPosition(
|
|
||||||
target: LatLng(48.8566, 2.3522), // Paris par défaut
|
|
||||||
zoom: 12.0,
|
|
||||||
),
|
|
||||||
markers: Set<Marker>.of(<Marker>[
|
|
||||||
Marker(
|
|
||||||
markerId: const MarkerId('selectedLocation'),
|
|
||||||
position: LatLng(48.8566, 2.3522), // Position par défaut
|
|
||||||
draggable: true,
|
|
||||||
onDragEnd: (newPosition) {
|
|
||||||
Navigator.of(context).pop(newPosition);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
onTap: (position) {
|
|
||||||
Navigator.of(context).pop(position);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:afterwork/data/datasources/event_remote_data_source.dart';
|
||||||
|
|
||||||
|
/// Widget pour afficher une carte d'événement.
|
||||||
class EventCard extends StatelessWidget {
|
class EventCard extends StatelessWidget {
|
||||||
|
final String eventId;
|
||||||
|
final EventRemoteDataSource eventRemoteDataSource;
|
||||||
|
final String userId;
|
||||||
|
final String userName;
|
||||||
|
final String userLastName;
|
||||||
final String profileImage;
|
final String profileImage;
|
||||||
final String name;
|
final String name;
|
||||||
final String datePosted;
|
final String datePosted;
|
||||||
@@ -19,6 +26,11 @@ class EventCard extends StatelessWidget {
|
|||||||
|
|
||||||
const EventCard({
|
const EventCard({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
required this.eventId,
|
||||||
|
required this.eventRemoteDataSource,
|
||||||
|
required this.userId,
|
||||||
|
required this.userName,
|
||||||
|
required this.userLastName,
|
||||||
required this.profileImage,
|
required this.profileImage,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.datePosted,
|
required this.datePosted,
|
||||||
@@ -33,7 +45,7 @@ class EventCard extends StatelessWidget {
|
|||||||
required this.onShare,
|
required this.onShare,
|
||||||
required this.onParticipate,
|
required this.onParticipate,
|
||||||
required this.onCloseEvent,
|
required this.onCloseEvent,
|
||||||
required this.onMoreOptions, required String assetImage,
|
required this.onMoreOptions,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -65,6 +77,7 @@ class EventCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construire l'en-tête de la carte avec les informations de l'utilisateur.
|
||||||
Widget _buildHeader() {
|
Widget _buildHeader() {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -94,16 +107,17 @@ class EventCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.more_vert, color: Colors.white),
|
icon: const Icon(Icons.more_vert, color: Colors.white),
|
||||||
onPressed: onMoreOptions,
|
onPressed: _onMoreOptions,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.close, color: Colors.white),
|
icon: const Icon(Icons.close, color: Colors.white),
|
||||||
onPressed: onCloseEvent,
|
onPressed: _onCloseEvent,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Afficher les détails de l'événement.
|
||||||
Widget _buildEventDetails() {
|
Widget _buildEventDetails() {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -125,6 +139,7 @@ class EventCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Afficher l'image de l'événement.
|
||||||
Widget _buildEventImage() {
|
Widget _buildEventImage() {
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
@@ -134,6 +149,7 @@ class EventCard extends StatelessWidget {
|
|||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
errorBuilder: (context, error, stackTrace) {
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
print('Erreur de chargement de l\'image: $error');
|
||||||
return Image.asset(
|
return Image.asset(
|
||||||
'lib/assets/images/placeholder.png',
|
'lib/assets/images/placeholder.png',
|
||||||
height: 180,
|
height: 180,
|
||||||
@@ -145,6 +161,7 @@ class EventCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Afficher les icônes d'interaction (réagir, commenter, partager).
|
||||||
Widget _buildInteractionRow() {
|
Widget _buildInteractionRow() {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
@@ -174,6 +191,7 @@ class EventCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bouton d'interaction personnalisé.
|
||||||
Widget _buildIconButton({
|
Widget _buildIconButton({
|
||||||
required IconData icon,
|
required IconData icon,
|
||||||
required String label,
|
required String label,
|
||||||
@@ -192,9 +210,10 @@ class EventCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bouton pour participer à l'événement.
|
||||||
Widget _buildParticipateButton() {
|
Widget _buildParticipateButton() {
|
||||||
return ElevatedButton(
|
return ElevatedButton(
|
||||||
onPressed: onParticipate,
|
onPressed: _onParticipate,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: const Color(0xFF1DBF73),
|
backgroundColor: const Color(0xFF1DBF73),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
@@ -206,4 +225,54 @@ class EventCard extends StatelessWidget {
|
|||||||
child: const Text('Participer', style: TextStyle(color: Colors.white)),
|
child: const Text('Participer', style: TextStyle(color: Colors.white)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logique pour réagir à l'événement.
|
||||||
|
void _onReact() async {
|
||||||
|
try {
|
||||||
|
print('Tentative de réaction à l\'événement $eventId par l\'utilisateur $userId');
|
||||||
|
await eventRemoteDataSource.reactToEvent(eventId, userId);
|
||||||
|
print('Réaction à l\'événement réussie');
|
||||||
|
// Mettre à jour l'interface utilisateur, par exemple augmenter le compteur de réactions.
|
||||||
|
} catch (e) {
|
||||||
|
// Gérer l'erreur.
|
||||||
|
print('Erreur lors de la réaction à l\'événement: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logique pour commenter l'événement.
|
||||||
|
void _onComment() {
|
||||||
|
// Implémenter la logique pour commenter un événement.
|
||||||
|
print('Commentaire sur l\'événement $eventId par l\'utilisateur $userId');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logique pour partager l'événement.
|
||||||
|
void _onShare() {
|
||||||
|
// Implémenter la logique pour partager un événement.
|
||||||
|
print('Partage de l\'événement $eventId par l\'utilisateur $userId');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logique pour participer à l'événement.
|
||||||
|
void _onParticipate() async {
|
||||||
|
try {
|
||||||
|
print('Tentative de participation à l\'événement $eventId par l\'utilisateur $userId');
|
||||||
|
await eventRemoteDataSource.participateInEvent(eventId, userId);
|
||||||
|
print('Participation à l\'événement réussie');
|
||||||
|
// Mettre à jour l'interface utilisateur, par exemple afficher un message de succès.
|
||||||
|
} catch (e) {
|
||||||
|
// Gérer l'erreur.
|
||||||
|
print('Erreur lors de la participation à l\'événement: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logique pour fermer l'événement.
|
||||||
|
void _onCloseEvent() {
|
||||||
|
// Implémenter la logique pour fermer un événement.
|
||||||
|
print('Fermeture de l\'événement $eventId');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logique pour afficher plus d'options.
|
||||||
|
void _onMoreOptions() {
|
||||||
|
// Implémenter la logique pour afficher plus d'options.
|
||||||
|
print('Affichage des options supplémentaires pour l\'événement $eventId');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +1,129 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../dialogs/add_event_dialog.dart';
|
import 'package:afterwork/data/models/event_model.dart';
|
||||||
|
import 'package:afterwork/data/datasources/event_remote_data_source.dart';
|
||||||
import 'event_card.dart';
|
import 'event_card.dart';
|
||||||
|
import '../dialogs/add_event_dialog.dart';
|
||||||
|
|
||||||
|
/// Écran principal pour afficher les événements.
|
||||||
class EventScreen extends StatelessWidget {
|
class EventScreen extends StatelessWidget {
|
||||||
const EventScreen({super.key});
|
final EventRemoteDataSource eventRemoteDataSource;
|
||||||
|
final String userId;
|
||||||
|
final String userName; // Nom de l'utilisateur
|
||||||
|
final String userLastName; // Prénom de l'utilisateur
|
||||||
|
const EventScreen({
|
||||||
|
Key? key,
|
||||||
|
required this.eventRemoteDataSource,
|
||||||
|
required this.userId,
|
||||||
|
required this.userName,
|
||||||
|
required this.userLastName,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Événements'),
|
title: const Text(
|
||||||
|
'Événements',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFF1DBF73), // Définit la couleur verte du texte
|
||||||
|
),
|
||||||
|
),
|
||||||
backgroundColor: const Color(0xFF1E1E2C),
|
backgroundColor: const Color(0xFF1E1E2C),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.add_circle_outline, size: 28, color: Color(0xFF1DBF73)),
|
icon: const Icon(Icons.add_circle_outline, size: 28, color: Color(0xFF1DBF73)),
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
_showAddEventDialog(context);
|
final eventData = await showDialog<Map<String, dynamic>>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AddEventDialog(
|
||||||
|
userId: userId,
|
||||||
|
userName: userName,
|
||||||
|
userLastName: userLastName,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (eventData != null) {
|
||||||
|
// Appeler l'API pour créer un nouvel événement.
|
||||||
|
try {
|
||||||
|
print('Tentative de création d\'un nouvel événement par l\'utilisateur $userId');
|
||||||
|
await eventRemoteDataSource.createEvent(eventData as EventModel);
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Événement ajouté avec succès !')),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print('Erreur lors de la création de l\'événement: $e');
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Erreur : $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView.builder(
|
body: FutureBuilder<List<EventModel>>(
|
||||||
padding: const EdgeInsets.all(16.0),
|
future: eventRemoteDataSource.getAllEvents(),
|
||||||
itemCount: 10,
|
builder: (context, snapshot) {
|
||||||
itemBuilder: (context, index) {
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return EventCard(
|
return const Center(child: CircularProgressIndicator());
|
||||||
profileImage: 'lib/assets/images/profile_picture.png',
|
} else if (snapshot.hasError) {
|
||||||
name: 'Nom Prénom',
|
print('Erreur lors de la récupération des événements: ${snapshot.error}');
|
||||||
datePosted: 'Posté le 24/08/2024',
|
return Center(child: Text('Erreur: ${snapshot.error}'));
|
||||||
eventTitle: 'Titre de l\'événement',
|
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||||
eventDescription: 'Description détaillée de l\'événement...',
|
return const Center(child: Text('Aucun événement trouvé.'));
|
||||||
eventImageUrl: 'lib/assets/images/profile_picture.png',
|
}
|
||||||
reactionsCount: 120,
|
|
||||||
commentsCount: 45,
|
final events = snapshot.data!;
|
||||||
sharesCount: 30,
|
print('Nombre d\'événements récupérés: ${events.length}');
|
||||||
onReact: () {
|
|
||||||
// Logique pour réagir à l'événement
|
return ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
itemCount: events.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final event = events[index];
|
||||||
|
print('Affichage de l\'événement ${event.id}');
|
||||||
|
|
||||||
|
return EventCard(
|
||||||
|
eventRemoteDataSource: eventRemoteDataSource,
|
||||||
|
userId: userId,
|
||||||
|
eventId: event.id,
|
||||||
|
userName: userName,
|
||||||
|
userLastName: userLastName,
|
||||||
|
profileImage: 'lib/assets/images/profile_picture.png',
|
||||||
|
name: '$userName $userLastName',
|
||||||
|
datePosted: 'Posté le 24/08/2024',
|
||||||
|
eventTitle: event.title,
|
||||||
|
eventDescription: event.description,
|
||||||
|
eventImageUrl: event.imageUrl ?? 'lib/assets/images/placeholder.png',
|
||||||
|
reactionsCount: 120, // Exemple de valeur
|
||||||
|
commentsCount: 45, // Exemple de valeur
|
||||||
|
sharesCount: 30, // Exemple de valeur
|
||||||
|
onReact: () {
|
||||||
|
print('Réaction à l\'événement ${event.id}');
|
||||||
|
},
|
||||||
|
onComment: () {
|
||||||
|
print('Commentaire sur l\'événement ${event.id}');
|
||||||
|
},
|
||||||
|
onShare: () {
|
||||||
|
print('Partage de l\'événement ${event.id}');
|
||||||
|
},
|
||||||
|
onParticipate: () {
|
||||||
|
print('Participation à l\'événement ${event.id}');
|
||||||
|
},
|
||||||
|
onCloseEvent: () {
|
||||||
|
print('Fermeture de l\'événement ${event.id}');
|
||||||
|
},
|
||||||
|
onMoreOptions: () {
|
||||||
|
print('Affichage des options pour l\'événement ${event.id}');
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
onComment: () {
|
|
||||||
// Logique pour commenter l'événement
|
|
||||||
},
|
|
||||||
onShare: () {
|
|
||||||
// Logique pour partager l'événement
|
|
||||||
},
|
|
||||||
onParticipate: () {
|
|
||||||
// Logique pour participer à l'événement
|
|
||||||
},
|
|
||||||
onCloseEvent: () {
|
|
||||||
// Logique pour fermer l'événement
|
|
||||||
},
|
|
||||||
onMoreOptions: () {
|
|
||||||
// Logique pour afficher plus d'options
|
|
||||||
},
|
|
||||||
assetImage: 'lib/assets/images/placeholder.png', // Ajoutez ce paramètre requis
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
backgroundColor: const Color(0xFF1E1E2C),
|
backgroundColor: const Color(0xFF1E1E2C),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showAddEventDialog(BuildContext context) {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return const AddEventDialog();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,26 @@ import 'package:afterwork/presentation/screens/profile/profile_screen.dart';
|
|||||||
import 'package:afterwork/presentation/screens/social/social_screen.dart';
|
import 'package:afterwork/presentation/screens/social/social_screen.dart';
|
||||||
import 'package:afterwork/presentation/screens/establishments/establishments_screen.dart';
|
import 'package:afterwork/presentation/screens/establishments/establishments_screen.dart';
|
||||||
import 'package:afterwork/presentation/screens/home/home_content.dart';
|
import 'package:afterwork/presentation/screens/home/home_content.dart';
|
||||||
|
import 'package:afterwork/data/datasources/event_remote_data_source.dart';
|
||||||
|
|
||||||
|
/// Classe principale pour l'écran d'accueil de l'application.
|
||||||
|
/// Cette classe gère la navigation entre les différentes sections de l'application
|
||||||
|
/// en utilisant un [TabController] pour contrôler les différents onglets.
|
||||||
|
/// Les actions de l'AppBar sont également personnalisées pour offrir des fonctionnalités
|
||||||
|
/// spécifiques comme la recherche, la publication et la messagerie.
|
||||||
class HomeScreen extends StatefulWidget {
|
class HomeScreen extends StatefulWidget {
|
||||||
const HomeScreen({super.key});
|
final EventRemoteDataSource eventRemoteDataSource;
|
||||||
|
final String userId;
|
||||||
|
final String userName;
|
||||||
|
final String userLastName;
|
||||||
|
|
||||||
|
const HomeScreen({
|
||||||
|
Key? key,
|
||||||
|
required this.eventRemoteDataSource,
|
||||||
|
required this.userId,
|
||||||
|
required this.userName,
|
||||||
|
required this.userLastName,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_HomeScreenState createState() => _HomeScreenState();
|
_HomeScreenState createState() => _HomeScreenState();
|
||||||
@@ -18,25 +35,32 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
// Initialisation du TabController avec 5 onglets.
|
||||||
_tabController = TabController(length: 5, vsync: this);
|
_tabController = TabController(length: 5, vsync: this);
|
||||||
|
debugPrint('HomeScreen initialisé avec userId: ${widget.userId}, userName: ${widget.userName}, userLastName: ${widget.userLastName}');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
// Nettoyage du TabController pour éviter les fuites de mémoire.
|
||||||
_tabController.dispose();
|
_tabController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
debugPrint('HomeScreen dispose appelé');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gestion des sélections dans le menu contextuel de l'AppBar.
|
||||||
void _onMenuSelected(BuildContext context, String option) {
|
void _onMenuSelected(BuildContext context, String option) {
|
||||||
// Implémente la logique pour chaque option ici
|
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case 'Publier':
|
case 'Publier':
|
||||||
// Redirige vers la page de publication
|
debugPrint('Option "Publier" sélectionnée');
|
||||||
|
// Rediriger vers la page de publication.
|
||||||
break;
|
break;
|
||||||
case 'Story':
|
case 'Story':
|
||||||
// Redirige vers la page de création de Story
|
debugPrint('Option "Story" sélectionnée');
|
||||||
|
// Rediriger vers la page de création de Story.
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
debugPrint('Option inconnue sélectionnée: $option');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,22 +69,25 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.black, // Fond noir pour l'AppBar
|
backgroundColor: Colors.black,
|
||||||
elevation: 0, // Enlève l'ombre sous l'AppBar
|
elevation: 0,
|
||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'lib/assets/images/logo.png', // Chemin correct de ton logo
|
'lib/assets/images/logo.png', // Chemin correct de votre logo.
|
||||||
height: 40,
|
height: 40,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
// Bouton +
|
// Bouton pour ajouter du contenu (Publier, Story).
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
backgroundColor: Colors.white, // Cercle blanc
|
backgroundColor: Colors.white,
|
||||||
radius: 18, // Réduit la taille du bouton
|
radius: 18,
|
||||||
child: PopupMenuButton<String>(
|
child: PopupMenuButton<String>(
|
||||||
onSelected: (value) => _onMenuSelected(context, value),
|
onSelected: (value) {
|
||||||
|
_onMenuSelected(context, value);
|
||||||
|
debugPrint('Menu contextuel sélectionné: $value');
|
||||||
|
},
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
const PopupMenuItem(
|
const PopupMenuItem(
|
||||||
value: 'Publier',
|
value: 'Publier',
|
||||||
@@ -71,39 +98,48 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
|||||||
child: Text('Story'),
|
child: Text('Story'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
icon: const Icon(Icons.add, color: Colors.blueAccent, size: 20), // Icône bleue légèrement plus petite
|
icon: const Icon(Icons.add, color: Colors.blueAccent, size: 20),
|
||||||
color: Colors.white, // Menu contextuel en blanc
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8), // Réduit l'espacement entre les boutons
|
const SizedBox(width: 8), // Espacement entre les boutons.
|
||||||
// Bouton Recherche
|
|
||||||
|
// Bouton Recherche.
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
radius: 18, // Réduit la taille du bouton
|
radius: 18,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: const Icon(Icons.search, color: Colors.blueAccent, size: 20), // Icône bleue légèrement plus petite
|
icon: const Icon(Icons.search, color: Colors.blueAccent, size: 20),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Implémente la logique de recherche ici
|
debugPrint('Bouton Recherche appuyé');
|
||||||
|
// Implémenter la logique de recherche ici.
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8), // Réduit l'espacement entre les boutons
|
const SizedBox(width: 8), // Espacement entre les boutons.
|
||||||
// Bouton Messagerie
|
|
||||||
|
// Bouton Messagerie.
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
radius: 18, // Réduit la taille du bouton
|
radius: 18,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: const Icon(Icons.message, color: Colors.blueAccent, size: 20), // Icône bleue légèrement plus petite
|
icon: const Icon(Icons.message, color: Colors.blueAccent, size: 20),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Implémente la logique de messagerie ici
|
debugPrint('Bouton Messagerie appuyé');
|
||||||
|
// Implémenter la logique de messagerie ici.
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8), // Réduit l'espacement entre les boutons
|
const SizedBox(width: 8), // Espacement entre les boutons.
|
||||||
],
|
],
|
||||||
bottom: TabBar(
|
bottom: TabBar(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
indicatorColor: Colors.blueAccent,
|
indicatorColor: Colors.blueAccent,
|
||||||
|
labelColor: Colors.white, // Couleur du texte sélectionné.
|
||||||
|
unselectedLabelColor: Colors.grey[400], // Couleur du texte non sélectionné.
|
||||||
|
onTap: (index) {
|
||||||
|
debugPrint('Onglet sélectionné: $index');
|
||||||
|
},
|
||||||
tabs: const [
|
tabs: const [
|
||||||
Tab(icon: Icon(Icons.home), text: 'Accueil'),
|
Tab(icon: Icon(Icons.home), text: 'Accueil'),
|
||||||
Tab(icon: Icon(Icons.event), text: 'Événements'),
|
Tab(icon: Icon(Icons.event), text: 'Événements'),
|
||||||
@@ -115,15 +151,20 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
|||||||
),
|
),
|
||||||
body: TabBarView(
|
body: TabBarView(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
children: const [
|
children: [
|
||||||
HomeContentScreen(), // Contenu de l'accueil
|
const HomeContentScreen(), // Contenu de l'accueil.
|
||||||
EventScreen(), // Écran des événements
|
EventScreen(
|
||||||
EstablishmentsScreen(), // Écran des établissements
|
eventRemoteDataSource: widget.eventRemoteDataSource,
|
||||||
SocialScreen(), // Écran social
|
userId: widget.userId,
|
||||||
ProfileScreen(), // Écran du profil
|
userName: widget.userName,
|
||||||
|
userLastName: widget.userLastName,
|
||||||
|
), // Écran des événements.
|
||||||
|
const EstablishmentsScreen(), // Écran des établissements.
|
||||||
|
const SocialScreen(), // Écran social.
|
||||||
|
const ProfileScreen(), // Écran du profil.
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.black, // Arrière-plan de l'écran en noir
|
backgroundColor: Colors.black, // Arrière-plan de l'écran en noir.
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
|
|
||||||
|
class LocationPickerScreen extends StatelessWidget {
|
||||||
|
const LocationPickerScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Sélectionnez une localisation'),
|
||||||
|
backgroundColor: const Color(0xFF1E1E2C),
|
||||||
|
),
|
||||||
|
body: GoogleMap(
|
||||||
|
initialCameraPosition: const CameraPosition(
|
||||||
|
target: LatLng(48.8566, 2.3522), // Paris par défaut
|
||||||
|
zoom: 12.0,
|
||||||
|
),
|
||||||
|
markers: <Marker>{
|
||||||
|
Marker(
|
||||||
|
markerId: const MarkerId('selectedLocation'),
|
||||||
|
position: const LatLng(48.8566, 2.3522), // Position par défaut
|
||||||
|
draggable: true,
|
||||||
|
onDragEnd: (newPosition) {
|
||||||
|
print('Nouvelle position sélectionnée: $newPosition');
|
||||||
|
Navigator.of(context).pop(newPosition);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onTap: (position) {
|
||||||
|
print('Position tapée: $position');
|
||||||
|
Navigator.of(context).pop(position);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,14 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:afterwork/data/datasources/user_remote_data_source.dart';
|
import 'package:afterwork/data/datasources/user_remote_data_source.dart';
|
||||||
import 'package:afterwork/data/models/user_model.dart';
|
import 'package:afterwork/data/models/user_model.dart';
|
||||||
import 'package:afterwork/presentation/screens/home/home_screen.dart';
|
import 'package:afterwork/presentation/screens/home/home_screen.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:afterwork/data/services/hash_password.dart';
|
||||||
|
import 'package:afterwork/data/services/secure_storage.dart';
|
||||||
|
import 'package:afterwork/data/services/preferences_helper.dart';
|
||||||
|
|
||||||
|
import '../../../data/datasources/event_remote_data_source.dart';
|
||||||
|
|
||||||
class LoginScreen extends StatefulWidget {
|
class LoginScreen extends StatefulWidget {
|
||||||
const LoginScreen({super.key});
|
const LoginScreen({super.key});
|
||||||
@@ -13,12 +19,15 @@ class LoginScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStateMixin {
|
class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStateMixin {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
String _userId = '';
|
||||||
String _email = '';
|
String _email = '';
|
||||||
String _password = '';
|
String _password = '';
|
||||||
bool _isPasswordVisible = false;
|
bool _isPasswordVisible = false;
|
||||||
bool _isSubmitting = false;
|
bool _isSubmitting = false;
|
||||||
|
|
||||||
final UserRemoteDataSource _userRemoteDataSource = UserRemoteDataSource(http.Client());
|
final UserRemoteDataSource _userRemoteDataSource = UserRemoteDataSource(http.Client());
|
||||||
|
final SecureStorage _secureStorage = SecureStorage();
|
||||||
|
final PreferencesHelper _preferencesHelper = PreferencesHelper();
|
||||||
|
|
||||||
late AnimationController _controller;
|
late AnimationController _controller;
|
||||||
late Animation<double> _buttonScaleAnimation;
|
late Animation<double> _buttonScaleAnimation;
|
||||||
@@ -41,12 +50,14 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Afficher/Masquer le mot de passe
|
||||||
void _togglePasswordVisibility() {
|
void _togglePasswordVisibility() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isPasswordVisible = !_isPasswordVisible;
|
_isPasswordVisible = !_isPasswordVisible;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Soumission du formulaire d'authentification
|
||||||
void _submit() async {
|
void _submit() async {
|
||||||
if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -54,25 +65,65 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
|||||||
});
|
});
|
||||||
_formKey.currentState!.save();
|
_formKey.currentState!.save();
|
||||||
|
|
||||||
try {
|
print("===== DEBUT DE LA SOUMISSION DU FORMULAIRE =====");
|
||||||
UserModel user = await _userRemoteDataSource.authenticateUser(_email, _password);
|
print("Email: $_email");
|
||||||
print('Connexion réussie : ${user.email}');
|
print("Mot de passe: $_password");
|
||||||
|
|
||||||
// Navigation vers la page d'accueil
|
try {
|
||||||
|
print('Début de l\'authentification'); // Débogage
|
||||||
|
|
||||||
|
// Hachage du mot de passe avec SHA-256
|
||||||
|
String hashedPassword = hashPassword(_password);
|
||||||
|
print("Mot de passe haché: $hashedPassword");
|
||||||
|
|
||||||
|
// Authentification via l'API avec un timeout
|
||||||
|
UserModel user = await _userRemoteDataSource
|
||||||
|
.authenticateUser(_email, hashedPassword, "unique_user_id")
|
||||||
|
.timeout(
|
||||||
|
Duration(seconds: 10),
|
||||||
|
onTimeout: () {
|
||||||
|
throw TimeoutException('Le temps de connexion a expiré. Veuillez réessayer.');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
print('Connexion réussie : ${user.userId} - ${user.email}');
|
||||||
|
|
||||||
|
// Sauvegarde des données de l'utilisateur après authentification
|
||||||
|
await _secureStorage.saveUserId(user.userId);
|
||||||
|
await _preferencesHelper.saveUserName(user.nom);
|
||||||
|
await _preferencesHelper.saveUserLastName(user.prenoms);
|
||||||
|
|
||||||
|
print("===== SAUVEGARDE DES DONNÉES UTILISATEUR =====");
|
||||||
|
print("User ID: ${user.userId}");
|
||||||
|
print("User Name: ${user.nom}");
|
||||||
|
print("User Last Name: ${user.prenoms}");
|
||||||
|
|
||||||
|
// Navigation vers l'écran d'accueil
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(builder: (context) => const HomeScreen()),
|
MaterialPageRoute(
|
||||||
|
builder: (context) => HomeScreen(
|
||||||
|
eventRemoteDataSource: EventRemoteDataSource(http.Client()),
|
||||||
|
userId: user.userId,
|
||||||
|
userName: user.nom,
|
||||||
|
userLastName: user.prenoms,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
print("===== NAVIGATION VERS HOME SCREEN =====");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Erreur lors de la connexion: $e');
|
print('Erreur lors de la connexion: $e');
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text(e.toString())),
|
SnackBar(content: Text('Erreur : ${e.toString()}')),
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
|
print('Fin du processus d\'authentification'); // Débogage
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSubmitting = false;
|
_isSubmitting = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
print("===== FORMULAIRE NON VALIDE =====");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +153,7 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
// Logo avec légère animation
|
// Logo animé
|
||||||
AnimatedBuilder(
|
AnimatedBuilder(
|
||||||
animation: _controller,
|
animation: _controller,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
@@ -154,15 +205,18 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
|||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
|
print("Erreur: Le champ email est vide");
|
||||||
return 'Veuillez entrer votre email';
|
return 'Veuillez entrer votre email';
|
||||||
}
|
}
|
||||||
if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
|
if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
|
||||||
|
print("Erreur: Le format de l'email est invalide");
|
||||||
return 'Veuillez entrer un email valide';
|
return 'Veuillez entrer un email valide';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onSaved: (value) {
|
onSaved: (value) {
|
||||||
_email = value ?? ''; // Utiliser une chaîne vide si value est null
|
_email = value ?? ''; // Utiliser une chaîne vide si value est null
|
||||||
|
print("Email sauvegardé: $_email");
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
@@ -180,9 +234,8 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
|||||||
prefixIcon: const Icon(Icons.lock, color: Colors.white),
|
prefixIcon: const Icon(Icons.lock, color: Colors.white),
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
_isPasswordVisible ? Icons.visibility : Icons.visibility_off,
|
_isPasswordVisible ? Icons.visibility : Icons.visibility_off,
|
||||||
color: Colors.white,
|
color: Colors.white),
|
||||||
),
|
|
||||||
onPressed: _togglePasswordVisibility,
|
onPressed: _togglePasswordVisibility,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -190,15 +243,18 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
|||||||
style: const TextStyle(color: Colors.white),
|
style: const TextStyle(color: Colors.white),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
|
print("Erreur: Le champ mot de passe est vide");
|
||||||
return 'Veuillez entrer votre mot de passe';
|
return 'Veuillez entrer votre mot de passe';
|
||||||
}
|
}
|
||||||
if (value.length < 6) {
|
if (value.length < 6) {
|
||||||
|
print("Erreur: Le mot de passe est trop court");
|
||||||
return 'Le mot de passe doit comporter au moins 6 caractères';
|
return 'Le mot de passe doit comporter au moins 6 caractères';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onSaved: (value) {
|
onSaved: (value) {
|
||||||
_password = value ?? ''; // Utiliser une chaîne vide si value est null
|
_password = value ?? ''; // Utiliser une chaîne vide si value est null
|
||||||
|
print("Mot de passe sauvegardé: $_password");
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
@@ -225,6 +281,7 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Naviguer vers la page d'inscription
|
// Naviguer vers la page d'inscription
|
||||||
|
print("Redirection vers la page d'inscription");
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
'Pas encore de compte ? Inscrivez-vous',
|
'Pas encore de compte ? Inscrivez-vous',
|
||||||
|
|||||||
@@ -7,274 +7,388 @@ class ProfileScreen extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Profil'),
|
title: const Text('Profil',
|
||||||
backgroundColor: Colors.black,
|
style: TextStyle(
|
||||||
|
color: Color(0xFF1DBF73), // Définit la couleur verte du texte
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: const Color(0xFF1E1E2C),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.settings),
|
icon: const Icon(Icons.settings, color: Colors.white),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Naviguer vers la page des paramètres
|
// Naviguer vers la page des paramètres
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: CustomScrollView(
|
body: ListView(
|
||||||
slivers: [
|
padding: const EdgeInsets.all(16.0),
|
||||||
SliverList(
|
children: [
|
||||||
delegate: SliverChildListDelegate([
|
_buildUserInfoCard(),
|
||||||
// Informations de l'Utilisateur
|
const SizedBox(height: 20),
|
||||||
_buildUserInfoSection(context),
|
_buildEditOptionsCard(),
|
||||||
const Divider(),
|
const SizedBox(height: 20),
|
||||||
// Options de Modification
|
_buildStatisticsSectionCard(),
|
||||||
_buildEditOptions(context),
|
const SizedBox(height: 20),
|
||||||
const Divider(),
|
_buildExpandableSectionCard(
|
||||||
// Statistiques Personnelles
|
title: 'Historique',
|
||||||
_buildStatisticsSection(context),
|
icon: Icons.history,
|
||||||
const Divider(),
|
children: [
|
||||||
// Historique
|
_buildAnimatedListTile(
|
||||||
_buildHistorySection(context),
|
icon: Icons.event_note,
|
||||||
const Divider(),
|
label: 'Historique des Événements',
|
||||||
// Préférences et Paramètres
|
onTap: () {
|
||||||
_buildPreferencesSection(context),
|
// Naviguer vers l'historique des événements
|
||||||
const Divider(),
|
},
|
||||||
// Autres Fonctions
|
),
|
||||||
_buildSupportSection(context),
|
_buildAnimatedListTile(
|
||||||
const Divider(),
|
icon: Icons.history,
|
||||||
// Suppression de Compte
|
label: 'Historique des Publications',
|
||||||
_buildAccountDeletionSection(context),
|
onTap: () {
|
||||||
]),
|
// Naviguer vers l'historique des publications
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.bookmark,
|
||||||
|
label: 'Historique de Réservations',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers l'historique des réservations
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildExpandableSectionCard(
|
||||||
|
title: 'Préférences et Paramètres',
|
||||||
|
icon: Icons.settings,
|
||||||
|
children: [
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.privacy_tip,
|
||||||
|
label: 'Paramètres de confidentialité',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers les paramètres de confidentialité
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.notifications,
|
||||||
|
label: 'Notifications',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers les paramètres de notification
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.language,
|
||||||
|
label: 'Langue de l\'application',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers les paramètres de langue
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.format_paint,
|
||||||
|
label: 'Thème de l\'application',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers les paramètres de thème
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildSupportSectionCard(),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildAccountDeletionCard(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
backgroundColor: const Color(0xFF1E1E2C),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildUserInfoCard() {
|
||||||
|
return Card(
|
||||||
|
color: const Color(0xFF292B37),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const CircleAvatar(
|
||||||
|
radius: 50,
|
||||||
|
backgroundImage: AssetImage('lib/assets/images/profile_picture.png'),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
const Text(
|
||||||
|
'GBANE Dahoud',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
letterSpacing: 1.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Text(
|
||||||
|
'pseudo',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Text(
|
||||||
|
'gbanedahoud@lions.dev',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEditOptionsCard() {
|
||||||
|
return Card(
|
||||||
|
color: const Color(0xFF292B37),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.edit,
|
||||||
|
label: 'Éditer le profil',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers la page d'édition de profil
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.camera_alt,
|
||||||
|
label: 'Changer la photo de profil',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers la page de changement de photo de profil
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.lock,
|
||||||
|
label: 'Changer le mot de passe',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers la page de changement de mot de passe
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildUserInfoSection(BuildContext context) {
|
Widget _buildStatisticsSectionCard() {
|
||||||
return const Column(
|
return Card(
|
||||||
children: [
|
color: const Color(0xFF292B37),
|
||||||
CircleAvatar(
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
radius: 50,
|
child: Padding(
|
||||||
backgroundImage: AssetImage('lib/assets/images/profile_picture.png'), //Photo de profil
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Statistiques Personnelles',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildStatTile(
|
||||||
|
icon: Icons.event,
|
||||||
|
label: 'Événements Participés',
|
||||||
|
value: '12',
|
||||||
|
),
|
||||||
|
_buildStatTile(
|
||||||
|
icon: Icons.place,
|
||||||
|
label: 'Établissements Visités',
|
||||||
|
value: '8',
|
||||||
|
),
|
||||||
|
_buildStatTile(
|
||||||
|
icon: Icons.post_add,
|
||||||
|
label: 'Publications',
|
||||||
|
value: '24',
|
||||||
|
),
|
||||||
|
_buildStatTile(
|
||||||
|
icon: Icons.group,
|
||||||
|
label: 'Amis/Followers',
|
||||||
|
value: '150',
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
|
||||||
Text(
|
|
||||||
'GBANE Dahoud',
|
|
||||||
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.white),
|
|
||||||
),
|
|
||||||
SizedBox(height: 5),
|
|
||||||
Text(
|
|
||||||
'pseudo',
|
|
||||||
style: TextStyle(fontSize: 16, color: Colors.grey),
|
|
||||||
),
|
|
||||||
SizedBox(height: 5),
|
|
||||||
Text(
|
|
||||||
'gbanedahoud@lions.dev',
|
|
||||||
style: TextStyle(fontSize: 14, color: Colors.grey),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildEditOptions(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.edit, color: Colors.blueAccent),
|
|
||||||
title: const Text('Éditer le profil'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers la page d'édition de profil
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.camera_alt, color: Colors.blueAccent),
|
|
||||||
title: const Text('Changer la photo de profil'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers la page de changement de photo de profil
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.lock, color: Colors.blueAccent),
|
|
||||||
title: const Text('Changer le mot de passe'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers la page de changement de mot de passe
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildStatisticsSection(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.all(16.0),
|
|
||||||
child: Text(
|
|
||||||
'Statistiques Personnelles',
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.event, color: Colors.blueAccent),
|
|
||||||
title: const Text('Événements Participés'),
|
|
||||||
trailing: const Text('12'), // Exemple de valeur
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers la page des événements participés
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.place, color: Colors.blueAccent),
|
|
||||||
title: const Text('Établissements Visités'),
|
|
||||||
trailing: const Text('8'), // Exemple de valeur
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers la page des établissements visités
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.post_add, color: Colors.blueAccent),
|
|
||||||
title: const Text('Publications'),
|
|
||||||
trailing: const Text('24'), // Exemple de valeur
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers la page des publications
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.group, color: Colors.blueAccent),
|
|
||||||
title: const Text('Amis/Followers'),
|
|
||||||
trailing: const Text('150'), // Exemple de valeur
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers la page des amis ou followers
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildHistorySection(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.all(16.0),
|
|
||||||
child: Text(
|
|
||||||
'Historique',
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.event_note, color: Colors.blueAccent),
|
|
||||||
title: const Text('Historique des Événements'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers l'historique des événements
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.history, color: Colors.blueAccent),
|
|
||||||
title: const Text('Historique des Publications'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers l'historique des publications
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.bookmark, color: Colors.blueAccent),
|
|
||||||
title: const Text('Historique de Réservations'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers l'historique des réservations
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildPreferencesSection(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.all(16.0),
|
|
||||||
child: Text(
|
|
||||||
'Préférences et Paramètres',
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.privacy_tip, color: Colors.blueAccent),
|
|
||||||
title: const Text('Paramètres de confidentialité'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers les paramètres de confidentialité
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.notifications, color: Colors.blueAccent),
|
|
||||||
title: const Text('Notifications'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers les paramètres de notification
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.language, color: Colors.blueAccent),
|
|
||||||
title: const Text('Langue de l\'application'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers les paramètres de langue
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.format_paint, color: Colors.blueAccent),
|
|
||||||
title: const Text('Thème de l\'application'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers les paramètres de thème
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildSupportSection(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.all(16.0),
|
|
||||||
child: Text(
|
|
||||||
'Support et Assistance',
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.help, color: Colors.blueAccent),
|
|
||||||
title: const Text('Support et Assistance'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers la page de support
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.article, color: Colors.blueAccent),
|
|
||||||
title: const Text('Conditions d\'utilisation'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers les conditions d'utilisation
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
leading: const Icon(Icons.privacy_tip, color: Colors.blueAccent),
|
|
||||||
title: const Text('Politique de confidentialité'),
|
|
||||||
onTap: () {
|
|
||||||
// Naviguer vers la politique de confidentialité
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAccountDeletionSection(BuildContext context) {
|
|
||||||
return ListTile(
|
|
||||||
leading: const Icon(Icons.delete, color: Colors.redAccent),
|
|
||||||
title: const Text(
|
|
||||||
'Supprimer le compte',
|
|
||||||
style: TextStyle(color: Colors.redAccent),
|
|
||||||
),
|
),
|
||||||
onTap: () {
|
);
|
||||||
// Implémenter la logique de suppression de compte
|
}
|
||||||
|
|
||||||
|
Widget _buildExpandableSectionCard({
|
||||||
|
required String title,
|
||||||
|
required IconData icon,
|
||||||
|
required List<Widget> children,
|
||||||
|
}) {
|
||||||
|
return Card(
|
||||||
|
color: const Color(0xFF292B37),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
child: ExpansionTile(
|
||||||
|
title: Text(
|
||||||
|
title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
leading: Icon(icon, color: const Color(0xFF1DBF73)),
|
||||||
|
iconColor: const Color(0xFF1DBF73),
|
||||||
|
collapsedIconColor: const Color(0xFF1DBF73),
|
||||||
|
children: children,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSupportSectionCard() {
|
||||||
|
return Card(
|
||||||
|
color: const Color(0xFF292B37),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child: Text(
|
||||||
|
'Support et Assistance',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.help,
|
||||||
|
label: 'Support et Assistance',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers la page de support
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.article,
|
||||||
|
label: 'Conditions d\'utilisation',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers les conditions d'utilisation
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildAnimatedListTile(
|
||||||
|
icon: Icons.privacy_tip,
|
||||||
|
label: 'Politique de confidentialité',
|
||||||
|
onTap: () {
|
||||||
|
// Naviguer vers la politique de confidentialité
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAccountDeletionCard(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
color: const Color(0xFF292B37),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
child: ListTile(
|
||||||
|
leading: const Icon(Icons.delete, color: Colors.redAccent),
|
||||||
|
title: const Text(
|
||||||
|
'Supprimer le compte',
|
||||||
|
style: TextStyle(color: Colors.redAccent, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
_showDeleteConfirmationDialog(context);
|
||||||
|
},
|
||||||
|
hoverColor: Colors.red.withOpacity(0.1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showDeleteConfirmationDialog(BuildContext context) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
backgroundColor: const Color(0xFF1E1E2C),
|
||||||
|
title: const Text(
|
||||||
|
'Confirmer la suppression',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
content: const Text(
|
||||||
|
'Êtes-vous sûr de vouloir supprimer votre compte ? Cette action est irréversible.',
|
||||||
|
style: TextStyle(color: Colors.white70),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(); // Fermer le popup
|
||||||
|
},
|
||||||
|
child: const Text(
|
||||||
|
'Annuler',
|
||||||
|
style: TextStyle(color: Color(0xFF1DBF73)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Logique de suppression du compte ici
|
||||||
|
Navigator.of(context).pop(); // Fermer le popup après la suppression
|
||||||
|
},
|
||||||
|
child: const Text(
|
||||||
|
'Supprimer',
|
||||||
|
style: TextStyle(color: Colors.redAccent),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildAnimatedListTile({
|
||||||
|
required IconData icon,
|
||||||
|
required String label,
|
||||||
|
required VoidCallback onTap,
|
||||||
|
}) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
splashColor: Colors.blueAccent.withOpacity(0.2),
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(icon, color: const Color(0xFF1DBF73)),
|
||||||
|
title: Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
|
||||||
|
),
|
||||||
|
hoverColor: Colors.blue.withOpacity(0.1),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatTile({
|
||||||
|
required IconData icon,
|
||||||
|
required String label,
|
||||||
|
required String value,
|
||||||
|
}) {
|
||||||
|
return ListTile(
|
||||||
|
leading: Icon(icon, color: const Color(0xFF1DBF73)),
|
||||||
|
title: Text(label, style: const TextStyle(color: Colors.white)),
|
||||||
|
trailing: Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
hoverColor: Colors.blue.withOpacity(0.1),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
flutter_secure_storage_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import flutter_secure_storage_macos
|
||||||
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
176
pubspec.lock
176
pubspec.lock
@@ -49,6 +49,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
|
crypto:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.5"
|
||||||
csslib:
|
csslib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -89,6 +97,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.3"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -118,6 +142,54 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.22"
|
version: "2.0.22"
|
||||||
|
flutter_secure_storage:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage
|
||||||
|
sha256: f2afec1f1762c040a349ea2a588e32f442da5d0db3494a52a929a97c9e550bc5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
|
flutter_secure_storage_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_linux
|
||||||
|
sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
flutter_secure_storage_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_macos
|
||||||
|
sha256: ff0768a6700ea1d9620e03518e2e25eac86a8bd07ca3556e9617bfa5ace4bd00
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
|
flutter_secure_storage_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_platform_interface
|
||||||
|
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
flutter_secure_storage_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_web
|
||||||
|
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
flutter_secure_storage_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_windows
|
||||||
|
sha256: ca89c8059cf439985aa83c59619b3674c7ef6cc2e86943d169a7369d6a69cab5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.3"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -208,6 +280,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.7"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -280,6 +360,38 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.5"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -304,6 +416,62 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_foundation
|
||||||
|
sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.2"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -397,6 +565,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.1 <4.0.0"
|
dart: ">=3.5.1 <4.0.0"
|
||||||
flutter: ">=3.22.0"
|
flutter: ">=3.22.0"
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
flutter_secure_storage: ^7.0.1
|
||||||
|
crypto: ^3.0.1
|
||||||
google_maps_flutter: ^2.0.10
|
google_maps_flutter: ^2.0.10
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
|
|
||||||
@@ -33,6 +35,7 @@ dev_dependencies:
|
|||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
shared_preferences: ^2.0.13
|
||||||
flutter_lints: ^4.0.0
|
flutter_lints: ^4.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
flutter_secure_storage_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|||||||
Reference in New Issue
Block a user