diff --git a/lib/assets/images/background.webp b/lib/assets/images/background.webp new file mode 100644 index 0000000..7bd2a94 Binary files /dev/null and b/lib/assets/images/background.webp differ diff --git a/lib/assets/images/logo.png b/lib/assets/images/logo.png new file mode 100644 index 0000000..847469e Binary files /dev/null and b/lib/assets/images/logo.png differ diff --git a/lib/config/injection/injection.dart b/lib/config/injection/injection.dart index 271bc79..5197f95 100644 --- a/lib/config/injection/injection.dart +++ b/lib/config/injection/injection.dart @@ -2,7 +2,7 @@ import 'package:get_it/get_it.dart'; import 'package:http/http.dart' as http; import '../../data/datasources/user_remote_data_source.dart'; -import '../../data/datasources/user_repository_impl.dart'; +import '../../data/repositories/user_repository_impl.dart'; import '../../domain/usecases/get_user.dart'; final sl = GetIt.instance; diff --git a/lib/config/router.dart b/lib/config/router.dart index e7eecde..f9ed4b7 100644 --- a/lib/config/router.dart +++ b/lib/config/router.dart @@ -1,16 +1,24 @@ import 'package:flutter/material.dart'; +import 'package:afterwork/presentation/screens/login/login_screen.dart'; import 'package:afterwork/presentation/screens/home/home_screen.dart'; +import 'package:afterwork/presentation/screens/event/event_screen.dart'; +import 'package:afterwork/presentation/screens/story/story_screen.dart'; class AppRouter { static Route generateRoute(RouteSettings settings) { switch (settings.name) { case '/': + return MaterialPageRoute(builder: (_) => const LoginScreen()); + case '/home': return MaterialPageRoute(builder: (_) => const HomeScreen()); - // Ajoute d'autres routes ici + case '/event': + return MaterialPageRoute(builder: (_) => const EventScreen()); + case '/story': + return MaterialPageRoute(builder: (_) => const StoryScreen()); default: return MaterialPageRoute( builder: (_) => const Scaffold( - body: Center(child: Text('Page not found')), + body: Center(child: Text('Page non trouvée')), ), ); } diff --git a/lib/core/constants/colors.dart b/lib/core/constants/colors.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/core/constants/urls.dart b/lib/core/constants/urls.dart index 601083b..1f807fd 100644 --- a/lib/core/constants/urls.dart +++ b/lib/core/constants/urls.dart @@ -1,5 +1,5 @@ class Urls { - static const String baseUrl = 'http://localhost:8085'; + static const String baseUrl = 'http://192.168.1.145:8085'; // static const String login = baseUrl + 'auth/login'; // static const String events = baseUrl + 'events'; // Ajoute d'autres URLs ici diff --git a/lib/core/errors/exceptions.dart b/lib/core/errors/exceptions.dart index 451f647..3d1ab7a 100644 --- a/lib/core/errors/exceptions.dart +++ b/lib/core/errors/exceptions.dart @@ -1,3 +1,12 @@ class ServerException implements Exception {} class CacheException implements Exception {} + +class AuthenticationException implements Exception { + final String message; + + AuthenticationException(this.message); + + @override + String toString() => 'AuthenticationException: $message'; +} diff --git a/lib/core/utils/validators.dart b/lib/core/utils/validators.dart new file mode 100644 index 0000000..a229fbf --- /dev/null +++ b/lib/core/utils/validators.dart @@ -0,0 +1,21 @@ +class Validators { + static String? validateEmail(String? value) { + if (value == null || value.isEmpty) { + return 'Veuillez entrer votre email'; + } + if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) { + return 'Veuillez entrer un email valide'; + } + return null; + } + + static String? validatePassword(String? value) { + if (value == null || value.isEmpty) { + return 'Veuillez entrer votre mot de passe'; + } + if (value.length < 6) { + return 'Le mot de passe doit comporter au moins 6 caractères'; + } + return null; + } +} diff --git a/lib/data/datasources/user_remote_data_source.dart b/lib/data/datasources/user_remote_data_source.dart index 62f3464..447b5df 100644 --- a/lib/data/datasources/user_remote_data_source.dart +++ b/lib/data/datasources/user_remote_data_source.dart @@ -10,6 +10,30 @@ class UserRemoteDataSource { UserRemoteDataSource(this.client); + Future authenticateUser(String email, String password) async { + if (email.isEmpty || password.isEmpty) { + throw Exception('Email ou mot de passe vide'); + } + + final response = await client.post( + Uri.parse('${Urls.baseUrl}/users/authenticate'), + headers: {'Content-Type': 'application/json'}, + body: jsonEncode({ + 'email': email, + 'motDePasse': password, + }), + ); + + if (response.statusCode == 200) { + final jsonResponse = json.decode(response.body); + return UserModel.fromJson(jsonResponse); + } else if (response.statusCode == 401) { + throw AuthenticationException('Email ou mot de passe incorrect'); + } else { + throw ServerException(); + } + } + Future getUser(String id) async { final response = await client.get(Uri.parse('${Urls.baseUrl}/user/$id')); diff --git a/lib/data/datasources/user_repository_impl.dart b/lib/data/datasources/user_repository_impl.dart deleted file mode 100644 index 164698b..0000000 --- a/lib/data/datasources/user_repository_impl.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:afterwork/domain/entities/user.dart'; -import 'package:afterwork/domain/repositories/user_repository.dart'; -import 'package:afterwork/data/datasources/user_remote_data_source.dart'; - -class UserRepositoryImpl implements UserRepository { - final UserRemoteDataSource remoteDataSource; - - UserRepositoryImpl({required this.remoteDataSource}); - - @override - Future getUser(String id) async { - return await remoteDataSource.getUser(id); - } -} diff --git a/lib/data/models/user_model.dart b/lib/data/models/user_model.dart index 2fd509e..a987f32 100644 --- a/lib/data/models/user_model.dart +++ b/lib/data/models/user_model.dart @@ -1,26 +1,37 @@ - -import '../../domain/entities/user.dart'; +import 'package:afterwork/domain/entities/user.dart'; class UserModel extends User { const UserModel({ - required super.id, - required super.name, - required super.email, - }); + required String id, + required String nom, + required String prenoms, + required String email, + required String motDePasse, + }) : super( + id: id, + nom: nom, + prenoms: prenoms, + email: email, + motDePasse: motDePasse, + ); factory UserModel.fromJson(Map json) { return UserModel( - id: json['id'], - name: json['name'], - email: json['email'], + id: json['id'] ?? '', + nom: json['nom'] ?? '', + prenoms: json['prenoms'] ?? '', + email: json['email'] ?? '', + motDePasse: json['motDePasse'] ?? '', ); } Map toJson() { return { 'id': id, - 'name': name, + 'nom': nom, + 'prenoms': prenoms, 'email': email, + 'motDePasse': motDePasse, }; } } diff --git a/lib/data/repositories/user_repository_impl.dart b/lib/data/repositories/user_repository_impl.dart new file mode 100644 index 0000000..020e840 --- /dev/null +++ b/lib/data/repositories/user_repository_impl.dart @@ -0,0 +1,21 @@ +import 'package:afterwork/domain/entities/user.dart'; +import 'package:afterwork/domain/repositories/user_repository.dart'; +import 'package:afterwork/data/datasources/user_remote_data_source.dart'; +import 'package:afterwork/data/models/user_model.dart'; + +class UserRepositoryImpl implements UserRepository { + final UserRemoteDataSource remoteDataSource; + + UserRepositoryImpl({required this.remoteDataSource}); + + @override + Future getUser(String id) async { + UserModel userModel = await remoteDataSource.getUser(id); + return userModel; // Retourne un UserModel qui est un sous-type de User + } + + Future authenticateUser(String email, String password) async { + UserModel userModel = await remoteDataSource.authenticateUser(email, password); + return userModel; // Retourne un UserModel qui est un sous-type de User + } +} diff --git a/lib/domain/entities/user.dart b/lib/domain/entities/user.dart index 1eed52c..5a25f95 100644 --- a/lib/domain/entities/user.dart +++ b/lib/domain/entities/user.dart @@ -2,11 +2,19 @@ import 'package:equatable/equatable.dart'; class User extends Equatable { final String id; - final String name; + final String nom; + final String prenoms; final String email; + final String motDePasse; - const User({required this.id, required this.name, required this.email}); + const User({ + required this.id, + required this.nom, + required this.prenoms, + required this.email, + required this.motDePasse, + }); @override - List get props => [id, name, email]; + List get props => [id, nom, prenoms, email, motDePasse]; } diff --git a/lib/main.dart b/lib/main.dart index 379d61d..b0b7c1b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,8 @@ class AfterWorkApp extends StatelessWidget { return MaterialApp( title: 'AfterWork', theme: AppTheme.lightTheme, + darkTheme: AppTheme.darkTheme, // Ajout du thème sombre + themeMode: ThemeMode.system, // Choix automatique du thème en fonction du système onGenerateRoute: AppRouter.generateRoute, initialRoute: '/', ); diff --git a/lib/presentation/screens/event/event_screen.dart b/lib/presentation/screens/event/event_screen.dart index ae876ad..f2f695c 100644 --- a/lib/presentation/screens/event/event_screen.dart +++ b/lib/presentation/screens/event/event_screen.dart @@ -7,12 +7,47 @@ class EventScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Événements'), + title: const Text('Créer un Événement'), + backgroundColor: Colors.blueAccent, ), - body: const Center( - child: Text('Liste des événements'), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Titre de l\'événement', + style: TextStyle(fontSize: 18, color: Colors.white), + ), + const SizedBox(height: 8), + TextField( + decoration: InputDecoration( + hintText: 'Entrez le titre de l\'événement', + filled: true, + fillColor: Colors.white.withOpacity(0.1), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide.none, + ), + hintStyle: const TextStyle(color: Colors.white70), + ), + style: const TextStyle(color: Colors.white), + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () { + // Logique pour créer l'événement + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blueAccent, + padding: const EdgeInsets.all(16.0), + ), + child: const Text('Créer l\'événement'), + ), + ], + ), ), + backgroundColor: Colors.black, ); } } - diff --git a/lib/presentation/screens/home/home_screen.dart b/lib/presentation/screens/home/home_screen.dart index a86dfee..2762241 100644 --- a/lib/presentation/screens/home/home_screen.dart +++ b/lib/presentation/screens/home/home_screen.dart @@ -1,17 +1,172 @@ import 'package:flutter/material.dart'; -class HomeScreen extends StatelessWidget { +class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); + @override + _HomeScreenState createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State with SingleTickerProviderStateMixin { + late TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 5, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + void _onMenuSelected(BuildContext context, String option) { + // Implémente la logique pour chaque option ici + switch (option) { + case 'Publier': + // Redirige vers la page de publication + break; + case 'Story': + // Redirige vers la page de création de Story + break; + default: + break; + } + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('AfterWork'), + backgroundColor: Colors.black, // Fond noir pour l'AppBar + elevation: 0, // Enlève l'ombre sous l'AppBar + leading: Padding( + padding: const EdgeInsets.all(8.0), + child: Image.asset( + 'lib/assets/images/logo.png', // Chemin correct de ton logo + height: 40, + ), + ), + actions: [ + // Bouton + + CircleAvatar( + backgroundColor: Colors.white, // Cercle blanc + radius: 18, // Réduit la taille du bouton + child: PopupMenuButton( + onSelected: (value) => _onMenuSelected(context, value), + itemBuilder: (context) => [ + const PopupMenuItem( + value: 'Publier', + child: Text('Publier'), + ), + const PopupMenuItem( + value: 'Story', + child: Text('Story'), + ), + ], + icon: const Icon(Icons.add, color: Colors.blueAccent, size: 20), // Icône bleue légèrement plus petite + color: Colors.white, // Menu contextuel en blanc + ), + ), + const SizedBox(width: 8), // Réduit l'espacement entre les boutons + // Bouton Recherche + CircleAvatar( + backgroundColor: Colors.white, + radius: 18, // Réduit la taille du bouton + child: IconButton( + icon: const Icon(Icons.search, color: Colors.blueAccent, size: 20), // Icône bleue légèrement plus petite + onPressed: () { + // Implémente la logique de recherche ici + }, + ), + ), + const SizedBox(width: 8), // Réduit l'espacement entre les boutons + // Bouton Messagerie + CircleAvatar( + backgroundColor: Colors.white, + radius: 18, // Réduit la taille du bouton + child: IconButton( + icon: const Icon(Icons.message, color: Colors.blueAccent, size: 20), // Icône bleue légèrement plus petite + onPressed: () { + // Implémente la logique de messagerie ici + }, + ), + ), + const SizedBox(width: 8), // Réduit l'espacement entre les boutons + ], + bottom: TabBar( + controller: _tabController, + indicatorColor: Colors.blueAccent, + tabs: const [ + Tab(icon: Icon(Icons.home), text: 'Accueil'), + Tab(icon: Icon(Icons.event), text: 'Événements'), + Tab(icon: Icon(Icons.location_city), text: 'Établissements'), + Tab(icon: Icon(Icons.people), text: 'Social'), + Tab(icon: Icon(Icons.person), text: 'Profil'), + ], + ), ), - body: const Center( - child: Text('Bienvenue sur AfterWork!'), + body: TabBarView( + controller: _tabController, + children: [ + _getSelectedScreen(0), + _getSelectedScreen(1), + _getSelectedScreen(2), + _getSelectedScreen(3), + _getSelectedScreen(4), + ], ), + backgroundColor: Colors.black, // Arrière-plan de l'écran en noir ); } + + // Cette méthode retourne le widget correspondant à l'index sélectionné + Widget _getSelectedScreen(int index) { + switch (index) { + case 0: + return const Center( + child: Text( + 'Accueil', + style: TextStyle(color: Colors.white, fontSize: 24), + ), + ); + case 1: + return const Center( + child: Text( + 'Événements', + style: TextStyle(color: Colors.white, fontSize: 24), + ), + ); + case 2: + return const Center( + child: Text( + 'Établissements', + style: TextStyle(color: Colors.white, fontSize: 24), + ), + ); + case 3: + return const Center( + child: Text( + 'Social', + style: TextStyle(color: Colors.white, fontSize: 24), + ), + ); + case 4: + return const Center( + child: Text( + 'Profil', + style: TextStyle(color: Colors.white, fontSize: 24), + ), + ); + default: + return const Center( + child: Text( + 'Accueil', + style: TextStyle(color: Colors.white, fontSize: 24), + ), + ); + } + } } diff --git a/lib/presentation/screens/login/login_screen.dart b/lib/presentation/screens/login/login_screen.dart new file mode 100644 index 0000000..984daa8 --- /dev/null +++ b/lib/presentation/screens/login/login_screen.dart @@ -0,0 +1,243 @@ +import 'package:flutter/material.dart'; +import 'package:afterwork/data/datasources/user_remote_data_source.dart'; +import 'package:afterwork/data/models/user_model.dart'; +import 'package:afterwork/presentation/screens/home/home_screen.dart'; +import 'package:http/http.dart' as http; + +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); + + @override + _LoginScreenState createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State with SingleTickerProviderStateMixin { + final _formKey = GlobalKey(); + String _email = ''; + String _password = ''; + bool _isPasswordVisible = false; + bool _isSubmitting = false; + + final UserRemoteDataSource _userRemoteDataSource = UserRemoteDataSource(http.Client()); + + late AnimationController _controller; + late Animation _buttonScaleAnimation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + ); + _buttonScaleAnimation = Tween(begin: 1.0, end: 1.1).animate( + CurvedAnimation(parent: _controller, curve: Curves.easeInOut), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _togglePasswordVisibility() { + setState(() { + _isPasswordVisible = !_isPasswordVisible; + }); + } + + void _submit() async { + if (_formKey.currentState!.validate()) { + setState(() { + _isSubmitting = true; + }); + _formKey.currentState!.save(); + + try { + UserModel user = await _userRemoteDataSource.authenticateUser(_email, _password); + print('Connexion réussie : ${user.email}'); + + // Navigation vers la page d'accueil + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const HomeScreen()), + ); + } catch (e) { + print('Erreur lors de la connexion: $e'); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(e.toString())), + ); + } finally { + setState(() { + _isSubmitting = false; + }); + } + } + } + + @override + Widget build(BuildContext context) { + final size = MediaQuery.of(context).size; + + return Scaffold( + body: Stack( + children: [ + // Arrière-plan avec dégradé + Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [Color(0xFF4A90E2), Color(0xFF9013FE)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + ), + ), + // Contenu de la page + Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Logo avec légère animation + AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return Transform.scale( + scale: _buttonScaleAnimation.value, + child: child, + ); + }, + child: GestureDetector( + onTapDown: (_) => _controller.forward(), + onTapUp: (_) => _controller.reverse(), + child: Image.asset( + 'lib/assets/images/logo.png', + height: size.height * 0.2, + ), + ), + ), + const SizedBox(height: 20), + const Text( + 'Bienvenue sur AfterWork', + style: TextStyle( + fontSize: 26, + fontWeight: FontWeight.bold, + color: Colors.white, + shadows: [ + Shadow( + offset: Offset(0, 2), + blurRadius: 6, + color: Colors.black26, + ), + ], + ), + ), + const SizedBox(height: 40), + // Champ Email + TextFormField( + decoration: InputDecoration( + labelText: 'Email', + filled: true, + fillColor: Colors.white.withOpacity(0.1), + labelStyle: const TextStyle(color: Colors.white), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide.none, + ), + prefixIcon: const Icon(Icons.email, color: Colors.white), + ), + keyboardType: TextInputType.emailAddress, + style: const TextStyle(color: Colors.white), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Veuillez entrer votre email'; + } + if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) { + return 'Veuillez entrer un email valide'; + } + return null; + }, + onSaved: (value) { + _email = value ?? ''; // Utiliser une chaîne vide si value est null + }, + ), + const SizedBox(height: 20), + // Champ Mot de passe + TextFormField( + decoration: InputDecoration( + labelText: 'Mot de passe', + filled: true, + fillColor: Colors.white.withOpacity(0.1), + labelStyle: const TextStyle(color: Colors.white), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide.none, + ), + prefixIcon: const Icon(Icons.lock, color: Colors.white), + suffixIcon: IconButton( + icon: Icon( + _isPasswordVisible ? Icons.visibility : Icons.visibility_off, + color: Colors.white, + ), + onPressed: _togglePasswordVisibility, + ), + ), + obscureText: !_isPasswordVisible, + style: const TextStyle(color: Colors.white), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Veuillez entrer votre mot de passe'; + } + if (value.length < 6) { + return 'Le mot de passe doit comporter au moins 6 caractères'; + } + return null; + }, + onSaved: (value) { + _password = value ?? ''; // Utiliser une chaîne vide si value est null + }, + ), + const SizedBox(height: 20), + // Bouton de connexion avec animation de soumission + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.all(16.0), + textStyle: const TextStyle(fontSize: 18), + backgroundColor: _isSubmitting ? Colors.grey : Colors.blueAccent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + onPressed: _isSubmitting ? null : _submit, + child: _isSubmitting + ? const CircularProgressIndicator(color: Colors.white) + : const Text('Connexion'), + ), + ), + const SizedBox(height: 20), + // Lien pour s'inscrire + TextButton( + onPressed: () { + // Naviguer vers la page d'inscription + }, + child: const Text( + 'Pas encore de compte ? Inscrivez-vous', + style: TextStyle(color: Colors.white), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/screens/story/story_screen.dart b/lib/presentation/screens/story/story_screen.dart new file mode 100644 index 0000000..a8509de --- /dev/null +++ b/lib/presentation/screens/story/story_screen.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; + +class StoryScreen extends StatelessWidget { + const StoryScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Créer une Story'), + backgroundColor: Colors.blueAccent, + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Texte de la Story', + style: TextStyle(fontSize: 18, color: Colors.white), + ), + const SizedBox(height: 8), + TextField( + maxLines: 5, + decoration: InputDecoration( + hintText: 'Rédigez votre Story', + filled: true, + fillColor: Colors.white.withOpacity(0.1), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10.0), + borderSide: BorderSide.none, + ), + hintStyle: const TextStyle(color: Colors.white70), + ), + style: const TextStyle(color: Colors.white), + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () { + // Logique pour créer la story + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blueAccent, + padding: const EdgeInsets.all(16.0), + ), + child: const Text('Publier la Story'), + ), + ], + ), + ), + backgroundColor: Colors.black, + ); + } +} diff --git a/lib/presentation/state_management/event_bloc.dart b/lib/presentation/state_management/event_bloc.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/presentation/state_management/payment_bloc.dart b/lib/presentation/state_management/payment_bloc.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/presentation/state_management/reservation_bloc.dart b/lib/presentation/state_management/reservation_bloc.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/presentation/widgets/custom_appbar.dart b/lib/presentation/widgets/custom_appbar.dart new file mode 100644 index 0000000..635b287 --- /dev/null +++ b/lib/presentation/widgets/custom_appbar.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { + final String title; + final List actions; + + const CustomAppBar({ + Key? key, + required this.title, + this.actions = const [], + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppBar( + backgroundColor: Colors.black, + title: Text( + title, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + actions: actions, + ); + } + + @override + Size get preferredSize => const Size.fromHeight(56.0); +} diff --git a/lib/presentation/widgets/custom_button.dart b/lib/presentation/widgets/custom_button.dart index 485d6bd..08d499f 100644 --- a/lib/presentation/widgets/custom_button.dart +++ b/lib/presentation/widgets/custom_button.dart @@ -4,14 +4,25 @@ class CustomButton extends StatelessWidget { final String text; final VoidCallback onPressed; - const CustomButton({super.key, required this.text, required this.onPressed}); + const CustomButton({ + Key? key, + required this.text, + required this.onPressed, + }) : super(key: key); @override Widget build(BuildContext context) { return ElevatedButton( onPressed: onPressed, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16.0), + textStyle: const TextStyle(fontSize: 18), + backgroundColor: Colors.blueAccent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), child: Text(text), ); } } - diff --git a/lib/presentation/widgets/custom_drawer.dart b/lib/presentation/widgets/custom_drawer.dart new file mode 100644 index 0000000..774bb63 --- /dev/null +++ b/lib/presentation/widgets/custom_drawer.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +class CustomDrawer extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + DrawerHeader( + decoration: BoxDecoration( + color: Colors.blueAccent, + ), + child: Text( + 'AfterWork', + style: TextStyle( + color: Colors.white, + fontSize: 24, + ), + ), + ), + ListTile( + leading: Icon(Icons.home), + title: Text('Accueil'), + onTap: () { + Navigator.pushNamed(context, '/home'); + }, + ), + ListTile( + leading: Icon(Icons.event), + title: Text('Événements'), + onTap: () { + Navigator.pushNamed(context, '/event'); + }, + ), + ListTile( + leading: Icon(Icons.camera_alt), // Icône mise à jour pour la story + title: Text('Story'), + onTap: () { + Navigator.pushNamed(context, '/story'); + }, + ), + ], + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 6427867..8376ad8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,10 @@ dev_dependencies: flutter: uses-material-design: true + assets: + - lib/assets/images/logo.png + - lib/assets/images/background.webp + # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg