Refactoring + Version améliorée

This commit is contained in:
DahoudG
2024-09-25 21:28:04 +00:00
parent 6b12cfeb41
commit 8e625c1080
28 changed files with 1113 additions and 261 deletions

View File

@@ -1,10 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:afterwork/presentation/screens/login/login_screen.dart'; import 'package:afterwork/presentation/screens/login/login_screen.dart';
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/presentation/screens/home/home_screen.dart'; import 'package:afterwork/presentation/screens/home/home_screen.dart';
import 'package:afterwork/presentation/screens/event/event_screen.dart';
import 'package:afterwork/data/datasources/event_remote_data_source.dart'; import 'package:afterwork/data/datasources/event_remote_data_source.dart';
import '../presentation/reservations/reservations_screen.dart'; import '../presentation/reservations/reservations_screen.dart';

View File

@@ -16,6 +16,7 @@ class AppColors {
static const Color lightError = Color(0xFFB00020); static const Color lightError = Color(0xFFB00020);
static const Color lightIconPrimary = Color(0xFF212121); // Icône primaire sombre static const Color lightIconPrimary = Color(0xFF212121); // Icône primaire sombre
static const Color lightIconSecondary = Color(0xFF757575); // Icône secondaire gris clair static const Color lightIconSecondary = Color(0xFF757575); // Icône secondaire gris clair
// Thème sombre // Thème sombre
static const Color darkPrimary = Color(0xFF121212); static const Color darkPrimary = Color(0xFF121212);
static const Color darkSecondary = Color(0xFFFF5722); static const Color darkSecondary = Color(0xFFFF5722);
@@ -31,6 +32,9 @@ class AppColors {
static const Color darkIconPrimary = Colors.white; // Icône primaire blanche static const Color darkIconPrimary = Colors.white; // Icône primaire blanche
static const Color darkIconSecondary = Color(0xFFBDBDBD); // Icône secondaire gris clair static const Color darkIconSecondary = Color(0xFFBDBDBD); // Icône secondaire gris clair
// Ajout du background personnalisé
static const Color backgroundCustom = Color(0xFF2C2C3E);
// Sélection automatique des couleurs en fonction du mode de thème // Sélection automatique des couleurs en fonction du mode de thème
static Color get primary => isDarkMode() ? darkPrimary : lightPrimary; static Color get primary => isDarkMode() ? darkPrimary : lightPrimary;
static Color get secondary => isDarkMode() ? darkSecondary : lightSecondary; static Color get secondary => isDarkMode() ? darkSecondary : lightSecondary;
@@ -45,6 +49,7 @@ class AppColors {
static Color get errorColor => isDarkMode() ? darkError : lightError; static Color get errorColor => isDarkMode() ? darkError : lightError;
static Color get iconPrimary => isDarkMode() ? darkIconPrimary : lightIconPrimary; static Color get iconPrimary => isDarkMode() ? darkIconPrimary : lightIconPrimary;
static Color get iconSecondary => isDarkMode() ? darkIconSecondary : lightIconSecondary; static Color get iconSecondary => isDarkMode() ? darkIconSecondary : lightIconSecondary;
static Color get customBackgroundColor => backgroundCustom;
/// Méthode utilitaire pour vérifier si le mode sombre est activé. /// Méthode utilitaire pour vérifier si le mode sombre est activé.
static bool isDarkMode() { static bool isDarkMode() {

View File

@@ -17,6 +17,7 @@ class Urls {
static const String removeParticipant = '$baseUrl/events'; // Append '/{id}/participants/{userId}' dynamically static const String removeParticipant = '$baseUrl/events'; // Append '/{id}/participants/{userId}' dynamically
static const String getNumberOfParticipants = '$baseUrl/events'; // Append '/{id}/participants/count' dynamically static const String getNumberOfParticipants = '$baseUrl/events'; // Append '/{id}/participants/count' dynamically
static const String closeEvent = '$baseUrl/events'; // Append '/{id}/close' dynamically static const String closeEvent = '$baseUrl/events'; // Append '/{id}/close' dynamically
static const String reopenEvent = '$baseUrl/events'; // Append '/{id}/reopen' dynamically
static const String updateEvent = '$baseUrl/events'; // Append '/{id}' dynamically static const String updateEvent = '$baseUrl/events'; // Append '/{id}' dynamically
static const String updateEventImage = '$baseUrl/events'; // Append '/{id}/image' dynamically static const String updateEventImage = '$baseUrl/events'; // Append '/{id}/image' dynamically
static const String getAllEvents = '$baseUrl/events'; static const String getAllEvents = '$baseUrl/events';

View File

@@ -174,7 +174,7 @@ class EventRemoteDataSource {
print('Réouverture de l\'événement avec l\'ID: $eventId'); print('Réouverture de l\'événement avec l\'ID: $eventId');
final response = await client.post( final response = await client.post(
Uri.parse('${Urls.baseUrl}/$eventId/reopen'), Uri.parse('${Urls.reopenEvent}/$eventId/reopen'),
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
); );

View File

@@ -7,7 +7,7 @@ class EventModel {
final String category; final String category;
final String link; final String link;
final String? imageUrl; // Nullable final String? imageUrl; // Nullable
final String creatorEmail; // Remplacer UserModel si le créateur est un email final String creatorEmail;
final List<dynamic> participants; // Si participants est une liste simple final List<dynamic> participants; // Si participants est une liste simple
final String status; final String status;
final int reactionsCount; final int reactionsCount;
@@ -43,7 +43,7 @@ class EventModel {
imageUrl: json['imageUrl'], // Peut être null imageUrl: json['imageUrl'], // Peut être null
creatorEmail: json['creatorEmail'], // Email du créateur creatorEmail: json['creatorEmail'], // Email du créateur
participants: json['participants'] ?? [], // Gérer les participants participants: json['participants'] ?? [], // Gérer les participants
status: json['status'] ?? 'open', // Par défaut à "open" si non fourni status: json['status'] ?? 'ouvert', // Par défaut à "ouvert" si non fourni
reactionsCount: json['reactionsCount'] ?? 0, reactionsCount: json['reactionsCount'] ?? 0,
commentsCount: json['commentsCount'] ?? 0, commentsCount: json['commentsCount'] ?? 0,
sharesCount: json['sharesCount'] ?? 0, sharesCount: json['sharesCount'] ?? 0,

View File

@@ -1,110 +1,118 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:io';
/// Dialogue pour ajouter un nouvel événement. import '../../widgets/category_field.dart';
/// Ce widget affiche un formulaire permettant à l'utilisateur de saisir les détails d'un événement. import '../../widgets/date_picker.dart';
/// Les logs permettent de suivre les actions de l'utilisateur dans ce dialogue. import '../../widgets/description_field.dart';
class AddEventDialog extends StatefulWidget { import '../../widgets/link_field.dart';
import '../../widgets/location_field.dart';
import '../../widgets/submit_button.dart';
import '../../widgets/title_field.dart';
import '../../widgets/image_preview_picker.dart';
class AddEventPage extends StatefulWidget {
final String userId; final String userId;
final String userName; final String userName;
final String userLastName; final String userLastName;
const AddEventDialog({ const AddEventPage({
Key? key, super.key,
required this.userId, required this.userId,
required this.userName, required this.userName,
required this.userLastName, required this.userLastName,
}) : super(key: key); });
@override @override
_AddEventDialogState createState() => _AddEventDialogState(); _AddEventPageState createState() => _AddEventPageState();
} }
class _AddEventDialogState extends State<AddEventDialog> { class _AddEventPageState extends State<AddEventPage> {
final _formKey = GlobalKey<FormState>(); // Clé pour valider le formulaire final _formKey = GlobalKey<FormState>(); // Form key for validation
String _eventName = ''; // Nom de l'événement
DateTime _selectedDate = DateTime.now(); // Date de l'événement String _title = '';
String _description = '';
DateTime? _selectedDate;
String _location = 'Abidjan';
String _category = '';
String _link = '';
LatLng? _selectedLatLng = const LatLng(5.348722, -3.985038); // Default coordinates
File? _selectedImageFile; // Store the selected image
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
print("Affichage du dialogue d'ajout d'événement."); return Scaffold(
appBar: AppBar(
return AlertDialog( title: const Text('Ajouter un événement'),
title: const Text('Ajouter un événement'), backgroundColor: const Color(0xFF1E1E2C),
content: Form( leading: IconButton(
key: _formKey, icon: const Icon(Icons.arrow_back),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Champ pour entrer le nom de l'événement
TextFormField(
decoration: const InputDecoration(labelText: 'Nom de l\'événement'),
onSaved: (value) {
_eventName = value ?? '';
print("Nom de l'événement saisi : $_eventName");
},
validator: (value) {
if (value == null || value.isEmpty) {
print("Erreur : le champ du nom de l'événement est vide.");
return 'Veuillez entrer un nom d\'événement';
}
return null;
},
),
const SizedBox(height: 20),
// Sélecteur de date pour l'événement
ElevatedButton(
onPressed: () async {
final selectedDate = await _selectDate(context);
if (selectedDate != null) {
setState(() {
_selectedDate = selectedDate;
print("Date de l'événement sélectionnée : $_selectedDate");
});
}
},
child: Text('Sélectionner la date : ${_selectedDate.toLocal()}'.split(' ')[0]),
),
],
),
),
actions: [
// Bouton pour annuler l'ajout de l'événement
TextButton(
onPressed: () { onPressed: () {
print("L'utilisateur a annulé l'ajout de l'événement.");
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: const Text('Annuler'),
), ),
// Bouton pour soumettre le formulaire et ajouter l'événement ),
TextButton( backgroundColor: const Color(0xFF1E1E2C),
onPressed: () { body: Column(
if (_formKey.currentState?.validate() == true) { children: [
_formKey.currentState?.save(); Expanded(
print("L'utilisateur a ajouté un événement : Nom = $_eventName, Date = $_selectedDate"); child: SingleChildScrollView(
Navigator.of(context).pop({ padding: const EdgeInsets.all(16.0),
'eventName': _eventName, child: Form(
'eventDate': _selectedDate, key: _formKey,
}); child: Column(
} crossAxisAlignment: CrossAxisAlignment.stretch,
}, children: [
child: const Text('Ajouter'), ImagePreviewPicker(
), onImagePicked: (File? imageFile) {
], setState(() {
_selectedImageFile = imageFile;
});
},
),
const SizedBox(height: 20),
TitleField(onSaved: (value) => setState(() => _title = value ?? '')),
const SizedBox(height: 12),
DescriptionField(onSaved: (value) => setState(() => _description = value ?? '')),
const SizedBox(height: 12),
DatePickerField(
selectedDate: _selectedDate,
onDatePicked: (picked) => setState(() {
_selectedDate = picked;
}),
),
const SizedBox(height: 12),
LocationField(
location: _location,
selectedLatLng: _selectedLatLng,
onLocationPicked: (pickedLocation) => setState(() {
_selectedLatLng = pickedLocation;
_location = '${pickedLocation?.latitude}, ${pickedLocation?.longitude}';
}),
),
const SizedBox(height: 12),
CategoryField(onSaved: (value) => setState(() => _category = value ?? '')),
const SizedBox(height: 12),
LinkField(onSaved: (value) => setState(() => _link = value ?? '')),
],
),
),
),
),
// Bouton en bas de l'écran
Padding(
padding: const EdgeInsets.all(16.0),
child: SubmitButton(
onPressed: () async {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// Logic to add the event goes here
}
},
),
),
],
),
); );
} }
/// Fonction pour afficher le sélecteur de date
Future<DateTime?> _selectDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime(2000),
lastDate: DateTime(2101),
);
if (picked != null) {
print("Date choisie dans le sélecteur : $picked");
}
return picked;
}
} }

View File

@@ -1,19 +1,25 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:afterwork/data/models/event_model.dart';
import 'package:afterwork/core/utils/date_formatter.dart'; // Importer DateFormatter
/// Widget pour afficher une carte d'événement. import '../../../data/models/event_model.dart';
import '../../widgets/event_header.dart';
import '../../widgets/event_image.dart';
import '../../widgets/event_interaction_row.dart';
import '../../widgets/event_status_badge.dart';
import '../../widgets/swipe_background.dart';
class EventCard extends StatelessWidget { class EventCard extends StatelessWidget {
final EventModel event; final EventModel event;
final String userId; final String userId;
final String userName; final String userName;
final String userLastName; final String userLastName;
final String status;
final VoidCallback onReact; final VoidCallback onReact;
final VoidCallback onComment; final VoidCallback onComment;
final VoidCallback onShare; final VoidCallback onShare;
final VoidCallback onParticipate; final VoidCallback onParticipate;
final VoidCallback onCloseEvent; final VoidCallback onCloseEvent;
final VoidCallback onReopenEvent; final VoidCallback onReopenEvent;
final Function onRemoveEvent;
const EventCard({ const EventCard({
Key? key, Key? key,
@@ -21,152 +27,93 @@ class EventCard extends StatelessWidget {
required this.userId, required this.userId,
required this.userName, required this.userName,
required this.userLastName, required this.userLastName,
required this.status,
required this.onReact, required this.onReact,
required this.onComment, required this.onComment,
required this.onShare, required this.onShare,
required this.onParticipate, required this.onParticipate,
required this.onCloseEvent, required this.onCloseEvent,
required this.onReopenEvent, required this.onReopenEvent,
required this.onRemoveEvent,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( final GlobalKey menuKey = GlobalKey();
color: const Color(0xFF2C2C3E),
margin: const EdgeInsets.symmetric(vertical: 10.0), return Dismissible(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), key: ValueKey(event.id),
child: Padding( direction: event.status == 'fermé'
padding: const EdgeInsets.all(12.0), ? DismissDirection.startToEnd
child: Column( : DismissDirection.endToStart,
crossAxisAlignment: CrossAxisAlignment.start, onDismissed: (direction) {
children: [ if (event.status == 'fermé') {
_buildHeader(), onReopenEvent();
const SizedBox(height: 10), } else {
Text( onCloseEvent();
event.title, onRemoveEvent(event.id);
style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold), }
), },
const SizedBox(height: 5), background: SwipeBackground(
Text( color: event.status == 'fermé' ? Colors.green : Colors.red,
event.description, icon: event.status == 'fermé' ? Icons.lock_open : Icons.lock,
style: const TextStyle(color: Colors.white70, fontSize: 14), label: event.status == 'fermé' ? 'Rouvrir' : 'Fermer',
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 10),
_buildEventImage(),
const Divider(color: Colors.white24),
_buildInteractionRow(),
const SizedBox(height: 10),
_buildStatusAndActions(),
],
),
), ),
); child: Card(
} color: const Color(0xFF2C2C3E),
margin: const EdgeInsets.symmetric(vertical: 10.0),
Widget _buildHeader() { shape:
// Convertir la date de l'événement (de String à DateTime) RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
DateTime? eventDate; child: Padding(
try { padding: const EdgeInsets.all(12.0),
eventDate = DateTime.parse(event.startDate); child: Column(
} catch (e) { crossAxisAlignment: CrossAxisAlignment.start,
eventDate = null; // Gérer le cas où la date ne serait pas valide children: [
} EventHeader(
userName: userName,
// Utiliser DateFormatter pour afficher une date lisible si elle est valide userLastName: userLastName,
String formattedDate = eventDate != null ? DateFormatter.formatDate(eventDate) : 'Date inconnue'; eventDate: event.startDate,
imageUrl: event.imageUrl,
return Row( menuKey: menuKey,
children: [ menuContext: context,
CircleAvatar(backgroundImage: NetworkImage(event.imageUrl ?? 'lib/assets/images/placeholder.png')), location: event.location,
const SizedBox(width: 10), ),
Column( const Divider(color: Colors.white24),
crossAxisAlignment: CrossAxisAlignment.start, Row(
children: [ children: [
Text( const Spacer(), // Pusher le badge statut à la droite.
'$userName $userLastName', EventStatusBadge(status: status),
style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), ],
), ),
const SizedBox(height: 5), Text(
Text( event.title,
formattedDate, // Utiliser la date formatée ici style: const TextStyle(
style: const TextStyle(color: Colors.white70, fontSize: 14), color: Colors.white,
), fontSize: 18,
], fontWeight: FontWeight.bold),
), ),
const Spacer(), const SizedBox(height: 5),
IconButton( Text(
icon: const Icon(Icons.more_vert, color: Colors.white), event.description,
onPressed: () { style: const TextStyle(color: Colors.white70, fontSize: 14),
// Logique d'affichage d'options supplémentaires pour l'événement. maxLines: 3,
// Vous pouvez utiliser un menu déroulant ou une boîte de dialogue ici. overflow: TextOverflow.ellipsis,
}, ),
), const SizedBox(height: 10),
], EventImage(imageUrl: event.imageUrl),
); const Divider(color: Colors.white24),
} EventInteractionRow(
onReact: onReact,
Widget _buildEventImage() { onComment: onComment,
return ClipRRect( onShare: onShare,
borderRadius: BorderRadius.circular(10.0), reactionsCount: event.reactionsCount,
child: Image.network( commentsCount: event.commentsCount,
event.imageUrl ?? 'lib/assets/images/placeholder.png', sharesCount: event.sharesCount,
height: 180, ),
width: double.infinity, ],
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Image.asset('lib/assets/images/placeholder.png'); // Image par défaut si erreur de chargement
}
),
);
}
Widget _buildInteractionRow() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildIconButton(Icons.thumb_up_alt_outlined, 'Réagir', event.reactionsCount, onReact),
_buildIconButton(Icons.comment_outlined, 'Commenter', event.commentsCount, onComment),
_buildIconButton(Icons.share_outlined, 'Partager', event.sharesCount, onShare),
],
);
}
Widget _buildIconButton(IconData icon, String label, int count, VoidCallback onPressed) {
return TextButton.icon(
onPressed: onPressed,
icon: Icon(icon, color: const Color(0xFF1DBF73), size: 20),
label: Text(
'$label ($count)',
style: const TextStyle(color: Colors.white70, fontSize: 12),
),
);
}
// Widget pour afficher le statut de l'événement et les actions associées (fermer, réouvrir)
Widget _buildStatusAndActions() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
event.status == 'closed' ? 'Événement fermé' : 'Événement ouvert',
style: TextStyle(
color: event.status == 'closed' ? Colors.red : Colors.green,
fontSize: 14,
fontWeight: FontWeight.bold,
), ),
), ),
event.status == 'closed' ),
? ElevatedButton(
onPressed: onReopenEvent,
child: const Text('Rouvrir l\'événement'),
)
: ElevatedButton(
onPressed: onCloseEvent,
child: const Text('Fermer l\'événement'),
),
],
); );
} }
} }

