- 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
### 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**
- "Upload Conteúdo" and "Criar Quiz" cards now match "Criar Turma" text alignment
- All cards use `Column` with `crossAxisAlignment: CrossAxisAlignment.start` for text section

View File

@@ -496,14 +496,20 @@ class FirebaseConstants {
**Dependencies**: Task 1.4
#### Subtasks:
- [ ] Implement Firebase Auth service
- [ ] Create user models and entities
- [ ] Build authentication repository
- [ ] Implement sign in use case
- [ ] Implement sign up use case
- [x] Implement Firebase Auth service
- [x] Create user models and entities
- [x] Build authentication repository
- [x] Implement sign in use case
- [x] Implement sign up use case
- [ ] Implement password reset
- [ ] Create authentication providers
- [ ] Build login/signup screens
- [x] Create authentication providers
- [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:

View File

@@ -28,7 +28,7 @@ This document tracks the overall progress of the AI Study Assistant project deve
-**Foundation:** 100% Complete
-**UI/UX:** 90% Complete
-**UI/UX:** 95% 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] Login page with improved design
- [x] Login page with improved design and navigation
- [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
- [ ] Signup page (needs update)
- [x] Signup page (updated with theme fixes)
- [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
- [ ] Some animations could be optimized
- None currently
@@ -419,6 +417,31 @@ This document tracks the overall progress of the AI Study Assistant project deve
### **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
- Ficheiro: `lib/features/materials/presentation/pages/teacher_materials_page.dart`
- **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**

View File

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

View File

@@ -1,8 +1,8 @@
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 {
// Primary Brand Colors
// Primary Brand Colors (same for both modes)
static const Color primaryTeal = Color(
0xFF82C9BD,
); // Main teal color - PRIMARY
@@ -10,17 +10,30 @@ class AppColors {
0xFFF68D2D,
); // Accent orange - SECONDARY
// Gradient Colors
// Gradient Colors (same for both modes)
static const Color gradientStart = Color(0xFF82C9BD); // Teal gradient start
static const Color gradientEnd = Color(
0xFF6AB8A8,
); // Darker teal gradient end
// Secondary Colors
// Secondary Colors (same for both modes)
static const Color secondaryTeal = Color(0xFF6AB8A8); // Darker teal
static const Color accentTeal = Color(0xFF5AA69A); // Lighter teal accent
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
static const Color background = Color(0xFFF8F9FA); // Light gray background
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 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
static const Color buttonPrimary = Color(0xFF82C9BD); // Primary button (teal)
static const Color buttonAccent = Color(0xFFF68D2D); // Accent button (orange)
static const Color buttonPrimary =
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 iconActive = Color(0xFF82C9BD); // Active icons (teal)
static const Color iconActive = AppColors.primaryTeal; // Active icons (teal)
static const Color iconInactive = Color(0xFF9CA3AF); // Inactive icons
// Chat Specific Colors
static const Color chatBubbleStudent = Color(
0xFF82C9BD,
); // Student messages (teal)
static const Color chatBubbleStudent =
AppColors.primaryTeal; // Student messages (teal)
static const Color chatBubbleAI = Color(0xFFF3F4F6); // AI messages
static const Color chatInputBackground = Color(
0xFFF8F9FA,
); // Input background
static const Color chatSendButton = Color(0xFF82C9BD); // Send button (teal)
static const Color chatSendButton =
AppColors.primaryTeal; // Send button (teal)
// Dark Mode Colors
static const Color darkBackground = Color(0xFF1F2937); // Dark background
static const Color darkSurface = Color(0xFF374151); // Dark surface
static const Color darkTextPrimary = Color(0xFFF9FAFB); // Dark primary text
static const Color darkTextSecondary = Color(
0xFFD1D5DB,
); // Dark secondary text
// Border Colors
static const Color border = Color(0xFFE2E8F0); // Border color
static const Color divider = Color(0xFFE5E7EB); // Divider color
// Legacy compatibility (for existing code)
@deprecated
static const Color primaryBlue = primaryTeal; // Map old primaryBlue to new primaryTeal
// Overlay Colors
static const Color overlay = Color(0x80000000); // Overlay color
}
/// 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,
brightness: Brightness.light,
colorScheme: ColorScheme.fromSeed(
seedColor: AppColors.primaryBlue,
seedColor: AppColors.primaryTeal,
brightness: Brightness.light,
primary: AppColors.primaryBlue,
secondary: AppColors.primaryTeal,
surface: AppColors.surface,
background: AppColors.background,
primary: AppColors.primaryTeal,
secondary: AppColors.primaryOrange,
surface: LightColors.surface,
background: LightColors.background,
error: AppColors.error,
),
// App Bar Theme
appBarTheme: const AppBarTheme(
backgroundColor: AppColors.surface,
foregroundColor: AppColors.textPrimary,
backgroundColor: LightColors.surface,
foregroundColor: LightColors.textPrimary,
elevation: 0,
centerTitle: true,
systemOverlayStyle: SystemUiOverlayStyle.dark,
titleTextStyle: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 18,
fontWeight: FontWeight.w600,
),
@@ -34,7 +34,7 @@ class AppTheme {
// Card Theme
cardTheme: CardThemeData(
color: AppColors.cardBackground,
color: LightColors.cardBackground,
elevation: 2,
shadowColor: Colors.black.withOpacity(0.08),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
@@ -44,10 +44,10 @@ class AppTheme {
// Elevated Button Theme
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonPrimary,
backgroundColor: LightColors.buttonPrimary,
foregroundColor: Colors.white,
elevation: 2,
shadowColor: AppColors.primaryBlue.withOpacity(0.3),
shadowColor: AppColors.primaryTeal.withOpacity(0.3),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
@@ -59,8 +59,8 @@ class AppTheme {
// Outlined Button Theme
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.primaryBlue,
side: BorderSide(color: AppColors.primaryBlue.withOpacity(0.3)),
foregroundColor: AppColors.primaryTeal,
side: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
@@ -72,7 +72,7 @@ class AppTheme {
// Text Button Theme
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: AppColors.primaryBlue,
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),
@@ -82,18 +82,18 @@ class AppTheme {
// Input Field Theme
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.surface,
fillColor: LightColors.surface,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: AppColors.primaryBlue.withOpacity(0.3)),
borderSide: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: AppColors.primaryBlue.withOpacity(0.3)),
borderSide: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.primaryBlue, width: 2),
borderSide: const BorderSide(color: AppColors.primaryTeal, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
@@ -103,9 +103,9 @@ class AppTheme {
horizontal: 16,
vertical: 12,
),
hintStyle: const TextStyle(color: AppColors.textHint, fontSize: 14),
hintStyle: const TextStyle(color: LightColors.textHint, fontSize: 14),
labelStyle: const TextStyle(
color: AppColors.textSecondary,
color: LightColors.textSecondary,
fontSize: 14,
fontWeight: FontWeight.w500,
),
@@ -113,85 +113,85 @@ class AppTheme {
// Text Field Theme
textSelectionTheme: TextSelectionThemeData(
cursorColor: AppColors.primaryBlue,
selectionColor: AppColors.primaryBlue.withOpacity(0.3),
selectionHandleColor: AppColors.primaryBlue,
cursorColor: AppColors.primaryTeal,
selectionColor: AppColors.primaryTeal.withOpacity(0.3),
selectionHandleColor: AppColors.primaryTeal,
),
// Text Theme
textTheme: const TextTheme(
displayLarge: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 32,
fontWeight: FontWeight.bold,
),
displayMedium: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 28,
fontWeight: FontWeight.bold,
),
displaySmall: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 24,
fontWeight: FontWeight.bold,
),
headlineLarge: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 22,
fontWeight: FontWeight.w600,
),
headlineMedium: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 20,
fontWeight: FontWeight.w600,
),
headlineSmall: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 18,
fontWeight: FontWeight.w600,
),
titleLarge: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
),
titleMedium: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 14,
fontWeight: FontWeight.w600,
),
titleSmall: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 12,
fontWeight: FontWeight.w600,
),
bodyLarge: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.normal,
),
bodyMedium: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 14,
fontWeight: FontWeight.normal,
),
bodySmall: TextStyle(
color: AppColors.textSecondary,
color: LightColors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.normal,
),
labelLarge: TextStyle(
color: AppColors.textPrimary,
color: LightColors.textPrimary,
fontSize: 14,
fontWeight: FontWeight.w500,
),
labelMedium: TextStyle(
color: AppColors.textSecondary,
color: LightColors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w500,
),
labelSmall: TextStyle(
color: AppColors.textHint,
color: LightColors.textHint,
fontSize: 10,
fontWeight: FontWeight.w500,
),
@@ -199,9 +199,9 @@ class AppTheme {
// Bottom Navigation Bar Theme
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: AppColors.surface,
selectedItemColor: AppColors.primaryBlue,
unselectedItemColor: AppColors.iconInactive,
backgroundColor: LightColors.surface,
selectedItemColor: AppColors.primaryTeal,
unselectedItemColor: LightColors.iconInactive,
type: BottomNavigationBarType.fixed,
elevation: 8,
selectedLabelStyle: TextStyle(
@@ -216,7 +216,7 @@ class AppTheme {
// Floating Action Button Theme
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: AppColors.primaryBlue,
backgroundColor: AppColors.primaryTeal,
foregroundColor: Colors.white,
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
@@ -224,28 +224,28 @@ class AppTheme {
// Divider Theme
dividerTheme: const DividerThemeData(
color: AppColors.buttonSecondary,
color: LightColors.divider,
thickness: 1,
space: 1,
),
// Icon Theme
iconTheme: const IconThemeData(color: AppColors.iconActive, size: 24),
iconTheme: const IconThemeData(color: LightColors.iconActive, size: 24),
// Progress Indicator Theme
progressIndicatorTheme: const ProgressIndicatorThemeData(
color: AppColors.primaryBlue,
linearTrackColor: AppColors.buttonSecondary,
circularTrackColor: AppColors.buttonSecondary,
color: AppColors.primaryTeal,
linearTrackColor: LightColors.buttonSecondary,
circularTrackColor: LightColors.buttonSecondary,
),
// Chip Theme
chipTheme: ChipThemeData(
backgroundColor: AppColors.buttonSecondary,
selectedColor: AppColors.primaryBlue.withOpacity(0.1),
disabledColor: AppColors.buttonSecondary.withOpacity(0.5),
labelStyle: const TextStyle(color: AppColors.textPrimary),
secondaryLabelStyle: const TextStyle(color: AppColors.textPrimary),
backgroundColor: LightColors.buttonSecondary,
selectedColor: AppColors.primaryTeal.withOpacity(0.1),
disabledColor: LightColors.buttonSecondary.withOpacity(0.5),
labelStyle: const TextStyle(color: LightColors.textPrimary),
secondaryLabelStyle: const TextStyle(color: LightColors.textPrimary),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
@@ -257,52 +257,90 @@ class AppTheme {
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: ColorScheme.fromSeed(
seedColor: AppColors.primaryBlue,
seedColor: AppColors.primaryTeal,
brightness: Brightness.dark,
primary: AppColors.primaryBlue,
secondary: AppColors.primaryTeal,
surface: AppColors.darkSurface,
background: AppColors.darkBackground,
primary: AppColors.primaryTeal,
secondary: AppColors.primaryOrange,
surface: DarkColors.surface,
background: DarkColors.background,
error: AppColors.error,
),
// Dark mode specific overrides would go here
appBarTheme: const AppBarTheme(
backgroundColor: AppColors.darkSurface,
foregroundColor: AppColors.darkTextPrimary,
backgroundColor: DarkColors.surface,
foregroundColor: DarkColors.textPrimary,
elevation: 0,
centerTitle: true,
systemOverlayStyle: SystemUiOverlayStyle.light,
titleTextStyle: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
cardTheme: CardThemeData(
color: AppColors.darkSurface,
color: DarkColors.cardBackground,
elevation: 2,
shadowColor: Colors.black.withOpacity(0.3),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
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
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.darkSurface,
fillColor: DarkColors.surface,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: AppColors.primaryBlue.withOpacity(0.3)),
borderSide: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: AppColors.primaryBlue.withOpacity(0.3)),
borderSide: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.primaryBlue, width: 2),
borderSide: const BorderSide(color: AppColors.primaryTeal, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
@@ -313,11 +351,11 @@ class AppTheme {
vertical: 12,
),
hintStyle: const TextStyle(
color: AppColors.darkTextSecondary,
color: DarkColors.textSecondary,
fontSize: 14,
),
labelStyle: const TextStyle(
color: AppColors.darkTextSecondary,
color: DarkColors.textSecondary,
fontSize: 14,
fontWeight: FontWeight.w500,
),
@@ -325,88 +363,141 @@ class AppTheme {
// Text Field Theme for Dark Mode
textSelectionTheme: TextSelectionThemeData(
cursorColor: AppColors.primaryBlue,
selectionColor: AppColors.primaryBlue.withOpacity(0.3),
selectionHandleColor: AppColors.primaryBlue,
cursorColor: AppColors.primaryTeal,
selectionColor: AppColors.primaryTeal.withOpacity(0.3),
selectionHandleColor: AppColors.primaryTeal,
),
textTheme: const TextTheme(
displayLarge: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 32,
fontWeight: FontWeight.bold,
),
displayMedium: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 28,
fontWeight: FontWeight.bold,
),
displaySmall: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 24,
fontWeight: FontWeight.bold,
),
headlineLarge: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 22,
fontWeight: FontWeight.w600,
),
headlineMedium: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 20,
fontWeight: FontWeight.w600,
),
headlineSmall: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 18,
fontWeight: FontWeight.w600,
),
titleLarge: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
),
titleMedium: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 14,
fontWeight: FontWeight.w600,
),
titleSmall: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 12,
fontWeight: FontWeight.w600,
),
bodyLarge: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.normal,
),
bodyMedium: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 14,
fontWeight: FontWeight.normal,
),
bodySmall: TextStyle(
color: AppColors.darkTextSecondary,
color: DarkColors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.normal,
),
labelLarge: TextStyle(
color: AppColors.darkTextPrimary,
color: DarkColors.textPrimary,
fontSize: 14,
fontWeight: FontWeight.w500,
),
labelMedium: TextStyle(
color: AppColors.darkTextSecondary,
color: DarkColors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w500,
),
labelSmall: TextStyle(
color: AppColors.darkTextSecondary,
color: DarkColors.textSecondary,
fontSize: 10,
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(
backgroundColor: const Color(0xFFF8F9FA),
backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar(
backgroundColor: Colors.white,
backgroundColor: Theme.of(context).colorScheme.surface,
elevation: 0,
title: Row(
children: [
@@ -58,8 +58,11 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
width: 40,
height: 40,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)],
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary.withOpacity(0.8),
],
),
borderRadius: BorderRadius.circular(20),
),
@@ -74,12 +77,15 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.grey[800],
color: Theme.of(context).colorScheme.onSurface,
),
),
Text(
'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: [
IconButton(
onPressed: _handleLogout,
icon: Icon(Icons.logout, color: Colors.grey[700]),
icon: Icon(
Icons.logout,
color: Theme.of(context).colorScheme.onSurface,
),
tooltip: 'Sair',
),
],
@@ -98,14 +107,14 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
// Messages area
Expanded(
child: Container(
decoration: const BoxDecoration(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFF8F9FA),
Color(0xFFE8F0FE),
Color(0xFFF8F9FA),
Theme.of(context).colorScheme.background,
Theme.of(context).colorScheme.primary.withOpacity(0.05),
Theme.of(context).colorScheme.background,
],
),
),
@@ -163,15 +172,24 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
gradient: isUser
? const LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)],
? LinearGradient(
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
: LinearGradient(
colors: [
Colors.white.withOpacity(0.95),
Colors.white.withOpacity(0.9),
Theme.of(
context,
).colorScheme.surface.withOpacity(0.95),
Theme.of(
context,
).colorScheme.surface.withOpacity(0.9),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
@@ -208,24 +226,32 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
data: content,
styleSheet: MarkdownStyleSheet(
p: TextStyle(
color: const Color(0xFF2D3748),
color: Theme.of(
context,
).colorScheme.onSurface,
fontSize: 16,
height: 1.4,
),
strong: TextStyle(
color: const Color(0xFF2D3748),
color: Theme.of(
context,
).colorScheme.onSurface,
fontSize: 16,
fontWeight: FontWeight.bold,
height: 1.4,
),
em: TextStyle(
color: const Color(0xFF2D3748),
color: Theme.of(
context,
).colorScheme.onSurface,
fontSize: 16,
fontStyle: FontStyle.italic,
height: 1.4,
),
listBullet: TextStyle(
color: const Color(0xFF2D3748),
color: Theme.of(
context,
).colorScheme.onSurface,
fontSize: 16,
height: 1.4,
),
@@ -247,7 +273,10 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
),
child: Text(
_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,
height: 36,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)],
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary.withOpacity(0.8),
],
),
borderRadius: BorderRadius.circular(18),
boxShadow: [
BoxShadow(
color: const Color(0xFF82C9BD).withOpacity(0.3),
color: Theme.of(context).colorScheme.primary.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -295,7 +327,7 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
bottom: bottomPadding + 16.0, // Add system navigation bar padding
),
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)),
boxShadow: [
BoxShadow(
@@ -308,8 +340,11 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.white,
border: Border.all(color: const Color(0xFFE2E8F0), width: 1),
color: Theme.of(context).colorScheme.surface,
border: Border.all(
color: Theme.of(context).colorScheme.outline.withOpacity(0.5),
width: 1,
),
),
child: Row(
children: [
@@ -317,9 +352,9 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
Expanded(
child: TextField(
controller: _messageController,
style: const TextStyle(
style: TextStyle(
fontSize: 16,
color: Color(0xFF1A1A1A), // Dark text for visibility
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.w500,
),
decoration: InputDecoration(
@@ -330,7 +365,7 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
),
hintText: 'Faz a tua pergunta!',
hintStyle: TextStyle(
color: Colors.grey[400],
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w400,
fontSize: 16,
),
@@ -351,20 +386,27 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
height: 44,
decoration: BoxDecoration(
gradient: _messageController.text.isNotEmpty
? const LinearGradient(
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)],
? LinearGradient(
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
: null,
color: _messageController.text.isNotEmpty
? null
: Colors.grey[300],
: Theme.of(context).colorScheme.outline.withOpacity(0.3),
borderRadius: BorderRadius.circular(22),
boxShadow: _messageController.text.isNotEmpty
? [
BoxShadow(
color: const Color(0xFF82C9BD).withOpacity(0.3),
color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -398,7 +440,8 @@ class _TutorChatPageSimpleState extends State<TutorChatPageSimple>
void _addWelcomeMessage() {
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!
@@ -465,7 +508,8 @@ Envia-me a tua pergunta sobre qualquer assunto educacional e vou usar o material
} catch (e) {
// Fallback to error message if API fails
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(() {
_messages.add({

View File

@@ -70,7 +70,9 @@ class _LoginPageState extends State<LoginPage> {
);
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
final uid = result?.user?.uid;
@@ -81,7 +83,9 @@ class _LoginPageState extends State<LoginPage> {
// Validar se o role selecionado corresponde ao role real
final selectedRole = widget.selectedRole;
if (selectedRole != null && actualRole != null && selectedRole != actualRole) {
if (selectedRole != null &&
actualRole != null &&
selectedRole != actualRole) {
// Role não corresponde - mostrar erro
setState(() {
_isLoading = false;
@@ -89,11 +93,14 @@ class _LoginPageState extends State<LoginPage> {
String errorMessage;
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') {
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 {
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);
@@ -151,14 +158,14 @@ class _LoginPageState extends State<LoginPage> {
return AlertDialog(
title: Text(
title,
style: const TextStyle(
color: Color(0xFF2D3748),
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
),
content: Text(
message,
style: const TextStyle(color: Color(0xFF2D3748)),
style: TextStyle(color: Theme.of(context).colorScheme.onSurface),
),
actions: [
TextButton(
@@ -167,9 +174,9 @@ class _LoginPageState extends State<LoginPage> {
// Fazer logout para limpar a sessão
AuthService.signOut();
},
child: const Text(
child: Text(
'Voltar',
style: TextStyle(color: Color(0xFF82C9BD)),
style: TextStyle(color: Theme.of(context).colorScheme.primary),
),
),
],
@@ -180,304 +187,403 @@ class _LoginPageState extends State<LoginPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFF8F9FA),
Color.fromRGBO(130, 201, 189, 0.1),
Color.fromRGBO(246, 141, 45, 0.05),
Color(0xFFF8F9FA),
],
),
),
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: 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),
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (!didPop) {
context.go('/role-selection');
}
},
child: Scaffold(
body: Stack(
children: [
// Main content
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
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: 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,
end: Alignment.bottomRight,
colors: [
AppColors.background,
AppColors.primaryBlue.withOpacity(0.05),
AppColors.gradientStart.withOpacity(0.1),
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,
],
),
),
@@ -92,7 +93,9 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
AppLocalizations.of(context)!.appTitle,
style: Theme.of(context).textTheme.headlineLarge
?.copyWith(
color: AppColors.textPrimary,
color: Theme.of(
context,
).colorScheme.onSurface,
fontWeight: FontWeight.bold,
),
)
@@ -149,7 +152,7 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
'Quem é você?',
style: Theme.of(context).textTheme.headlineMedium
?.copyWith(
color: AppColors.textPrimary,
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.w600,
),
)
@@ -351,7 +354,7 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: isSelected
? Colors.white
: AppColors.textPrimary,
: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.w600,
),
),
@@ -410,4 +413,4 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
context.go('/signup?role=$_selectedRole');
}
}
}
}

View File

@@ -101,315 +101,444 @@ class _SignupPageState extends State<SignupPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFF8F9FA),
Color.fromRGBO(130, 201, 189, 0.1),
Color.fromRGBO(246, 141, 45, 0.05),
Color(0xFFF8F9FA),
],
),
),
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: 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),
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (!didPop) {
context.go('/role-selection');
}
},
child: Scaffold(
body: Stack(
children: [
// Main content
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
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: 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) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFF82C9BD),
Color(0xFF7BA89C),
Color(0xFFF68D2D),
Color(0xFFF8F9FA),
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary.withOpacity(0.8),
Theme.of(context).colorScheme.secondary,
Theme.of(context).colorScheme.background,
],
stops: [0.0, 0.2, 0.6, 1.0],
),

View File

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

View File

@@ -39,7 +39,7 @@ class ProgressHeroWidget extends StatelessWidget {
Text(
'Seu Progresso',
style: TextStyle(
color: const Color(0xFF2D3748),
color: Theme.of(context).colorScheme.onSurface,
fontSize: 24,
fontWeight: FontWeight.bold,
),
@@ -48,7 +48,7 @@ class ProgressHeroWidget extends StatelessWidget {
Text(
'Continue assim, $userName!',
style: TextStyle(
color: const Color(0xFF718096),
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 16,
),
),
@@ -56,9 +56,12 @@ class ProgressHeroWidget extends StatelessWidget {
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: const Color(0xFF82C9BD),
color: Theme.of(context).colorScheme.primary,
borderRadius: BorderRadius.circular(20),
),
child: Row(
@@ -93,8 +96,8 @@ class ProgressHeroWidget extends StatelessWidget {
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color(0xFF82C9BD),
const Color(0xFF6BA8A0),
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary.withOpacity(0.8),
],
),
borderRadius: BorderRadius.circular(20),
@@ -132,7 +135,7 @@ class ProgressHeroWidget extends StatelessWidget {
],
),
const SizedBox(height: 16),
// Progress Bar
Container(
height: 12,
@@ -186,77 +189,90 @@ class ProgressHeroWidget extends StatelessWidget {
// Mastered Concepts
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(
Icons.school,
color: Color(0xFFF68D2D),
size: 20,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Theme.of(
context,
).colorScheme.outline.withOpacity(0.2),
),
boxShadow: [
BoxShadow(
color: Theme.of(
context,
).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
const SizedBox(width: 8),
const Text(
'Conceitos Dominados',
style: TextStyle(
color: Color(0xFF2D3748),
fontSize: 16,
fontWeight: FontWeight.bold,
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
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(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Container(
width: 8,
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)),
)
.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(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 1,
),
border: Border.all(color: Colors.white.withOpacity(0.3), width: 1),
),
child: Column(
children: [
@@ -292,10 +305,7 @@ class ProgressHeroWidget extends StatelessWidget {
const SizedBox(height: 4),
Text(
label,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
),
style: const TextStyle(color: Colors.white, fontSize: 12),
textAlign: TextAlign.center,
),
],

View File

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

View File

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

View File

@@ -13,207 +13,248 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
final userEmail = user?.email ?? '';
return Container(
margin: const EdgeInsets.only(top: 24),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0)),
boxShadow: [
BoxShadow(
color: Colors.black.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,
),
margin: const EdgeInsets.only(top: 24),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
),
boxShadow: [
BoxShadow(
color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
userName,
style: const TextStyle(
color: Color(0xFF2D3748),
fontSize: 18,
fontWeight: FontWeight.bold,
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Profile Header
Row(
children: [
Container(
width: 48,
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),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
child: const Icon(
Icons.school,
color: Colors.white,
size: 24,
),
),
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: [
Text(
userEmail,
style: const TextStyle(
color: Color(0xFF718096),
'Qualidade do Conteúdo',
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
if (userEmail.length > 20) ...[
const SizedBox(width: 8),
const Icon(
Icons.more_horiz,
color: Color(0xFF718096),
size: 16,
const SizedBox(height: 2),
Text(
'12 conteúdos verificados • 2 pendentes de revisão',
style: TextStyle(
color: Theme.of(
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),
// Quick Stats Row
Row(
children: [
_buildQuickStat(
icon: Icons.check_circle,
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));
)
.animate()
.slideY(
duration: const Duration(milliseconds: 800),
curve: Curves.easeOut,
)
.then(delay: const Duration(milliseconds: 400));
}
Widget _buildQuickStat({
@@ -245,10 +286,7 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
const SizedBox(height: 2),
Text(
label,
style: TextStyle(
color: color.withOpacity(0.8),
fontSize: 10,
),
style: TextStyle(color: color.withOpacity(0.8), fontSize: 10),
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(
children: [
Container(
@@ -282,8 +325,8 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
Expanded(
child: Text(
name,
style: const TextStyle(
color: Color(0xFF4A5568),
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 14,
),
),

View File

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

View File

@@ -35,7 +35,7 @@ class TeacherHeroWidget extends StatelessWidget {
Text(
'Visão Geral da Turma',
style: TextStyle(
color: const Color(0xFF2D3748),
color: Theme.of(context).colorScheme.onSurface,
fontSize: 24,
fontWeight: FontWeight.bold,
),
@@ -44,7 +44,7 @@ class TeacherHeroWidget extends StatelessWidget {
Text(
'Acompanhe o progresso dos seus alunos',
style: TextStyle(
color: const Color(0xFF718096),
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 16,
),
),
@@ -52,19 +52,18 @@ class TeacherHeroWidget extends StatelessWidget {
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: const Color(0xFFF68D2D),
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.people,
color: Colors.white,
size: 16,
),
const Icon(Icons.people, color: Colors.white, size: 16),
const SizedBox(width: 4),
Text(
'$totalStudents alunos',
@@ -89,14 +88,14 @@ class TeacherHeroWidget extends StatelessWidget {
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color(0xFF82C9BD),
const Color(0xFF6BA8A0),
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary.withOpacity(0.8),
],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
color: Theme.of(context).colorScheme.shadow.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 10),
),
@@ -128,7 +127,7 @@ class TeacherHeroWidget extends StatelessWidget {
],
),
const SizedBox(height: 16),
// Progress Bar
Container(
height: 12,
@@ -182,64 +181,76 @@ class TeacherHeroWidget extends StatelessWidget {
// Recent Activity
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE2E8F0)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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,
),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Theme.of(
context,
).colorScheme.outline.withOpacity(0.2),
),
boxShadow: [
BoxShadow(
color: Theme.of(
context,
).colorScheme.shadow.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
const SizedBox(height: 12),
_buildActivityItem(
'15 alunos completaram o quiz de Derivadas',
'Hoje, 14:30',
const Color(0xFF82C9BD),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
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(
'Novo conteúdo: Regra da Cadeia',
'Ontem, 09:15',
const Color(0xFFF68D2D),
),
const SizedBox(height: 8),
_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)),
)
.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(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.white.withOpacity(0.3),
width: 1,
),
border: Border.all(color: Colors.white.withOpacity(0.3), width: 1),
),
child: Column(
children: [
@@ -275,10 +283,7 @@ class TeacherHeroWidget extends StatelessWidget {
const SizedBox(height: 4),
Text(
label,
style: const TextStyle(
color: Colors.white,
fontSize: 12,
),
style: const TextStyle(color: Colors.white, fontSize: 12),
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(
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
decoration: BoxDecoration(color: color, shape: BoxShape.circle),
),
const SizedBox(width: 12),
Expanded(
@@ -304,16 +311,18 @@ class TeacherHeroWidget extends StatelessWidget {
children: [
Text(
text,
style: const TextStyle(
color: Color(0xFF4A5568),
style: TextStyle(
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 14,
),
),
const SizedBox(height: 2),
Text(
time,
style: const TextStyle(
color: Color(0xFF718096),
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant.withOpacity(0.7),
fontSize: 12,
),
),

View File

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

View File

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

View File

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

View File

@@ -29,17 +29,21 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
},
child: Scaffold(
body: Container(
decoration: const BoxDecoration(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFF82C9BD),
Color(0xFF7BA89C),
Color(0xFFF68D2D),
Color(0xFFF8F9FA),
],
stops: [0.0, 0.2, 0.6, 1.0],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: themeMode == ThemeMode.light
? [
const Color(0xFFD4E8E8),
const Color(0xFFE8D4C0),
const Color(0xFFD8E0E8),
]
: [
Theme.of(context).colorScheme.primary.withOpacity(0.1),
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
Theme.of(context).colorScheme.background,
],
),
),
child: SafeArea(
@@ -54,11 +58,11 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => context.go('/student-dashboard'),
),
const Expanded(
Expanded(
child: Text(
'Configurações',
style: TextStyle(
color: Colors.white,
color: Theme.of(context).colorScheme.onSurface,
fontSize: 24,
fontWeight: FontWeight.bold,
),
@@ -187,19 +191,23 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isDark ? Colors.white70 : AppColors.textSecondary,
color: isDark
? Colors.white70
: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
Container(
decoration: BoxDecoration(
color: isDark ? Colors.grey[800] : AppColors.surface,
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 2,
offset: const Offset(0, 1),
color: Theme.of(
context,
).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),
title: Text(
'Tema',
style: TextStyle(color: isDark ? Colors.white : AppColors.textPrimary),
style: TextStyle(
color: isDark
? Colors.white
: Theme.of(context).colorScheme.onSurface,
),
),
subtitle: Text(
isDarkModeAvailable
? 'Atual: ${_getThemeModeString(currentTheme)}'
: 'Light Mode (padrão)',
style: TextStyle(
color: isDarkModeAvailable
? (isDark ? Colors.white70 : AppColors.textSecondary)
: AppColors.textHint,
),
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
),
trailing: isDarkModeAvailable
? 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),
title: Text(
title,
style: TextStyle(color: isDark ? Colors.white : AppColors.textPrimary),
style: TextStyle(
color: isDark
? Colors.white
: Theme.of(context).colorScheme.onSurface,
),
),
subtitle: Text(
subtitle,
style: TextStyle(
color: isDark ? Colors.white70 : AppColors.textSecondary,
color: isDark
? Colors.white70
: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
trailing: Switch(
@@ -312,15 +329,24 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
leading: Icon(icon, color: AppColors.primaryTeal),
title: Text(
title,
style: TextStyle(color: isDark ? Colors.white : AppColors.textPrimary),
style: TextStyle(
color: isDark
? Colors.white
: Theme.of(context).colorScheme.onSurface,
),
),
subtitle: Text(
subtitle,
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,
);
}
@@ -336,12 +362,18 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
leading: Icon(icon, color: AppColors.primaryTeal),
title: Text(
title,
style: TextStyle(color: isDark ? Colors.white : AppColors.textPrimary),
style: TextStyle(
color: isDark
? Colors.white
: Theme.of(context).colorScheme.onSurface,
),
),
subtitle: Text(
subtitle,
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_animate/flutter_animate.dart';
import 'package:go_router/go_router.dart';
import '../../../../core/theme/app_colors.dart';
import '../../../../core/services/auth_service.dart';
import '../../../../core/services/session_service.dart';
import '../../../../l10n/app_localizations.dart';
@@ -119,10 +118,10 @@ class _SplashPageState extends State<SplashPage> {
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.background,
AppColors.primaryTeal.withOpacity(0.05),
AppColors.primaryOrange.withOpacity(0.03),
AppColors.background,
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,
],
),
),
@@ -147,7 +146,9 @@ class _SplashPageState extends State<SplashPage> {
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: AppColors.primaryTeal.withOpacity(0.2),
color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.2),
width: 2,
),
),
@@ -174,7 +175,9 @@ class _SplashPageState extends State<SplashPage> {
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: AppColors.primaryOrange.withOpacity(0.3),
color: Theme.of(
context,
).colorScheme.secondary.withOpacity(0.3),
width: 3,
),
),
@@ -199,10 +202,10 @@ class _SplashPageState extends State<SplashPage> {
width: 80,
height: 80,
decoration: BoxDecoration(
gradient: const LinearGradient(
gradient: LinearGradient(
colors: [
AppColors.primaryTeal,
AppColors.primaryOrange,
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.secondary,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
@@ -210,14 +213,16 @@ class _SplashPageState extends State<SplashPage> {
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: AppColors.primaryTeal.withOpacity(0.3),
color: Theme.of(
context,
).colorScheme.primary.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 8),
),
BoxShadow(
color: AppColors.primaryOrange.withOpacity(
0.2,
),
color: Theme.of(
context,
).colorScheme.secondary.withOpacity(0.2),
blurRadius: 15,
offset: const Offset(0, 4),
),
@@ -260,10 +265,10 @@ class _SplashPageState extends State<SplashPage> {
// School name with gradient
ShaderMask(
shaderCallback: (bounds) => const LinearGradient(
shaderCallback: (bounds) => LinearGradient(
colors: [
AppColors.primaryTeal,
AppColors.primaryOrange,
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.secondary,
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
@@ -295,8 +300,12 @@ class _SplashPageState extends State<SplashPage> {
ShaderMask(
shaderCallback: (bounds) => LinearGradient(
colors: [
AppColors.primaryOrange.withOpacity(0.8),
AppColors.primaryTeal.withOpacity(0.8),
Theme.of(
context,
).colorScheme.secondary.withOpacity(0.8),
Theme.of(
context,
).colorScheme.primary.withOpacity(0.8),
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
@@ -345,7 +354,9 @@ class _SplashPageState extends State<SplashPage> {
'A preparar a sua experiência...',
style: Theme.of(context).textTheme.bodySmall
?.copyWith(
color: AppColors.primaryOrange,
color: Theme.of(
context,
).colorScheme.secondary,
fontWeight: FontWeight.w500,
),
)
@@ -357,7 +368,9 @@ class _SplashPageState extends State<SplashPage> {
.then()
.shimmer(
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(
width: 12,
height: 12,
decoration: const BoxDecoration(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [AppColors.primaryTeal, AppColors.primaryOrange],
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.secondary,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
@@ -418,8 +434,8 @@ class _SplashPageState extends State<SplashPage> {
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.primaryTeal.withOpacity(0.3),
AppColors.primaryOrange.withOpacity(0.2),
Theme.of(context).colorScheme.primary.withOpacity(0.3),
Theme.of(context).colorScheme.secondary.withOpacity(0.2),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import '../../../../core/theme/app_colors.dart';
class TutorChatPage extends StatelessWidget {
const TutorChatPage({super.key});
@@ -7,20 +6,34 @@ class TutorChatPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.background,
backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar(
title: const Text('AI Tutor'),
backgroundColor: AppColors.surface,
foregroundColor: AppColors.textPrimary,
backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: Theme.of(context).colorScheme.onSurface,
elevation: 0,
),
body: const Center(
child: Text(
'AI Tutor Chat - Coming Soon',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
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 '../../../../core/theme/app_colors.dart';
class LoadingPage extends StatelessWidget {
const LoadingPage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
backgroundColor: AppColors.background,
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation<Color>(AppColors.primaryBlue),
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.primary,
),
),
SizedBox(height: 24),
const SizedBox(height: 24),
Text(
'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 '../../../../core/theme/app_colors.dart';
import '../../../../l10n/app_localizations.dart';
class NotFoundPage extends StatelessWidget {
@@ -9,33 +8,37 @@ class NotFoundPage extends StatelessWidget {
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Scaffold(
backgroundColor: AppColors.background,
backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar(
title: Text(l10n.pageNotFound),
backgroundColor: AppColors.surface,
foregroundColor: AppColors.textPrimary,
backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: Theme.of(context).colorScheme.onSurface,
elevation: 0,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
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),
Text(
l10n.pageNotFound,
style: const TextStyle(
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
color: Theme.of(context).colorScheme.onSurface,
),
),
const SizedBox(height: 8),
Text(
'A página que procura não existe.',
style: const TextStyle(
style: TextStyle(
fontSize: 16,
color: AppColors.textSecondary,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],