- Dark / light mode a funcionar no lado do aluno

- Atualização dos ficheiros markdown.
This commit is contained in:
2026-05-14 22:07:03 +01:00
parent 55ec2521cf
commit 62b9a107bc
30 changed files with 2582 additions and 1839 deletions

View File

@@ -87,6 +87,29 @@
- Loading state with CircularProgressIndicator - Loading state with CircularProgressIndicator
### Fixed ### Fixed
- **Settings Profile Card UI** - Fixed white background and duplicate email display
- Background now uses Theme.of(context).colorScheme.surface instead of hardcoded white
- User info displays displayName (bold, larger) on top, email (smaller) below
- File: lib/features/settings/presentation/pages/profile_edit_page.dart
- **Signup Page Input Field Theming** - Fixed dark backgrounds in light mode
- Input fields now use conditional background based on theme brightness
- Light mode: surface color, Dark mode: surfaceContainerHighest
- Applied to name, email, and password fields
- File: lib/features/auth/presentation/pages/signup_page.dart
- **Dashboard Progress Container Theming** - Fixed dark background in light mode
- "Ótimo progresso!" container now uses conditional background
- Light mode: surface color, Dark mode: surfaceContainerHighest
- File: lib/features/dashboard/presentation/widgets/profile_section_widget.dart
- **Authentication Navigation** - Enhanced back button behavior
- Removed AppBar from login and signup pages
- Added PopScope for Android back button navigation to role-selection
- Added custom positioned back button with aesthetic design
- Positioned at top: 50 to avoid notification bar overlap
- Files: lib/features/auth/presentation/pages/login_page.dart, signup_page.dart
- **Unified Quick Action Cards Text Style** - **Unified Quick Action Cards Text Style**
- "Upload Conteúdo" and "Criar Quiz" cards now match "Criar Turma" text alignment - "Upload Conteúdo" and "Criar Quiz" cards now match "Criar Turma" text alignment
- All cards use `Column` with `crossAxisAlignment: CrossAxisAlignment.start` for text section - All cards use `Column` with `crossAxisAlignment: CrossAxisAlignment.start` for text section

View File