View File

@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:afterwork/data/models/event_model.dart'; import 'package:afterwork/data/models/event_model.dart';
import 'package:afterwork/presentation/screens/event/event_card.dart'; import 'package:afterwork/presentation/screens/event/event_card.dart';
import '../../state_management/event_bloc.dart';
import '../dialogs/add_event_dialog.dart'; import '../dialogs/add_event_dialog.dart';
import '../../state_management/event_bloc.dart';
class EventScreen extends StatefulWidget { class EventScreen extends StatefulWidget {
final String userId; final String userId;
@@ -43,25 +43,18 @@ class _EventScreenState extends State<EventScreen> {
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: () async { onPressed: () {
final eventData = await showDialog<Map<String, dynamic>>( // Naviguer vers une nouvelle page pour ajouter un événement
context: context, Navigator.push(
builder: (BuildContext context) { context,
return AddEventDialog( MaterialPageRoute(
builder: (context) => AddEventPage(
userId: widget.userId, userId: widget.userId,
userName: widget.userName, userName: widget.userName,
userLastName: widget.userLastName, userLastName: widget.userLastName,
); ),
}, ),
); );
if (eventData != null) {
// Ajouter l'événement en appelant l'API via le bloc
context.read<EventBloc>().add(AddEvent(EventModel.fromJson(eventData)));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Événement ajouté avec succès !')),
);
}
}, },
), ),
], ],
@@ -92,7 +85,17 @@ class _EventScreenState extends State<EventScreen> {
onParticipate: () => _onParticipate(event.id), onParticipate: () => _onParticipate(event.id),
onCloseEvent: () => _onCloseEvent(event.id), onCloseEvent: () => _onCloseEvent(event.id),
onReopenEvent: () => _onReopenEvent(event.id), onReopenEvent: () => _onReopenEvent(event.id),
onRemoveEvent: (String eventId) {
// Retirer l'événement localement après la fermeture
setState(() {
// Logique pour retirer l'événement de la liste localement
// Par exemple, vous pouvez appeler le bloc ou mettre à jour l'état local ici
context.read<EventBloc>().add(RemoveEvent(eventId));
});
},
status: event.status,
); );
}, },
); );
} else if (state is EventError) { } else if (state is EventError) {

View File

@@ -3,7 +3,6 @@ import '../../../core/constants/colors.dart'; // Importez les couleurs dynamique
import '../../widgets/friend_suggestions.dart'; import '../../widgets/friend_suggestions.dart';
import '../../widgets/group_list.dart'; import '../../widgets/group_list.dart';
import '../../widgets/popular_activity_list.dart'; import '../../widgets/popular_activity_list.dart';
import '../../widgets/quick_action_button.dart';
import '../../widgets/recommended_event_list.dart'; import '../../widgets/recommended_event_list.dart';
import '../../widgets/section_header.dart'; import '../../widgets/section_header.dart';
import '../../widgets/story_section.dart'; import '../../widgets/story_section.dart';

View File

@@ -59,6 +59,7 @@ class EventBloc extends Bloc<EventEvent, EventState> {
on<AddEvent>(_onAddEvent); on<AddEvent>(_onAddEvent);
on<CloseEvent>(_onCloseEvent); on<CloseEvent>(_onCloseEvent);
on<ReopenEvent>(_onReopenEvent); on<ReopenEvent>(_onReopenEvent);
on<RemoveEvent>(_onRemoveEvent); // Ajout du gestionnaire pour RemoveEvent
} }
// Gestion du chargement des événements // Gestion du chargement des événements
@@ -107,4 +108,21 @@ class EventBloc extends Bloc<EventEvent, EventState> {
emit(EventError('Erreur lors de la réouverture de l\'événement.')); emit(EventError('Erreur lors de la réouverture de l\'événement.'));
} }
} }
// Gestion de la suppression locale d'un événement
Future<void> _onRemoveEvent(RemoveEvent event, Emitter<EventState> emit) async {
if (state is EventLoaded) {
// Supprimer l'événement de la liste locale sans recharger tout
final List<EventModel> updatedEvents = List.from((state as EventLoaded).events)
..removeWhere((e) => e.id == event.eventId);
emit(EventLoaded(updatedEvents));
}
}
} }
class RemoveEvent extends EventEvent {
final String eventId;
RemoveEvent(this.eventId);
}

