diff --git a/lib/config/router.dart b/lib/config/router.dart index d862690..966491d 100644 --- a/lib/config/router.dart +++ b/lib/config/router.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.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/profile/profile_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/event/event_screen.dart'; import 'package:afterwork/data/datasources/event_remote_data_source.dart'; import '../presentation/reservations/reservations_screen.dart'; diff --git a/lib/core/constants/colors.dart b/lib/core/constants/colors.dart index 2b35052..c4c6a82 100644 --- a/lib/core/constants/colors.dart +++ b/lib/core/constants/colors.dart @@ -16,6 +16,7 @@ class AppColors { static const Color lightError = Color(0xFFB00020); static const Color lightIconPrimary = Color(0xFF212121); // Icône primaire sombre static const Color lightIconSecondary = Color(0xFF757575); // Icône secondaire gris clair + // Thème sombre static const Color darkPrimary = Color(0xFF121212); 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 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 static Color get primary => isDarkMode() ? darkPrimary : lightPrimary; static Color get secondary => isDarkMode() ? darkSecondary : lightSecondary; @@ -45,6 +49,7 @@ class AppColors { static Color get errorColor => isDarkMode() ? darkError : lightError; static Color get iconPrimary => isDarkMode() ? darkIconPrimary : lightIconPrimary; static Color get iconSecondary => isDarkMode() ? darkIconSecondary : lightIconSecondary; + static Color get customBackgroundColor => backgroundCustom; /// Méthode utilitaire pour vérifier si le mode sombre est activé. static bool isDarkMode() { diff --git a/lib/core/constants/urls.dart b/lib/core/constants/urls.dart index bf2d1bd..8a5f9d4 100644 --- a/lib/core/constants/urls.dart +++ b/lib/core/constants/urls.dart @@ -17,6 +17,7 @@ class Urls { 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 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 updateEventImage = '$baseUrl/events'; // Append '/{id}/image' dynamically static const String getAllEvents = '$baseUrl/events'; diff --git a/lib/data/datasources/event_remote_data_source.dart b/lib/data/datasources/event_remote_data_source.dart index e0b1842..fe4ea06 100644 --- a/lib/data/datasources/event_remote_data_source.dart +++ b/lib/data/datasources/event_remote_data_source.dart @@ -174,7 +174,7 @@ class EventRemoteDataSource { print('Réouverture de l\'événement avec l\'ID: $eventId'); final response = await client.post( - Uri.parse('${Urls.baseUrl}/$eventId/reopen'), + Uri.parse('${Urls.reopenEvent}/$eventId/reopen'), headers: {'Content-Type': 'application/json'}, ); diff --git a/lib/data/models/event_model.dart b/lib/data/models/event_model.dart index 47b00c0..1ac70fc 100644 --- a/lib/data/models/event_model.dart +++ b/lib/data/models/event_model.dart @@ -7,7 +7,7 @@ class EventModel { final String category; final String link; final String? imageUrl; // Nullable - final String creatorEmail; // Remplacer UserModel si le créateur est un email + final String creatorEmail; final List participants; // Si participants est une liste simple final String status; final int reactionsCount; @@ -43,7 +43,7 @@ class EventModel { imageUrl: json['imageUrl'], // Peut être null creatorEmail: json['creatorEmail'], // Email du créateur 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, commentsCount: json['commentsCount'] ?? 0, sharesCount: json['sharesCount'] ?? 0, diff --git a/lib/presentation/screens/dialogs/add_event_dialog.dart b/lib/presentation/screens/dialogs/add_event_dialog.dart index 9800012..1e690e8 100644 --- a/lib/presentation/screens/dialogs/add_event_dialog.dart +++ b/lib/presentation/screens/dialogs/add_event_dialog.dart @@ -1,110 +1,118 @@ import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'dart:io'; -/// Dialogue pour ajouter un nouvel événement. -/// Ce widget affiche un formulaire permettant à l'utilisateur de saisir les détails d'un événement. -/// Les logs permettent de suivre les actions de l'utilisateur dans ce dialogue. -class AddEventDialog extends StatefulWidget { +import '../../widgets/category_field.dart'; +import '../../widgets/date_picker.dart'; +import '../../widgets/description_field.dart'; +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 userName; final String userLastName; - const AddEventDialog({ - Key? key, + const AddEventPage({ + super.key, required this.userId, required this.userName, required this.userLastName, - }) : super(key: key); + }); @override - _AddEventDialogState createState() => _AddEventDialogState(); + _AddEventPageState createState() => _AddEventPageState(); } -class _AddEventDialogState extends State { - final _formKey = GlobalKey(); // Clé pour valider le formulaire - String _eventName = ''; // Nom de l'événement - DateTime _selectedDate = DateTime.now(); // Date de l'événement +class _AddEventPageState extends State { + final _formKey = GlobalKey(); // Form key for validation + + 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 Widget build(BuildContext context) { - print("Affichage du dialogue d'ajout d'événement."); - - return AlertDialog( - title: const Text('Ajouter un événement'), - content: Form( - key: _formKey, - 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( + return Scaffold( + appBar: AppBar( + title: const Text('Ajouter un événement'), + backgroundColor: const Color(0xFF1E1E2C), + leading: IconButton( + icon: const Icon(Icons.arrow_back), onPressed: () { - print("L'utilisateur a annulé l'ajout de l'événement."); Navigator.of(context).pop(); }, - child: const Text('Annuler'), ), - // Bouton pour soumettre le formulaire et ajouter l'événement - TextButton( - onPressed: () { - if (_formKey.currentState?.validate() == true) { - _formKey.currentState?.save(); - print("L'utilisateur a ajouté un événement : Nom = $_eventName, Date = $_selectedDate"); - Navigator.of(context).pop({ - 'eventName': _eventName, - 'eventDate': _selectedDate, - }); - } - }, - child: const Text('Ajouter'), - ), - ], + ), + backgroundColor: const Color(0xFF1E1E2C), + body: Column( + children: [ + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + 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 _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; - } } diff --git a/lib/presentation/screens/event/event_card.dart b/lib/presentation/screens/event/event_card.dart index 74a2952..d57fc92 100644 --- a/lib/presentation/screens/event/event_card.dart +++ b/lib/presentation/screens/event/event_card.dart @@ -1,19 +1,25 @@ 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 { final EventModel event; final String userId; final String userName; final String userLastName; + final String status; final VoidCallback onReact; final VoidCallback onComment; final VoidCallback onShare; final VoidCallback onParticipate; final VoidCallback onCloseEvent; final VoidCallback onReopenEvent; + final Function onRemoveEvent; const EventCard({ Key? key, @@ -21,152 +27,93 @@ class EventCard extends StatelessWidget { required this.userId, required this.userName, required this.userLastName, + required this.status, required this.onReact, required this.onComment, required this.onShare, required this.onParticipate, required this.onCloseEvent, required this.onReopenEvent, + required this.onRemoveEvent, }) : super(key: key); @override Widget build(BuildContext context) { - return Card( - color: const Color(0xFF2C2C3E), - margin: const EdgeInsets.symmetric(vertical: 10.0), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildHeader(), - const SizedBox(height: 10), - Text( - event.title, - style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 5), - Text( - event.description, - style: const TextStyle(color: Colors.white70, fontSize: 14), - maxLines: 3, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 10), - _buildEventImage(), - const Divider(color: Colors.white24), - _buildInteractionRow(), - const SizedBox(height: 10), - _buildStatusAndActions(), - ], - ), + final GlobalKey menuKey = GlobalKey(); + + return Dismissible( + key: ValueKey(event.id), + direction: event.status == 'fermé' + ? DismissDirection.startToEnd + : DismissDirection.endToStart, + onDismissed: (direction) { + if (event.status == 'fermé') { + onReopenEvent(); + } else { + onCloseEvent(); + onRemoveEvent(event.id); + } + }, + background: SwipeBackground( + color: event.status == 'fermé' ? Colors.green : Colors.red, + icon: event.status == 'fermé' ? Icons.lock_open : Icons.lock, + label: event.status == 'fermé' ? 'Rouvrir' : 'Fermer', ), - ); - } - - Widget _buildHeader() { - // Convertir la date de l'événement (de String à DateTime) - DateTime? eventDate; - try { - eventDate = DateTime.parse(event.startDate); - } catch (e) { - eventDate = null; // Gérer le cas où la date ne serait pas valide - } - - // Utiliser DateFormatter pour afficher une date lisible si elle est valide - String formattedDate = eventDate != null ? DateFormatter.formatDate(eventDate) : 'Date inconnue'; - - return Row( - children: [ - CircleAvatar(backgroundImage: NetworkImage(event.imageUrl ?? 'lib/assets/images/placeholder.png')), - const SizedBox(width: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '$userName $userLastName', - style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 5), - Text( - formattedDate, // Utiliser la date formatée ici - style: const TextStyle(color: Colors.white70, fontSize: 14), - ), - ], - ), - const Spacer(), - IconButton( - icon: const Icon(Icons.more_vert, color: Colors.white), - onPressed: () { - // Logique d'affichage d'options supplémentaires pour l'événement. - // Vous pouvez utiliser un menu déroulant ou une boîte de dialogue ici. - }, - ), - ], - ); - } - - Widget _buildEventImage() { - return ClipRRect( - borderRadius: BorderRadius.circular(10.0), - child: Image.network( - event.imageUrl ?? 'lib/assets/images/placeholder.png', - 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, + child: Card( + color: const Color(0xFF2C2C3E), + margin: const EdgeInsets.symmetric(vertical: 10.0), + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + EventHeader( + userName: userName, + userLastName: userLastName, + eventDate: event.startDate, + imageUrl: event.imageUrl, + menuKey: menuKey, + menuContext: context, + location: event.location, + ), + const Divider(color: Colors.white24), + Row( + children: [ + const Spacer(), // Pusher le badge statut à la droite. + EventStatusBadge(status: status), + ], + ), + Text( + event.title, + style: const TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.bold), + ), + const SizedBox(height: 5), + Text( + event.description, + style: const TextStyle(color: Colors.white70, fontSize: 14), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 10), + EventImage(imageUrl: event.imageUrl), + const Divider(color: Colors.white24), + EventInteractionRow( + onReact: onReact, + onComment: onComment, + onShare: onShare, + reactionsCount: event.reactionsCount, + commentsCount: event.commentsCount, + sharesCount: event.sharesCount, + ), + ], ), ), - event.status == 'closed' - ? ElevatedButton( - onPressed: onReopenEvent, - child: const Text('Rouvrir l\'événement'), - ) - : ElevatedButton( - onPressed: onCloseEvent, - child: const Text('Fermer l\'événement'), - ), - ], + ), ); } } diff --git a/lib/presentation/screens/event/event_screen.dart b/lib/presentation/screens/event/event_screen.dart index 8a88f0d..895b194 100644 --- a/lib/presentation/screens/event/event_screen.dart +++ b/lib/presentation/screens/event/event_screen.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:afterwork/data/models/event_model.dart'; import 'package:afterwork/presentation/screens/event/event_card.dart'; -import '../../state_management/event_bloc.dart'; import '../dialogs/add_event_dialog.dart'; +import '../../state_management/event_bloc.dart'; class EventScreen extends StatefulWidget { final String userId; @@ -43,25 +43,18 @@ class _EventScreenState extends State { actions: [ IconButton( icon: const Icon(Icons.add_circle_outline, size: 28, color: Color(0xFF1DBF73)), - onPressed: () async { - final eventData = await showDialog>( - context: context, - builder: (BuildContext context) { - return AddEventDialog( + onPressed: () { + // Naviguer vers une nouvelle page pour ajouter un événement + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddEventPage( userId: widget.userId, userName: widget.userName, userLastName: widget.userLastName, - ); - }, + ), + ), ); - - if (eventData != null) { - // Ajouter l'événement en appelant l'API via le bloc - context.read().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 { onParticipate: () => _onParticipate(event.id), onCloseEvent: () => _onCloseEvent(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().add(RemoveEvent(eventId)); + }); + }, + status: event.status, ); + }, ); } else if (state is EventError) { diff --git a/lib/presentation/screens/home/home_content.dart b/lib/presentation/screens/home/home_content.dart index 40ca158..a7a05df 100644 --- a/lib/presentation/screens/home/home_content.dart +++ b/lib/presentation/screens/home/home_content.dart @@ -3,7 +3,6 @@ import '../../../core/constants/colors.dart'; // Importez les couleurs dynamique import '../../widgets/friend_suggestions.dart'; import '../../widgets/group_list.dart'; import '../../widgets/popular_activity_list.dart'; -import '../../widgets/quick_action_button.dart'; import '../../widgets/recommended_event_list.dart'; import '../../widgets/section_header.dart'; import '../../widgets/story_section.dart'; diff --git a/lib/presentation/state_management/event_bloc.dart b/lib/presentation/state_management/event_bloc.dart index 1192f7b..1c9fbc4 100644 --- a/lib/presentation/state_management/event_bloc.dart +++ b/lib/presentation/state_management/event_bloc.dart @@ -59,6 +59,7 @@ class EventBloc extends Bloc { on(_onAddEvent); on(_onCloseEvent); on(_onReopenEvent); + on(_onRemoveEvent); // Ajout du gestionnaire pour RemoveEvent } // Gestion du chargement des événements @@ -107,4 +108,21 @@ class EventBloc extends Bloc { emit(EventError('Erreur lors de la réouverture de l\'événement.')); } } + + // Gestion de la suppression locale d'un événement + Future _onRemoveEvent(RemoveEvent event, Emitter emit) async { + if (state is EventLoaded) { + // Supprimer l'événement de la liste locale sans recharger tout + final List 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); +} + diff --git a/lib/presentation/widgets/category_field.dart b/lib/presentation/widgets/category_field.dart new file mode 100644 index 0000000..88b3266 --- /dev/null +++ b/lib/presentation/widgets/category_field.dart @@ -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 onSaved; + + const CategoryField({Key? key, required this.onSaved}) : super(key: key); + + @override + _CategoryFieldState createState() => _CategoryFieldState(); +} + +class _CategoryFieldState extends State { + String? _selectedCategory; + Map> _categoryMap = {}; // Map pour stocker les catégories et sous-catégories + List> _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 _loadCategories() async { + try { + final String jsonString = await rootBundle.rootBundle.loadString('lib/assets/json/event_categories.json'); + final Map jsonMap = json.decode(jsonString); + + final Map> categoryMap = {}; + jsonMap['categories'].forEach((key, value) { + categoryMap[key] = List.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> _buildDropdownItems() { + List> items = []; + + _categoryMap.forEach((category, subcategories) { + items.add( + DropdownMenuItem( + enabled: false, + child: Text( + category, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.white70, + ), + ), + ), + ); + + for (String subcategory in subcategories) { + items.add( + DropdownMenuItem( + 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( + 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, + ); + } + +} diff --git a/lib/presentation/widgets/date_picker.dart b/lib/presentation/widgets/date_picker.dart new file mode 100644 index 0000000..3332e97 --- /dev/null +++ b/lib/presentation/widgets/date_picker.dart @@ -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), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/widgets/description_field.dart b/lib/presentation/widgets/description_field.dart new file mode 100644 index 0000000..c8d95f7 --- /dev/null +++ b/lib/presentation/widgets/description_field.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +class DescriptionField extends StatelessWidget { + final FormFieldSetter 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, + ); + } +} diff --git a/lib/presentation/widgets/event_header.dart b/lib/presentation/widgets/event_header.dart new file mode 100644 index 0000000..81ab9f9 --- /dev/null +++ b/lib/presentation/widgets/event_header.dart @@ -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); + }, + ), + ], + ); + } +} diff --git a/lib/presentation/widgets/event_image.dart b/lib/presentation/widgets/event_image.dart new file mode 100644 index 0000000..8a77961 --- /dev/null +++ b/lib/presentation/widgets/event_image.dart @@ -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, + ), + ), + ); + } +} diff --git a/lib/presentation/widgets/event_interaction_row.dart b/lib/presentation/widgets/event_interaction_row.dart new file mode 100644 index 0000000..b67a198 --- /dev/null +++ b/lib/presentation/widgets/event_interaction_row.dart @@ -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), + ), + ); + } +} diff --git a/lib/presentation/widgets/event_list.dart b/lib/presentation/widgets/event_list.dart index 6a608d1..1644a82 100644 --- a/lib/presentation/widgets/event_list.dart +++ b/lib/presentation/widgets/event_list.dart @@ -26,6 +26,9 @@ class EventList extends StatelessWidget { onParticipate: () => _handleParticipate(event), onCloseEvent: () => _handleCloseEvent(event), onReopenEvent: () => _handleReopenEvent(event), + onRemoveEvent: () => _handleRemoveEvent(event), + status: '', + ); }, ); @@ -55,4 +58,10 @@ class EventList extends StatelessWidget { void _handleReopenEvent(EventModel event) { print('Événement ${event.title} réouvert'); } + + void _handleRemoveEvent(EventModel event) { + print('Événement ${event.title} retiré'); + } + + } diff --git a/lib/presentation/widgets/event_menu.dart b/lib/presentation/widgets/event_menu.dart new file mode 100644 index 0000000..15c5d9f --- /dev/null +++ b/lib/presentation/widgets/event_menu.dart @@ -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'); + } + }); +} diff --git a/lib/presentation/widgets/event_status_badge.dart b/lib/presentation/widgets/event_status_badge.dart new file mode 100644 index 0000000..05c0a24 --- /dev/null +++ b/lib/presentation/widgets/event_status_badge.dart @@ -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, + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/widgets/image_picker.dart b/lib/presentation/widgets/image_picker.dart new file mode 100644 index 0000000..77c251d --- /dev/null +++ b/lib/presentation/widgets/image_picker.dart @@ -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), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/widgets/image_preview_picker.dart b/lib/presentation/widgets/image_preview_picker.dart new file mode 100644 index 0000000..d82307f --- /dev/null +++ b/lib/presentation/widgets/image_preview_picker.dart @@ -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 { + File? _selectedImageFile; + + Future _pickImage() async { + final ImagePicker picker = ImagePicker(); + + final XFile? pickedFile = await showModalBottomSheet( + 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), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/widgets/link_field.dart b/lib/presentation/widgets/link_field.dart new file mode 100644 index 0000000..15bb2eb --- /dev/null +++ b/lib/presentation/widgets/link_field.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class LinkField extends StatelessWidget { + final FormFieldSetter 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, + ); + } +} diff --git a/lib/presentation/widgets/location_field.dart b/lib/presentation/widgets/location_field.dart new file mode 100644 index 0000000..8f8a270 --- /dev/null +++ b/lib/presentation/widgets/location_field.dart @@ -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), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/widgets/submit_button.dart b/lib/presentation/widgets/submit_button.dart new file mode 100644 index 0000000..bf5edbb --- /dev/null +++ b/lib/presentation/widgets/submit_button.dart @@ -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 + ), + ), + ), + ); + } +} diff --git a/lib/presentation/widgets/swipe_background.dart b/lib/presentation/widgets/swipe_background.dart new file mode 100644 index 0000000..7bcbe2e --- /dev/null +++ b/lib/presentation/widgets/swipe_background.dart @@ -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)), + ], + ), + ); + } +} diff --git a/lib/presentation/widgets/title_field.dart b/lib/presentation/widgets/title_field.dart new file mode 100644 index 0000000..3fd5db1 --- /dev/null +++ b/lib/presentation/widgets/title_field.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +class TitleField extends StatelessWidget { + final FormFieldSetter 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, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 390b734..54b82d9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -81,6 +81,22 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -205,10 +221,10 @@ packages: dependency: transitive description: name: file_selector_macos - sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385 + sha256: cb284e267f8e2a45a904b5c094d2ba51d0aabfc20b1538ab786d9ef7dc2bf75c url: "https://pub.dev" source: hosted - version: "0.9.4" + version: "0.9.4+1" file_selector_platform_interface: dependency: transitive description: @@ -254,14 +270,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.1.6" - flutter_lints: + flutter_launcher_icons: dependency: "direct dev" description: - name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + name: flutter_launcher_icons + sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "0.14.1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -380,10 +396,10 @@ packages: dependency: transitive description: name: google_maps_flutter_android - sha256: "36e75af1d0bd4c7391eacdaedf9ca7632c5b9709c5ec618b04489b79ea2b3f82" + sha256: "10cf27bee8c560f8e69992b3a0f27ddf1d7acbea622ddb13ef3f587848a73f26" url: "https://pub.dev" source: hosted - version: "2.14.6" + version: "2.14.7" google_maps_flutter_ios: dependency: transitive description: @@ -520,6 +536,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -544,14 +568,6 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: "direct main" description: @@ -769,7 +785,7 @@ packages: source: hosted version: "2.1.0" shared_preferences: - dependency: "direct dev" + dependency: "direct main" description: name: shared_preferences sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" @@ -973,6 +989,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" sdks: dart: ">=3.5.1 <4.0.0" flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index 76fd946..2cb6a95 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: flutter: sdk: flutter + shared_preferences: ^2.0.0 image_picker: ^0.8.4+8 camerawesome: ^2.1.0 path_provider: ^2.0.11 @@ -53,6 +54,12 @@ dependency_overrides: dev_dependencies: flutter_test: 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 flutter_lints: ^4.0.0