- Dark / light mode a funcionar no lado do aluno
- Atualização dos ficheiros markdown.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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**
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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],
|
||||
),
|
||||
|
||||
@@ -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],
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user