View File

@@ -0,0 +1,119 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' as rootBundle;
class CategoryField extends StatefulWidget {
final FormFieldSetter<String> onSaved;
const CategoryField({Key? key, required this.onSaved}) : super(key: key);
@override
_CategoryFieldState createState() => _CategoryFieldState();
}
class _CategoryFieldState extends State<CategoryField> {
String? _selectedCategory;
Map<String, List<String>> _categoryMap = {}; // Map pour stocker les catégories et sous-catégories
List<DropdownMenuItem<String>> _dropdownItems = []; // Liste pour stocker les éléments de menu déroulant
@override
void initState() {
super.initState();
_loadCategories(); // Charger les catégories à partir du JSON
}
// Charger les catégories depuis le fichier JSON
Future<void> _loadCategories() async {
try {
final String jsonString = await rootBundle.rootBundle.loadString('lib/assets/json/event_categories.json');
final Map<String, dynamic> jsonMap = json.decode(jsonString);
final Map<String, List<String>> categoryMap = {};
jsonMap['categories'].forEach((key, value) {
categoryMap[key] = List<String>.from(value);
});
setState(() {
_categoryMap = categoryMap;
_dropdownItems = _buildDropdownItems();
});
// Ajouter un log pour vérifier si les catégories sont bien chargées
print("Catégories chargées: $_categoryMap");
} catch (e) {
print("Erreur lors du chargement des catégories : $e");
}
}
// Construire les éléments du menu déroulant avec catégorisation
List<DropdownMenuItem<String>> _buildDropdownItems() {
List<DropdownMenuItem<String>> items = [];
_categoryMap.forEach((category, subcategories) {
items.add(
DropdownMenuItem<String>(
enabled: false,
child: Text(
category,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white70,
),
),
),
);
for (String subcategory in subcategories) {
items.add(
DropdownMenuItem<String>(
value: subcategory,
child: Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Text(
subcategory,
style: const TextStyle(color: Colors.white),
),
),
),
);
}
});
// Ajouter un log pour vérifier si les éléments sont bien créés
print("Éléments créés pour le menu déroulant: ${items.length}");
return items;
}
@override
Widget build(BuildContext context) {
return _dropdownItems.isEmpty
? CircularProgressIndicator() // Affiche un indicateur de chargement si les éléments ne sont pas encore prêts
: DropdownButtonFormField<String>(
value: _selectedCategory,
decoration: InputDecoration(
labelText: 'Catégorie',
labelStyle: const TextStyle(color: Colors.white70),
filled: true,
fillColor: Colors.white.withOpacity(0.1),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide.none,
),
prefixIcon: const Icon(Icons.category, color: Colors.white70),
),
style: const TextStyle(color: Colors.white),
dropdownColor: const Color(0xFF2C2C3E),
iconEnabledColor: Colors.white70,
items: _dropdownItems,
onChanged: (String? newValue) {
setState(() {
_selectedCategory = newValue;
});
},
onSaved: widget.onSaved,
);
}
}