@@ -496,14 +496,20 @@ class FirebaseConstants {
**Dependencies**: Task 1.4 **Dependencies**: Task 1.4
#### Subtasks: #### Subtasks:
- [ ] Implement Firebase Auth service - [x] Implement Firebase Auth service
- [ ] Create user models and entities - [x] Create user models and entities
- [ ] Build authentication repository - [x] Build authentication repository
- [ ] Implement sign in use case - [x] Implement sign in use case
- [ ] Implement sign up use case - [x] Implement sign up use case
- [ ] Implement password reset - [ ] Implement password reset
- [ ] Create authentication providers - [x] Create authentication providers
- [ ] Build login/signup screens - [x] Build login/signup screens (with navigation improvements and theme fixes)
**Recent Updates:**
- ✅ Enhanced authentication navigation with PopScope for Android back button support
- ✅ Removed AppBar and added custom positioned back button with aesthetic design
- ✅ Fixed theme consistency in signup page input fields (light/dark mode)
- ✅ Navigation flow complete: back button navigates to role-selection
#### Implementation: #### Implementation:

View File

@@ -28,7 +28,7 @@ This document tracks the overall progress of the AI Study Assistant project deve
-**Foundation:** 100% Complete -**Foundation:** 100% Complete
-**UI/UX:** 90% Complete -**UI/UX:** 95% Complete
-**Internationalization:** 100% Complete -**Internationalization:** 100% Complete
@@ -66,7 +66,7 @@ This document tracks the overall progress of the AI Study Assistant project deve
- [x] Splash screen with animations - [x] Splash screen with animations
- [x] Login page with improved design - [x] Login page with improved design and navigation
- [x] Role selection page (student/teacher) - [x] Role selection page (student/teacher)
@@ -76,7 +76,7 @@ This document tracks the overall progress of the AI Study Assistant project deve
- [x] Dark/light theme support - [x] Dark/light theme support
- [ ] Signup page (needs update) - [x] Signup page (updated with theme fixes)
- [x] Dashboard pages (fixed overflow issue) - [x] Dashboard pages (fixed overflow issue)
@@ -355,11 +355,9 @@ This document tracks the overall progress of the AI Study Assistant project deve
### **Minor Issues (1)** ### **Minor Issues (0)**
- [ ] Signup page needs design update - None currently
- [ ] Some animations could be optimized
@@ -419,6 +417,31 @@ This document tracks the overall progress of the AI Study Assistant project deve
### **Last 24 Hours:** ### **Last 24 Hours:**
-**Fixed Settings Profile Card UI** - profile_edit_page.dart
- Background: Changed from hardcoded white to Theme.of(context).colorScheme.surface
- User info: Fixed duplicate email display, now shows displayName (bold, fontSize 16) on top and email (fontSize 14) below
- Shadow: Updated to use Theme.of(context).colorScheme.shadow.withOpacity(0.1)
-**Fixed Signup Page Input Fields** - signup_page.dart
- Background: Changed to conditional based on brightness
- Light mode: Theme.of(context).colorScheme.surface
- Dark mode: Theme.of(context).colorScheme.surfaceContainerHighest
- Applied to name, email, and password fields (lines 259-265, 316-322, 386-392)
-**Fixed Dashboard Progress Container** - profile_section_widget.dart
- "Ótimo progresso!" container background changed to conditional
- Light mode: Theme.of(context).colorScheme.surface
- Dark mode: Theme.of(context).colorScheme.surfaceContainerHighest
-**Enhanced Authentication Navigation** - login_page.dart & signup_page.dart
- Removed AppBar from both pages
- Added PopScope with canPop: false and onPopInvokedWithResult to navigate to '/role-selection'
- Added custom positioned back button (top: 50, left: 16) with:
- Semi-transparent container (colorScheme.surface.withOpacity(0.8))
- Rounded corners (12px)
- Subtle shadow (colorScheme.shadow.withOpacity(0.1), blurRadius: 8)
- Positioned below notification bar to avoid overlap
-**Teacher Materials Upload Page** - Nova tela dedicada para professores enviarem materiais para a IA -**Teacher Materials Upload Page** - Nova tela dedicada para professores enviarem materiais para a IA
- Ficheiro: `lib/features/materials/presentation/pages/teacher_materials_page.dart` - Ficheiro: `lib/features/materials/presentation/pages/teacher_materials_page.dart`
- **FASE 1**: Criar tela com AppBar "Materiais da Turma" e design consistente - **FASE 1**: Criar tela com AppBar "Materiais da Turma" e design consistente
@@ -712,7 +735,7 @@ This document tracks the overall progress of the AI Study Assistant project deve
**📊 Last Updated: 2024-05-06 21:43** **📊 Last Updated: 2026-05-14 21:04**
**🔄 Auto-Update: Enabled** **🔄 Auto-Update: Enabled**

View File

@@ -25,22 +25,16 @@ class ThemeNotifier extends StateNotifier<ThemeMode> {
/// Change theme mode /// Change theme mode
Future<void> setThemeMode(ThemeMode themeMode) async { Future<void> setThemeMode(ThemeMode themeMode) async {
// For now, only allow light mode state = themeMode;
// Future: Allow dark mode when available await ThemeService.setThemeMode(themeMode);
if (themeMode == ThemeMode.light || ThemeService.isDarkModeAvailable()) {
state = themeMode;
await ThemeService.setThemeMode(themeMode);
}
} }
/// Toggle between light and dark mode (for future use) /// Toggle between light and dark mode
Future<void> toggleTheme() async { Future<void> toggleTheme() async {
if (ThemeService.isDarkModeAvailable()) { final newTheme = state == ThemeMode.light
final newTheme = state == ThemeMode.light ? ThemeMode.dark
? ThemeMode.dark : ThemeMode.light;
: ThemeMode.light; await setThemeMode(newTheme);
await setThemeMode(newTheme);
}
} }
/// Reset to default theme /// Reset to default theme

View File

@@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// EPVC School Color Palette - New Color Scheme /// EPVC School Color Palette - New Color Scheme with Light/Dark Mode Support
class AppColors { class AppColors {
// Primary Brand Colors // Primary Brand Colors (same for both modes)
static const Color primaryTeal = Color( static const Color primaryTeal = Color(
0xFF82C9BD, 0xFF82C9BD,
); // Main teal color - PRIMARY ); // Main teal color - PRIMARY
@@ -10,17 +10,30 @@ class AppColors {
0xFFF68D2D, 0xFFF68D2D,
); // Accent orange - SECONDARY ); // Accent orange - SECONDARY
// Gradient Colors // Gradient Colors (same for both modes)
static const Color gradientStart = Color(0xFF82C9BD); // Teal gradient start static const Color gradientStart = Color(0xFF82C9BD); // Teal gradient start
static const Color gradientEnd = Color( static const Color gradientEnd = Color(
0xFF6AB8A8, 0xFF6AB8A8,
); // Darker teal gradient end ); // Darker teal gradient end
// Secondary Colors // Secondary Colors (same for both modes)
static const Color secondaryTeal = Color(0xFF6AB8A8); // Darker teal static const Color secondaryTeal = Color(0xFF6AB8A8); // Darker teal
static const Color accentTeal = Color(0xFF5AA69A); // Lighter teal accent static const Color accentTeal = Color(0xFF5AA69A); // Lighter teal accent
static const Color lightOrange = Color(0xFFF7A960); // Lighter orange static const Color lightOrange = Color(0xFFF7A960); // Lighter orange
// Status Colors (same for both modes)
static const Color success = Color(0xFF10B981); // Green for success
static const Color warning = Color(0xFFF59E0B); // Amber for warnings
static const Color error = Color(0xFFEF4444); // Red for errors
static const Color info = Color(0xFF3B82F6); // Blue for info
// Legacy compatibility (for existing code)
@deprecated
static const Color primaryBlue = primaryTeal; // Map old primaryBlue to new primaryTeal
}
/// Light Mode Colors
class LightColors {
// Neutral Colors // Neutral Colors
static const Color background = Color(0xFFF8F9FA); // Light gray background static const Color background = Color(0xFFF8F9FA); // Light gray background
static const Color surface = Color(0xFFFFFFFF); // White surfaces static const Color surface = Color(0xFFFFFFFF); // White surfaces
@@ -31,38 +44,68 @@ class AppColors {
static const Color textSecondary = Color(0xFF6B7280); // Secondary text static const Color textSecondary = Color(0xFF6B7280); // Secondary text
static const Color textHint = Color(0xFF9CA3AF); // Hint text static const Color textHint = Color(0xFF9CA3AF); // Hint text
// Status Colors
static const Color success = Color(0xFF10B981); // Green for success
static const Color warning = Color(0xFFF59E0B); // Amber for warnings
static const Color error = Color(0xFFEF4444); // Red for errors
static const Color info = Color(0xFF3B82F6); // Blue for info
// Interactive Colors // Interactive Colors
static const Color buttonPrimary = Color(0xFF82C9BD); // Primary button (teal) static const Color buttonPrimary =
static const Color buttonAccent = Color(0xFFF68D2D); // Accent button (orange) AppColors.primaryTeal; // Primary button (teal)
static const Color buttonAccent =
AppColors.primaryOrange; // Accent button (orange)
static const Color buttonSecondary = Color(0xFFE5E7EB); // Secondary button static const Color buttonSecondary = Color(0xFFE5E7EB); // Secondary button
static const Color iconActive = Color(0xFF82C9BD); // Active icons (teal) static const Color iconActive = AppColors.primaryTeal; // Active icons (teal)
static const Color iconInactive = Color(0xFF9CA3AF); // Inactive icons static const Color iconInactive = Color(0xFF9CA3AF); // Inactive icons
// Chat Specific Colors // Chat Specific Colors
static const Color chatBubbleStudent = Color( static const Color chatBubbleStudent =
0xFF82C9BD, AppColors.primaryTeal; // Student messages (teal)
); // Student messages (teal)
static const Color chatBubbleAI = Color(0xFFF3F4F6); // AI messages static const Color chatBubbleAI = Color(0xFFF3F4F6); // AI messages
static const Color chatInputBackground = Color( static const Color chatInputBackground = Color(
0xFFF8F9FA, 0xFFF8F9FA,
); // Input background ); // Input background
static const Color chatSendButton = Color(0xFF82C9BD); // Send button (teal) static const Color chatSendButton =
AppColors.primaryTeal; // Send button (teal)
// Dark Mode Colors // Border Colors
static const Color darkBackground = Color(0xFF1F2937); // Dark background static const Color border = Color(0xFFE2E8F0); // Border color
static const Color darkSurface = Color(0xFF374151); // Dark surface static const Color divider = Color(0xFFE5E7EB); // Divider color
static const Color darkTextPrimary = Color(0xFFF9FAFB); // Dark primary text
static const Color darkTextSecondary = Color(
0xFFD1D5DB,
); // Dark secondary text
// Legacy compatibility (for existing code) // Overlay Colors
@deprecated static const Color overlay = Color(0x80000000); // Overlay color
static const Color primaryBlue = primaryTeal; // Map old primaryBlue to new primaryTeal }
/// Dark Mode Colors
class DarkColors {
// Neutral Colors
static const Color background = Color(0xFF1F2937); // Dark background
static const Color surface = Color(0xFF374151); // Dark surface
static const Color cardBackground = Color(0xFF374151); // Dark cards
// Text Colors
static const Color textPrimary = Color(0xFFF9FAFB); // Dark primary text
static const Color textSecondary = Color(0xFFD1D5DB); // Dark secondary text
static const Color textHint = Color(0xFF9CA3AF); // Hint text
// Interactive Colors
static const Color buttonPrimary =
AppColors.primaryTeal; // Primary button (teal)
static const Color buttonAccent =
AppColors.primaryOrange; // Accent button (orange)
static const Color buttonSecondary = Color(0xFF4B5563); // Secondary button
static const Color iconActive = AppColors.primaryTeal; // Active icons (teal)
static const Color iconInactive = Color(0xFF6B7280); // Inactive icons
// Chat Specific Colors
static const Color chatBubbleStudent =
AppColors.primaryTeal; // Student messages (teal)
static const Color chatBubbleAI = Color(0xFF4B5563); // AI messages (darker)
static const Color chatInputBackground = Color(
0xFF374151,
); // Input background
static const Color chatSendButton =
AppColors.primaryTeal; // Send button (teal)
// Border Colors
static const Color border = Color(0xFF4B5563); // Border color
static const Color divider = Color(0xFF4B5563); // Divider color
// Overlay Colors
static const Color overlay = Color(0x80000000); // Overlay color
} }

View File

@@ -9,24 +9,24 @@ class AppTheme {
useMaterial3: true, useMaterial3: true,
brightness: Brightness.light, brightness: Brightness.light,
colorScheme: ColorScheme.fromSeed( colorScheme: ColorScheme.fromSeed(
seedColor: AppColors.primaryBlue, seedColor: AppColors.primaryTeal,
brightness: Brightness.light, brightness: Brightness.light,
primary: AppColors.primaryBlue, primary: AppColors.primaryTeal,
secondary: AppColors.primaryTeal, secondary: AppColors.primaryOrange,
surface: AppColors.surface, surface: LightColors.surface,
background: AppColors.background, background: LightColors.background,
error: AppColors.error, error: AppColors.error,
), ),
// App Bar Theme // App Bar Theme
appBarTheme: const AppBarTheme( appBarTheme: const AppBarTheme(
backgroundColor: AppColors.surface, backgroundColor: LightColors.surface,
foregroundColor: AppColors.textPrimary, foregroundColor: LightColors.textPrimary,
elevation: 0, elevation: 0,
centerTitle: true, centerTitle: true,
systemOverlayStyle: SystemUiOverlayStyle.dark, systemOverlayStyle: SystemUiOverlayStyle.dark,
titleTextStyle: TextStyle( titleTextStyle: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@@ -34,7 +34,7 @@ class AppTheme {
// Card Theme // Card Theme
cardTheme: CardThemeData( cardTheme: CardThemeData(
color: AppColors.cardBackground, color: LightColors.cardBackground,
elevation: 2, elevation: 2,
shadowColor: Colors.black.withOpacity(0.08), shadowColor: Colors.black.withOpacity(0.08),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
@@ -44,10 +44,10 @@ class AppTheme {
// Elevated Button Theme // Elevated Button Theme
elevatedButtonTheme: ElevatedButtonThemeData( elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonPrimary, backgroundColor: LightColors.buttonPrimary,
foregroundColor: Colors.white, foregroundColor: Colors.white,
elevation: 2, elevation: 2,
shadowColor: AppColors.primaryBlue.withOpacity(0.3), shadowColor: AppColors.primaryTeal.withOpacity(0.3),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
@@ -59,8 +59,8 @@ class AppTheme {
// Outlined Button Theme // Outlined Button Theme
outlinedButtonTheme: OutlinedButtonThemeData( outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
foregroundColor: AppColors.primaryBlue, foregroundColor: AppColors.primaryTeal,
side: BorderSide(color: AppColors.primaryBlue.withOpacity(0.3)), side: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
@@ -72,7 +72,7 @@ class AppTheme {
// Text Button Theme // Text Button Theme
textButtonTheme: TextButtonThemeData( textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: AppColors.primaryBlue, foregroundColor: AppColors.primaryTeal,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
textStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600), textStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
@@ -82,18 +82,18 @@ class AppTheme {
// Input Field Theme // Input Field Theme
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
filled: true, filled: true,
fillColor: AppColors.surface, fillColor: LightColors.surface,
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: AppColors.primaryBlue.withOpacity(0.3)), borderSide: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
), ),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: AppColors.primaryBlue.withOpacity(0.3)), borderSide: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.primaryBlue, width: 2), borderSide: const BorderSide(color: AppColors.primaryTeal, width: 2),
), ),
errorBorder: OutlineInputBorder( errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
@@ -103,9 +103,9 @@ class AppTheme {
horizontal: 16, horizontal: 16,
vertical: 12, vertical: 12,
), ),
hintStyle: const TextStyle(color: AppColors.textHint, fontSize: 14), hintStyle: const TextStyle(color: LightColors.textHint, fontSize: 14),
labelStyle: const TextStyle( labelStyle: const TextStyle(
color: AppColors.textSecondary, color: LightColors.textSecondary,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@@ -113,85 +113,85 @@ class AppTheme {
// Text Field Theme // Text Field Theme
textSelectionTheme: TextSelectionThemeData( textSelectionTheme: TextSelectionThemeData(
cursorColor: AppColors.primaryBlue, cursorColor: AppColors.primaryTeal,
selectionColor: AppColors.primaryBlue.withOpacity(0.3), selectionColor: AppColors.primaryTeal.withOpacity(0.3),
selectionHandleColor: AppColors.primaryBlue, selectionHandleColor: AppColors.primaryTeal,
), ),
// Text Theme // Text Theme
textTheme: const TextTheme( textTheme: const TextTheme(
displayLarge: TextStyle( displayLarge: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 32, fontSize: 32,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
displayMedium: TextStyle( displayMedium: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 28, fontSize: 28,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
displaySmall: TextStyle( displaySmall: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
headlineLarge: TextStyle( headlineLarge: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
headlineMedium: TextStyle( headlineMedium: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
headlineSmall: TextStyle( headlineSmall: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
titleLarge: TextStyle( titleLarge: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
titleMedium: TextStyle( titleMedium: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
titleSmall: TextStyle( titleSmall: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
bodyLarge: TextStyle( bodyLarge: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
bodyMedium: TextStyle( bodyMedium: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
bodySmall: TextStyle( bodySmall: TextStyle(
color: AppColors.textSecondary, color: LightColors.textSecondary,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
labelLarge: TextStyle( labelLarge: TextStyle(
color: AppColors.textPrimary, color: LightColors.textPrimary,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
labelMedium: TextStyle( labelMedium: TextStyle(
color: AppColors.textSecondary, color: LightColors.textSecondary,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
labelSmall: TextStyle( labelSmall: TextStyle(
color: AppColors.textHint, color: LightColors.textHint,
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@@ -199,9 +199,9 @@ class AppTheme {
// Bottom Navigation Bar Theme // Bottom Navigation Bar Theme
bottomNavigationBarTheme: const BottomNavigationBarThemeData( bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: AppColors.surface, backgroundColor: LightColors.surface,
selectedItemColor: AppColors.primaryBlue, selectedItemColor: AppColors.primaryTeal,
unselectedItemColor: AppColors.iconInactive, unselectedItemColor: LightColors.iconInactive,
type: BottomNavigationBarType.fixed, type: BottomNavigationBarType.fixed,
elevation: 8, elevation: 8,
selectedLabelStyle: TextStyle( selectedLabelStyle: TextStyle(
@@ -216,7 +216,7 @@ class AppTheme {
// Floating Action Button Theme // Floating Action Button Theme
floatingActionButtonTheme: FloatingActionButtonThemeData( floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: AppColors.primaryBlue, backgroundColor: AppColors.primaryTeal,
foregroundColor: Colors.white, foregroundColor: Colors.white,
elevation: 4, elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
@@ -224,28 +224,28 @@ class AppTheme {
// Divider Theme // Divider Theme
dividerTheme: const DividerThemeData( dividerTheme: const DividerThemeData(
color: AppColors.buttonSecondary, color: LightColors.divider,
thickness: 1, thickness: 1,
space: 1, space: 1,
), ),
// Icon Theme // Icon Theme
iconTheme: const IconThemeData(color: AppColors.iconActive, size: 24), iconTheme: const IconThemeData(color: LightColors.iconActive, size: 24),
// Progress Indicator Theme // Progress Indicator Theme
progressIndicatorTheme: const ProgressIndicatorThemeData( progressIndicatorTheme: const ProgressIndicatorThemeData(
color: AppColors.primaryBlue, color: AppColors.primaryTeal,
linearTrackColor: AppColors.buttonSecondary, linearTrackColor: LightColors.buttonSecondary,
circularTrackColor: AppColors.buttonSecondary, circularTrackColor: LightColors.buttonSecondary,
), ),
// Chip Theme // Chip Theme
chipTheme: ChipThemeData( chipTheme: ChipThemeData(
backgroundColor: AppColors.buttonSecondary, backgroundColor: LightColors.buttonSecondary,
selectedColor: AppColors.primaryBlue.withOpacity(0.1), selectedColor: AppColors.primaryTeal.withOpacity(0.1),
disabledColor: AppColors.buttonSecondary.withOpacity(0.5), disabledColor: LightColors.buttonSecondary.withOpacity(0.5),
labelStyle: const TextStyle(color: AppColors.textPrimary), labelStyle: const TextStyle(color: LightColors.textPrimary),
secondaryLabelStyle: const TextStyle(color: AppColors.textPrimary), secondaryLabelStyle: const TextStyle(color: LightColors.textPrimary),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
), ),
@@ -257,52 +257,90 @@ class AppTheme {
useMaterial3: true, useMaterial3: true,
brightness: Brightness.dark, brightness: Brightness.dark,
colorScheme: ColorScheme.fromSeed( colorScheme: ColorScheme.fromSeed(
seedColor: AppColors.primaryBlue, seedColor: AppColors.primaryTeal,
brightness: Brightness.dark, brightness: Brightness.dark,
primary: AppColors.primaryBlue, primary: AppColors.primaryTeal,
secondary: AppColors.primaryTeal, secondary: AppColors.primaryOrange,
surface: AppColors.darkSurface, surface: DarkColors.surface,
background: AppColors.darkBackground, background: DarkColors.background,
error: AppColors.error, error: AppColors.error,
), ),
// Dark mode specific overrides would go here // Dark mode specific overrides would go here
appBarTheme: const AppBarTheme( appBarTheme: const AppBarTheme(
backgroundColor: AppColors.darkSurface, backgroundColor: DarkColors.surface,
foregroundColor: AppColors.darkTextPrimary, foregroundColor: DarkColors.textPrimary,
elevation: 0, elevation: 0,
centerTitle: true, centerTitle: true,
systemOverlayStyle: SystemUiOverlayStyle.light, systemOverlayStyle: SystemUiOverlayStyle.light,
titleTextStyle: TextStyle( titleTextStyle: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
cardTheme: CardThemeData( cardTheme: CardThemeData(
color: AppColors.darkSurface, color: DarkColors.cardBackground,
elevation: 2, elevation: 2,
shadowColor: Colors.black.withOpacity(0.3), shadowColor: Colors.black.withOpacity(0.3),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
), ),
// Elevated Button Theme for Dark Mode
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: DarkColors.buttonPrimary,
foregroundColor: Colors.white,
elevation: 2,
shadowColor: AppColors.primaryTeal.withOpacity(0.3),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
),
// Outlined Button Theme for Dark Mode
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.primaryTeal,
side: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
),
// Text Button Theme for Dark Mode
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: AppColors.primaryTeal,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
textStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
),
// Input Field Theme for Dark Mode // Input Field Theme for Dark Mode
inputDecorationTheme: InputDecorationTheme( inputDecorationTheme: InputDecorationTheme(
filled: true, filled: true,
fillColor: AppColors.darkSurface, fillColor: DarkColors.surface,
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: AppColors.primaryBlue.withOpacity(0.3)), borderSide: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
), ),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: AppColors.primaryBlue.withOpacity(0.3)), borderSide: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.primaryBlue, width: 2), borderSide: const BorderSide(color: AppColors.primaryTeal, width: 2),
), ),
errorBorder: OutlineInputBorder( errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
@@ -313,11 +351,11 @@ class AppTheme {
vertical: 12, vertical: 12,
), ),
hintStyle: const TextStyle( hintStyle: const TextStyle(
color: AppColors.darkTextSecondary, color: DarkColors.textSecondary,
fontSize: 14, fontSize: 14,
), ),
labelStyle: const TextStyle( labelStyle: const TextStyle(
color: AppColors.darkTextSecondary, color: DarkColors.textSecondary,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@@ -325,88 +363,141 @@ class AppTheme {
// Text Field Theme for Dark Mode // Text Field Theme for Dark Mode
textSelectionTheme: TextSelectionThemeData( textSelectionTheme: TextSelectionThemeData(
cursorColor: AppColors.primaryBlue, cursorColor: AppColors.primaryTeal,
selectionColor: AppColors.primaryBlue.withOpacity(0.3), selectionColor: AppColors.primaryTeal.withOpacity(0.3),
selectionHandleColor: AppColors.primaryBlue, selectionHandleColor: AppColors.primaryTeal,
), ),
textTheme: const TextTheme( textTheme: const TextTheme(
displayLarge: TextStyle( displayLarge: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 32, fontSize: 32,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
displayMedium: TextStyle( displayMedium: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 28, fontSize: 28,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
displaySmall: TextStyle( displaySmall: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
headlineLarge: TextStyle( headlineLarge: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
headlineMedium: TextStyle( headlineMedium: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
headlineSmall: TextStyle( headlineSmall: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
titleLarge: TextStyle( titleLarge: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
titleMedium: TextStyle( titleMedium: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
titleSmall: TextStyle( titleSmall: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
bodyLarge: TextStyle( bodyLarge: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
bodyMedium: TextStyle( bodyMedium: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
bodySmall: TextStyle( bodySmall: TextStyle(
color: AppColors.darkTextSecondary, color: DarkColors.textSecondary,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
labelLarge: TextStyle( labelLarge: TextStyle(
color: AppColors.darkTextPrimary, color: DarkColors.textPrimary,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
labelMedium: TextStyle( labelMedium: TextStyle(
color: AppColors.darkTextSecondary, color: DarkColors.textSecondary,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
labelSmall: TextStyle( labelSmall: TextStyle(
color: AppColors.darkTextSecondary, color: DarkColors.textSecondary,
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
// Bottom Navigation Bar Theme for Dark Mode
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: DarkColors.surface,
selectedItemColor: AppColors.primaryTeal,
unselectedItemColor: DarkColors.iconInactive,
type: BottomNavigationBarType.fixed,
elevation: 8,
selectedLabelStyle: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
),
unselectedLabelStyle: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
),
),
// Floating Action Button Theme for Dark Mode
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: AppColors.primaryTeal,
foregroundColor: Colors.white,
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
),
// Divider Theme for Dark Mode
dividerTheme: const DividerThemeData(
color: DarkColors.divider,
thickness: 1,
space: 1,
),
// Icon Theme for Dark Mode
iconTheme: const IconThemeData(color: DarkColors.iconActive, size: 24),
// Progress Indicator Theme for Dark Mode
progressIndicatorTheme: const ProgressIndicatorThemeData(
color: AppColors.primaryTeal,
linearTrackColor: DarkColors.buttonSecondary,
circularTrackColor: DarkColors.buttonSecondary,
),
// Chip Theme for Dark Mode
chipTheme: ChipThemeData(
backgroundColor: DarkColors.buttonSecondary,
selectedColor: AppColors.primaryTeal.withOpacity(0.1),
disabledColor: DarkColors.buttonSecondary.withOpacity(0.5),
labelStyle: const TextStyle(color: DarkColors.textPrimary),
secondaryLabelStyle: const TextStyle(color: DarkColors.textPrimary),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
); );
} }
} }

View File

@@ -48,9 +48,9 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
} }
}, },
child: Scaffold( child: Scaffold(
backgroundColor: const Color(0xFFF8F9FA), backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.white, backgroundColor: Theme.of(context).colorScheme.surface,
elevation: 0, elevation: 0,
title: Row( title: Row(
children: [ children: [
@@ -58,8 +58,11 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
width: 40, width: 40,
height: 40, height: 40,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: const LinearGradient( gradient: LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)], colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary.withOpacity(0.8),
],
), ),
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
), ),
@@ -74,12 +77,15 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.grey[800], color: Theme.of(context).colorScheme.onSurface,
), ),
), ),
Text( Text(
'Seu tutor educacional inteligente', 'Seu tutor educacional inteligente',
style: TextStyle(fontSize: 12, color: Colors.grey[600]), style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
), ),
], ],
), ),
@@ -88,7 +94,10 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
actions: [ actions: [
IconButton( IconButton(
onPressed: _handleLogout, onPressed: _handleLogout,
icon: Icon(Icons.logout, color: Colors.grey[700]), icon: Icon(
Icons.logout,
color: Theme.of(context).colorScheme.onSurface,
),
tooltip: 'Sair', tooltip: 'Sair',
), ),
], ],
@@ -98,14 +107,14 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
// Messages area // Messages area
Expanded( Expanded(
child: Container( child: Container(
decoration: const BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [
Color(0xFFF8F9FA), Theme.of(context).colorScheme.background,
Color(0xFFE8F0FE), Theme.of(context).colorScheme.primary.withOpacity(0.05),
Color(0xFFF8F9FA), Theme.of(context).colorScheme.background,
], ],
), ),
), ),
@@ -163,15 +172,24 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: isUser gradient: isUser
? const LinearGradient( ? LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)], colors: [
Theme.of(context).colorScheme.primary,
Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
) )
: LinearGradient( : LinearGradient(
colors: [ colors: [
Colors.white.withOpacity(0.95), Theme.of(
Colors.white.withOpacity(0.9), context,
).colorScheme.surface.withOpacity(0.95),
Theme.of(
context,
).colorScheme.surface.withOpacity(0.9),
], ],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
@@ -208,24 +226,32 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
data: content, data: content,
styleSheet: MarkdownStyleSheet( styleSheet: MarkdownStyleSheet(
p: TextStyle( p: TextStyle(
color: const Color(0xFF2D3748), color: Theme.of(
context,
).colorScheme.onSurface,
fontSize: 16, fontSize: 16,
height: 1.4, height: 1.4,
), ),
strong: TextStyle( strong: TextStyle(
color: const Color(0xFF2D3748), color: Theme.of(
context,
).colorScheme.onSurface,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
height: 1.4, height: 1.4,
), ),
em: TextStyle( em: TextStyle(
color: const Color(0xFF2D3748), color: Theme.of(
context,
).colorScheme.onSurface,
fontSize: 16, fontSize: 16,
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
height: 1.4, height: 1.4,
), ),
listBullet: TextStyle( listBullet: TextStyle(
color: const Color(0xFF2D3748), color: Theme.of(
context,
).colorScheme.onSurface,
fontSize: 16, fontSize: 16,
height: 1.4, height: 1.4,
), ),
@@ -247,7 +273,10 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
), ),
child: Text( child: Text(
_formatTimestamp(timestamp), _formatTimestamp(timestamp),
style: TextStyle(fontSize: 11, color: Colors.grey[600]), style: TextStyle(
fontSize: 11,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
), ),
), ),
], ],
@@ -267,13 +296,16 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
width: 36, width: 36,
height: 36, height: 36,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: const LinearGradient( gradient: LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)], colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary.withOpacity(0.8),
],
), ),
borderRadius: BorderRadius.circular(18), borderRadius: BorderRadius.circular(18),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: const Color(0xFF82C9BD).withOpacity(0.3), color: Theme.of(context).colorScheme.primary.withOpacity(0.3),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@@ -295,7 +327,7 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
bottom: bottomPadding + 16.0, // Add system navigation bar padding bottom: bottomPadding + 16.0, // Add system navigation bar padding
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.95), color: Theme.of(context).colorScheme.surface.withOpacity(0.95),
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)), borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
@@ -308,8 +340,11 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
color: Colors.white, color: Theme.of(context).colorScheme.surface,
border: Border.all(color: const Color(0xFFE2E8F0), width: 1), border: Border.all(
color: Theme.of(context).colorScheme.outline.withOpacity(0.5),
width: 1,
),
), ),
child: Row( child: Row(
children: [ children: [
@@ -317,9 +352,9 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
Expanded( Expanded(
child: TextField( child: TextField(
controller: _messageController, controller: _messageController,
style: const TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: Color(0xFF1A1A1A), // Dark text for visibility color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
decoration: InputDecoration( decoration: InputDecoration(
@@ -330,7 +365,7 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
), ),
hintText: 'Faz a tua pergunta!', hintText: 'Faz a tua pergunta!',
hintStyle: TextStyle( hintStyle: TextStyle(
color: Colors.grey[400], color: Theme.of(context).colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontSize: 16, fontSize: 16,
), ),
@@ -351,20 +386,27 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
height: 44, height: 44,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: _messageController.text.isNotEmpty gradient: _messageController.text.isNotEmpty
? const LinearGradient( ? LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)], colors: [
Theme.of(context).colorScheme.primary,
Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
) )
: null, : null,
color: _messageController.text.isNotEmpty color: _messageController.text.isNotEmpty
? null ? null
: Colors.grey[300], : Theme.of(context).colorScheme.outline.withOpacity(0.3),
borderRadius: BorderRadius.circular(22), borderRadius: BorderRadius.circular(22),
boxShadow: _messageController.text.isNotEmpty boxShadow: _messageController.text.isNotEmpty
? [ ? [
BoxShadow( BoxShadow(
color: const Color(0xFF82C9BD).withOpacity(0.3), color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.3),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@@ -398,7 +440,8 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
void _addWelcomeMessage() { void _addWelcomeMessage() {
final welcomeMessage = { final welcomeMessage = {
'content': '''**Olá! Sou o GOAT, o teu Assistente IA oficial do Teach it.** 🐐 'content':
'''**Olá! Sou o GOAT, o teu Assistente IA oficial do Teach it.** 🐐
Estou aqui para te ajudar a aprender de forma confiante e motivadora! Estou aqui para te ajudar a aprender de forma confiante e motivadora!
@@ -465,7 +508,8 @@ Envia-me a tua pergunta sobre qualquer assunto educacional e vou usar o material
} catch (e) { } catch (e) {
// Fallback to error message if API fails // Fallback to error message if API fails
Logger.error('RAG AI Service error: $e'); Logger.error('RAG AI Service error: $e');
final aiResponse = 'Desculpe, ocorreu um erro ao processar a pergunta. Tente novamente.'; final aiResponse =
'Desculpe, ocorreu um erro ao processar a pergunta. Tente novamente.';
setState(() { setState(() {
_messages.add({ _messages.add({

View File

@@ -70,7 +70,9 @@ class _LoginPageState extends State<LoginPage> {
); );
print('DEBUG: Login Firebase bem-sucedido'); print('DEBUG: Login Firebase bem-sucedido');
print('DEBUG: Role selecionado na tela anterior: ${widget.selectedRole}'); print(
'DEBUG: Role selecionado na tela anterior: ${widget.selectedRole}',
);
// Ler role na Firestore // Ler role na Firestore
final uid = result?.user?.uid; final uid = result?.user?.uid;
@@ -81,7 +83,9 @@ class _LoginPageState extends State<LoginPage> {
// Validar se o role selecionado corresponde ao role real // Validar se o role selecionado corresponde ao role real
final selectedRole = widget.selectedRole; final selectedRole = widget.selectedRole;
if (selectedRole != null && actualRole != null && selectedRole != actualRole) { if (selectedRole != null &&
actualRole != null &&
selectedRole != actualRole) {
// Role não corresponde - mostrar erro // Role não corresponde - mostrar erro
setState(() { setState(() {
_isLoading = false; _isLoading = false;
@@ -89,11 +93,14 @@ class _LoginPageState extends State<LoginPage> {
String errorMessage; String errorMessage;
if (selectedRole == 'teacher' && actualRole == 'student') { if (selectedRole == 'teacher' && actualRole == 'student') {
errorMessage = 'Este email está registado como Aluno. Não pode aceder à área de Professores.'; errorMessage =
'Este email está registado como Aluno. Não pode aceder à área de Professores.';
} else if (selectedRole == 'student' && actualRole == 'teacher') { } else if (selectedRole == 'student' && actualRole == 'teacher') {
errorMessage = 'Este email está registado como Professor. Não pode aceder à área de Alunos.'; errorMessage =
'Este email está registado como Professor. Não pode aceder à área de Alunos.';
} else { } else {
errorMessage = 'O tipo de utilizador selecionado não corresponde ao perfil registado.'; errorMessage =
'O tipo de utilizador selecionado não corresponde ao perfil registado.';
} }
_showRoleErrorDialog('Acesso Negado', errorMessage); _showRoleErrorDialog('Acesso Negado', errorMessage);
@@ -151,14 +158,14 @@ class _LoginPageState extends State<LoginPage> {
return AlertDialog( return AlertDialog(
title: Text( title: Text(
title, title,
style: const TextStyle( style: TextStyle(
color: Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
content: Text( content: Text(
message, message,
style: const TextStyle(color: Color(0xFF2D3748)), style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
), ),
actions: [ actions: [
TextButton( TextButton(
@@ -167,9 +174,9 @@ class _LoginPageState extends State<LoginPage> {
// Fazer logout para limpar a sessão // Fazer logout para limpar a sessão
AuthService.signOut(); AuthService.signOut();
}, },
child: const Text( child: Text(
'Voltar', 'Voltar',
style: TextStyle(color: Color(0xFF82C9BD)), style: TextStyle(color: Theme.of(context).colorScheme.primary),
), ),
), ),
], ],
@@ -180,304 +187,403 @@ class _LoginPageState extends State<LoginPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return PopScope(
body: Container( canPop: false,
decoration: const BoxDecoration( onPopInvokedWithResult: (didPop, result) {
gradient: LinearGradient( if (!didPop) {
begin: Alignment.topLeft, context.go('/role-selection');
end: Alignment.bottomRight, }
colors: [ },
Color(0xFFF8F9FA), child: Scaffold(
Color.fromRGBO(130, 201, 189, 0.1), body: Stack(
Color.fromRGBO(246, 141, 45, 0.05), children: [
Color(0xFFF8F9FA), // Main content
], Container(
), decoration: BoxDecoration(
), gradient: LinearGradient(
child: SafeArea( begin: Alignment.topLeft,
child: Center( end: Alignment.bottomRight,
child: SingleChildScrollView( colors: [
padding: const EdgeInsets.all(24.0), Theme.of(context).colorScheme.background,
child: Form( Theme.of(context).colorScheme.primary.withOpacity(0.1),
key: _formKey, Theme.of(context).colorScheme.secondary.withOpacity(0.05),
child: Column( Theme.of(context).colorScheme.background,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 60),
// Logo/Title
Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(16.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10.0,
offset: const Offset(0, 4),
),
],
),
child: Column(
children: [
Text(
'EPVC',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
foreground: Paint()
..shader = LinearGradient(
colors: [
const Color(0xFF82C9BD),
const Color(0xFFF68D2D),
],
).createShader(Rect.fromLTWH(0, 0, 200, 20)),
),
),
const SizedBox(height: 8),
Text(
'Escola Profissional de Vila do Conde',
style: TextStyle(
fontSize: 14,
color: const Color(0xFF2D3748),
fontWeight: FontWeight.w500,
),
),
],
),
).animate().fadeIn(
duration: const Duration(milliseconds: 800),
),
const SizedBox(height: 40),
// Login form
Container(
padding: const EdgeInsets.all(24.0),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(16.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10.0,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Entrar',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Color(0xFF2D3748),
),
),
const SizedBox(height: 24),
// Email field
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
style: const TextStyle(color: Color(0xFF2D3748)),
decoration: InputDecoration(
labelText: 'Email',
labelStyle: const TextStyle(
color: Color(0xFF2D3748),
),
hintStyle: const TextStyle(
color: Color(0xFF718096),
),
prefixIcon: const Icon(
Icons.email,
color: Color(0xFF82C9BD),
),
border: InputBorder.none,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: const BorderSide(
color: Color(0xFFE2E8F0),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: const BorderSide(
color: Color(0xFF82C9BD),
),
),
filled: true,
fillColor: const Color(0xFFF8F9FA),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email é obrigatório';
}
if (!value.contains('@')) {
return 'Email inválido';
}
return null;
},
),
const SizedBox(height: 16),
// Password field
TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
style: const TextStyle(color: Color(0xFF2D3748)),
decoration: InputDecoration(
labelText: 'Palavra-passe',
labelStyle: const TextStyle(
color: Color(0xFF2D3748),
),
hintStyle: const TextStyle(
color: Color(0xFF718096),
),
prefixIcon: const Icon(
Icons.lock,
color: Color(0xFF82C9BD),
),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility
: Icons.visibility_off,
color: const Color(0xFF82C9BD),
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
border: InputBorder.none,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: const BorderSide(
color: Color(0xFFE2E8F0),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: const BorderSide(
color: Color(0xFF82C9BD),
),
),
filled: true,
fillColor: const Color(0xFFF8F9FA),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Palavra-passe é obrigatória';
}
if (value.length < 6) {
return 'Palavra-passe muito curta';
}
return null;
},
),
const SizedBox(height: 12),
// Remember me checkbox
Row(
children: [
Checkbox(
value: _rememberMe,
onChanged: (bool? value) {
setState(() {
_rememberMe = value ?? false;
});
},
activeColor: const Color(0xFF82C9BD),
checkColor: Colors.white,
),
GestureDetector(
onTap: () {
setState(() {
_rememberMe = !_rememberMe;
});
},
child: Text(
'Manter sessão iniciada',
style: TextStyle(
color: const Color(0xFF2D3748),
fontSize: 14,
fontWeight: _rememberMe
? FontWeight.w500
: FontWeight.normal,
),
),
),
],
),
const SizedBox(height: 12),
// Login button
SizedBox(
height: 50,
child: ElevatedButton(
onPressed: _isLoading ? null : _handleLogin,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF82C9BD),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
elevation: 2,
),
child: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
)
: Text(
'Entrar',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 16),
// Signup link
GestureDetector(
onTap: () {
context.go('/signup');
},
child: Text(
'Não tem conta? Criar aqui',
style: const TextStyle(
color: Color(0xFF82C9BD),
fontWeight: FontWeight.w500,
),
),
),
],
),
).animate().fadeIn(
duration: const Duration(milliseconds: 1000),
),
const SizedBox(height: 40),
], ],
), ),
), ),
child: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 60),
// Logo/Title
Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Theme.of(
context,
).colorScheme.surface.withOpacity(0.9),
borderRadius: BorderRadius.circular(16.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10.0,
offset: const Offset(0, 4),
),
],
),
child: Column(
children: [
Text(
'EPVC',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
foreground: Paint()
..shader =
LinearGradient(
colors: [
Theme.of(
context,
).colorScheme.primary,
Theme.of(
context,
).colorScheme.secondary,
],
).createShader(
Rect.fromLTWH(0, 0, 200, 20),
),
),
),
const SizedBox(height: 8),
Text(
'Escola Profissional de Vila do Conde',
style: TextStyle(
fontSize: 14,
color: Theme.of(
context,
).colorScheme.onSurface,
fontWeight: FontWeight.w500,
),
),
],
),
).animate().fadeIn(
duration: const Duration(milliseconds: 800),
),
const SizedBox(height: 40),
// Login form
Container(
padding: const EdgeInsets.all(24.0),
decoration: BoxDecoration(
color: Theme.of(
context,
).colorScheme.surface.withOpacity(0.9),
borderRadius: BorderRadius.circular(16.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10.0,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Entrar',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Theme.of(
context,
).colorScheme.onSurface,
),
),
const SizedBox(height: 24),
// Email field
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
),
decoration: InputDecoration(
labelText: 'Email',
labelStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
),
hintStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
),
prefixIcon: Icon(
Icons.email,
color: Theme.of(
context,
).colorScheme.primary,
),
border: InputBorder.none,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.outline.withOpacity(0.5),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.primary,
),
),
filled: true,
fillColor: Theme.of(
context,
).colorScheme.surface,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email é obrigatório';
}
if (!value.contains('@')) {
return 'Email inválido';
}
return null;
},
),
const SizedBox(height: 16),
// Password field
TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
),
decoration: InputDecoration(
labelText: 'Palavra-passe',
labelStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
),
hintStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
),
prefixIcon: Icon(
Icons.lock,
color: Theme.of(
context,
).colorScheme.primary,
),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility
: Icons.visibility_off,
color: Theme.of(
context,
).colorScheme.primary,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
border: InputBorder.none,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.outline.withOpacity(0.5),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.primary,
),
),
filled: true,
fillColor: Theme.of(
context,
).colorScheme.surface,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Palavra-passe é obrigatória';
}
if (value.length < 6) {
return 'Palavra-passe muito curta';
}
return null;
},
),
const SizedBox(height: 12),
// Remember me checkbox
Row(
children: [
Checkbox(
value: _rememberMe,
onChanged: (bool? value) {
setState(() {
_rememberMe = value ?? false;
});
},
activeColor: Theme.of(
context,
).colorScheme.primary,
checkColor: Colors.white,
),
GestureDetector(
onTap: () {
setState(() {
_rememberMe = !_rememberMe;
});
},
child: Text(
'Manter sessão iniciada',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
fontSize: 14,
fontWeight: _rememberMe
? FontWeight.w500
: FontWeight.normal,
),
),
),
],
),
const SizedBox(height: 12),
// Login button
SizedBox(
height: 50,
child: ElevatedButton(
onPressed: _isLoading ? null : _handleLogin,
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(
context,
).colorScheme.primary,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
8.0,
),
),
elevation: 2,
),
child: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
)
: Text(
'Entrar',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 16),
// Signup link
GestureDetector(
onTap: () {
context.go('/signup');
},
child: Text(
'Não tem conta? Criar aqui',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.primary,
fontWeight: FontWeight.w500,
),
),
),
],
),
).animate().fadeIn(
duration: const Duration(milliseconds: 1000),
),
const SizedBox(height: 40),
],
),
),
),
),
),
), ),
), // Custom back button
Positioned(
top: 50,
left: 16,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface.withOpacity(0.8),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Theme.of(
context,
).colorScheme.shadow.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: IconButton(
icon: Icon(
Icons.arrow_back,
color: Theme.of(context).colorScheme.onSurface,
),
onPressed: () => context.go('/role-selection'),
),
),
),
],
), ),
), ),
); );

View File

@@ -23,9 +23,10 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: [
AppColors.background, Theme.of(context).colorScheme.background,
AppColors.primaryBlue.withOpacity(0.05), Theme.of(context).colorScheme.primary.withOpacity(0.1),
AppColors.gradientStart.withOpacity(0.1), Theme.of(context).colorScheme.secondary.withOpacity(0.05),
Theme.of(context).colorScheme.background,
], ],
), ),
), ),
@@ -92,7 +93,9 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
AppLocalizations.of(context)!.appTitle, AppLocalizations.of(context)!.appTitle,
style: Theme.of(context).textTheme.headlineLarge style: Theme.of(context).textTheme.headlineLarge
?.copyWith( ?.copyWith(
color: AppColors.textPrimary, color: Theme.of(
context,
).colorScheme.onSurface,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
) )
@@ -149,7 +152,7 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
'Quem é você?', 'Quem é você?',
style: Theme.of(context).textTheme.headlineMedium style: Theme.of(context).textTheme.headlineMedium
?.copyWith( ?.copyWith(
color: AppColors.textPrimary, color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
) )
@@ -351,7 +354,7 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
style: Theme.of(context).textTheme.titleLarge?.copyWith( style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: isSelected color: isSelected
? Colors.white ? Colors.white
: AppColors.textPrimary, : Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),

View File

@@ -101,315 +101,444 @@ class _SignupPageState extends State<SignupPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return PopScope(
body: Container( canPop: false,
decoration: const BoxDecoration( onPopInvokedWithResult: (didPop, result) {
gradient: LinearGradient( if (!didPop) {
begin: Alignment.topLeft, context.go('/role-selection');
end: Alignment.bottomRight, }
colors: [ },
Color(0xFFF8F9FA), child: Scaffold(
Color.fromRGBO(130, 201, 189, 0.1), body: Stack(
Color.fromRGBO(246, 141, 45, 0.05), children: [
Color(0xFFF8F9FA), // Main content
], Container(
), decoration: BoxDecoration(
), gradient: LinearGradient(
child: SafeArea( begin: Alignment.topLeft,
child: Center( end: Alignment.bottomRight,
child: SingleChildScrollView( colors: [
padding: const EdgeInsets.all(24.0), Theme.of(context).colorScheme.background,
child: Form( Theme.of(context).colorScheme.primary.withOpacity(0.1),
key: _formKey, Theme.of(context).colorScheme.secondary.withOpacity(0.05),
child: Column( Theme.of(context).colorScheme.background,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 60),
// Logo/Title
Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(16.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10.0,
offset: const Offset(0, 4),
),
],
),
child: Column(
children: [
Text(
'EPVC',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
foreground: Paint()
..shader = LinearGradient(
colors: [
const Color(0xFF82C9BD),
const Color(0xFFF68D2D),
],
).createShader(Rect.fromLTWH(0, 0, 200, 20)),
),
),
const SizedBox(height: 8),
Text(
'Escola Profissional de Vila do Conde',
style: TextStyle(
fontSize: 14,
color: const Color(0xFF2D3748),
fontWeight: FontWeight.w500,
),
),
],
),
).animate().fadeIn(
duration: const Duration(milliseconds: 800),
),
const SizedBox(height: 40),
// Signup form
Container(
padding: const EdgeInsets.all(24.0),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(16.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10.0,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Criar Conta',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Color(0xFF2D3748),
),
),
const SizedBox(height: 24),
// Name field
TextFormField(
controller: _nameController,
keyboardType: TextInputType.name,
style: const TextStyle(color: Color(0xFF2D3748)),
decoration: InputDecoration(
labelText: 'Nome Completo',
labelStyle: const TextStyle(
color: Color(0xFF2D3748),
),
hintStyle: const TextStyle(
color: Color(0xFF718096),
),
prefixIcon: const Icon(
Icons.person,
color: Color(0xFF82C9BD),
),
border: InputBorder.none,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: const BorderSide(
color: Color(0xFFE2E8F0),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: const BorderSide(
color: Color(0xFF82C9BD),
),
),
filled: true,
fillColor: const Color(0xFFF8F9FA),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Nome é obrigatório';
}
if (value.length < 2) {
return 'Nome muito curto';
}
return null;
},
),
const SizedBox(height: 16),
// Email field
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
style: const TextStyle(color: Color(0xFF2D3748)),
decoration: InputDecoration(
labelText: 'Email',
labelStyle: const TextStyle(
color: Color(0xFF2D3748),
),
hintStyle: const TextStyle(
color: Color(0xFF718096),
),
prefixIcon: const Icon(
Icons.email,
color: Color(0xFF82C9BD),
),
border: InputBorder.none,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: const BorderSide(
color: Color(0xFFE2E8F0),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: const BorderSide(
color: Color(0xFF82C9BD),
),
),
filled: true,
fillColor: const Color(0xFFF8F9FA),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email é obrigatório';
}
if (!value.contains('@')) {
return 'Email inválido';
}
return null;
},
),
const SizedBox(height: 16),
// Password field
TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
style: const TextStyle(color: Color(0xFF2D3748)),
decoration: InputDecoration(
labelText: 'Palavra-passe',
labelStyle: const TextStyle(
color: Color(0xFF2D3748),
),
hintStyle: const TextStyle(
color: Color(0xFF718096),
),
prefixIcon: const Icon(
Icons.lock,
color: Color(0xFF82C9BD),
),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility
: Icons.visibility_off,
color: const Color(0xFF82C9BD),
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
border: InputBorder.none,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: const BorderSide(
color: Color(0xFFE2E8F0),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: const BorderSide(
color: Color(0xFF82C9BD),
),
),
filled: true,
fillColor: const Color(0xFFF8F9FA),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Palavra-passe é obrigatória';
}
if (value.length < 6) {
return 'Palavra-passe muito curta';
}
return null;
},
),
const SizedBox(height: 24),
// Signup button
SizedBox(
height: 50,
child: ElevatedButton(
onPressed: _isLoading ? null : _handleSignup,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF82C9BD),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
elevation: 2,
),
child: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
)
: Text(
'Criar Conta',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 16),
// Login link
GestureDetector(
onTap: () {
context.go('/login?role=$_selectedRole');
},
child: Text(
'Já tem conta? Entrar aqui',
style: const TextStyle(
color: Color(0xFF82C9BD),
fontWeight: FontWeight.w500,
),
),
),
],
),
).animate().fadeIn(
duration: const Duration(milliseconds: 1000),
),
const SizedBox(height: 40),
], ],
), ),
), ),
child: SafeArea(
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 60),
// Logo/Title
Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Theme.of(
context,
).colorScheme.surface.withOpacity(0.9),
borderRadius: BorderRadius.circular(16.0),
boxShadow: [
BoxShadow(
color: Theme.of(
context,
).colorScheme.shadow.withOpacity(0.1),
blurRadius: 10.0,
offset: const Offset(0, 4),
),
],
),
child: Column(
children: [
Text(
'EPVC',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
foreground: Paint()
..shader =
LinearGradient(
colors: [
Theme.of(
context,
).colorScheme.primary,
Theme.of(
context,
).colorScheme.secondary,
],
).createShader(
Rect.fromLTWH(0, 0, 200, 20),
),
),
),
const SizedBox(height: 8),
Text(
'Escola Profissional de Vila do Conde',
style: TextStyle(
fontSize: 14,
color: Theme.of(
context,
).colorScheme.onSurface,
fontWeight: FontWeight.w500,
),
),
],
),
).animate().fadeIn(
duration: const Duration(milliseconds: 800),
),
const SizedBox(height: 40),
// Signup form
Container(
padding: const EdgeInsets.all(24.0),
decoration: BoxDecoration(
color: Theme.of(
context,
).colorScheme.surface.withOpacity(0.9),
borderRadius: BorderRadius.circular(16.0),
boxShadow: [
BoxShadow(
color: Theme.of(
context,
).colorScheme.shadow.withOpacity(0.1),
blurRadius: 10.0,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Criar Conta',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Theme.of(
context,
).colorScheme.onSurface,
),
),
const SizedBox(height: 24),
// Name field
TextFormField(
controller: _nameController,
keyboardType: TextInputType.name,
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
),
decoration: InputDecoration(
labelText: 'Nome Completo',
labelStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
),
hintStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
),
prefixIcon: Icon(
Icons.person,
color: Theme.of(
context,
).colorScheme.primary,
),
border: InputBorder.none,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.outline.withOpacity(0.3),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.primary,
),
),
filled: true,
fillColor:
Theme.of(context).brightness ==
Brightness.dark
? Theme.of(
context,
).colorScheme.surfaceContainerHighest
: Theme.of(context).colorScheme.surface,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Nome é obrigatório';
}
if (value.length < 2) {
return 'Nome muito curto';
}
return null;
},
),
const SizedBox(height: 16),
// Email field
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
),
decoration: InputDecoration(
labelText: 'Email',
labelStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
),
hintStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
),
prefixIcon: Icon(
Icons.email,
color: Theme.of(
context,
).colorScheme.primary,
),
border: InputBorder.none,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.outline.withOpacity(0.3),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.primary,
),
),
filled: true,
fillColor:
Theme.of(context).brightness ==
Brightness.dark
? Theme.of(
context,
).colorScheme.surfaceContainerHighest
: Theme.of(context).colorScheme.surface,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email é obrigatório';
}
if (!value.contains('@')) {
return 'Email inválido';
}
return null;
},
),
const SizedBox(height: 16),
// Password field
TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
),
decoration: InputDecoration(
labelText: 'Palavra-passe',
labelStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
),
hintStyle: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
),
prefixIcon: Icon(
Icons.lock,
color: Theme.of(
context,
).colorScheme.primary,
),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility
: Icons.visibility_off,
color: Theme.of(
context,
).colorScheme.primary,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
border: InputBorder.none,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.outline.withOpacity(0.3),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color: Theme.of(
context,
).colorScheme.primary,
),
),
filled: true,
fillColor:
Theme.of(context).brightness ==
Brightness.dark
? Theme.of(
context,
).colorScheme.surfaceContainerHighest
: Theme.of(context).colorScheme.surface,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Palavra-passe é obrigatória';
}
if (value.length < 6) {
return 'Palavra-passe muito curta';
}
return null;
},
),
const SizedBox(height: 24),
// Signup button
SizedBox(
height: 50,
child: ElevatedButton(
onPressed: _isLoading
? null
: _handleSignup,
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(
context,
).colorScheme.primary,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
8.0,
),
),
elevation: 2,
),
child: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
)
: Text(
'Criar Conta',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 16),
// Login link
GestureDetector(
onTap: () {
context.go('/login?role=$_selectedRole');
},
child: Text(
'Já tem conta? Entrar aqui',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.primary,
fontWeight: FontWeight.w500,
),
),
),
],
),
).animate().fadeIn(
duration: const Duration(milliseconds: 1000),
),
const SizedBox(height: 40),
],
),
),
),
),
),
), ),
), // Custom back button
Positioned(
top: 50,
left: 16,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface.withOpacity(0.8),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Theme.of(
context,
).colorScheme.shadow.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: IconButton(
icon: Icon(
Icons.arrow_back,
color: Theme.of(context).colorScheme.onSurface,
),
onPressed: () => context.go('/role-selection'),
),
),
),
],
), ),
), ),
); );

