diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index aba161e..1b07153 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -3,15 +3,18 @@ import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:google_fonts/google_fonts.dart'; import 'theme/app_theme.dart'; +import 'theme/theme_provider.dart'; import 'screens/auth/login_screen.dart'; -import 'screens/auth/register_screen.dart'; import 'screens/auth/parent_register_screen.dart'; import 'screens/home/home_screen.dart'; +import 'navigation/app_router.dart'; void main() { runApp( - ChangeNotifierProvider( - create: (_) => AppTheme(), + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => ThemeProvider()), + ], child: const MyApp(), ), ); @@ -24,10 +27,6 @@ final _router = GoRouter( path: '/login', builder: (context, state) => const LoginScreen(), ), - GoRoute( - path: '/register', - builder: (context, state) => const RegisterScreen(), - ), GoRoute( path: '/parent-register', builder: (context, state) => const ParentRegisterScreen(), @@ -44,12 +43,19 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, appTheme, _) => MaterialApp.router( - title: 'P\'titsPas', - theme: appTheme.lightTheme, - routerConfig: _router, - ), + return Consumer( + builder: (context, themeProvider, _) { + final appTheme = AppTheme(); + return MaterialApp.router( + title: 'P\'titsPas', + theme: themeProvider.currentTheme == ThemeType.darkTheme + ? appTheme.darkTheme + : (themeProvider.currentTheme == ThemeType.pastelTheme + ? appTheme.pastelTheme + : appTheme.lightTheme), + routerConfig: _router, + ); + }, ); } } \ No newline at end of file diff --git a/frontend/lib/screens/auth/login_screen.dart b/frontend/lib/screens/auth/login_screen.dart index 767fbcf..2a301ce 100644 --- a/frontend/lib/screens/auth/login_screen.dart +++ b/frontend/lib/screens/auth/login_screen.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import '../../services/auth_service.dart'; +import '../../theme/theme_provider.dart'; +import '../../theme/app_theme.dart'; +import 'package:provider/provider.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @@ -15,6 +18,17 @@ class _LoginScreenState extends State { final _passwordController = TextEditingController(); bool _isLoading = false; + String _getThemeName(ThemeType type) { + switch (type) { + case ThemeType.defaultTheme: + return "P'titsPas"; + case ThemeType.pastelTheme: + return "Pastel"; + case ThemeType.darkTheme: + return "Sombre"; + } + } + @override void dispose() { _emailController.dispose(); @@ -56,6 +70,29 @@ class _LoginScreenState extends State { return Scaffold( appBar: AppBar( title: const Text('Connexion'), + actions: [ + Consumer( + builder: (context, themeProvider, child) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: DropdownButton( + value: themeProvider.currentTheme, + items: ThemeType.values.map((ThemeType type) { + return DropdownMenuItem( + value: type, + child: Text(_getThemeName(type)), + ); + }).toList(), + onChanged: (ThemeType? newValue) { + if (newValue != null) { + themeProvider.setTheme(newValue); + } + }, + ), + ); + }, + ), + ], ), body: Center( child: SingleChildScrollView( @@ -105,11 +142,6 @@ class _LoginScreenState extends State { : const Text('Se connecter'), ), const SizedBox(height: 16), - TextButton( - onPressed: () => context.go('/register'), - child: const Text('Créer un compte'), - ), - const SizedBox(height: 8), TextButton( onPressed: () => context.go('/parent-register'), child: const Text('Créer un compte parent'), diff --git a/frontend/lib/screens/home/home_screen.dart b/frontend/lib/screens/home/home_screen.dart index 046f037..4c2237c 100644 --- a/frontend/lib/screens/home/home_screen.dart +++ b/frontend/lib/screens/home/home_screen.dart @@ -1,16 +1,53 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../../theme/theme_provider.dart'; +import '../../theme/app_theme.dart'; class HomeScreen extends StatelessWidget { const HomeScreen({super.key}); + String _getThemeName(ThemeType type) { + switch (type) { + case ThemeType.defaultTheme: + return "P'titsPas"; + case ThemeType.pastelTheme: + return "Pastel"; + case ThemeType.darkTheme: + return "Sombre"; + } + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('P\'titsPas'), + title: const Text('Accueil'), + actions: [ + Consumer( + builder: (context, themeProvider, child) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: DropdownButton( + value: themeProvider.currentTheme, + items: ThemeType.values.map((ThemeType type) { + return DropdownMenuItem( + value: type, + child: Text(_getThemeName(type)), + ); + }).toList(), + onChanged: (ThemeType? newValue) { + if (newValue != null) { + themeProvider.setTheme(newValue); + } + }, + ), + ); + }, + ), + ], ), body: const Center( - child: Text('Bienvenue sur P\'titsPas'), + child: Text('Bienvenue sur P\'titsPas !'), ), ); } diff --git a/frontend/lib/theme/app_theme.dart b/frontend/lib/theme/app_theme.dart index c4a3129..5d79f30 100644 --- a/frontend/lib/theme/app_theme.dart +++ b/frontend/lib/theme/app_theme.dart @@ -4,6 +4,7 @@ import 'package:google_fonts/google_fonts.dart'; enum ThemeType { defaultTheme, pastelTheme, + darkTheme, // Ajouter d'autres thèmes ici } @@ -30,18 +31,96 @@ class AppTheme extends ChangeNotifier { static const Color _pastelWarningColor = Color(0xFFFFE4B5); // Jaune Pastel static const Color _pastelSuccessColor = Color(0xFFA5D6A7); // Vert Pastel + // Couleurs pour le thème sombre + static const Color _darkPrimaryColor = Color(0xFF4299E1); + static const Color _darkSecondaryColor = Color(0xFF2D3748); + static const Color _darkBackgroundColor = Color(0xFF1A202C); + static const Color _darkTextColor = Color(0xFFF7FAFC); + static const Color _darkErrorColor = Color(0xFFFEB2B2); + static const Color _darkWarningColor = Color(0xFFFBD38D); + static const Color _darkSuccessColor = Color(0xFF9AE6B4); + // Configuration du thème actuel ThemeType _currentTheme = ThemeType.defaultTheme; ThemeType get currentTheme => _currentTheme; // Getters pour les couleurs du thème actuel - Color get primaryColor => _currentTheme == ThemeType.defaultTheme ? _defaultPrimaryColor : _pastelPrimaryColor; - Color get secondaryColor => _currentTheme == ThemeType.defaultTheme ? _defaultSecondaryColor : _pastelSecondaryColor; - Color get backgroundColor => _currentTheme == ThemeType.defaultTheme ? _defaultBackgroundColor : _pastelBackgroundColor; - Color get textColor => _currentTheme == ThemeType.defaultTheme ? _defaultTextColor : _pastelTextColor; - Color get errorColor => _currentTheme == ThemeType.defaultTheme ? _defaultErrorColor : _pastelErrorColor; - Color get warningColor => _currentTheme == ThemeType.defaultTheme ? _defaultWarningColor : _pastelWarningColor; - Color get successColor => _currentTheme == ThemeType.defaultTheme ? _defaultSuccessColor : _pastelSuccessColor; + Color get primaryColor { + switch (_currentTheme) { + case ThemeType.defaultTheme: + return _defaultPrimaryColor; + case ThemeType.pastelTheme: + return _pastelPrimaryColor; + case ThemeType.darkTheme: + return _darkPrimaryColor; + } + } + + Color get secondaryColor { + switch (_currentTheme) { + case ThemeType.defaultTheme: + return _defaultSecondaryColor; + case ThemeType.pastelTheme: + return _pastelSecondaryColor; + case ThemeType.darkTheme: + return _darkSecondaryColor; + } + } + + Color get backgroundColor { + switch (_currentTheme) { + case ThemeType.defaultTheme: + return _defaultBackgroundColor; + case ThemeType.pastelTheme: + return _pastelBackgroundColor; + case ThemeType.darkTheme: + return _darkBackgroundColor; + } + } + + Color get textColor { + switch (_currentTheme) { + case ThemeType.defaultTheme: + return _defaultTextColor; + case ThemeType.pastelTheme: + return _pastelTextColor; + case ThemeType.darkTheme: + return _darkTextColor; + } + } + + Color get errorColor { + switch (_currentTheme) { + case ThemeType.defaultTheme: + return _defaultErrorColor; + case ThemeType.pastelTheme: + return _pastelErrorColor; + case ThemeType.darkTheme: + return _darkErrorColor; + } + } + + Color get warningColor { + switch (_currentTheme) { + case ThemeType.defaultTheme: + return _defaultWarningColor; + case ThemeType.pastelTheme: + return _pastelWarningColor; + case ThemeType.darkTheme: + return _darkWarningColor; + } + } + + Color get successColor { + switch (_currentTheme) { + case ThemeType.defaultTheme: + return _defaultSuccessColor; + case ThemeType.pastelTheme: + return _pastelSuccessColor; + case ThemeType.darkTheme: + return _darkSuccessColor; + } + } // Méthode pour changer de thème void setTheme(ThemeType theme) { @@ -51,40 +130,54 @@ class AppTheme extends ChangeNotifier { // Thème Material 3 ThemeData get lightTheme { + return _buildTheme(_defaultPrimaryColor, _defaultSecondaryColor, _defaultBackgroundColor, _defaultTextColor, _defaultErrorColor, _defaultWarningColor, _defaultSuccessColor); + } + + // Thème Material 3 pastel + ThemeData get pastelTheme { + return _buildTheme(_pastelPrimaryColor, _pastelSecondaryColor, _pastelBackgroundColor, _pastelTextColor, _pastelErrorColor, _pastelWarningColor, _pastelSuccessColor); + } + + // Thème Material 3 sombre + ThemeData get darkTheme { + return _buildTheme(_darkPrimaryColor, _darkSecondaryColor, _darkBackgroundColor, _darkTextColor, _darkErrorColor, _darkWarningColor, _darkSuccessColor); + } + + ThemeData _buildTheme(Color primary, Color secondary, Color background, Color text, Color error, Color warning, Color success) { return ThemeData( useMaterial3: true, colorScheme: ColorScheme.light( - primary: primaryColor, - secondary: secondaryColor, - background: backgroundColor, - error: errorColor, - tertiary: warningColor, + primary: primary, + secondary: secondary, + background: background, + error: error, + tertiary: warning, onPrimary: Colors.white, onSecondary: Colors.white, - onBackground: textColor, + onBackground: text, onError: Colors.white, - onTertiary: textColor, + onTertiary: text, ), - scaffoldBackgroundColor: backgroundColor, + scaffoldBackgroundColor: background, textTheme: TextTheme( - displayLarge: _getTitleStyle(32), - displayMedium: _getTitleStyle(28), - displaySmall: _getTitleStyle(24), - headlineMedium: _getTitleStyle(20), - headlineSmall: _getTitleStyle(18), - titleLarge: _getTitleStyle(16), - bodyLarge: _getBodyStyle(16), - bodyMedium: _getBodyStyle(14), - bodySmall: _getBodyStyle(12), + displayLarge: _getTitleStyle(32, text), + displayMedium: _getTitleStyle(28, text), + displaySmall: _getTitleStyle(24, text), + headlineMedium: _getTitleStyle(20, text), + headlineSmall: _getTitleStyle(18, text), + titleLarge: _getTitleStyle(16, text), + bodyLarge: _getBodyStyle(16, text), + bodyMedium: _getBodyStyle(14, text), + bodySmall: _getBodyStyle(12, text), ), appBarTheme: AppBarTheme( - backgroundColor: primaryColor, + backgroundColor: primary, foregroundColor: Colors.white, - titleTextStyle: _getTitleStyle(20).copyWith(color: Colors.white), + titleTextStyle: _getTitleStyle(20, Colors.white), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( - backgroundColor: primaryColor, + backgroundColor: primary, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( @@ -94,7 +187,7 @@ class AppTheme extends ChangeNotifier { ), inputDecorationTheme: InputDecorationTheme( filled: true, - fillColor: secondaryColor.withOpacity(0.5), + fillColor: secondary.withOpacity(0.5), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide.none, @@ -105,31 +198,31 @@ class AppTheme extends ChangeNotifier { ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: BorderSide(color: primaryColor, width: 2), + borderSide: BorderSide(color: primary, width: 2), ), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), ), snackBarTheme: SnackBarThemeData( - backgroundColor: errorColor, - contentTextStyle: _getBodyStyle(14).copyWith(color: Colors.white), + backgroundColor: error, + contentTextStyle: _getBodyStyle(14, Colors.white), ), ); } // Méthodes privées pour la typographie - TextStyle _getTitleStyle(double fontSize) { + TextStyle _getTitleStyle(double fontSize, Color color) { return GoogleFonts.merienda( fontSize: fontSize, fontWeight: FontWeight.w600, - color: textColor, + color: color, ); } - TextStyle _getBodyStyle(double fontSize) { + TextStyle _getBodyStyle(double fontSize, Color color) { return GoogleFonts.merriweather( fontSize: fontSize, fontWeight: FontWeight.w300, - color: textColor, + color: color, ); } } \ No newline at end of file diff --git a/frontend/lib/theme/theme_provider.dart b/frontend/lib/theme/theme_provider.dart new file mode 100644 index 0000000..9fa05f2 --- /dev/null +++ b/frontend/lib/theme/theme_provider.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'app_theme.dart'; + +class ThemeProvider with ChangeNotifier { + ThemeType _currentTheme = ThemeType.defaultTheme; + static const String _themeKey = 'theme_type'; + + ThemeType get currentTheme => _currentTheme; + + ThemeProvider() { + _loadTheme(); + } + + Future _loadTheme() async { + final prefs = await SharedPreferences.getInstance(); + final themeIndex = prefs.getInt(_themeKey) ?? 0; + _currentTheme = ThemeType.values[themeIndex]; + notifyListeners(); + } + + Future setTheme(ThemeType theme) async { + _currentTheme = theme; + final prefs = await SharedPreferences.getInstance(); + await prefs.setInt(_themeKey, theme.index); + notifyListeners(); + } + + bool get isDarkMode => _currentTheme == ThemeType.darkTheme; +} \ No newline at end of file