View File

@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
class DatePickerField extends StatelessWidget {
final DateTime? selectedDate;
final Function(DateTime?) onDatePicked;
const DatePickerField({Key? key, this.selectedDate, required this.onDatePicked}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime.now(),
lastDate: DateTime(2101),
);
if (picked != null) {
onDatePicked(picked);
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(10.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
selectedDate == null
? 'Sélectionnez une date'
: '${selectedDate!.day}/${selectedDate!.month}/${selectedDate!.year}',
style: const TextStyle(color: Colors.white70),
),
const Icon(Icons.calendar_today, color: Colors.white70),
],
),
),
);
}
}

View File

@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
class DescriptionField extends StatelessWidget {
final FormFieldSetter<String> onSaved;
const DescriptionField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(
labelText: 'Description',
labelStyle: const TextStyle(color: Colors.white70),
filled: true,
fillColor: Colors.white.withOpacity(0.1),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide.none,
),
prefixIcon: const Icon(Icons.description, color: Colors.white70),
),
style: const TextStyle(color: Colors.white),
maxLines: 3,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer une description';
}
return null;
},
onSaved: onSaved,
);
}
}

View File

@@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
import 'package:afterwork/core/utils/date_formatter.dart';
import 'event_status_badge.dart';
import 'event_menu.dart';
class EventHeader extends StatelessWidget {
final String userName;
final String userLastName;
final String? eventDate;
final String? imageUrl;
final String location; // Ajout du paramètre "location" pour le lieu de l'événement
final GlobalKey menuKey;
final BuildContext menuContext;
const EventHeader({
Key? key,
required this.userName,
required this.userLastName,
this.eventDate,
this.imageUrl,
required this.location, // Initialisation de "location"
required this.menuKey,
required this.menuContext,
}) : super(key: key);
@override
Widget build(BuildContext context) {
DateTime? date;
try {
date = DateTime.parse(eventDate ?? '');
} catch (e) {
date = null;
}
String formattedDate = date != null ? DateFormatter.formatDate(date) : 'Date inconnue';
return Row(
children: [
CircleAvatar(
backgroundColor: Colors.grey.shade800,
backgroundImage: imageUrl != null && imageUrl!.isNotEmpty
? NetworkImage(imageUrl!)
: const AssetImage('lib/assets/images/placeholder.png') as ImageProvider,
radius: 22,
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'$userName $userLastName',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
formattedDate,
style: const TextStyle(
color: Colors.white54,
fontSize: 12,
),
),
const SizedBox(height: 4),
// Utilisation de Row pour afficher le lieu sur la même ligne
Row(
children: [
Expanded(
child: Text(
location.isNotEmpty ? location : 'Lieu non spécifié',
style: const TextStyle(
color: Colors.white60,
fontSize: 12,
fontStyle: FontStyle.italic,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
],
),
],
),
),
IconButton(
key: menuKey,
icon: const Icon(Icons.more_vert, color: Colors.white54, size: 20),
splashRadius: 20,
onPressed: () {
showEventOptions(menuContext, menuKey);
},
),
],
);
}
}