View File

@@ -76,15 +76,15 @@ class _StudentDashboardPageState extends State<StudentDashboardPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: Container( body: Container(
decoration: const BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [
Color(0xFF82C9BD), Theme.of(context).colorScheme.primary,
Color(0xFF7BA89C), Theme.of(context).colorScheme.primary.withOpacity(0.8),
Color(0xFFF68D2D), Theme.of(context).colorScheme.secondary,
Color(0xFFF8F9FA), Theme.of(context).colorScheme.background,
], ],
stops: [0.0, 0.2, 0.6, 1.0], stops: [0.0, 0.2, 0.6, 1.0],
), ),

View File

@@ -71,15 +71,15 @@ class _TeacherDashboardPageState extends State<TeacherDashboardPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: Container( body: Container(
decoration: const BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [
Color(0xFF82C9BD), Theme.of(context).colorScheme.primary,
Color(0xFF7BA89C), Theme.of(context).colorScheme.primary.withOpacity(0.8),
Color(0xFFF68D2D), Theme.of(context).colorScheme.secondary,
Color(0xFFF8F9FA), Theme.of(context).colorScheme.background,
], ],
stops: [0.0, 0.2, 0.6, 1.0], stops: [0.0, 0.2, 0.6, 1.0],
), ),

View File

@@ -17,12 +17,14 @@ class ProfileSectionWidget extends StatelessWidget {
margin: const EdgeInsets.only(top: 24), margin: const EdgeInsets.only(top: 24),
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0)), border: Border.all(
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -38,8 +40,13 @@ class ProfileSectionWidget extends StatelessWidget {
width: 48, width: 48,
height: 48, height: 48,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: const LinearGradient( gradient: LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA8A0)], colors: [
Theme.of(context).colorScheme.primary,
Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
],
), ),
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
), ),
@@ -56,8 +63,8 @@ class ProfileSectionWidget extends StatelessWidget {
children: [ children: [
Text( Text(
userName, userName,
style: const TextStyle( style: TextStyle(
color: Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -70,16 +77,20 @@ class ProfileSectionWidget extends StatelessWidget {
children: [ children: [
Text( Text(
userEmail, userEmail,
style: const TextStyle( style: TextStyle(
color: Color(0xFF718096), color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 14, fontSize: 14,
), ),
), ),
if (userEmail.length > 20) ...[ if (userEmail.length > 20) ...[
const SizedBox(width: 8), const SizedBox(width: 8),
const Icon( Icon(
Icons.more_horiz, Icons.more_horiz,
color: Color(0xFF718096), color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
size: 16, size: 16,
), ),
], ],
@@ -96,12 +107,14 @@ class ProfileSectionWidget extends StatelessWidget {
child: Container( child: Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFF68D2D).withOpacity(0.1), color: Theme.of(
context,
).colorScheme.secondary.withOpacity(0.1),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
child: const Icon( child: Icon(
Icons.settings, Icons.settings,
color: Color(0xFFF68D2D), color: Theme.of(context).colorScheme.secondary,
size: 20, size: 20,
), ),
), ),
@@ -113,16 +126,16 @@ class ProfileSectionWidget extends StatelessWidget {
// Achievements // Achievements
Row( Row(
children: [ children: [
const Icon( Icon(
Icons.emoji_events, Icons.emoji_events,
color: Color(0xFFF68D2D), color: Theme.of(context).colorScheme.secondary,
size: 20, size: 20,
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
const Text( Text(
'Conquistas', 'Conquistas',
style: TextStyle( style: TextStyle(
color: Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -137,25 +150,27 @@ class ProfileSectionWidget extends StatelessWidget {
_buildAchievementBadge( _buildAchievementBadge(
icon: Icons.local_fire_department, icon: Icons.local_fire_department,
label: '7 dias', label: '7 dias',
color: const Color(0xFFF68D2D), color: Theme.of(context).colorScheme.secondary,
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
_buildAchievementBadge( _buildAchievementBadge(
icon: Icons.school, icon: Icons.school,
label: '3 conceitos', label: '3 conceitos',
color: const Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
_buildAchievementBadge( _buildAchievementBadge(
icon: Icons.speed, icon: Icons.speed,
label: 'Rápido', label: 'Rápido',
color: const Color(0xFF6BA8A0), color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
_buildAchievementBadge( _buildAchievementBadge(
icon: Icons.star, icon: Icons.star,
label: '100%', label: '100%',
color: const Color(0xFF4CAF50), color: Theme.of(context).colorScheme.tertiary,
), ),
], ],
), ),
@@ -165,15 +180,21 @@ class ProfileSectionWidget extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFF8F9FA), color: Theme.of(context).brightness == Brightness.dark
? Theme.of(context).colorScheme.surfaceContainerHighest
: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE2E8F0)), border: Border.all(
color: Theme.of(
context,
).colorScheme.outline.withOpacity(0.2),
),
), ),
child: Row( child: Row(
children: [ children: [
const Icon( Icon(
Icons.trending_up, Icons.trending_up,
color: Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
size: 20, size: 20,
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
@@ -181,10 +202,10 @@ class ProfileSectionWidget extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'Ótimo progresso!', 'Ótimo progresso!',
style: TextStyle( style: TextStyle(
color: Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -192,8 +213,10 @@ class ProfileSectionWidget extends StatelessWidget {
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
'Você está 15% acima da média esta semana', 'Você está 15% acima da média esta semana',
style: const TextStyle( style: TextStyle(
color: Color(0xFF718096), color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 12, fontSize: 12,
), ),
), ),

View File

@@ -39,7 +39,7 @@ class ProgressHeroWidget extends StatelessWidget {
Text( Text(
'Seu Progresso', 'Seu Progresso',
style: TextStyle( style: TextStyle(
color: const Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -48,7 +48,7 @@ class ProgressHeroWidget extends StatelessWidget {
Text( Text(
'Continue assim, $userName!', 'Continue assim, $userName!',
style: TextStyle( style: TextStyle(
color: const Color(0xFF718096), color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 16, fontSize: 16,
), ),
), ),
@@ -56,9 +56,12 @@ class ProgressHeroWidget extends StatelessWidget {
), ),
), ),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
), ),
child: Row( child: Row(
@@ -93,8 +96,8 @@ class ProgressHeroWidget extends StatelessWidget {
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: [
const Color(0xFF82C9BD), Theme.of(context).colorScheme.primary,
const Color(0xFF6BA8A0), Theme.of(context).colorScheme.primary.withOpacity(0.8),
], ],
), ),
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
@@ -186,77 +189,90 @@ class ProgressHeroWidget extends StatelessWidget {
// Mastered Concepts // Mastered Concepts
Container( Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0)), border: Border.all(
boxShadow: [ color: Theme.of(
BoxShadow( context,
color: Colors.black.withOpacity(0.05), ).colorScheme.outline.withOpacity(0.2),
blurRadius: 10, ),
offset: const Offset(0, 4), boxShadow: [
), BoxShadow(
], color: Theme.of(
), context,
child: Column( ).colorScheme.shadow.withOpacity(0.05),
crossAxisAlignment: CrossAxisAlignment.start, blurRadius: 10,
children: [ offset: const Offset(0, 4),
Row(
children: [
const Icon(
Icons.school,
color: Color(0xFFF68D2D),
size: 20,
), ),
const SizedBox(width: 8), ],
const Text( ),
'Conceitos Dominados', child: Column(
style: TextStyle( crossAxisAlignment: CrossAxisAlignment.start,
color: Color(0xFF2D3748), children: [
fontSize: 16, Row(
fontWeight: FontWeight.bold, children: [
Icon(
Icons.school,
color: Theme.of(context).colorScheme.secondary,
size: 20,
),
const SizedBox(width: 8),
Text(
'Conceitos Dominados',
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 12),
...masteredConcepts.map(
(concept) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
shape: BoxShape.circle,
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
concept,
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 14,
),
),
),
Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.primary,
size: 16,
),
],
),
), ),
), ),
], ],
), ),
const SizedBox(height: 12), )
...masteredConcepts.map((concept) => Padding( .animate()
padding: const EdgeInsets.only(bottom: 8), .slideX(
child: Row( duration: const Duration(milliseconds: 800),
children: [ curve: Curves.easeOut,
Container( )
width: 8, .then(delay: const Duration(milliseconds: 200)),
height: 8,
decoration: const BoxDecoration(
color: Color(0xFF82C9BD),
shape: BoxShape.circle,
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
concept,
style: const TextStyle(
color: Color(0xFF4A5568),
fontSize: 14,
),
),
),
const Icon(
Icons.check_circle,
color: Color(0xFF82C9BD),
size: 16,
),
],
),
)),
],
),
).animate().slideX(
duration: const Duration(milliseconds: 800),
curve: Curves.easeOut,
).then(delay: const Duration(milliseconds: 200)),
], ],
), ),
); );
@@ -272,10 +288,7 @@ class ProgressHeroWidget extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2), color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all( border: Border.all(color: Colors.white.withOpacity(0.3), width: 1),
color: Colors.white.withOpacity(0.3),
width: 1,
),
), ),
child: Column( child: Column(
children: [ children: [
@@ -292,10 +305,7 @@ class ProgressHeroWidget extends StatelessWidget {
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
label, label,
style: const TextStyle( style: const TextStyle(color: Colors.white, fontSize: 12),
color: Colors.white,
fontSize: 12,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],

View File

@@ -15,7 +15,7 @@ class QuickAccessWidget extends StatelessWidget {
Text( Text(
'Acesso Rápido', 'Acesso Rápido',
style: TextStyle( style: TextStyle(
color: const Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -48,15 +48,18 @@ class QuickAccessWidget extends StatelessWidget {
return Container( return Container(
height: 150, height: 150,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: const LinearGradient( gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [Color(0xFF82C9BD), Color(0xFF6BA8A0)], colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary.withOpacity(0.8),
],
), ),
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: const Color(0xFF82C9BD).withOpacity(0.3), color: Theme.of(context).colorScheme.primary.withOpacity(0.3),
blurRadius: 15, blurRadius: 15,
offset: const Offset(0, 8), offset: const Offset(0, 8),
), ),
@@ -102,7 +105,7 @@ class QuickAccessWidget extends StatelessWidget {
vertical: 4, vertical: 4,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFF68D2D), color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: const Text( child: const Text(
@@ -159,12 +162,15 @@ class QuickAccessWidget extends StatelessWidget {
return Container( return Container(
height: 150, height: 150,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0), width: 1), border: Border.all(
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
width: 1,
),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -192,34 +198,38 @@ class QuickAccessWidget extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFF68D2D).withOpacity(0.1), color: Theme.of(
context,
).colorScheme.secondary.withOpacity(0.1),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: const Icon( child: Icon(
Icons.quiz, Icons.quiz,
color: Color(0xFFF68D2D), color: Theme.of(context).colorScheme.secondary,
size: 24, size: 24,
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
const Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'Quiz', 'Quiz',
style: TextStyle( style: TextStyle(
color: Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
'Teste conhecimentos', 'Testa os teus conhecimentos',
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
color: Color(0xFF718096), color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 12, fontSize: 12,
height: 1.2, height: 1.2,
), ),
@@ -242,14 +252,17 @@ class QuickAccessWidget extends StatelessWidget {
Widget _buildJoinClassCard(BuildContext context) { Widget _buildJoinClassCard(BuildContext context) {
return Container( return Container(
height: 80, height: 86,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0), width: 1), border: Border.all(
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
width: 1,
),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -262,9 +275,7 @@ class QuickAccessWidget extends StatelessWidget {
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(builder: (_) => const JoinClassPage()),
builder: (_) => const JoinClassPage(),
),
); );
}, },
child: Padding( child: Padding(
@@ -274,17 +285,19 @@ class QuickAccessWidget extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF82C9BD).withOpacity(0.1), color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: const Icon( child: Icon(
Icons.group_add, Icons.group_add,
color: Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
size: 24, size: 24,
), ),
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
const Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -292,27 +305,29 @@ class QuickAccessWidget extends StatelessWidget {
Text( Text(
'Entrar numa Turma', 'Entrar numa Turma',
style: TextStyle( style: TextStyle(
color: Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
'Junta-te a uma turma com o código', 'Junta-te a uma turma com o código',
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(
color: Color(0xFF718096), color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 13, fontSize: 13,
), ),
), ),
], ],
), ),
), ),
const Icon( Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
color: Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
size: 16, size: 16,
), ),
], ],

View File

@@ -23,11 +23,11 @@ class StudentClassesListWidget extends StatelessWidget {
.snapshots(), .snapshots(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
return const Center( return Center(
child: Padding( child: Padding(
padding: EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
), ),
), ),
); );
@@ -44,10 +44,7 @@ class StudentClassesListWidget extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 16.0), padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text( child: Text(
'Ainda não entraste em nenhuma turma.', 'Ainda não entraste em nenhuma turma.',
style: TextStyle( style: TextStyle(color: Colors.grey[600], fontSize: 14),
color: Colors.grey[600],
fontSize: 14,
),
), ),
); );
} }
@@ -58,9 +55,9 @@ class StudentClassesListWidget extends StatelessWidget {
Text( Text(
'As Minhas Turmas', 'As Minhas Turmas',
style: Theme.of(context).textTheme.titleLarge?.copyWith( style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: const Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
SizedBox( SizedBox(
@@ -102,7 +99,10 @@ class StudentClassesListWidget extends StatelessWidget {
} }
return FutureBuilder<DocumentSnapshot>( return FutureBuilder<DocumentSnapshot>(
future: FirebaseFirestore.instance.collection('classes').doc(classId).get(), future: FirebaseFirestore.instance
.collection('classes')
.doc(classId)
.get(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData || !snapshot.data!.exists) { if (!snapshot.hasData || !snapshot.data!.exists) {
return Container( return Container(
@@ -110,19 +110,19 @@ class StudentClassesListWidget extends StatelessWidget {
height: 150, height: 150,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
], ],
), ),
child: const Center( child: Center(
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
strokeWidth: 2, strokeWidth: 2,
), ),
), ),
@@ -138,11 +138,11 @@ class StudentClassesListWidget extends StatelessWidget {
height: 150, height: 150,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -155,20 +155,20 @@ class StudentClassesListWidget extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF82C9BD).withOpacity(0.1), color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: const Icon( child: Icon(
Icons.school, Icons.school,
color: Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
size: 24, size: 24,
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Text( Text(
className, className,
style: const TextStyle( style: TextStyle(
color: Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -179,7 +179,7 @@ class StudentClassesListWidget extends StatelessWidget {
Text( Text(
'Código: $classCode', 'Código: $classCode',
style: TextStyle( style: TextStyle(
color: Colors.grey[600], color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 13, fontSize: 13,
), ),
), ),

View File

@@ -13,207 +13,248 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
final userEmail = user?.email ?? ''; final userEmail = user?.email ?? '';
return Container( return Container(
margin: const EdgeInsets.only(top: 24), margin: const EdgeInsets.only(top: 24),
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0)), border: Border.all(
boxShadow: [ color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
BoxShadow( ),
color: Colors.black.withOpacity(0.05), boxShadow: [
blurRadius: 10, BoxShadow(
offset: const Offset(0, 4), color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
), blurRadius: 10,
], offset: const Offset(0, 4),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Profile Header
Row(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA8A0)],
),
borderRadius: BorderRadius.circular(24),
),
child: const Icon(
Icons.school,
color: Colors.white,
size: 24,
),
), ),
const SizedBox(width: 16), ],
Expanded( ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( // Profile Header
userName, Row(
style: const TextStyle( children: [
color: Color(0xFF2D3748), Container(
fontSize: 18, width: 48,
fontWeight: FontWeight.bold, height: 48,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
],
), ),
borderRadius: BorderRadius.circular(24),
), ),
const SizedBox(height: 2), child: const Icon(
SingleChildScrollView( Icons.school,
scrollDirection: Axis.horizontal, color: Colors.white,
child: Row( size: 24,
mainAxisSize: MainAxisSize.min, ),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
userName,
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 2),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
userEmail,
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 14,
),
),
if (userEmail.length > 20) ...[
const SizedBox(width: 8),
Icon(
Icons.more_horiz,
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
size: 16,
),
],
],
),
),
],
),
),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(
context,
).colorScheme.secondary.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.settings,
color: Theme.of(context).colorScheme.secondary,
size: 20,
),
),
],
),
const SizedBox(height: 20),
// Quick Stats Row
Row(
children: [
_buildQuickStat(
icon: Icons.check_circle,
label: 'Alunos Ativos',
value: '18/24',
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(width: 12),
_buildQuickStat(
icon: Icons.warning_amber,
label: 'Precisam Apoio',
value: '3',
color: Theme.of(context).colorScheme.secondary,
),
const SizedBox(width: 12),
_buildQuickStat(
icon: Icons.emoji_events,
label: 'Média Turma',
value: '72%',
color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
),
],
),
const SizedBox(height: 20),
// Top Performing Students Preview
Row(
children: [
Icon(
Icons.leaderboard,
color: Theme.of(context).colorScheme.secondary,
size: 20,
),
const SizedBox(width: 8),
Text(
'Melhores Desempenhos',
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 12),
// Student List Preview
_buildStudentPerformanceItem(
context,
'Ana Silva',
95,
Theme.of(context).colorScheme.tertiary,
),
const SizedBox(height: 8),
_buildStudentPerformanceItem(
context,
'João Costa',
88,
Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 8),
_buildStudentPerformanceItem(
context,
'Maria Santos',
82,
Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 8),
_buildStudentPerformanceItem(
context,
'Pedro Lima',
45,
Theme.of(context).colorScheme.secondary,
),
const SizedBox(height: 20),
// Content Quality Alert
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Theme.of(
context,
).colorScheme.outline.withOpacity(0.2),
),
),
child: Row(
children: [
Icon(
Icons.info_outline,
color: Theme.of(context).colorScheme.primary,
size: 20,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
userEmail, 'Qualidade do Conteúdo',
style: const TextStyle( style: TextStyle(
color: Color(0xFF718096), color: Theme.of(context).colorScheme.onSurface,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.bold,
), ),
), ),
if (userEmail.length > 20) ...[ const SizedBox(height: 2),
const SizedBox(width: 8), Text(
const Icon( '12 conteúdos verificados • 2 pendentes de revisão',
Icons.more_horiz, style: TextStyle(
color: Color(0xFF718096), color: Theme.of(
size: 16, context,
).colorScheme.onSurfaceVariant,
fontSize: 12,
), ),
], ),
], ],
), ),
), ),
], ],
), ),
), ),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFFF68D2D).withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.settings,
color: Color(0xFFF68D2D),
size: 20,
),
),
], ],
), ),
const SizedBox(height: 20), )
.animate()
// Quick Stats Row .slideY(
Row( duration: const Duration(milliseconds: 800),
children: [ curve: Curves.easeOut,
_buildQuickStat( )
icon: Icons.check_circle, .then(delay: const Duration(milliseconds: 400));
label: 'Alunos Ativos',
value: '18/24',
color: const Color(0xFF82C9BD),
),
const SizedBox(width: 12),
_buildQuickStat(
icon: Icons.warning_amber,
label: 'Precisam Apoio',
value: '3',
color: const Color(0xFFF68D2D),
),
const SizedBox(width: 12),
_buildQuickStat(
icon: Icons.emoji_events,
label: 'Média Turma',
value: '72%',
color: const Color(0xFF6BA8A0),
),
],
),
const SizedBox(height: 20),
// Top Performing Students Preview
Row(
children: [
const Icon(
Icons.leaderboard,
color: Color(0xFFF68D2D),
size: 20,
),
const SizedBox(width: 8),
const Text(
'Melhores Desempenhos',
style: TextStyle(
color: Color(0xFF2D3748),
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 12),
// Student List Preview
_buildStudentPerformanceItem('Ana Silva', 95, const Color(0xFF4CAF50)),
const SizedBox(height: 8),
_buildStudentPerformanceItem('João Costa', 88, const Color(0xFF82C9BD)),
const SizedBox(height: 8),
_buildStudentPerformanceItem('Maria Santos', 82, const Color(0xFF82C9BD)),
const SizedBox(height: 8),
_buildStudentPerformanceItem('Pedro Lima', 45, const Color(0xFFF68D2D)),
const SizedBox(height: 20),
// Content Quality Alert
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFF8F9FA),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFE2E8F0)),
),
child: Row(
children: [
const Icon(
Icons.info_outline,
color: Color(0xFF82C9BD),
size: 20,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Qualidade do Conteúdo',
style: TextStyle(
color: Color(0xFF2D3748),
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 2),
Text(
'12 conteúdos verificados • 2 pendentes de revisão',
style: const TextStyle(
color: Color(0xFF718096),
fontSize: 12,
),
),
],
),
),
],
),
),
],
),
)
.animate()
.slideY(
duration: const Duration(milliseconds: 800),
curve: Curves.easeOut,
)
.then(delay: const Duration(milliseconds: 400));
} }
Widget _buildQuickStat({ Widget _buildQuickStat({
@@ -245,10 +286,7 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
label, label,
style: TextStyle( style: TextStyle(color: color.withOpacity(0.8), fontSize: 10),
color: color.withOpacity(0.8),
fontSize: 10,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
@@ -257,7 +295,12 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
); );
} }
Widget _buildStudentPerformanceItem(String name, int score, Color color) { Widget _buildStudentPerformanceItem(
BuildContext context,
String name,
int score,
Color color,
) {
return Row( return Row(
children: [ children: [
Container( Container(
@@ -282,8 +325,8 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
Expanded( Expanded(
child: Text( child: Text(
name, name,
style: const TextStyle( style: TextStyle(
color: Color(0xFF4A5568), color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 14, fontSize: 14,
), ),
), ),

View File

@@ -24,11 +24,11 @@ class TeacherClassesListWidget extends StatelessWidget {
.snapshots(), .snapshots(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
return const Center( return Center(
child: Padding( child: Padding(
padding: EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: CircularProgressIndicator( child: CircularProgressIndicator(
color: Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
), ),
), ),
); );
@@ -46,7 +46,7 @@ class TeacherClassesListWidget extends StatelessWidget {
child: Text( child: Text(
'Ainda não criaste nenhuma turma.', 'Ainda não criaste nenhuma turma.',
style: TextStyle( style: TextStyle(
color: Colors.grey[600], color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 14, fontSize: 14,
), ),
), ),
@@ -59,9 +59,9 @@ class TeacherClassesListWidget extends StatelessWidget {
Text( Text(
'As Minhas Turmas', 'As Minhas Turmas',
style: Theme.of(context).textTheme.titleLarge?.copyWith( style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: const Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
SizedBox( SizedBox(
@@ -105,10 +105,8 @@ class TeacherClassesListWidget extends StatelessWidget {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => ClassStudentsPage( builder: (_) =>
classId: classId, ClassStudentsPage(classId: classId, className: className),
className: className,
),
), ),
); );
}, },
@@ -117,11 +115,11 @@ class TeacherClassesListWidget extends StatelessWidget {
height: 150, height: 150,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -134,20 +132,20 @@ class TeacherClassesListWidget extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF82C9BD).withOpacity(0.1), color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), ),
child: const Icon( child: Icon(
Icons.school, Icons.school,
color: Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
size: 24, size: 24,
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Text( Text(
className, className,
style: const TextStyle( style: TextStyle(
color: Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -158,7 +156,7 @@ class TeacherClassesListWidget extends StatelessWidget {
Text( Text(
'Código: $classCode', 'Código: $classCode',
style: TextStyle( style: TextStyle(
color: Colors.grey[600], color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 13, fontSize: 13,
), ),
), ),

View File

@@ -35,7 +35,7 @@ class TeacherHeroWidget extends StatelessWidget {
Text( Text(
'Visão Geral da Turma', 'Visão Geral da Turma',
style: TextStyle( style: TextStyle(
color: const Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -44,7 +44,7 @@ class TeacherHeroWidget extends StatelessWidget {
Text( Text(
'Acompanhe o progresso dos seus alunos', 'Acompanhe o progresso dos seus alunos',
style: TextStyle( style: TextStyle(
color: const Color(0xFF718096), color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 16, fontSize: 16,
), ),
), ),
@@ -52,19 +52,18 @@ class TeacherHeroWidget extends StatelessWidget {
), ),
), ),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFF68D2D), color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const Icon( const Icon(Icons.people, color: Colors.white, size: 16),
Icons.people,
color: Colors.white,
size: 16,
),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
'$totalStudents alunos', '$totalStudents alunos',
@@ -89,14 +88,14 @@ class TeacherHeroWidget extends StatelessWidget {
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: [
const Color(0xFF82C9BD), Theme.of(context).colorScheme.primary,
const Color(0xFF6BA8A0), Theme.of(context).colorScheme.primary.withOpacity(0.8),
], ],
), ),
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.1), color: Theme.of(context).colorScheme.shadow.withOpacity(0.1),
blurRadius: 20, blurRadius: 20,
offset: const Offset(0, 10), offset: const Offset(0, 10),
), ),
@@ -182,64 +181,76 @@ class TeacherHeroWidget extends StatelessWidget {
// Recent Activity // Recent Activity
Container( Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0)), border: Border.all(
boxShadow: [ color: Theme.of(
BoxShadow( context,
color: Colors.black.withOpacity(0.05), ).colorScheme.outline.withOpacity(0.2),
blurRadius: 10, ),
offset: const Offset(0, 4), boxShadow: [
), BoxShadow(
], color: Theme.of(
), context,
child: Column( ).colorScheme.shadow.withOpacity(0.05),
crossAxisAlignment: CrossAxisAlignment.start, blurRadius: 10,
children: [ offset: const Offset(0, 4),
Row(
children: [
const Icon(
Icons.trending_up,
color: Color(0xFFF68D2D),
size: 20,
),
const SizedBox(width: 8),
const Text(
'Atividade Recente',
style: TextStyle(
color: Color(0xFF2D3748),
fontSize: 16,
fontWeight: FontWeight.bold,
),
), ),
], ],
), ),
const SizedBox(height: 12), child: Column(
_buildActivityItem( crossAxisAlignment: CrossAxisAlignment.start,
'15 alunos completaram o quiz de Derivadas', children: [
'Hoje, 14:30', Row(
const Color(0xFF82C9BD), children: [
Icon(
Icons.trending_up,
color: Theme.of(context).colorScheme.secondary,
size: 20,
),
const SizedBox(width: 8),
Text(
'Atividade Recente',
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 12),
_buildActivityItem(
context,
'15 alunos completaram o quiz de Derivadas',
'Hoje, 14:30',
Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 8),
_buildActivityItem(
context,
'Novo conteúdo: Regra da Cadeia',
'Ontem, 09:15',
Theme.of(context).colorScheme.secondary,
),
const SizedBox(height: 8),
_buildActivityItem(
context,
'3 alunos precisam de apoio em Limites',
'Ontem, 16:45',
Theme.of(context).colorScheme.error,
),
],
), ),
const SizedBox(height: 8), )
_buildActivityItem( .animate()
'Novo conteúdo: Regra da Cadeia', .slideX(
'Ontem, 09:15', duration: const Duration(milliseconds: 800),
const Color(0xFFF68D2D), curve: Curves.easeOut,
), )
const SizedBox(height: 8), .then(delay: const Duration(milliseconds: 200)),
_buildActivityItem(
'3 alunos precisam de apoio em Limites',
'Ontem, 16:45',
const Color(0xFFE53E3E),
),
],
),
).animate().slideX(
duration: const Duration(milliseconds: 800),
curve: Curves.easeOut,
).then(delay: const Duration(milliseconds: 200)),
], ],
), ),
); );
@@ -255,10 +266,7 @@ class TeacherHeroWidget extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2), color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all( border: Border.all(color: Colors.white.withOpacity(0.3), width: 1),
color: Colors.white.withOpacity(0.3),
width: 1,
),
), ),
child: Column( child: Column(
children: [ children: [
@@ -275,10 +283,7 @@ class TeacherHeroWidget extends StatelessWidget {
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
label, label,
style: const TextStyle( style: const TextStyle(color: Colors.white, fontSize: 12),
color: Colors.white,
fontSize: 12,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
@@ -286,16 +291,18 @@ class TeacherHeroWidget extends StatelessWidget {
); );
} }
Widget _buildActivityItem(String text, String time, Color color) { Widget _buildActivityItem(
BuildContext context,
String text,
String time,
Color color,
) {
return Row( return Row(
children: [ children: [
Container( Container(
width: 8, width: 8,
height: 8, height: 8,
decoration: BoxDecoration( decoration: BoxDecoration(color: color, shape: BoxShape.circle),
color: color,
shape: BoxShape.circle,
),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
@@ -304,16 +311,18 @@ class TeacherHeroWidget extends StatelessWidget {
children: [ children: [
Text( Text(
text, text,
style: const TextStyle( style: TextStyle(
color: Color(0xFF4A5568), color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 14, fontSize: 14,
), ),
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
time, time,
style: const TextStyle( style: TextStyle(
color: Color(0xFF718096), color: Theme.of(
context,
).colorScheme.onSurfaceVariant.withOpacity(0.7),
fontSize: 12, fontSize: 12,
), ),
), ),

View File

@@ -13,7 +13,8 @@ class TeacherQuickActionsWidget extends StatefulWidget {
const TeacherQuickActionsWidget({super.key}); const TeacherQuickActionsWidget({super.key});
@override @override
State<TeacherQuickActionsWidget> createState() => _TeacherQuickActionsWidgetState(); State<TeacherQuickActionsWidget> createState() =>
_TeacherQuickActionsWidgetState();
} }
class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> { class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
@@ -22,463 +23,494 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Ações Rápidas',
style: TextStyle(
color: const Color(0xFF2D3748),
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
// Primary Actions Row
Row(
children: [ children: [
// Upload Content Card (Primary) Text(
Expanded(flex: 2, child: _buildUploadContentCard(context)), 'Ações Rápidas',
const SizedBox(width: 16), style: TextStyle(
// Create Class Card color: Theme.of(context).colorScheme.onSurface,
Expanded(flex: 2, child: _buildCreateClassCard(context)), fontSize: 20,
const SizedBox(width: 16), fontWeight: FontWeight.bold,
// Create Quiz Card (Secondary) ),
Expanded(flex: 2, child: _buildCreateQuizCard(context)), ),
], const SizedBox(height: 16),
),
const SizedBox(height: 16),
// Secondary Actions Row // Primary Actions Row
Row( Row(
children: [ children: [
// Manage Students Card // Upload Content Card (Primary)
Expanded(child: _buildManageStudentsCard(context)), Expanded(flex: 2, child: _buildUploadContentCard(context)),
const SizedBox(width: 16), const SizedBox(width: 16),
// View Analytics Card // Create Class Card
Expanded(child: _buildViewAnalyticsCard(context)), Expanded(flex: 2, child: _buildCreateClassCard(context)),
const SizedBox(width: 16),
// Create Quiz Card (Secondary)
Expanded(flex: 2, child: _buildCreateQuizCard(context)),
],
),
const SizedBox(height: 16),
// Secondary Actions Row
Row(
children: [
// Manage Students Card
Expanded(child: _buildManageStudentsCard(context)),
const SizedBox(width: 16),
// View Analytics Card
Expanded(child: _buildViewAnalyticsCard(context)),
],
),
], ],
), )
], .animate()
) .slideY(
.animate() duration: const Duration(milliseconds: 800),
.slideY( curve: Curves.easeOut,
duration: const Duration(milliseconds: 800), )
curve: Curves.easeOut, .then(delay: const Duration(milliseconds: 200));
)
.then(delay: const Duration(milliseconds: 200));
} }
Widget _buildUploadContentCard(BuildContext context) { Widget _buildUploadContentCard(BuildContext context) {
return Container( return Container(
constraints: const BoxConstraints(minHeight: 135, maxHeight: 160), constraints: const BoxConstraints(minHeight: 135, maxHeight: 160),
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: const LinearGradient( gradient: LinearGradient(
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [Color(0xFF82C9BD), Color(0xFF6BA8A0)], colors: [
), Theme.of(context).colorScheme.primary,
borderRadius: BorderRadius.circular(16), Theme.of(context).colorScheme.primary.withOpacity(0.8),
boxShadow: [
BoxShadow(
color: const Color(0xFF82C9BD).withOpacity(0.3),
blurRadius: 15,
offset: const Offset(0, 8),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const TeacherMaterialsPage(),
),
),
child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.upload_file,
color: Colors.white,
size: 22,
),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 3,
),
decoration: BoxDecoration(
color: const Color(0xFFF68D2D),
borderRadius: BorderRadius.circular(10),
),
child: const Text(
'NOVO',
style: TextStyle(
color: Colors.white,
fontSize: 9,
fontWeight: FontWeight.bold,
),
),
),
],
),
const Spacer(),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Upload Conteúdo',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
'PDFs, textos, imagens',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 12,
height: 1.2,
),
),
],
),
], ],
), ),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Theme.of(context).colorScheme.primary.withOpacity(0.3),
blurRadius: 15,
offset: const Offset(0, 8),
),
],
), ),
), child: Material(
), color: Colors.transparent,
) child: InkWell(
.animate() borderRadius: BorderRadius.circular(16),
.scale( onTap: () => Navigator.push(
duration: const Duration(milliseconds: 600), context,
curve: Curves.elasticOut, MaterialPageRoute(builder: (_) => const TeacherMaterialsPage()),
) ),
.then(delay: const Duration(milliseconds: 100)); child: Padding(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.upload_file,
color: Colors.white,
size: 22,
),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 3,
),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(10),
),
child: const Text(
'NOVO',
style: TextStyle(
color: Colors.white,
fontSize: 9,
fontWeight: FontWeight.bold,
),
),
),
],
),
const Spacer(),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Upload Conteúdo',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 4),
Text(
'PDFs, textos, imagens',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Colors.white,
fontSize: 12,
height: 1.2,
),
),
],
),
],
),
),
),
),
)
.animate()
.scale(
duration: const Duration(milliseconds: 600),
curve: Curves.elasticOut,
)
.then(delay: const Duration(milliseconds: 100));
} }
Widget _buildCreateQuizCard(BuildContext context) { Widget _buildCreateQuizCard(BuildContext context) {
return Container( return Container(
height: 150, height: 150,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0), width: 1), border: Border.all(
boxShadow: [ color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
BoxShadow( width: 1,
color: Colors.black.withOpacity(0.05), ),
blurRadius: 10, boxShadow: [
offset: const Offset(0, 4), BoxShadow(
color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
), ),
], child: Material(
), color: Colors.transparent,
child: Material( child: InkWell(
color: Colors.transparent, borderRadius: BorderRadius.circular(16),
child: InkWell( onTap: () => context.go('/teacher/quiz/create'),
borderRadius: BorderRadius.circular(16), child: Padding(
onTap: () => context.go('/teacher/quiz/create'), padding: const EdgeInsets.all(14),
child: Padding( child: Column(
padding: const EdgeInsets.all(14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: const Color(0xFFF68D2D).withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.quiz,
color: Color(0xFFF68D2D),
size: 24,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text( Container(
'Criar Quiz', padding: const EdgeInsets.all(10),
style: TextStyle( decoration: BoxDecoration(
color: Color(0xFF2D3748), color: Theme.of(
fontSize: 16, context,
fontWeight: FontWeight.bold, ).colorScheme.secondary.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
Icons.quiz,
color: Theme.of(context).colorScheme.secondary,
size: 24,
), ),
), ),
const SizedBox(height: 4), Column(
Text( crossAxisAlignment: CrossAxisAlignment.start,
'Avaliações interativas', children: [
maxLines: 2, Text(
overflow: TextOverflow.ellipsis, 'Criar Quiz',
style: TextStyle( style: TextStyle(
color: const Color(0xFF718096), color: Theme.of(context).colorScheme.onSurface,
fontSize: 12, fontSize: 16,
height: 1.2, fontWeight: FontWeight.bold,
), ),
),
const SizedBox(height: 4),
Text(
'Avaliações interativas',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 12,
height: 1.2,
),
),
],
), ),
], ],
), ),
], ),
), ),
), ),
), )
), .animate()
) .scale(
.animate() duration: const Duration(milliseconds: 600),
.scale( curve: Curves.elasticOut,
duration: const Duration(milliseconds: 600), )
curve: Curves.elasticOut, .then(delay: const Duration(milliseconds: 200));
)
.then(delay: const Duration(milliseconds: 200));
} }
Widget _buildManageStudentsCard(BuildContext context) { Widget _buildManageStudentsCard(BuildContext context) {
return Container( return Container(
height: 120, height: 120,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0), width: 1), border: Border.all(
boxShadow: [ color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
BoxShadow( width: 1,
color: Colors.black.withOpacity(0.05), ),
blurRadius: 10, boxShadow: [
offset: const Offset(0, 4), BoxShadow(
color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
), ),
], child: Material(
), color: Colors.transparent,
child: Material( child: InkWell(
color: Colors.transparent, borderRadius: BorderRadius.circular(16),
child: InkWell( onTap: () => context.go('/teacher/students'),
borderRadius: BorderRadius.circular(16), child: Padding(
onTap: () => context.go('/teacher/students'), padding: const EdgeInsets.all(16),
child: Padding( child: Column(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFF82C9BD).withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.people,
color: Color(0xFF82C9BD),
size: 20,
),
),
const Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Container(
'Gerir Alunos', padding: const EdgeInsets.all(8),
style: TextStyle( decoration: BoxDecoration(
color: Color(0xFF2D3748), color: Theme.of(
fontSize: 14, context,
fontWeight: FontWeight.bold, ).colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
Icons.people,
color: Theme.of(context).colorScheme.primary,
size: 20,
), ),
), ),
Text( Column(
'Acesso e permissões', crossAxisAlignment: CrossAxisAlignment.start,
style: TextStyle( children: [
color: Color(0xFF718096), Text(
fontSize: 11, 'Gerir Alunos',
), style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
Text(
'Acesso e permissões',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 11,
),
),
],
), ),
], ],
), ),
], ),
), ),
), ),
), )
), .animate()
) .scale(
.animate() duration: const Duration(milliseconds: 600),
.scale( curve: Curves.elasticOut,
duration: const Duration(milliseconds: 600), )
curve: Curves.elasticOut, .then(delay: const Duration(milliseconds: 300));
)
.then(delay: const Duration(milliseconds: 300));
} }
Widget _buildViewAnalyticsCard(BuildContext context) { Widget _buildViewAnalyticsCard(BuildContext context) {
return Container( return Container(
height: 120, height: 120,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0), width: 1), border: Border.all(
boxShadow: [ color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
BoxShadow( width: 1,
color: Colors.black.withOpacity(0.05), ),
blurRadius: 10, boxShadow: [
offset: const Offset(0, 4), BoxShadow(
color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
), ),
], child: Material(
), color: Colors.transparent,
child: Material( child: InkWell(
color: Colors.transparent, borderRadius: BorderRadius.circular(16),
child: InkWell( onTap: () => context.go('/teacher/analytics'),
borderRadius: BorderRadius.circular(16), child: Padding(
onTap: () => context.go('/teacher/analytics'), padding: const EdgeInsets.all(16),
child: Padding( child: Column(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFF6BA8A0).withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
Icons.analytics,
color: Color(0xFF6BA8A0),
size: 20,
),
),
const Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Container(
'Analytics', padding: const EdgeInsets.all(8),
style: TextStyle( decoration: BoxDecoration(
color: Color(0xFF2D3748), color: Theme.of(
fontSize: 14, context,
fontWeight: FontWeight.bold, ).colorScheme.primary.withOpacity(0.8),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
Icons.analytics,
color: Theme.of(context).colorScheme.primary,
size: 20,
), ),
), ),
Text( Column(
'Desempenho da turma', crossAxisAlignment: CrossAxisAlignment.start,
style: TextStyle( children: [
color: Color(0xFF718096), Text(
fontSize: 11, 'Analytics',
), style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
Text(
'Desempenho da turma',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 11,
),
),
],
), ),
], ],
), ),
], ),
), ),
), ),
), )
), .animate()
) .scale(
.animate() duration: const Duration(milliseconds: 600),
.scale( curve: Curves.elasticOut,
duration: const Duration(milliseconds: 600), )
curve: Curves.elasticOut, .then(delay: const Duration(milliseconds: 400));
)
.then(delay: const Duration(milliseconds: 400));
} }
Widget _buildCreateClassCard(BuildContext context) { Widget _buildCreateClassCard(BuildContext context) {
return Container( return Container(
constraints: const BoxConstraints(minHeight: 135, maxHeight: 160), constraints: const BoxConstraints(minHeight: 135, maxHeight: 160),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0), width: 1), border: Border.all(
boxShadow: [ color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
BoxShadow( width: 1,
color: Colors.black.withOpacity(0.05), ),
blurRadius: 10, boxShadow: [
offset: const Offset(0, 4), BoxShadow(
color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
), ),
], child: Material(
), color: Colors.transparent,
child: Material( child: InkWell(
color: Colors.transparent, borderRadius: BorderRadius.circular(16),
child: InkWell( onTap: _isCreatingClass
borderRadius: BorderRadius.circular(16), ? null
onTap: _isCreatingClass ? null : () => _showCreateClassDialog(context), : () => _showCreateClassDialog(context),
child: Padding( child: Padding(
padding: const EdgeInsets.all(14), padding: const EdgeInsets.all(14),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: const Color(0xFF82C9BD).withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: _isCreatingClass
? const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Color(0xFF82C9BD),
),
)
: const Icon(
Icons.school,
color: Color(0xFF82C9BD),
size: 24,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Container(
'Criar Turma', padding: const EdgeInsets.all(10),
style: TextStyle( decoration: BoxDecoration(
color: const Color(0xFF2D3748), color: Theme.of(
fontSize: 16, context,
fontWeight: FontWeight.bold, ).colorScheme.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
), ),
child: _isCreatingClass
? SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Theme.of(context).colorScheme.primary,
),
)
: Icon(
Icons.school,
color: Theme.of(context).colorScheme.primary,
size: 24,
),
), ),
const SizedBox(height: 4), Column(
Text( crossAxisAlignment: CrossAxisAlignment.start,
'Gerar código de acesso', children: [
maxLines: 2, Text(
overflow: TextOverflow.ellipsis, 'Criar Turma',
style: TextStyle( style: TextStyle(
color: const Color(0xFF718096), color: Theme.of(context).colorScheme.onSurface,
fontSize: 12, fontSize: 16,
height: 1.2, fontWeight: FontWeight.bold,
), ),
),
const SizedBox(height: 4),
Text(
'Gerar código de acesso',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 12,
height: 1.2,
),
),
],
), ),
], ],
), ),
], ),
), ),
), ),
), )
), .animate()
) .scale(
.animate() duration: const Duration(milliseconds: 600),
.scale( curve: Curves.elasticOut,
duration: const Duration(milliseconds: 600), )
curve: Curves.elasticOut, .then(delay: const Duration(milliseconds: 150));
)
.then(delay: const Duration(milliseconds: 150));
} }
void _showCreateClassDialog(BuildContext context) { void _showCreateClassDialog(BuildContext context) {
@@ -491,10 +523,10 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
title: const Text( title: Text(
'Criar Nova Turma', 'Criar Nova Turma',
style: TextStyle( style: TextStyle(
color: Color(0xFF2D3748), color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
@@ -502,10 +534,10 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'Digite o nome da turma:', 'Digite o nome da turma:',
style: TextStyle( style: TextStyle(
color: Color(0xFF718096), color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 14, fontSize: 14,
), ),
), ),
@@ -515,7 +547,9 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Ex: Matemática 9º Ano', hintText: 'Ex: Matemática 9º Ano',
filled: true, filled: true,
fillColor: const Color(0xFFF7FAFC), fillColor: Theme.of(
context,
).colorScheme.surfaceContainerHighest,
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none, borderSide: BorderSide.none,
@@ -526,8 +560,8 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
), ),
focusedBorder: OutlineInputBorder( focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide( borderSide: BorderSide(
color: Color(0xFF82C9BD), color: Theme.of(context).colorScheme.primary,
width: 2, width: 2,
), ),
), ),
@@ -538,9 +572,11 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.of(dialogContext).pop(), onPressed: () => Navigator.of(dialogContext).pop(),
child: const Text( child: Text(
'Cancelar', 'Cancelar',
style: TextStyle(color: Color(0xFF718096)), style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
), ),
), ),
ElevatedButton( ElevatedButton(
@@ -552,7 +588,7 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
} }
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF82C9BD), backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white, foregroundColor: Colors.white,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
@@ -601,8 +637,10 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('Turma "$className" criada com sucesso! Código: $classCode'), content: Text(
backgroundColor: const Color(0xFF82C9BD), 'Turma "$className" criada com sucesso! Código: $classCode',
),
backgroundColor: Theme.of(context).colorScheme.primary,
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../../../core/theme/app_colors.dart';
class ProfilePage extends StatelessWidget { class ProfilePage extends StatelessWidget {
const ProfilePage({super.key}); const ProfilePage({super.key});
@@ -15,20 +14,24 @@ class ProfilePage extends StatelessWidget {
context.go('/student-dashboard'); context.go('/student-dashboard');
}, },
child: Scaffold( child: Scaffold(
backgroundColor: AppColors.background, backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar( appBar: AppBar(
title: const Text('Profile'), title: const Text('Profile'),
backgroundColor: AppColors.surface, backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: AppColors.textPrimary, foregroundColor: Theme.of(context).colorScheme.onSurface,
elevation: 0, elevation: 0,
), ),
body: const Center( body: Container(
child: Text( decoration: BoxDecoration(
'Profile Page - Coming Soon', gradient: LinearGradient(
style: TextStyle( begin: Alignment.topLeft,
fontSize: 24, end: Alignment.bottomRight,
fontWeight: FontWeight.bold, colors: [
color: AppColors.textPrimary, Theme.of(context).colorScheme.background,
Theme.of(context).colorScheme.primary.withOpacity(0.1),
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
Theme.of(context).colorScheme.background,
],
), ),
), ),
), ),

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../../../core/theme/app_colors.dart';
class QuizListPage extends StatelessWidget { class QuizListPage extends StatelessWidget {
const QuizListPage({super.key}); const QuizListPage({super.key});
@@ -15,20 +14,34 @@ class QuizListPage extends StatelessWidget {
context.go('/student-dashboard'); context.go('/student-dashboard');
}, },
child: Scaffold( child: Scaffold(
backgroundColor: AppColors.background, backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar( appBar: AppBar(
title: const Text('Quizzes'), title: const Text('Quizzes'),
backgroundColor: AppColors.surface, backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: AppColors.textPrimary, foregroundColor: Theme.of(context).colorScheme.onSurface,
elevation: 0, elevation: 0,
), ),
body: const Center( body: Container(
child: Text( decoration: BoxDecoration(
'Quiz List - Coming Soon', gradient: LinearGradient(
style: TextStyle( begin: Alignment.topLeft,
fontSize: 24, end: Alignment.bottomRight,
fontWeight: FontWeight.bold, colors: [
color: AppColors.textPrimary, Theme.of(context).colorScheme.background,
Theme.of(context).colorScheme.primary.withOpacity(0.1),
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
Theme.of(context).colorScheme.background,
],
),
),
child: Center(
child: Text(
'Quiz List - Coming Soon',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onSurface,
),
), ),
), ),
), ),

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../../../core/theme/app_colors.dart';
class QuizPage extends StatelessWidget { class QuizPage extends StatelessWidget {
final String quizId; final String quizId;
@@ -17,22 +16,25 @@ class QuizPage extends StatelessWidget {
context.go('/quiz'); context.go('/quiz');
}, },
child: Scaffold( child: Scaffold(
backgroundColor: AppColors.background, backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar( appBar: AppBar(
title: Text('Quiz $quizId'), title: Text('Quiz $quizId'),
backgroundColor: AppColors.surface, backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: AppColors.textPrimary, foregroundColor: Theme.of(context).colorScheme.onSurface,
elevation: 0, elevation: 0,
), ),
body: Center( body: Container(
child: Text( decoration: BoxDecoration(
'Quiz Page - ID: $quizId\nComing Soon', gradient: LinearGradient(
style: const TextStyle( begin: Alignment.topLeft,
fontSize: 24, end: Alignment.bottomRight,
fontWeight: FontWeight.bold, colors: [
color: AppColors.textPrimary, Theme.of(context).colorScheme.background,
Theme.of(context).colorScheme.primary.withOpacity(0.1),
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
Theme.of(context).colorScheme.background,
],
), ),
textAlign: TextAlign.center,
), ),
), ),
), ),

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../../../core/theme/app_colors.dart';
/// Help page with user guides /// Help page with user guides
class HelpPage extends StatelessWidget { class HelpPage extends StatelessWidget {
@@ -17,17 +16,17 @@ class HelpPage extends StatelessWidget {
}, },
child: Scaffold( child: Scaffold(
body: Container( body: Container(
decoration: const BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [
Color(0xFF82C9BD), Theme.of(context).colorScheme.primary,
Color(0xFF7BA89C), Theme.of(context).colorScheme.primary.withOpacity(0.9),
Color(0xFFF68D2D), Theme.of(context).colorScheme.secondary,
Color(0xFFF8F9FA), Theme.of(context).colorScheme.background,
], ],
stops: [0.0, 0.2, 0.6, 1.0], stops: const [0.0, 0.2, 0.6, 1.0],
), ),
), ),
child: SafeArea( child: SafeArea(
@@ -39,14 +38,17 @@ class HelpPage extends StatelessWidget {
child: Row( child: Row(
children: [ children: [
IconButton( IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white), icon: Icon(
Icons.arrow_back,
color: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () => context.pop(), onPressed: () => context.pop(),
), ),
const Expanded( Expanded(
child: Text( child: Text(
'Ajuda e Suporte', 'Ajuda e Suporte',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Theme.of(context).colorScheme.onPrimary,
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -62,6 +64,7 @@ class HelpPage extends StatelessWidget {
child: Column( child: Column(
children: [ children: [
_buildGuideCard( _buildGuideCard(
context: context,
icon: Icons.school, icon: Icons.school,
title: 'Como usar o AI Tutor', title: 'Como usar o AI Tutor',
description: description:
@@ -70,6 +73,7 @@ class HelpPage extends StatelessWidget {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildGuideCard( _buildGuideCard(
context: context,
icon: Icons.quiz, icon: Icons.quiz,
title: 'Como fazer quizzes', title: 'Como fazer quizzes',
description: description:
@@ -78,6 +82,7 @@ class HelpPage extends StatelessWidget {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildGuideCard( _buildGuideCard(
context: context,
icon: Icons.trending_up, icon: Icons.trending_up,
title: 'Ver o seu progresso', title: 'Ver o seu progresso',
description: description:
@@ -86,6 +91,7 @@ class HelpPage extends StatelessWidget {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildGuideCard( _buildGuideCard(
context: context,
icon: Icons.settings, icon: Icons.settings,
title: 'Configurações da app', title: 'Configurações da app',
description: description:
@@ -97,11 +103,13 @@ class HelpPage extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.1), color: Theme.of(
context,
).colorScheme.shadow.withOpacity(0.1),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -110,34 +118,40 @@ class HelpPage extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'Perguntas Frequentes', 'Perguntas Frequentes',
style: TextStyle( style: TextStyle(
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.textPrimary, color: Theme.of(
context,
).colorScheme.onSurface,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildFAQItem( _buildFAQItem(
context: context,
question: 'Como posso alterar a minha senha?', question: 'Como posso alterar a minha senha?',
answer: answer:
'Pode alterar a sua senha através da secção de segurança nas definições.', 'Pode alterar a sua senha através da secção de segurança nas definições.',
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildFAQItem( _buildFAQItem(
context: context,
question: 'Os meus dados estão seguros?', question: 'Os meus dados estão seguros?',
answer: answer:
'Sim, utilizamos encriptação de ponta a ponta para proteger todos os seus dados.', 'Sim, utilizamos encriptação de ponta a ponta para proteger todos os seus dados.',
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildFAQItem( _buildFAQItem(
context: context,
question: 'Posso usar a app offline?', question: 'Posso usar a app offline?',
answer: answer:
'Algumas funcionalidades estão disponíveis offline, mas para o AI Tutor necessita de conexão à internet.', 'Algumas funcionalidades estão disponíveis offline, mas para o AI Tutor necessita de conexão à internet.',
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildFAQItem( _buildFAQItem(
context: context,
question: 'Como contactar o suporte?', question: 'Como contactar o suporte?',
answer: answer:
'Pode contactar-nos através do email suporte@teachit.com.', 'Pode contactar-nos através do email suporte@teachit.com.',
@@ -150,11 +164,13 @@ class HelpPage extends StatelessWidget {
Container( Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.1), color: Theme.of(
context,
).colorScheme.shadow.withOpacity(0.1),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -163,20 +179,24 @@ class HelpPage extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'Ainda precisa de ajuda?', 'Ainda precisa de ajuda?',
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.textPrimary, color: Theme.of(
context,
).colorScheme.onSurface,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
const Text( Text(
'A nossa equipa de suporte está disponível para ajudar.', 'A nossa equipa de suporte está disponível para ajudar.',
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: AppColors.textSecondary, color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@@ -187,8 +207,12 @@ class HelpPage extends StatelessWidget {
icon: const Icon(Icons.email), icon: const Icon(Icons.email),
label: const Text('Contactar Suporte'), label: const Text('Contactar Suporte'),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryTeal, backgroundColor: Theme.of(
foregroundColor: Colors.white, context,
).colorScheme.primary,
foregroundColor: Theme.of(
context,
).colorScheme.onPrimary,
), ),
), ),
], ],
@@ -207,6 +231,7 @@ class HelpPage extends StatelessWidget {
} }
Widget _buildGuideCard({ Widget _buildGuideCard({
required BuildContext context,
required IconData icon, required IconData icon,
required String title, required String title,
required String description, required String description,
@@ -215,11 +240,11 @@ class HelpPage extends StatelessWidget {
return Container( return Container(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.1), color: Theme.of(context).colorScheme.shadow.withOpacity(0.1),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -234,12 +259,19 @@ class HelpPage extends StatelessWidget {
width: 50, width: 50,
height: 50, height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: const LinearGradient( gradient: LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA8A0)], colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary.withOpacity(0.8),
],
), ),
borderRadius: BorderRadius.circular(25), borderRadius: BorderRadius.circular(25),
), ),
child: Icon(icon, color: Colors.white, size: 24), child: Icon(
icon,
color: Theme.of(context).colorScheme.onPrimary,
size: 24,
),
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Expanded( Expanded(
@@ -248,38 +280,45 @@ class HelpPage extends StatelessWidget {
children: [ children: [
Text( Text(
title, title,
style: const TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.textPrimary, color: Theme.of(context).colorScheme.onSurface,
), ),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(
description, description,
style: const TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
color: AppColors.textSecondary, color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
], ],
), ),
), ),
const Icon(Icons.chevron_right, color: AppColors.iconInactive), Icon(
Icons.chevron_right,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
], ],
), ),
), ),
); );
} }
Widget _buildFAQItem({required String question, required String answer}) { Widget _buildFAQItem({
required BuildContext context,
required String question,
required String answer,
}) {
return ExpansionTile( return ExpansionTile(
title: Text( title: Text(
question, question,
style: const TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColors.textPrimary, color: Theme.of(context).colorScheme.onSurface,
), ),
), ),
children: [ children: [
@@ -287,9 +326,9 @@ class HelpPage extends StatelessWidget {
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Text( child: Text(
answer, answer,
style: const TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
color: AppColors.textSecondary, color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
), ),

View File

@@ -98,17 +98,16 @@ class _ProfileEditPageState extends ConsumerState<ProfileEditPage> {
}, },
child: Scaffold( child: Scaffold(
body: Container( body: Container(
decoration: const BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topLeft,
end: Alignment.bottomCenter, end: Alignment.bottomRight,
colors: [ colors: [
Color(0xFF82C9BD), Theme.of(context).colorScheme.background,
Color(0xFF7BA89C), Theme.of(context).colorScheme.primary.withOpacity(0.1),
Color(0xFFF68D2D), Theme.of(context).colorScheme.secondary.withOpacity(0.05),
Color(0xFFF8F9FA), Theme.of(context).colorScheme.background,
], ],
stops: [0.0, 0.2, 0.6, 1.0],
), ),
), ),
child: SafeArea( child: SafeArea(
@@ -143,11 +142,13 @@ class _ProfileEditPageState extends ConsumerState<ProfileEditPage> {
child: Container( child: Container(
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(24),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.1), color: Theme.of(
context,
).colorScheme.shadow.withOpacity(0.1),
blurRadius: 10, blurRadius: 10,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -181,10 +182,23 @@ class _ProfileEditPageState extends ConsumerState<ProfileEditPage> {
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text(
user?.displayName ?? 'Utilizador',
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurface,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text( Text(
user?.email ?? '', user?.email ?? '',
style: const TextStyle( style: TextStyle(
color: AppColors.textSecondary, color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 14, fontSize: 14,
), ),
), ),
@@ -194,12 +208,12 @@ class _ProfileEditPageState extends ConsumerState<ProfileEditPage> {
const SizedBox(height: 32), const SizedBox(height: 32),
// Name field // Name field
const Text( Text(
'Nome', 'Nome',
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.textPrimary, color: Theme.of(context).colorScheme.onSurface,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
@@ -208,7 +222,9 @@ class _ProfileEditPageState extends ConsumerState<ProfileEditPage> {
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Introduza o seu nome', hintText: 'Introduza o seu nome',
filled: true, filled: true,
fillColor: AppColors.background, fillColor: Theme.of(
context,
).colorScheme.surface,
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none, borderSide: BorderSide.none,
@@ -228,12 +244,12 @@ class _ProfileEditPageState extends ConsumerState<ProfileEditPage> {
const SizedBox(height: 24), const SizedBox(height: 24),
// Phone field (optional) // Phone field (optional)
const Text( Text(
'Telefone (opcional)', 'Telefone (opcional)',
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.textPrimary, color: Theme.of(context).colorScheme.onSurface,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
@@ -243,7 +259,9 @@ class _ProfileEditPageState extends ConsumerState<ProfileEditPage> {
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Introduza o seu telefone', hintText: 'Introduza o seu telefone',
filled: true, filled: true,
fillColor: AppColors.background, fillColor: Theme.of(
context,
).colorScheme.surface,
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none, borderSide: BorderSide.none,
@@ -257,12 +275,12 @@ class _ProfileEditPageState extends ConsumerState<ProfileEditPage> {
const SizedBox(height: 24), const SizedBox(height: 24),
// Bio field (optional) // Bio field (optional)
const Text( Text(
'Bio (opcional)', 'Bio (opcional)',
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.textPrimary, color: Theme.of(context).colorScheme.onSurface,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
@@ -272,7 +290,9 @@ class _ProfileEditPageState extends ConsumerState<ProfileEditPage> {
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'Conte um pouco sobre si', hintText: 'Conte um pouco sobre si',
filled: true, filled: true,
fillColor: AppColors.background, fillColor: Theme.of(
context,
).colorScheme.surface,
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none, borderSide: BorderSide.none,

View File

@@ -29,17 +29,21 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
}, },
child: Scaffold( child: Scaffold(
body: Container( body: Container(
decoration: const BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topLeft,
end: Alignment.bottomCenter, end: Alignment.bottomRight,
colors: [ colors: themeMode == ThemeMode.light
Color(0xFF82C9BD), ? [
Color(0xFF7BA89C), const Color(0xFFD4E8E8),
Color(0xFFF68D2D), const Color(0xFFE8D4C0),
Color(0xFFF8F9FA), const Color(0xFFD8E0E8),
], ]
stops: [0.0, 0.2, 0.6, 1.0], : [
Theme.of(context).colorScheme.primary.withOpacity(0.1),
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
Theme.of(context).colorScheme.background,
],
), ),
), ),
child: SafeArea( child: SafeArea(
@@ -54,11 +58,11 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
icon: const Icon(Icons.arrow_back, color: Colors.white), icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => context.go('/student-dashboard'), onPressed: () => context.go('/student-dashboard'),
), ),
const Expanded( Expanded(
child: Text( child: Text(
'Configurações', 'Configurações',
style: TextStyle( style: TextStyle(
color: Colors.white, color: Theme.of(context).colorScheme.onSurface,
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@@ -187,19 +191,23 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: isDark ? Colors.white70 : AppColors.textSecondary, color: isDark
? Colors.white70
: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
), ),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDark ? Colors.grey[800] : AppColors.surface, color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Theme.of(
blurRadius: 2, context,
offset: const Offset(0, 1), ).colorScheme.onSurfaceVariant.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
), ),
], ],
), ),
@@ -220,17 +228,17 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
leading: const Icon(Icons.palette, color: AppColors.primaryTeal), leading: const Icon(Icons.palette, color: AppColors.primaryTeal),
title: Text( title: Text(
'Tema', 'Tema',
style: TextStyle(color: isDark ? Colors.white : AppColors.textPrimary), style: TextStyle(
color: isDark
? Colors.white
: Theme.of(context).colorScheme.onSurface,
),
), ),
subtitle: Text( subtitle: Text(
isDarkModeAvailable isDarkModeAvailable
? 'Atual: ${_getThemeModeString(currentTheme)}' ? 'Atual: ${_getThemeModeString(currentTheme)}'
: 'Light Mode (padrão)', : 'Light Mode (padrão)',
style: TextStyle( style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
color: isDarkModeAvailable
? (isDark ? Colors.white70 : AppColors.textSecondary)
: AppColors.textHint,
),
), ),
trailing: isDarkModeAvailable trailing: isDarkModeAvailable
? DropdownButton<ThemeMode>( ? DropdownButton<ThemeMode>(
@@ -255,7 +263,10 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
), ),
], ],
) )
: const Icon(Icons.lock, color: AppColors.textHint), : Icon(
Icons.lock,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
); );
} }
@@ -283,12 +294,18 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
leading: const Icon(Icons.notifications, color: AppColors.primaryTeal), leading: const Icon(Icons.notifications, color: AppColors.primaryTeal),
title: Text( title: Text(
title, title,
style: TextStyle(color: isDark ? Colors.white : AppColors.textPrimary), style: TextStyle(
color: isDark
? Colors.white
: Theme.of(context).colorScheme.onSurface,
),
), ),
subtitle: Text( subtitle: Text(
subtitle, subtitle,
style: TextStyle( style: TextStyle(
color: isDark ? Colors.white70 : AppColors.textSecondary, color: isDark
? Colors.white70
: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
trailing: Switch( trailing: Switch(
@@ -312,15 +329,24 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
leading: Icon(icon, color: AppColors.primaryTeal), leading: Icon(icon, color: AppColors.primaryTeal),
title: Text( title: Text(
title, title,
style: TextStyle(color: isDark ? Colors.white : AppColors.textPrimary), style: TextStyle(
color: isDark
? Colors.white
: Theme.of(context).colorScheme.onSurface,
),
), ),
subtitle: Text( subtitle: Text(
subtitle, subtitle,
style: TextStyle( style: TextStyle(
color: isDark ? Colors.white70 : AppColors.textSecondary, color: isDark
? Colors.white70
: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
trailing: const Icon(Icons.chevron_right, color: AppColors.iconInactive), trailing: Icon(
Icons.chevron_right,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
onTap: onTap, onTap: onTap,
); );
} }
@@ -336,12 +362,18 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
leading: Icon(icon, color: AppColors.primaryTeal), leading: Icon(icon, color: AppColors.primaryTeal),
title: Text( title: Text(
title, title,
style: TextStyle(color: isDark ? Colors.white : AppColors.textPrimary), style: TextStyle(
color: isDark
? Colors.white
: Theme.of(context).colorScheme.onSurface,
),
), ),
subtitle: Text( subtitle: Text(
subtitle, subtitle,
style: TextStyle( style: TextStyle(
color: isDark ? Colors.white70 : AppColors.textSecondary, color: isDark
? Colors.white70
: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
); );

View File

@@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_animate/flutter_animate.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../../../core/theme/app_colors.dart';
import '../../../../core/services/auth_service.dart'; import '../../../../core/services/auth_service.dart';
import '../../../../core/services/session_service.dart'; import '../../../../core/services/session_service.dart';
import '../../../../l10n/app_localizations.dart'; import '../../../../l10n/app_localizations.dart';
@@ -119,10 +118,10 @@ class _SplashPageState extends State<SplashPage> {
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: [
AppColors.background, Theme.of(context).colorScheme.background,
AppColors.primaryTeal.withOpacity(0.05), Theme.of(context).colorScheme.primary.withOpacity(0.1),
AppColors.primaryOrange.withOpacity(0.03), Theme.of(context).colorScheme.secondary.withOpacity(0.05),
AppColors.background, Theme.of(context).colorScheme.background,
], ],
), ),
), ),
@@ -147,7 +146,9 @@ class _SplashPageState extends State<SplashPage> {
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: Border.all(
color: AppColors.primaryTeal.withOpacity(0.2), color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.2),
width: 2, width: 2,
), ),
), ),
@@ -174,7 +175,9 @@ class _SplashPageState extends State<SplashPage> {
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: Border.all(
color: AppColors.primaryOrange.withOpacity(0.3), color: Theme.of(
context,
).colorScheme.secondary.withOpacity(0.3),
width: 3, width: 3,
), ),
), ),
@@ -199,10 +202,10 @@ class _SplashPageState extends State<SplashPage> {
width: 80, width: 80,
height: 80, height: 80,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: const LinearGradient( gradient: LinearGradient(
colors: [ colors: [
AppColors.primaryTeal, Theme.of(context).colorScheme.primary,
AppColors.primaryOrange, Theme.of(context).colorScheme.secondary,
], ],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
@@ -210,14 +213,16 @@ class _SplashPageState extends State<SplashPage> {
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: AppColors.primaryTeal.withOpacity(0.3), color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.3),
blurRadius: 20, blurRadius: 20,
offset: const Offset(0, 8), offset: const Offset(0, 8),
), ),
BoxShadow( BoxShadow(
color: AppColors.primaryOrange.withOpacity( color: Theme.of(
0.2, context,
), ).colorScheme.secondary.withOpacity(0.2),
blurRadius: 15, blurRadius: 15,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -260,10 +265,10 @@ class _SplashPageState extends State<SplashPage> {
// School name with gradient // School name with gradient
ShaderMask( ShaderMask(
shaderCallback: (bounds) => const LinearGradient( shaderCallback: (bounds) => LinearGradient(
colors: [ colors: [
AppColors.primaryTeal, Theme.of(context).colorScheme.primary,
AppColors.primaryOrange, Theme.of(context).colorScheme.secondary,
], ],
begin: Alignment.centerLeft, begin: Alignment.centerLeft,
end: Alignment.centerRight, end: Alignment.centerRight,
@@ -295,8 +300,12 @@ class _SplashPageState extends State<SplashPage> {
ShaderMask( ShaderMask(
shaderCallback: (bounds) => LinearGradient( shaderCallback: (bounds) => LinearGradient(
colors: [ colors: [
AppColors.primaryOrange.withOpacity(0.8), Theme.of(
AppColors.primaryTeal.withOpacity(0.8), context,
).colorScheme.secondary.withOpacity(0.8),
Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
], ],
begin: Alignment.centerLeft, begin: Alignment.centerLeft,
end: Alignment.centerRight, end: Alignment.centerRight,
@@ -345,7 +354,9 @@ class _SplashPageState extends State<SplashPage> {
'A preparar a sua experiência...', 'A preparar a sua experiência...',
style: Theme.of(context).textTheme.bodySmall style: Theme.of(context).textTheme.bodySmall
?.copyWith( ?.copyWith(
color: AppColors.primaryOrange, color: Theme.of(
context,
).colorScheme.secondary,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
) )
@@ -357,7 +368,9 @@ class _SplashPageState extends State<SplashPage> {
.then() .then()
.shimmer( .shimmer(
duration: const Duration(milliseconds: 2000), duration: const Duration(milliseconds: 2000),
color: AppColors.primaryTeal.withOpacity(0.3), color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.3),
), ),
], ],
), ),
@@ -374,9 +387,12 @@ class _SplashPageState extends State<SplashPage> {
return Container( return Container(
width: 12, width: 12,
height: 12, height: 12,
decoration: const BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [AppColors.primaryTeal, AppColors.primaryOrange], colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.secondary,
],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
), ),
@@ -418,8 +434,8 @@ class _SplashPageState extends State<SplashPage> {
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [
AppColors.primaryTeal.withOpacity(0.3), Theme.of(context).colorScheme.primary.withOpacity(0.3),
AppColors.primaryOrange.withOpacity(0.2), Theme.of(context).colorScheme.secondary.withOpacity(0.2),
], ],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../../core/theme/app_colors.dart';
class TutorChatPage extends StatelessWidget { class TutorChatPage extends StatelessWidget {
const TutorChatPage({super.key}); const TutorChatPage({super.key});
@@ -7,20 +6,34 @@ class TutorChatPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColors.background, backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar( appBar: AppBar(
title: const Text('AI Tutor'), title: const Text('AI Tutor'),
backgroundColor: AppColors.surface, backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: AppColors.textPrimary, foregroundColor: Theme.of(context).colorScheme.onSurface,
elevation: 0, elevation: 0,
), ),
body: const Center( body: Container(
child: Text( decoration: BoxDecoration(
'AI Tutor Chat - Coming Soon', gradient: LinearGradient(
style: TextStyle( begin: Alignment.topLeft,
fontSize: 24, end: Alignment.bottomRight,
fontWeight: FontWeight.bold, colors: [
color: AppColors.textPrimary, Theme.of(context).colorScheme.background,
Theme.of(context).colorScheme.primary.withOpacity(0.1),
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
Theme.of(context).colorScheme.background,
],
),
),
child: Center(
child: Text(
'AI Tutor Chat - Coming Soon',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onSurface,
),
), ),
), ),
), ),

View File

@@ -1,25 +1,29 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../../core/theme/app_colors.dart';
class LoadingPage extends StatelessWidget { class LoadingPage extends StatelessWidget {
const LoadingPage({super.key}); const LoadingPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const Scaffold( return Scaffold(
backgroundColor: AppColors.background, backgroundColor: Theme.of(context).colorScheme.background,
body: Center( body: Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
CircularProgressIndicator( CircularProgressIndicator(
strokeWidth: 3, strokeWidth: 3,
valueColor: AlwaysStoppedAnimation<Color>(AppColors.primaryBlue), valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
), ),
SizedBox(height: 24), const SizedBox(height: 24),
Text( Text(
'Loading...', 'Loading...',
style: TextStyle(fontSize: 16, color: AppColors.textSecondary), style: TextStyle(
fontSize: 16,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
), ),
], ],
), ),

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../../core/theme/app_colors.dart';
import '../../../../l10n/app_localizations.dart'; import '../../../../l10n/app_localizations.dart';
class NotFoundPage extends StatelessWidget { class NotFoundPage extends StatelessWidget {
@@ -9,33 +8,37 @@ class NotFoundPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
return Scaffold( return Scaffold(
backgroundColor: AppColors.background, backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar( appBar: AppBar(
title: Text(l10n.pageNotFound), title: Text(l10n.pageNotFound),
backgroundColor: AppColors.surface, backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: AppColors.textPrimary, foregroundColor: Theme.of(context).colorScheme.onSurface,
elevation: 0, elevation: 0,
), ),
body: Center( body: Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.error_outline, size: 64, color: AppColors.error), Icon(
Icons.error_outline,
size: 64,
color: Theme.of(context).colorScheme.error,
),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
l10n.pageNotFound, l10n.pageNotFound,
style: const TextStyle( style: TextStyle(
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.textPrimary, color: Theme.of(context).colorScheme.onSurface,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
'A página que procura não existe.', 'A página que procura não existe.',
style: const TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: AppColors.textSecondary, color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
], ],