View File

@@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
class EventImage extends StatelessWidget {
final String? imageUrl;
final double aspectRatio;
const EventImage({Key? key, this.imageUrl, this.aspectRatio = 16 / 9}) : super(key: key);
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: AspectRatio(
aspectRatio: aspectRatio,
child: imageUrl != null && imageUrl!.isNotEmpty
? Image.network(
imageUrl!,
width: double.infinity,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return _buildPlaceholderImage();
},
)
: _buildPlaceholderImage(),
),
);
}
Widget _buildPlaceholderImage() {
return Container(
color: Colors.grey[800],
child: Center(
child: Icon(
Icons.image_not_supported,
color: Colors.grey[400],
size: 50,
),
),
);
}
}

View File

@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
class EventInteractionRow extends StatelessWidget {
final VoidCallback onReact;
final VoidCallback onComment;
final VoidCallback onShare;
final int reactionsCount;
final int commentsCount;
final int sharesCount;
const EventInteractionRow({
Key? key,
required this.onReact,
required this.onComment,
required this.onShare,
required this.reactionsCount,
required this.commentsCount,
required this.sharesCount,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildIconButton(Icons.thumb_up_alt_outlined, 'Réagir', reactionsCount, onReact),
_buildIconButton(Icons.comment_outlined, 'Commenter', commentsCount, onComment),
_buildIconButton(Icons.share_outlined, 'Partager', sharesCount, onShare),
],
);
}
Widget _buildIconButton(IconData icon, String label, int count, VoidCallback onPressed) {
return TextButton.icon(
onPressed: onPressed,
icon: Icon(icon, color: const Color(0xFF1DBF73), size: 18),
label: Text(
'$label ($count)',
style: const TextStyle(color: Colors.white70, fontSize: 12),
),
);
}
}

View File

@@ -26,6 +26,9 @@ class EventList extends StatelessWidget {
onParticipate: () => _handleParticipate(event), onParticipate: () => _handleParticipate(event),
onCloseEvent: () => _handleCloseEvent(event), onCloseEvent: () => _handleCloseEvent(event),
onReopenEvent: () => _handleReopenEvent(event), onReopenEvent: () => _handleReopenEvent(event),
onRemoveEvent: () => _handleRemoveEvent(event),
status: '',
); );
}, },
); );
@@ -55,4 +58,10 @@ class EventList extends StatelessWidget {
void _handleReopenEvent(EventModel event) { void _handleReopenEvent(EventModel event) {
print('Événement ${event.title} réouvert'); print('Événement ${event.title} réouvert');
} }
void _handleRemoveEvent(EventModel event) {
print('Événement ${event.title} retiré');
}
} }

View File

@@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
void showEventOptions(BuildContext context, GlobalKey key) {
final RenderBox renderBox = key.currentContext!.findRenderObject() as RenderBox;
final Offset offset = renderBox.localToGlobal(Offset.zero);
final RelativeRect position = RelativeRect.fromLTRB(
offset.dx,
offset.dy,
offset.dx + renderBox.size.width,
offset.dy + renderBox.size.height,
);
showMenu(
context: context,
position: position,
items: [
PopupMenuItem(
value: 'details',
child: Row(
children: [
Icon(Icons.info_outline, color: Colors.blue.shade400, size: 18), // Icône plus petite et bleue
const SizedBox(width: 10),
Text(
'Voir les détails',
style: TextStyle(
color: Colors.blue.shade700, // Texte bleu foncé
fontWeight: FontWeight.w500, // Poids de police plus fin
fontSize: 14, // Taille légèrement réduite
),
),
],
),
),
PopupMenuItem(
value: 'edit',
child: Row(
children: [
Icon(Icons.edit, color: Colors.orange.shade400, size: 18),
const SizedBox(width: 10),
Text(
'Modifier l\'événement',
style: TextStyle(
color: Colors.orange.shade700,
fontWeight: FontWeight.w500,
fontSize: 14,
),
),
],
),
),
PopupMenuItem(
value: 'delete',
child: Row(
children: [
Icon(Icons.delete_outline, color: Colors.red.shade400, size: 18),
const SizedBox(width: 10),
Text(
'Supprimer l\'événement',
style: TextStyle(
color: Colors.red.shade700,
fontWeight: FontWeight.w500,
fontSize: 14,
),
),
],
),
),
],
elevation: 5.0, // Réduction de l'élévation pour une ombre plus subtile
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0), // Ajout de bordures arrondies
side: BorderSide(color: Colors.grey.shade300), // Bordure fine et douce
),
color: Colors.white, // Fond blanc pur pour un contraste élégant
).then((value) {
// Gérer les actions en fonction de la sélection
if (value == 'details') {
print('Voir les détails');
} else if (value == 'edit') {
print('Modifier l\'événement');
} else if (value == 'delete') {
print('Supprimer l\'événement');
}
});
}

View File

@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
class EventStatusBadge extends StatelessWidget {
final String status;
const EventStatusBadge({Key? key, required this.status}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
decoration: BoxDecoration(
color: status == 'fermé' ? Colors.red.withOpacity(0.2) : Colors.green.withOpacity(0.2),
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: status == 'fermé' ? Colors.red : Colors.green,
width: 1.0,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
status == 'fermé' ? Icons.lock : Icons.lock_open,
color: status == 'fermé' ? Colors.red : Colors.green,
size: 16.0,
),
const SizedBox(width: 5),
Text(
status == 'fermé' ? 'Fermé' : 'Ouvert',
style: TextStyle(
color: status == 'fermé' ? Colors.red : Colors.green,
fontSize: 12,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
),
),
],
),
);
}
}

View File

@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
class ImagePickerField extends StatelessWidget {
final String? imagePath;
final VoidCallback onImagePicked;
const ImagePickerField({Key? key, this.imagePath, required this.onImagePicked}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onImagePicked,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(10.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
imagePath == null
? 'Sélectionnez une image'
: 'Image sélectionnée: $imagePath',
style: const TextStyle(color: Colors.white70),
),
const Icon(Icons.image, color: Colors.white70),
],
),
),
);
}
}

View File

@@ -0,0 +1,100 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class ImagePreviewPicker extends StatefulWidget {
final void Function(File?) onImagePicked;
const ImagePreviewPicker({Key? key, required this.onImagePicked}) : super(key: key);
@override
_ImagePreviewPickerState createState() => _ImagePreviewPickerState();
}
class _ImagePreviewPickerState extends State<ImagePreviewPicker> {
File? _selectedImageFile;
Future<void> _pickImage() async {
final ImagePicker picker = ImagePicker();
final XFile? pickedFile = await showModalBottomSheet<XFile?>(
context: context,
builder: (BuildContext context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.camera_alt),
title: const Text('Take a Photo'),
onTap: () async {
Navigator.pop(context, await picker.pickImage(source: ImageSource.camera));
},
),
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Choose from Gallery'),
onTap: () async {
Navigator.pop(context, await picker.pickImage(source: ImageSource.gallery));
},
),
],
),
);
},
);
if (pickedFile != null) {
setState(() {
_selectedImageFile = File(pickedFile.path);
widget.onImagePicked(_selectedImageFile); // Pass the picked image to the parent
});
}
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _pickImage,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Aperçu de l\'image (16:9)',
style: TextStyle(color: Colors.white70),
),
const SizedBox(height: 8),
AspectRatio(
aspectRatio: 16 / 9,
child: Container(
decoration: BoxDecoration(
color: Colors.black26,
borderRadius: BorderRadius.circular(10.0),
border: Border.all(color: Colors.white70, width: 1),
),
child: _selectedImageFile != null
? ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Image.file(
_selectedImageFile!,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return const Center(
child: Icon(Icons.error, color: Colors.red),
);
},
),
)
: const Center(
child: Text(
'Cliquez pour ajouter une image',
style: TextStyle(color: Colors.white54),
),
),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class LinkField extends StatelessWidget {
final FormFieldSetter<String> onSaved;
const LinkField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(
labelText: 'Lien (optionnel)',
labelStyle: const TextStyle(color: Colors.white70),
filled: true,
fillColor: Colors.white.withOpacity(0.1),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide.none,
),
prefixIcon: const Icon(Icons.link, color: Colors.white70),
),
style: const TextStyle(color: Colors.white),
onSaved: onSaved,
);
}
}

View File

@@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../screens/location/location_picker_Screen.dart';
class LocationField extends StatelessWidget {
final String location;
final LatLng? selectedLatLng;
final Function(LatLng?) onLocationPicked;
const LocationField({Key? key, required this.location, this.selectedLatLng, required this.onLocationPicked}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () async {
final LatLng? pickedLocation = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LocationPickerScreen(),
),
);
if (pickedLocation != null) {
onLocationPicked(pickedLocation);
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.circular(10.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
selectedLatLng == null
? 'Sélectionnez une localisation'
: 'Localisation: $location',
style: const TextStyle(color: Colors.white70),
),
const Icon(Icons.location_on, color: Colors.white70),
],
),
),
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:afterwork/core/constants/colors.dart';
class SubmitButton extends StatelessWidget {
final VoidCallback onPressed;
const SubmitButton({Key? key, required this.onPressed}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [
Color(0xFF1DBF73), // Start of the gradient
Color(0xFF11998E), // End of the gradient
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 8,
offset: const Offset(2, 4), // Shadow position
),
],
borderRadius: BorderRadius.circular(8.0),
),
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent, // Button background is transparent to show gradient
shadowColor: Colors.transparent, // Remove the default shadow
padding: const EdgeInsets.symmetric(vertical: 14.0),
minimumSize: const Size(double.infinity, 50), // Bigger button size
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
child: const Text(
'Créer l\'événement',
style: TextStyle(
color: Colors.white,
fontSize: 16, // Increase font size
fontWeight: FontWeight.bold, // Bold text
letterSpacing: 1.2, // Spacing between letters
),
),
),
);
}
}

View File

@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
class SwipeBackground extends StatelessWidget {
final Color color;
final IconData icon;
final String label;
const SwipeBackground({
Key? key,
required this.color,
required this.icon,
required this.label,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: color,
alignment: Alignment.centerRight,
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(icon, color: Colors.white),
const SizedBox(width: 10),
Text(label, style: const TextStyle(color: Colors.white)),
],
),
);
}
}

View File

@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
class TitleField extends StatelessWidget {
final FormFieldSetter<String> onSaved;
const TitleField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(
labelText: 'Titre',
labelStyle: const TextStyle(color: Colors.white70),
filled: true,
fillColor: Colors.white.withOpacity(0.1),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide.none,
),
prefixIcon: const Icon(Icons.title, color: Colors.white70),
),
style: const TextStyle(color: Colors.white),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer un titre';
}
return null;
},
onSaved: onSaved,
);
}
}

View File

@@ -81,6 +81,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.3"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
url: "https://pub.dev"
source: hosted
version: "0.4.1"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@@ -205,10 +221,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: file_selector_macos name: file_selector_macos
sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385 sha256: cb284e267f8e2a45a904b5c094d2ba51d0aabfc20b1538ab786d9ef7dc2bf75c
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.9.4" version: "0.9.4+1"
file_selector_platform_interface: file_selector_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -254,14 +270,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.1.6" version: "8.1.6"
flutter_lints: flutter_launcher_icons:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_launcher_icons
sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "0.14.1"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
@@ -380,10 +396,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: google_maps_flutter_android name: google_maps_flutter_android
sha256: "36e75af1d0bd4c7391eacdaedf9ca7632c5b9709c5ec618b04489b79ea2b3f82" sha256: "10cf27bee8c560f8e69992b3a0f27ddf1d7acbea622ddb13ef3f587848a73f26"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.14.6" version: "2.14.7"
google_maps_flutter_ios: google_maps_flutter_ios:
dependency: transitive dependency: transitive
description: description:
@@ -520,6 +536,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.7" version: "0.6.7"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@@ -544,14 +568,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
loading_icon_button: loading_icon_button:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -769,7 +785,7 @@ packages:
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
shared_preferences: shared_preferences:
dependency: "direct dev" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
@@ -973,6 +989,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.5.0" version: "6.5.0"
yaml:
dependency: transitive
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
sdks: sdks:
dart: ">=3.5.1 <4.0.0" dart: ">=3.5.1 <4.0.0"
flutter: ">=3.24.0" flutter: ">=3.24.0"

View File

@@ -11,6 +11,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
shared_preferences: ^2.0.0
image_picker: ^0.8.4+8 image_picker: ^0.8.4+8
camerawesome: ^2.1.0 camerawesome: ^2.1.0
path_provider: ^2.0.11 path_provider: ^2.0.11
@@ -53,6 +54,12 @@ dependency_overrides:
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_launcher_icons: ^0.14.1
flutter_icons:
android: true
ios: true
image_path: "lib/assets/images/app_icon.png"
shared_preferences: ^2.0.13 shared_preferences: ^2.0.13
flutter_lints: ^4.0.0 flutter_lints: ^4.0.0