Modificação nos modos, correção de textos cortados
This commit is contained in:
@@ -1,111 +1,90 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// EPVC School Color Palette - New Color Scheme with Light/Dark Mode Support
|
/// EPVC School Color Palette - Light/Dark Mode Support
|
||||||
class AppColors {
|
class AppColors {
|
||||||
// Primary Brand Colors (same for both modes)
|
// Primary Brand Colors (light mode)
|
||||||
static const Color primaryTeal = Color(
|
static const Color primaryTeal = Color(0xFF82C9BD);
|
||||||
0xFF82C9BD,
|
static const Color primaryOrange = Color(0xFFF68D2D);
|
||||||
); // Main teal color - PRIMARY
|
|
||||||
static const Color primaryOrange = Color(
|
|
||||||
0xFFF68D2D,
|
|
||||||
); // Accent orange - SECONDARY
|
|
||||||
|
|
||||||
// Gradient Colors (same for both modes)
|
// Gradient Colors (light mode)
|
||||||
static const Color gradientStart = Color(0xFF82C9BD); // Teal gradient start
|
static const Color gradientStart = Color(0xFF82C9BD);
|
||||||
static const Color gradientEnd = Color(
|
static const Color gradientEnd = Color(0xFF6AB8A8);
|
||||||
0xFF6AB8A8,
|
|
||||||
); // Darker teal gradient end
|
|
||||||
|
|
||||||
// Secondary Colors (same for both modes)
|
// Secondary Colors (light mode)
|
||||||
static const Color secondaryTeal = Color(0xFF6AB8A8); // Darker teal
|
static const Color secondaryTeal = Color(0xFF6AB8A8);
|
||||||
static const Color accentTeal = Color(0xFF5AA69A); // Lighter teal accent
|
static const Color accentTeal = Color(0xFF5AA69A);
|
||||||
static const Color lightOrange = Color(0xFFF7A960); // Lighter orange
|
static const Color lightOrange = Color(0xFFF7A960);
|
||||||
|
|
||||||
// Status Colors (same for both modes)
|
// Status Colors (shared)
|
||||||
static const Color success = Color(0xFF10B981); // Green for success
|
static const Color success = Color(0xFF10B981);
|
||||||
static const Color warning = Color(0xFFF59E0B); // Amber for warnings
|
static const Color warning = Color(0xFFF59E0B);
|
||||||
static const Color error = Color(0xFFEF4444); // Red for errors
|
static const Color error = Color(0xFFEF4444);
|
||||||
static const Color info = Color(0xFF3B82F6); // Blue for info
|
static const Color info = Color(0xFF3B82F6);
|
||||||
|
|
||||||
// Legacy compatibility (for existing code)
|
@Deprecated('Use AppColors.primaryTeal')
|
||||||
@deprecated
|
static const Color primaryBlue = primaryTeal;
|
||||||
static const Color primaryBlue = primaryTeal; // Map old primaryBlue to new primaryTeal
|
}
|
||||||
|
|
||||||
|
/// Brand colors tuned for dark mode (lower luminance, same hue family).
|
||||||
|
class DarkBrandColors {
|
||||||
|
static const Color primaryTeal = Color(0xFF4D8F84);
|
||||||
|
static const Color primaryOrange = Color(0xFFC47A2A);
|
||||||
|
static const Color gradientStart = Color(0xFF3D7A70);
|
||||||
|
static const Color gradientEnd = Color(0xFF2F635C);
|
||||||
|
static const Color secondaryTeal = Color(0xFF3D7A70);
|
||||||
|
static const Color accentTeal = Color(0xFF356B62);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Light Mode Colors
|
/// Light Mode Colors
|
||||||
class LightColors {
|
class LightColors {
|
||||||
// Neutral Colors
|
static const Color background = Color(0xFFF8F9FA);
|
||||||
static const Color background = Color(0xFFF8F9FA); // Light gray background
|
static const Color surface = Color(0xFFFFFFFF);
|
||||||
static const Color surface = Color(0xFFFFFFFF); // White surfaces
|
static const Color surfaceVariant = Color(0xFFF3F4F6);
|
||||||
static const Color cardBackground = Color(0xFFFFFFFF); // White cards
|
static const Color cardBackground = Color(0xFFFFFFFF);
|
||||||
|
|
||||||
// Text Colors
|
static const Color textPrimary = Color(0xFF1A1A1A);
|
||||||
static const Color textPrimary = Color(0xFF1A1A1A); // Primary text
|
static const Color textSecondary = Color(0xFF6B7280);
|
||||||
static const Color textSecondary = Color(0xFF6B7280); // Secondary text
|
static const Color textHint = Color(0xFF9CA3AF);
|
||||||
static const Color textHint = Color(0xFF9CA3AF); // Hint text
|
|
||||||
|
|
||||||
// Interactive Colors
|
static const Color buttonPrimary = AppColors.primaryTeal;
|
||||||
static const Color buttonPrimary =
|
static const Color buttonAccent = AppColors.primaryOrange;
|
||||||
AppColors.primaryTeal; // Primary button (teal)
|
static const Color buttonSecondary = Color(0xFFE5E7EB);
|
||||||
static const Color buttonAccent =
|
static const Color iconActive = AppColors.primaryTeal;
|
||||||
AppColors.primaryOrange; // Accent button (orange)
|
static const Color iconInactive = Color(0xFF9CA3AF);
|
||||||
static const Color buttonSecondary = Color(0xFFE5E7EB); // Secondary button
|
|
||||||
static const Color iconActive = AppColors.primaryTeal; // Active icons (teal)
|
|
||||||
static const Color iconInactive = Color(0xFF9CA3AF); // Inactive icons
|
|
||||||
|
|
||||||
// Chat Specific Colors
|
static const Color chatBubbleStudent = AppColors.primaryTeal;
|
||||||
static const Color chatBubbleStudent =
|
static const Color chatBubbleAI = Color(0xFFF3F4F6);
|
||||||
AppColors.primaryTeal; // Student messages (teal)
|
static const Color chatInputBackground = Color(0xFFF8F9FA);
|
||||||
static const Color chatBubbleAI = Color(0xFFF3F4F6); // AI messages
|
static const Color chatSendButton = AppColors.primaryTeal;
|
||||||
static const Color chatInputBackground = Color(
|
|
||||||
0xFFF8F9FA,
|
|
||||||
); // Input background
|
|
||||||
static const Color chatSendButton =
|
|
||||||
AppColors.primaryTeal; // Send button (teal)
|
|
||||||
|
|
||||||
// Border Colors
|
static const Color border = Color(0xFFE2E8F0);
|
||||||
static const Color border = Color(0xFFE2E8F0); // Border color
|
static const Color divider = Color(0xFFE5E7EB);
|
||||||
static const Color divider = Color(0xFFE5E7EB); // Divider color
|
static const Color overlay = Color(0x80000000);
|
||||||
|
|
||||||
// Overlay Colors
|
|
||||||
static const Color overlay = Color(0x80000000); // Overlay color
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dark Mode Colors
|
/// Dark Mode Colors
|
||||||
class DarkColors {
|
class DarkColors {
|
||||||
// Neutral Colors
|
static const Color background = Color(0xFF0F1218);
|
||||||
static const Color background = Color(0xFF1F2937); // Dark background
|
static const Color surface = Color(0xFF1A2332);
|
||||||
static const Color surface = Color(0xFF374151); // Dark surface
|
static const Color surfaceVariant = Color(0xFF243044);
|
||||||
static const Color cardBackground = Color(0xFF374151); // Dark cards
|
static const Color cardBackground = Color(0xFF1A2332);
|
||||||
|
|
||||||
// Text Colors
|
static const Color textPrimary = Color(0xFFF9FAFB);
|
||||||
static const Color textPrimary = Color(0xFFF9FAFB); // Dark primary text
|
static const Color textSecondary = Color(0xFFD1D5DB);
|
||||||
static const Color textSecondary = Color(0xFFD1D5DB); // Dark secondary text
|
static const Color textHint = Color(0xFF9CA3AF);
|
||||||
static const Color textHint = Color(0xFF9CA3AF); // Hint text
|
|
||||||
|
|
||||||
// Interactive Colors
|
static const Color buttonPrimary = DarkBrandColors.primaryTeal;
|
||||||
static const Color buttonPrimary =
|
static const Color buttonAccent = DarkBrandColors.primaryOrange;
|
||||||
AppColors.primaryTeal; // Primary button (teal)
|
static const Color buttonSecondary = Color(0xFF2D3A4D);
|
||||||
static const Color buttonAccent =
|
static const Color iconActive = DarkBrandColors.primaryTeal;
|
||||||
AppColors.primaryOrange; // Accent button (orange)
|
static const Color iconInactive = Color(0xFF6B7280);
|
||||||
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 = DarkBrandColors.primaryTeal;
|
||||||
static const Color chatBubbleStudent =
|
static const Color chatBubbleAI = Color(0xFF243044);
|
||||||
AppColors.primaryTeal; // Student messages (teal)
|
static const Color chatInputBackground = Color(0xFF1A2332);
|
||||||
static const Color chatBubbleAI = Color(0xFF4B5563); // AI messages (darker)
|
static const Color chatSendButton = DarkBrandColors.primaryTeal;
|
||||||
static const Color chatInputBackground = Color(
|
|
||||||
0xFF374151,
|
|
||||||
); // Input background
|
|
||||||
static const Color chatSendButton =
|
|
||||||
AppColors.primaryTeal; // Send button (teal)
|
|
||||||
|
|
||||||
// Border Colors
|
static const Color border = Color(0xFF2D3A4D);
|
||||||
static const Color border = Color(0xFF4B5563); // Border color
|
static const Color divider = Color(0xFF2D3A4D);
|
||||||
static const Color divider = Color(0xFF4B5563); // Divider color
|
static const Color overlay = Color(0x80000000);
|
||||||
|
|
||||||
// Overlay Colors
|
|
||||||
static const Color overlay = Color(0x80000000); // Overlay color
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'app_colors.dart';
|
import 'app_colors.dart';
|
||||||
|
import 'app_theme_extension.dart';
|
||||||
|
|
||||||
/// Application Theme Configuration
|
/// Application Theme Configuration
|
||||||
class AppTheme {
|
class AppTheme {
|
||||||
@@ -12,11 +13,18 @@ class AppTheme {
|
|||||||
seedColor: AppColors.primaryTeal,
|
seedColor: AppColors.primaryTeal,
|
||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
primary: AppColors.primaryTeal,
|
primary: AppColors.primaryTeal,
|
||||||
|
onPrimary: Colors.white,
|
||||||
secondary: AppColors.primaryOrange,
|
secondary: AppColors.primaryOrange,
|
||||||
|
onSecondary: Colors.white,
|
||||||
surface: LightColors.surface,
|
surface: LightColors.surface,
|
||||||
|
onSurface: LightColors.textPrimary,
|
||||||
|
onSurfaceVariant: LightColors.textSecondary,
|
||||||
|
surfaceContainerHighest: LightColors.surfaceVariant,
|
||||||
background: LightColors.background,
|
background: LightColors.background,
|
||||||
error: AppColors.error,
|
error: AppColors.error,
|
||||||
),
|
),
|
||||||
|
scaffoldBackgroundColor: LightColors.background,
|
||||||
|
extensions: [AppThemeExtras.light],
|
||||||
|
|
||||||
// App Bar Theme
|
// App Bar Theme
|
||||||
appBarTheme: const AppBarTheme(
|
appBarTheme: const AppBarTheme(
|
||||||
@@ -256,17 +264,22 @@ class AppTheme {
|
|||||||
return ThemeData(
|
return ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
colorScheme: ColorScheme.fromSeed(
|
colorScheme: ColorScheme.dark(
|
||||||
seedColor: AppColors.primaryTeal,
|
primary: DarkBrandColors.primaryTeal,
|
||||||
brightness: Brightness.dark,
|
onPrimary: Colors.white,
|
||||||
primary: AppColors.primaryTeal,
|
secondary: DarkBrandColors.primaryOrange,
|
||||||
secondary: AppColors.primaryOrange,
|
onSecondary: Colors.white,
|
||||||
surface: DarkColors.surface,
|
surface: DarkColors.surface,
|
||||||
|
onSurface: DarkColors.textPrimary,
|
||||||
|
onSurfaceVariant: DarkColors.textSecondary,
|
||||||
|
surfaceContainerHighest: DarkColors.surfaceVariant,
|
||||||
|
surfaceContainerLow: DarkColors.background,
|
||||||
background: DarkColors.background,
|
background: DarkColors.background,
|
||||||
error: AppColors.error,
|
error: AppColors.error,
|
||||||
|
outline: DarkColors.border,
|
||||||
),
|
),
|
||||||
|
scaffoldBackgroundColor: DarkColors.background,
|
||||||
// Dark mode specific overrides would go here
|
extensions: [AppThemeExtras.dark],
|
||||||
appBarTheme: const AppBarTheme(
|
appBarTheme: const AppBarTheme(
|
||||||
backgroundColor: DarkColors.surface,
|
backgroundColor: DarkColors.surface,
|
||||||
foregroundColor: DarkColors.textPrimary,
|
foregroundColor: DarkColors.textPrimary,
|
||||||
@@ -294,7 +307,7 @@ class AppTheme {
|
|||||||
backgroundColor: DarkColors.buttonPrimary,
|
backgroundColor: DarkColors.buttonPrimary,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
elevation: 2,
|
elevation: 2,
|
||||||
shadowColor: AppColors.primaryTeal.withOpacity(0.3),
|
shadowColor: DarkBrandColors.primaryTeal.withOpacity(0.3),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
@@ -306,8 +319,10 @@ class AppTheme {
|
|||||||
// Outlined Button Theme for Dark Mode
|
// Outlined Button Theme for Dark Mode
|
||||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
foregroundColor: AppColors.primaryTeal,
|
foregroundColor: DarkBrandColors.primaryTeal,
|
||||||
side: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
|
side: BorderSide(
|
||||||
|
color: DarkBrandColors.primaryTeal.withOpacity(0.3),
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
@@ -319,7 +334,7 @@ class AppTheme {
|
|||||||
// Text Button Theme for Dark Mode
|
// Text Button Theme for Dark Mode
|
||||||
textButtonTheme: TextButtonThemeData(
|
textButtonTheme: TextButtonThemeData(
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: AppColors.primaryTeal,
|
foregroundColor: DarkBrandColors.primaryTeal,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
textStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
|
textStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
|
||||||
@@ -332,15 +347,22 @@ class AppTheme {
|
|||||||
fillColor: DarkColors.surface,
|
fillColor: DarkColors.surface,
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
|
borderSide: BorderSide(
|
||||||
|
color: DarkBrandColors.primaryTeal.withOpacity(0.3),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: BorderSide(color: AppColors.primaryTeal.withOpacity(0.3)),
|
borderSide: BorderSide(
|
||||||
|
color: DarkBrandColors.primaryTeal.withOpacity(0.3),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderSide: const BorderSide(color: AppColors.primaryTeal, width: 2),
|
borderSide: const BorderSide(
|
||||||
|
color: DarkBrandColors.primaryTeal,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
errorBorder: OutlineInputBorder(
|
errorBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
@@ -363,9 +385,9 @@ class AppTheme {
|
|||||||
|
|
||||||
// Text Field Theme for Dark Mode
|
// Text Field Theme for Dark Mode
|
||||||
textSelectionTheme: TextSelectionThemeData(
|
textSelectionTheme: TextSelectionThemeData(
|
||||||
cursorColor: AppColors.primaryTeal,
|
cursorColor: DarkBrandColors.primaryTeal,
|
||||||
selectionColor: AppColors.primaryTeal.withOpacity(0.3),
|
selectionColor: DarkBrandColors.primaryTeal.withOpacity(0.3),
|
||||||
selectionHandleColor: AppColors.primaryTeal,
|
selectionHandleColor: DarkBrandColors.primaryTeal,
|
||||||
),
|
),
|
||||||
|
|
||||||
textTheme: const TextTheme(
|
textTheme: const TextTheme(
|
||||||
@@ -449,7 +471,7 @@ class AppTheme {
|
|||||||
// Bottom Navigation Bar Theme for Dark Mode
|
// Bottom Navigation Bar Theme for Dark Mode
|
||||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||||
backgroundColor: DarkColors.surface,
|
backgroundColor: DarkColors.surface,
|
||||||
selectedItemColor: AppColors.primaryTeal,
|
selectedItemColor: DarkBrandColors.primaryTeal,
|
||||||
unselectedItemColor: DarkColors.iconInactive,
|
unselectedItemColor: DarkColors.iconInactive,
|
||||||
type: BottomNavigationBarType.fixed,
|
type: BottomNavigationBarType.fixed,
|
||||||
elevation: 8,
|
elevation: 8,
|
||||||
@@ -465,7 +487,7 @@ class AppTheme {
|
|||||||
|
|
||||||
// Floating Action Button Theme for Dark Mode
|
// Floating Action Button Theme for Dark Mode
|
||||||
floatingActionButtonTheme: FloatingActionButtonThemeData(
|
floatingActionButtonTheme: FloatingActionButtonThemeData(
|
||||||
backgroundColor: AppColors.primaryTeal,
|
backgroundColor: DarkBrandColors.primaryTeal,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
@@ -483,7 +505,7 @@ class AppTheme {
|
|||||||
|
|
||||||
// Progress Indicator Theme for Dark Mode
|
// Progress Indicator Theme for Dark Mode
|
||||||
progressIndicatorTheme: const ProgressIndicatorThemeData(
|
progressIndicatorTheme: const ProgressIndicatorThemeData(
|
||||||
color: AppColors.primaryTeal,
|
color: DarkBrandColors.primaryTeal,
|
||||||
linearTrackColor: DarkColors.buttonSecondary,
|
linearTrackColor: DarkColors.buttonSecondary,
|
||||||
circularTrackColor: DarkColors.buttonSecondary,
|
circularTrackColor: DarkColors.buttonSecondary,
|
||||||
),
|
),
|
||||||
@@ -491,7 +513,7 @@ class AppTheme {
|
|||||||
// Chip Theme for Dark Mode
|
// Chip Theme for Dark Mode
|
||||||
chipTheme: ChipThemeData(
|
chipTheme: ChipThemeData(
|
||||||
backgroundColor: DarkColors.buttonSecondary,
|
backgroundColor: DarkColors.buttonSecondary,
|
||||||
selectedColor: AppColors.primaryTeal.withOpacity(0.1),
|
selectedColor: DarkBrandColors.primaryTeal.withOpacity(0.1),
|
||||||
disabledColor: DarkColors.buttonSecondary.withOpacity(0.5),
|
disabledColor: DarkColors.buttonSecondary.withOpacity(0.5),
|
||||||
labelStyle: const TextStyle(color: DarkColors.textPrimary),
|
labelStyle: const TextStyle(color: DarkColors.textPrimary),
|
||||||
secondaryLabelStyle: const TextStyle(color: DarkColors.textPrimary),
|
secondaryLabelStyle: const TextStyle(color: DarkColors.textPrimary),
|
||||||
|
|||||||
106
lib/core/theme/app_theme_extension.dart
Normal file
106
lib/core/theme/app_theme_extension.dart
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'app_colors.dart';
|
||||||
|
|
||||||
|
/// Theme extension for gradients and colors not covered by [ColorScheme].
|
||||||
|
@immutable
|
||||||
|
class AppThemeExtras extends ThemeExtension<AppThemeExtras> {
|
||||||
|
const AppThemeExtras({
|
||||||
|
required this.dashboardBackgroundGradient,
|
||||||
|
required this.dashboardGradientStops,
|
||||||
|
required this.heroProgressStart,
|
||||||
|
required this.heroProgressEnd,
|
||||||
|
required this.actionCardGradientStart,
|
||||||
|
required this.actionCardGradientEnd,
|
||||||
|
required this.authBackgroundGradient,
|
||||||
|
required this.dashboardHeaderTextColor,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<Color> dashboardBackgroundGradient;
|
||||||
|
final List<double> dashboardGradientStops;
|
||||||
|
final Color heroProgressStart;
|
||||||
|
final Color heroProgressEnd;
|
||||||
|
final Color actionCardGradientStart;
|
||||||
|
final Color actionCardGradientEnd;
|
||||||
|
final List<Color> authBackgroundGradient;
|
||||||
|
final Color dashboardHeaderTextColor;
|
||||||
|
|
||||||
|
static final AppThemeExtras light = AppThemeExtras(
|
||||||
|
dashboardBackgroundGradient: [
|
||||||
|
AppColors.primaryTeal,
|
||||||
|
AppColors.primaryTeal.withValues(alpha: 0.8),
|
||||||
|
AppColors.primaryOrange,
|
||||||
|
LightColors.background,
|
||||||
|
],
|
||||||
|
dashboardGradientStops: [0.0, 0.2, 0.6, 1.0],
|
||||||
|
heroProgressStart: Colors.white,
|
||||||
|
heroProgressEnd: Color(0xFFF8F9FA),
|
||||||
|
actionCardGradientStart: AppColors.primaryTeal,
|
||||||
|
actionCardGradientEnd: AppColors.gradientEnd,
|
||||||
|
authBackgroundGradient: [
|
||||||
|
const Color(0xFFD4E8E8),
|
||||||
|
const Color(0xFFE8D4C0),
|
||||||
|
const Color(0xFFD8E0E8),
|
||||||
|
],
|
||||||
|
dashboardHeaderTextColor: Colors.white,
|
||||||
|
);
|
||||||
|
|
||||||
|
static final AppThemeExtras dark = AppThemeExtras(
|
||||||
|
dashboardBackgroundGradient: [
|
||||||
|
DarkColors.surfaceVariant,
|
||||||
|
DarkColors.surface,
|
||||||
|
DarkColors.background,
|
||||||
|
DarkColors.background,
|
||||||
|
],
|
||||||
|
dashboardGradientStops: [0.0, 0.25, 0.55, 1.0],
|
||||||
|
heroProgressStart: Colors.white.withValues(alpha: 0.9),
|
||||||
|
heroProgressEnd: Colors.white.withValues(alpha: 0.55),
|
||||||
|
actionCardGradientStart: DarkBrandColors.gradientStart,
|
||||||
|
actionCardGradientEnd: DarkBrandColors.gradientEnd,
|
||||||
|
authBackgroundGradient: [
|
||||||
|
DarkColors.surfaceVariant,
|
||||||
|
DarkColors.surface,
|
||||||
|
DarkColors.background,
|
||||||
|
],
|
||||||
|
dashboardHeaderTextColor: DarkColors.textPrimary,
|
||||||
|
);
|
||||||
|
|
||||||
|
static AppThemeExtras of(BuildContext context) {
|
||||||
|
return Theme.of(context).extension<AppThemeExtras>() ?? light;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AppThemeExtras copyWith({
|
||||||
|
List<Color>? dashboardBackgroundGradient,
|
||||||
|
List<double>? dashboardGradientStops,
|
||||||
|
Color? heroProgressStart,
|
||||||
|
Color? heroProgressEnd,
|
||||||
|
Color? actionCardGradientStart,
|
||||||
|
Color? actionCardGradientEnd,
|
||||||
|
List<Color>? authBackgroundGradient,
|
||||||
|
Color? dashboardHeaderTextColor,
|
||||||
|
}) {
|
||||||
|
return AppThemeExtras(
|
||||||
|
dashboardBackgroundGradient:
|
||||||
|
dashboardBackgroundGradient ?? this.dashboardBackgroundGradient,
|
||||||
|
dashboardGradientStops:
|
||||||
|
dashboardGradientStops ?? this.dashboardGradientStops,
|
||||||
|
heroProgressStart: heroProgressStart ?? this.heroProgressStart,
|
||||||
|
heroProgressEnd: heroProgressEnd ?? this.heroProgressEnd,
|
||||||
|
actionCardGradientStart:
|
||||||
|
actionCardGradientStart ?? this.actionCardGradientStart,
|
||||||
|
actionCardGradientEnd:
|
||||||
|
actionCardGradientEnd ?? this.actionCardGradientEnd,
|
||||||
|
authBackgroundGradient:
|
||||||
|
authBackgroundGradient ?? this.authBackgroundGradient,
|
||||||
|
dashboardHeaderTextColor:
|
||||||
|
dashboardHeaderTextColor ?? this.dashboardHeaderTextColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AppThemeExtras lerp(ThemeExtension<AppThemeExtras>? other, double t) {
|
||||||
|
if (other is! AppThemeExtras) return this;
|
||||||
|
return t < 0.5 ? this : other;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,10 +53,12 @@ class _ChatInputState extends State<ChatInput> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withOpacity(0.95),
|
color: cs.surface.withOpacity(0.98),
|
||||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@@ -169,17 +171,19 @@ class _ChatInputState extends State<ChatInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildInputField(BuildContext context) {
|
Widget _buildInputField(BuildContext context) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [
|
colors: [
|
||||||
Colors.grey[100]!,
|
cs.surfaceContainerHighest,
|
||||||
Colors.grey[50]!,
|
cs.surface,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Colors.grey[300]!,
|
color: cs.outline.withOpacity(0.3),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -190,9 +194,9 @@ class _ChatInputState extends State<ChatInput> {
|
|||||||
controller: widget.controller,
|
controller: widget.controller,
|
||||||
focusNode: _focusNode,
|
focusNode: _focusNode,
|
||||||
maxLines: _isExpanded ? 5 : 1,
|
maxLines: _isExpanded ? 5 : 1,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: Color(0xFF2D3748),
|
color: cs.onSurface,
|
||||||
),
|
),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Faça sua pergunta sobre o conteúdo...',
|
hintText: 'Faça sua pergunta sobre o conteúdo...',
|
||||||
@@ -253,8 +257,11 @@ class _ChatInputState extends State<ChatInput> {
|
|||||||
height: 48,
|
height: 48,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: widget.controller.text.isNotEmpty
|
gradient: widget.controller.text.isNotEmpty
|
||||||
? const LinearGradient(
|
? LinearGradient(
|
||||||
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)],
|
colors: [
|
||||||
|
cs.primary,
|
||||||
|
cs.primary.withOpacity(0.85),
|
||||||
|
],
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import '../../../../core/services/rag_service.dart';
|
import '../../../../core/services/rag_service.dart';
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
|
|
||||||
/// Widget for displaying chat messages with source citations
|
/// Widget for displaying chat messages with source citations
|
||||||
class MessageBubble extends StatelessWidget {
|
class MessageBubble extends StatelessWidget {
|
||||||
@@ -73,22 +74,28 @@ class MessageBubble extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAvatar(BuildContext context) {
|
Widget _buildAvatar(BuildContext context) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
final extras = AppThemeExtras.of(context);
|
||||||
|
final accent = isUser ? cs.primary : cs.secondary;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: 36,
|
width: 36,
|
||||||
height: 36,
|
height: 36,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: isUser
|
gradient: isUser
|
||||||
? const LinearGradient(
|
? LinearGradient(
|
||||||
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)],
|
colors: [
|
||||||
|
extras.actionCardGradientStart,
|
||||||
|
extras.actionCardGradientEnd,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
: const LinearGradient(
|
: LinearGradient(
|
||||||
colors: [Color(0xFFF68D2D), Color(0xFFE67E22)],
|
colors: [cs.secondary, cs.secondary.withOpacity(0.85)],
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(18),
|
borderRadius: BorderRadius.circular(18),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: (isUser ? const Color(0xFF82C9BD) : const Color(0xFFF68D2D))
|
color: accent.withOpacity(0.3),
|
||||||
.withOpacity(0.3),
|
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
offset: const Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
@@ -103,6 +110,9 @@ class MessageBubble extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMessageBubble(BuildContext context) {
|
Widget _buildMessageBubble(BuildContext context) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
final extras = AppThemeExtras.of(context);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: MediaQuery.of(context).size.width * 0.75,
|
maxWidth: MediaQuery.of(context).size.width * 0.75,
|
||||||
@@ -110,15 +120,18 @@ class MessageBubble extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: isUser
|
gradient: isUser
|
||||||
? const LinearGradient(
|
? LinearGradient(
|
||||||
colors: [Color(0xFF82C9BD), Color(0xFF6BA5A0)],
|
colors: [
|
||||||
|
extras.actionCardGradientStart,
|
||||||
|
extras.actionCardGradientEnd,
|
||||||
|
],
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
)
|
)
|
||||||
: LinearGradient(
|
: LinearGradient(
|
||||||
colors: [
|
colors: [
|
||||||
Colors.white.withOpacity(0.95),
|
cs.surfaceContainerHighest,
|
||||||
Colors.white.withOpacity(0.9),
|
cs.surface,
|
||||||
],
|
],
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
@@ -154,24 +167,24 @@ class MessageBubble extends StatelessWidget {
|
|||||||
data: content,
|
data: content,
|
||||||
styleSheet: MarkdownStyleSheet(
|
styleSheet: MarkdownStyleSheet(
|
||||||
p: TextStyle(
|
p: TextStyle(
|
||||||
color: const Color(0xFF2D3748),
|
color: cs.onSurface,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
height: 1.4,
|
height: 1.4,
|
||||||
),
|
),
|
||||||
strong: TextStyle(
|
strong: TextStyle(
|
||||||
color: const Color(0xFF2D3748),
|
color: cs.onSurface,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
height: 1.4,
|
height: 1.4,
|
||||||
),
|
),
|
||||||
em: TextStyle(
|
em: TextStyle(
|
||||||
color: const Color(0xFF2D3748),
|
color: cs.onSurface,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic,
|
||||||
height: 1.4,
|
height: 1.4,
|
||||||
),
|
),
|
||||||
listBullet: TextStyle(
|
listBullet: TextStyle(
|
||||||
color: const Color(0xFF2D3748),
|
color: cs.onSurface,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
height: 1.4,
|
height: 1.4,
|
||||||
),
|
),
|
||||||
@@ -262,7 +275,7 @@ class MessageBubble extends StatelessWidget {
|
|||||||
Icon(
|
Icon(
|
||||||
Icons.menu_book,
|
Icons.menu_book,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: const Color(0xFF82C9BD),
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -280,7 +293,7 @@ class MessageBubble extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFF82C9BD).withOpacity(0.1),
|
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -288,7 +301,7 @@ class MessageBubble extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: const Color(0xFF82C9BD),
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import '../../../../l10n/app_localizations.dart';
|
import '../../../../l10n/app_localizations.dart';
|
||||||
import '../../../../core/services/auth_service.dart';
|
import '../../../../core/services/auth_service.dart';
|
||||||
import '../../../../core/services/session_service.dart';
|
import '../../../../core/services/session_service.dart';
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
import '../../../../shared/presentation/widgets/custom_notification.dart';
|
import '../../../../shared/presentation/widgets/custom_notification.dart';
|
||||||
|
|
||||||
class LoginPage extends StatefulWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
@@ -212,12 +213,16 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
colors: [
|
colors: Theme.of(context).brightness == Brightness.dark
|
||||||
Theme.of(context).colorScheme.background,
|
? AppThemeExtras.of(context).authBackgroundGradient
|
||||||
Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
: [
|
||||||
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
|
Theme.of(context).colorScheme.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,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../../core/theme/app_colors.dart';
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
import '../../../../l10n/app_localizations.dart';
|
import '../../../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
class RoleSelectionPage extends StatefulWidget {
|
class RoleSelectionPage extends StatefulWidget {
|
||||||
@@ -22,12 +22,14 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
colors: [
|
colors: Theme.of(context).brightness == Brightness.dark
|
||||||
Theme.of(context).colorScheme.background,
|
? AppThemeExtras.of(context).authBackgroundGradient
|
||||||
Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
: [
|
||||||
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
|
Theme.of(context).colorScheme.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,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@@ -51,10 +53,12 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
width: 100,
|
width: 100,
|
||||||
height: 100,
|
height: 100,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: const LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [
|
colors: [
|
||||||
AppColors.gradientStart,
|
AppThemeExtras.of(context)
|
||||||
AppColors.gradientEnd,
|
.actionCardGradientStart,
|
||||||
|
AppThemeExtras.of(context)
|
||||||
|
.actionCardGradientEnd,
|
||||||
],
|
],
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
@@ -62,9 +66,10 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
borderRadius: BorderRadius.circular(25),
|
borderRadius: BorderRadius.circular(25),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: AppColors.primaryBlue.withOpacity(
|
color: Theme.of(context)
|
||||||
0.3,
|
.colorScheme
|
||||||
),
|
.primary
|
||||||
|
.withOpacity(0.3),
|
||||||
blurRadius: 25,
|
blurRadius: 25,
|
||||||
offset: const Offset(0, 10),
|
offset: const Offset(0, 10),
|
||||||
),
|
),
|
||||||
@@ -113,15 +118,14 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
ShaderMask(
|
ShaderMask(
|
||||||
shaderCallback: (bounds) =>
|
shaderCallback: (bounds) => LinearGradient(
|
||||||
const LinearGradient(
|
colors: [
|
||||||
colors: [
|
Theme.of(context).colorScheme.primary,
|
||||||
AppColors.primaryTeal,
|
Theme.of(context).colorScheme.secondary,
|
||||||
AppColors.primaryOrange,
|
],
|
||||||
],
|
begin: Alignment.centerLeft,
|
||||||
begin: Alignment.centerLeft,
|
end: Alignment.centerRight,
|
||||||
end: Alignment.centerRight,
|
).createShader(bounds),
|
||||||
).createShader(bounds),
|
|
||||||
child: Text(
|
child: Text(
|
||||||
AppLocalizations.of(context)!.schoolName,
|
AppLocalizations.of(context)!.schoolName,
|
||||||
style: Theme.of(context).textTheme.bodyMedium
|
style: Theme.of(context).textTheme.bodyMedium
|
||||||
@@ -172,7 +176,9 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
Text(
|
Text(
|
||||||
'Selecione o seu papel para continuar',
|
'Selecione o seu papel para continuar',
|
||||||
style: Theme.of(context).textTheme.bodyLarge
|
style: Theme.of(context).textTheme.bodyLarge
|
||||||
?.copyWith(color: AppColors.primaryOrange),
|
?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.animate()
|
.animate()
|
||||||
.fadeIn(
|
.fadeIn(
|
||||||
@@ -196,7 +202,7 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
'Aluno',
|
'Aluno',
|
||||||
Icons.school_outlined,
|
Icons.school_outlined,
|
||||||
'student',
|
'student',
|
||||||
AppColors.gradientStart,
|
Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
@@ -206,7 +212,7 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
'Professor',
|
'Professor',
|
||||||
Icons.person_outline,
|
Icons.person_outline,
|
||||||
'teacher',
|
'teacher',
|
||||||
AppColors.gradientEnd,
|
Theme.of(context).colorScheme.secondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -233,12 +239,15 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
? _handleContinue
|
? _handleContinue
|
||||||
: null,
|
: null,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: AppColors.primaryBlue,
|
backgroundColor:
|
||||||
foregroundColor: Colors.white,
|
Theme.of(context).colorScheme.primary,
|
||||||
|
foregroundColor:
|
||||||
|
Theme.of(context).colorScheme.onPrimary,
|
||||||
elevation: 4,
|
elevation: 4,
|
||||||
shadowColor: AppColors.primaryBlue.withOpacity(
|
shadowColor: Theme.of(context)
|
||||||
0.3,
|
.colorScheme
|
||||||
),
|
.primary
|
||||||
|
.withOpacity(0.3),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
),
|
),
|
||||||
@@ -322,12 +331,14 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
color: isSelected ? null : Colors.white,
|
color: isSelected
|
||||||
|
? null
|
||||||
|
: Theme.of(context).colorScheme.surface,
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? gradientColor
|
? gradientColor
|
||||||
: AppColors.primaryBlue.withOpacity(0.2),
|
: Theme.of(context).colorScheme.primary.withOpacity(0.2),
|
||||||
width: isSelected ? 2 : 1,
|
width: isSelected ? 2 : 1,
|
||||||
),
|
),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
@@ -346,7 +357,9 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
Icon(
|
Icon(
|
||||||
icon,
|
icon,
|
||||||
size: 48,
|
size: 48,
|
||||||
color: isSelected ? Colors.white : AppColors.primaryBlue,
|
color: isSelected
|
||||||
|
? Colors.white
|
||||||
|
: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Text(
|
Text(
|
||||||
@@ -386,7 +399,7 @@ class _RoleSelectionPageState extends State<RoleSelectionPage> {
|
|||||||
width: 4,
|
width: 4,
|
||||||
height: 4,
|
height: 4,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.gradientStart.withOpacity(0.3),
|
color: Theme.of(context).colorScheme.primary.withOpacity(0.3),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter_animate/flutter_animate.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../../l10n/app_localizations.dart';
|
import '../../../../l10n/app_localizations.dart';
|
||||||
import '../../../../core/services/auth_service.dart';
|
import '../../../../core/services/auth_service.dart';
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
import '../../../../shared/presentation/widgets/custom_notification.dart';
|
import '../../../../shared/presentation/widgets/custom_notification.dart';
|
||||||
|
|
||||||
class SignupPage extends StatefulWidget {
|
class SignupPage extends StatefulWidget {
|
||||||
@@ -241,12 +242,16 @@ class _SignupPageState extends State<SignupPage> {
|
|||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
colors: [
|
colors: Theme.of(context).brightness == Brightness.dark
|
||||||
Theme.of(context).colorScheme.background,
|
? AppThemeExtras.of(context).authBackgroundGradient
|
||||||
Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
: [
|
||||||
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
|
Theme.of(context).colorScheme.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,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
@@ -364,7 +369,7 @@ class _SignupPageState extends State<SignupPage> {
|
|||||||
).colorScheme.onSurface,
|
).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Nome Completo',
|
labelText: 'Primeiro Nome',
|
||||||
labelStyle: TextStyle(
|
labelStyle: TextStyle(
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../../core/services/auth_service.dart';
|
import '../../../../core/services/auth_service.dart';
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
import '../widgets/progress_hero_widget.dart';
|
import '../widgets/progress_hero_widget.dart';
|
||||||
import '../widgets/quick_access_widget.dart';
|
import '../widgets/quick_access_widget.dart';
|
||||||
import '../widgets/student_classes_list_widget.dart';
|
import '../widgets/student_classes_list_widget.dart';
|
||||||
@@ -83,19 +84,17 @@ class _StudentDashboardPageState extends State<StudentDashboardPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final themeExtras = AppThemeExtras.of(context);
|
||||||
|
final headerColor = themeExtras.dashboardHeaderTextColor;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Container(
|
body: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
end: Alignment.bottomCenter,
|
end: Alignment.bottomCenter,
|
||||||
colors: [
|
colors: themeExtras.dashboardBackgroundGradient,
|
||||||
Theme.of(context).colorScheme.primary,
|
stops: themeExtras.dashboardGradientStops,
|
||||||
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],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
@@ -114,17 +113,17 @@ class _StudentDashboardPageState extends State<StudentDashboardPage> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Bem-vindo, $_userName!',
|
'Bem-vindo, $_userName!',
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: headerColor,
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
const Text(
|
Text(
|
||||||
'Seu progresso de estudos',
|
'Seu progresso de estudos',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: headerColor,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w300,
|
fontWeight: FontWeight.w300,
|
||||||
),
|
),
|
||||||
@@ -133,7 +132,7 @@ class _StudentDashboardPageState extends State<StudentDashboardPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.logout, color: Colors.white),
|
icon: Icon(Icons.logout, color: headerColor),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await AuthService.signOut();
|
await AuthService.signOut();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../../core/services/auth_service.dart';
|
import '../../../../core/services/auth_service.dart';
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
import '../widgets/teacher_hero_widget.dart';
|
import '../widgets/teacher_hero_widget.dart';
|
||||||
import '../widgets/teacher_quick_actions_widget.dart';
|
import '../widgets/teacher_quick_actions_widget.dart';
|
||||||
import '../widgets/teacher_classes_list_widget.dart';
|
import '../widgets/teacher_classes_list_widget.dart';
|
||||||
@@ -79,19 +80,17 @@ class _TeacherDashboardPageState extends State<TeacherDashboardPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final themeExtras = AppThemeExtras.of(context);
|
||||||
|
final headerColor = themeExtras.dashboardHeaderTextColor;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Container(
|
body: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
end: Alignment.bottomCenter,
|
end: Alignment.bottomCenter,
|
||||||
colors: [
|
colors: themeExtras.dashboardBackgroundGradient,
|
||||||
Theme.of(context).colorScheme.primary,
|
stops: themeExtras.dashboardGradientStops,
|
||||||
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],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
@@ -110,17 +109,17 @@ class _TeacherDashboardPageState extends State<TeacherDashboardPage> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Bem-vindo, $_userName!',
|
'Bem-vindo, $_userName!',
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: headerColor,
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
const Text(
|
Text(
|
||||||
'Painel de Gestão de Conteúdo',
|
'Painel de Gestão de Conteúdo',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: headerColor,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w300,
|
fontWeight: FontWeight.w300,
|
||||||
),
|
),
|
||||||
@@ -129,7 +128,7 @@ class _TeacherDashboardPageState extends State<TeacherDashboardPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.logout, color: Colors.white),
|
icon: Icon(Icons.logout, color: headerColor),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await AuthService.signOut();
|
await AuthService.signOut();
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|||||||
@@ -0,0 +1,308 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
|
|
||||||
|
/// Layout variant for dashboard quick-action cards.
|
||||||
|
enum DashboardActionCardLayout { vertical, horizontal }
|
||||||
|
|
||||||
|
/// Reusable action card with flexible height and wrapping subtitles.
|
||||||
|
class DashboardActionCard extends StatelessWidget {
|
||||||
|
const DashboardActionCard({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.subtitle,
|
||||||
|
required this.icon,
|
||||||
|
required this.onTap,
|
||||||
|
this.layout = DashboardActionCardLayout.vertical,
|
||||||
|
this.minHeight = 150,
|
||||||
|
this.useGradient = false,
|
||||||
|
this.badge,
|
||||||
|
this.iconSize = 24,
|
||||||
|
this.iconPadding = 10,
|
||||||
|
this.titleFontSize,
|
||||||
|
this.subtitleFontSize,
|
||||||
|
this.padding,
|
||||||
|
this.leadingIcon,
|
||||||
|
this.onTapDisabled,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String subtitle;
|
||||||
|
final IconData icon;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
final bool? onTapDisabled;
|
||||||
|
final DashboardActionCardLayout layout;
|
||||||
|
final double minHeight;
|
||||||
|
final bool useGradient;
|
||||||
|
final String? badge;
|
||||||
|
final double iconSize;
|
||||||
|
final double iconPadding;
|
||||||
|
final double? titleFontSize;
|
||||||
|
final double? subtitleFontSize;
|
||||||
|
final EdgeInsetsGeometry? padding;
|
||||||
|
final Widget? leadingIcon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
final extras = AppThemeExtras.of(context);
|
||||||
|
final isHorizontal = layout == DashboardActionCardLayout.horizontal;
|
||||||
|
final effectivePadding =
|
||||||
|
padding ?? EdgeInsets.all(isHorizontal ? 16 : (useGradient ? 20 : 14));
|
||||||
|
|
||||||
|
final decoration = BoxDecoration(
|
||||||
|
gradient: useGradient
|
||||||
|
? LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [
|
||||||
|
extras.actionCardGradientStart,
|
||||||
|
extras.actionCardGradientEnd,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
color: useGradient ? null : cs.surface,
|
||||||
|
borderRadius: BorderRadius.circular(minHeight <= 110 ? 12 : 16),
|
||||||
|
border: useGradient
|
||||||
|
? null
|
||||||
|
: Border.all(color: cs.outline.withOpacity(0.2), width: 1),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: (useGradient ? cs.primary : cs.shadow).withOpacity(
|
||||||
|
useGradient ? 0.3 : 0.05,
|
||||||
|
),
|
||||||
|
blurRadius: useGradient ? 15 : 10,
|
||||||
|
offset: Offset(0, useGradient ? 8 : 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
final titleColor = useGradient ? Colors.white : cs.onSurface;
|
||||||
|
final subtitleColor =
|
||||||
|
useGradient ? Colors.white : cs.onSurfaceVariant;
|
||||||
|
final iconBgColor = useGradient
|
||||||
|
? Colors.white.withOpacity(0.2)
|
||||||
|
: cs.primary.withOpacity(0.1);
|
||||||
|
final iconColor = useGradient ? Colors.white : cs.primary;
|
||||||
|
|
||||||
|
final effectiveMinHeight = minHeight > 0 ? minHeight : null;
|
||||||
|
|
||||||
|
return ConstrainedBox(
|
||||||
|
constraints: effectiveMinHeight != null
|
||||||
|
? BoxConstraints(minHeight: effectiveMinHeight)
|
||||||
|
: const BoxConstraints(),
|
||||||
|
child: Container(
|
||||||
|
decoration: decoration,
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(minHeight <= 110 ? 12 : 16),
|
||||||
|
onTap: onTapDisabled == true ? null : onTap,
|
||||||
|
child: Padding(
|
||||||
|
padding: effectivePadding,
|
||||||
|
child: isHorizontal
|
||||||
|
? _buildHorizontalContent(
|
||||||
|
context,
|
||||||
|
titleColor,
|
||||||
|
subtitleColor,
|
||||||
|
iconBgColor,
|
||||||
|
iconColor,
|
||||||
|
)
|
||||||
|
: _buildVerticalContent(
|
||||||
|
context,
|
||||||
|
titleColor,
|
||||||
|
subtitleColor,
|
||||||
|
iconBgColor,
|
||||||
|
iconColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHorizontalContent(
|
||||||
|
BuildContext context,
|
||||||
|
Color titleColor,
|
||||||
|
Color subtitleColor,
|
||||||
|
Color iconBgColor,
|
||||||
|
Color iconColor,
|
||||||
|
) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
_iconBox(iconBgColor, iconColor),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: _titleSubtitleColumn(
|
||||||
|
titleColor,
|
||||||
|
subtitleColor,
|
||||||
|
titleSize: titleFontSize ?? 16,
|
||||||
|
subtitleSize: subtitleFontSize ?? 13,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(Icons.arrow_forward_ios, color: cs.primary, size: 16),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildVerticalContent(
|
||||||
|
BuildContext context,
|
||||||
|
Color titleColor,
|
||||||
|
Color subtitleColor,
|
||||||
|
Color iconBgColor,
|
||||||
|
Color iconColor,
|
||||||
|
) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
final isCompact = minHeight <= 130;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
leadingIcon ?? _iconBox(iconBgColor, iconColor),
|
||||||
|
if (badge != null) ...[
|
||||||
|
const Spacer(),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: isCompact ? 6 : 10,
|
||||||
|
vertical: isCompact ? 3 : 4,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: cs.secondary,
|
||||||
|
borderRadius: BorderRadius.circular(isCompact ? 10 : 12),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
badge!,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: isCompact ? 9 : 10,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: isCompact ? 8 : 12),
|
||||||
|
_titleSubtitleColumn(
|
||||||
|
titleColor,
|
||||||
|
subtitleColor,
|
||||||
|
titleSize: titleFontSize ?? (useGradient ? 18 : 16),
|
||||||
|
subtitleSize: subtitleFontSize ?? (isCompact ? 11 : 12),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _iconBox(Color bgColor, Color iconColor) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(iconPadding),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: bgColor,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Icon(icon, color: iconColor, size: iconSize),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _titleSubtitleColumn(
|
||||||
|
Color titleColor,
|
||||||
|
Color subtitleColor, {
|
||||||
|
required double titleSize,
|
||||||
|
required double subtitleSize,
|
||||||
|
}) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
color: titleColor,
|
||||||
|
fontSize: titleSize,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
height: 1.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (subtitle.isNotEmpty) ...[
|
||||||
|
SizedBox(height: subtitleSize >= 13 ? 4 : 2),
|
||||||
|
Text(
|
||||||
|
subtitle,
|
||||||
|
style: TextStyle(
|
||||||
|
color: subtitleColor,
|
||||||
|
fontSize: subtitleSize,
|
||||||
|
height: 1.25,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Surface-styled vertical card (Quiz, Criar Turma, etc.).
|
||||||
|
class DashboardActionCardSurface extends StatelessWidget {
|
||||||
|
const DashboardActionCardSurface({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.subtitle,
|
||||||
|
required this.icon,
|
||||||
|
required this.onTap,
|
||||||
|
this.minHeight = 150,
|
||||||
|
this.iconColor,
|
||||||
|
this.leadingWidget,
|
||||||
|
this.onTapDisabled,
|
||||||
|
this.titleFontSize = 14,
|
||||||
|
this.subtitleFontSize = 11,
|
||||||
|
this.iconSize = 20,
|
||||||
|
this.padding = const EdgeInsets.all(12),
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String subtitle;
|
||||||
|
final IconData icon;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
final bool? onTapDisabled;
|
||||||
|
final double minHeight;
|
||||||
|
final Color? iconColor;
|
||||||
|
final Widget? leadingWidget;
|
||||||
|
final double titleFontSize;
|
||||||
|
final double subtitleFontSize;
|
||||||
|
final double iconSize;
|
||||||
|
final EdgeInsetsGeometry padding;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
|
final effectiveIconColor = iconColor ?? cs.secondary;
|
||||||
|
|
||||||
|
return DashboardActionCard(
|
||||||
|
title: title,
|
||||||
|
subtitle: subtitle,
|
||||||
|
icon: icon,
|
||||||
|
onTap: onTap,
|
||||||
|
onTapDisabled: onTapDisabled,
|
||||||
|
minHeight: minHeight,
|
||||||
|
useGradient: false,
|
||||||
|
iconSize: iconSize,
|
||||||
|
leadingIcon: leadingWidget ??
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: effectiveIconColor.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Icon(icon, color: effectiveIconColor, size: iconSize),
|
||||||
|
),
|
||||||
|
titleFontSize: titleFontSize,
|
||||||
|
subtitleFontSize: subtitleFontSize,
|
||||||
|
padding: padding,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import '../../../../core/services/auth_service.dart';
|
import '../../../../core/services/auth_service.dart';
|
||||||
import '../../../../core/routing/app_router.dart';
|
import '../../../../core/routing/app_router.dart';
|
||||||
@@ -42,10 +44,8 @@ class ProfileSectionWidget extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [
|
colors: [
|
||||||
Theme.of(context).colorScheme.primary,
|
AppThemeExtras.of(context).actionCardGradientStart,
|
||||||
Theme.of(
|
AppThemeExtras.of(context).actionCardGradientEnd,
|
||||||
context,
|
|
||||||
).colorScheme.primary.withOpacity(0.8),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(24),
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
|
|
||||||
/// Progress tracking hero section for student dashboard
|
/// Progress tracking hero section for student dashboard
|
||||||
class ProgressHeroWidget extends StatelessWidget {
|
class ProgressHeroWidget extends StatelessWidget {
|
||||||
final String userName;
|
final String userName;
|
||||||
@@ -148,8 +150,11 @@ class ProgressHeroWidget extends StatelessWidget {
|
|||||||
widthFactor: overallProgress,
|
widthFactor: overallProgress,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: const LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [Colors.white, Color(0xFFF8F9FA)],
|
colors: [
|
||||||
|
AppThemeExtras.of(context).heroProgressStart,
|
||||||
|
AppThemeExtras.of(context).heroProgressEnd,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../classes/presentation/pages/join_class_page.dart';
|
import '../../../classes/presentation/pages/join_class_page.dart';
|
||||||
|
import 'dashboard_action_card.dart';
|
||||||
|
|
||||||
/// Quick access cards for Tutor IA and Quiz with fixed overflow
|
/// Quick access cards for Tutor IA and Quiz
|
||||||
class QuickAccessWidget extends StatelessWidget {
|
class QuickAccessWidget extends StatelessWidget {
|
||||||
const QuickAccessWidget({super.key});
|
const QuickAccessWidget({super.key});
|
||||||
|
|
||||||
@@ -21,18 +22,23 @@ class QuickAccessWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
IntrinsicHeight(
|
||||||
Row(
|
child: Row(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
// Tutor IA Card (Primary)
|
children: [
|
||||||
Expanded(flex: 3, child: _buildTutorIACard(context)),
|
Expanded(
|
||||||
const SizedBox(width: 16),
|
flex: 3,
|
||||||
// Quiz Card (Secondary)
|
child: _buildTutorIACard(context),
|
||||||
Expanded(flex: 2, child: _buildQuizCard(context)),
|
),
|
||||||
],
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: _buildQuizCard(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
// Join Class Card
|
|
||||||
_buildJoinClassCard(context),
|
_buildJoinClassCard(context),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -45,110 +51,13 @@ class QuickAccessWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTutorIACard(BuildContext context) {
|
Widget _buildTutorIACard(BuildContext context) {
|
||||||
return Container(
|
return DashboardActionCard(
|
||||||
height: 150,
|
title: 'Tutor IA',
|
||||||
decoration: BoxDecoration(
|
subtitle: 'Assistente de estudos',
|
||||||
gradient: LinearGradient(
|
icon: Icons.psychology,
|
||||||
begin: Alignment.topLeft,
|
useGradient: true,
|
||||||
end: Alignment.bottomRight,
|
minHeight: 150,
|
||||||
colors: [
|
onTap: () => context.go('/ai-tutor'),
|
||||||
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),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
onTap: () {
|
|
||||||
print('DEBUG: AI Tutor card clicked!');
|
|
||||||
try {
|
|
||||||
context.go('/ai-tutor');
|
|
||||||
print('DEBUG: Navigation to AI Tutor successful');
|
|
||||||
} catch (e) {
|
|
||||||
print('DEBUG: Navigation error: $e');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(7),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.2),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.psychology,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 10,
|
|
||||||
vertical: 4,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'NOVO',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
const Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Tutor IA',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
//SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'Assistente de estudos',
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 12,
|
|
||||||
height: 1.2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.animate()
|
.animate()
|
||||||
.scale(
|
.scale(
|
||||||
@@ -159,80 +68,14 @@ class QuickAccessWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildQuizCard(BuildContext context) {
|
Widget _buildQuizCard(BuildContext context) {
|
||||||
return Container(
|
final cs = Theme.of(context).colorScheme;
|
||||||
height: 150,
|
return DashboardActionCardSurface(
|
||||||
decoration: BoxDecoration(
|
title: 'Quiz',
|
||||||
color: Theme.of(context).colorScheme.surface,
|
subtitle: 'Testa os teus conhecimentos',
|
||||||
borderRadius: BorderRadius.circular(16),
|
icon: Icons.quiz,
|
||||||
border: Border.all(
|
minHeight: 150,
|
||||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
|
iconColor: cs.secondary,
|
||||||
width: 1,
|
onTap: () => context.go('/quiz'),
|
||||||
),
|
|
||||||
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('/quiz'),
|
|
||||||
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: 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: 12),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Quiz',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'Testa os teus conhecimentos',
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onSurfaceVariant,
|
|
||||||
fontSize: 12,
|
|
||||||
height: 1.2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.animate()
|
.animate()
|
||||||
.scale(
|
.scale(
|
||||||
@@ -243,90 +86,18 @@ class QuickAccessWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildJoinClassCard(BuildContext context) {
|
Widget _buildJoinClassCard(BuildContext context) {
|
||||||
return Container(
|
return DashboardActionCard(
|
||||||
height: 86,
|
title: 'Entrar numa Turma',
|
||||||
decoration: BoxDecoration(
|
subtitle: 'Junta-te a uma turma com o código',
|
||||||
color: Theme.of(context).colorScheme.surface,
|
icon: Icons.group_add,
|
||||||
borderRadius: BorderRadius.circular(16),
|
layout: DashboardActionCardLayout.horizontal,
|
||||||
border: Border.all(
|
minHeight: 0,
|
||||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
|
onTap: () {
|
||||||
width: 1,
|
Navigator.push(
|
||||||
),
|
context,
|
||||||
boxShadow: [
|
MaterialPageRoute(builder: (_) => const JoinClassPage()),
|
||||||
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: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(builder: (_) => const JoinClassPage()),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.primary.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
Icons.group_add,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Entrar numa Turma',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'Junta-te a uma turma com o código',
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onSurfaceVariant,
|
|
||||||
fontSize: 13,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.animate()
|
.animate()
|
||||||
.scale(
|
.scale(
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class StudentClassesListWidget extends StatelessWidget {
|
|||||||
if (!snapshot.hasData || !snapshot.data!.exists) {
|
if (!snapshot.hasData || !snapshot.data!.exists) {
|
||||||
return Container(
|
return Container(
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 150,
|
constraints: const BoxConstraints(minHeight: 150),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
@@ -135,7 +135,7 @@ class StudentClassesListWidget extends StatelessWidget {
|
|||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 150,
|
constraints: const BoxConstraints(minHeight: 150),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
@@ -172,7 +172,7 @@ class StudentClassesListWidget extends StatelessWidget {
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import '../../../../core/services/auth_service.dart';
|
import '../../../../core/services/auth_service.dart';
|
||||||
|
|
||||||
@@ -41,10 +43,8 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [
|
colors: [
|
||||||
Theme.of(context).colorScheme.primary,
|
AppThemeExtras.of(context).actionCardGradientStart,
|
||||||
Theme.of(
|
AppThemeExtras.of(context).actionCardGradientEnd,
|
||||||
context,
|
|
||||||
).colorScheme.primary.withOpacity(0.8),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(24),
|
||||||
@@ -288,8 +288,7 @@ class TeacherAnalyticsPreviewWidget extends StatelessWidget {
|
|||||||
label,
|
label,
|
||||||
style: TextStyle(color: color.withOpacity(0.8), fontSize: 10),
|
style: TextStyle(color: color.withOpacity(0.8), fontSize: 10),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
maxLines: 2,
|
maxLines: 3,
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ class TeacherClassesListWidget extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 150,
|
constraints: const BoxConstraints(minHeight: 150),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
@@ -149,7 +149,7 @@ class TeacherClassesListWidget extends StatelessWidget {
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
|
|
||||||
/// Hero section for teacher dashboard showing class overview
|
/// Hero section for teacher dashboard showing class overview
|
||||||
class TeacherHeroWidget extends StatelessWidget {
|
class TeacherHeroWidget extends StatelessWidget {
|
||||||
final String userName;
|
final String userName;
|
||||||
@@ -34,8 +36,6 @@ class TeacherHeroWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Visão Geral da Turma',
|
'Visão Geral da Turma',
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
@@ -45,8 +45,6 @@ class TeacherHeroWidget extends StatelessWidget {
|
|||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'Acompanhe o progresso dos seus alunos',
|
'Acompanhe o progresso dos seus alunos',
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
@@ -148,8 +146,11 @@ class TeacherHeroWidget extends StatelessWidget {
|
|||||||
widthFactor: classAverageProgress,
|
widthFactor: classAverageProgress,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: const LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [Colors.white, Color(0xFFF8F9FA)],
|
colors: [
|
||||||
|
AppThemeExtras.of(context).heroProgressStart,
|
||||||
|
AppThemeExtras.of(context).heroProgressEnd,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
|
|
||||||
import '../../../../core/services/auth_service.dart';
|
import '../../../../core/services/auth_service.dart';
|
||||||
import '../../../../features/materials/presentation/pages/teacher_materials_page.dart';
|
import '../../../../features/materials/presentation/pages/teacher_materials_page.dart';
|
||||||
|
import 'dashboard_action_card.dart';
|
||||||
|
|
||||||
/// Quick access cards for teacher actions
|
/// Quick access cards for teacher actions
|
||||||
class TeacherQuickActionsWidget extends StatefulWidget {
|
class TeacherQuickActionsWidget extends StatefulWidget {
|
||||||
@@ -20,8 +21,25 @@ class TeacherQuickActionsWidget extends StatefulWidget {
|
|||||||
class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
|
class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
|
||||||
bool _isCreatingClass = false;
|
bool _isCreatingClass = false;
|
||||||
|
|
||||||
|
/// Mesmas dimensões dos cards em "As Minhas Turmas".
|
||||||
|
static const double _scrollCardWidth = 200;
|
||||||
|
static const double _scrollRowHeight = 150;
|
||||||
|
static const double _cardMinHeight = 150;
|
||||||
|
static const EdgeInsets _cardPadding = EdgeInsets.all(16);
|
||||||
|
static const double _titleFontSize = 16;
|
||||||
|
static const double _subtitleFontSize = 13;
|
||||||
|
static const double _iconSize = 24;
|
||||||
|
static const double _iconPadding = 10;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final cards = [
|
||||||
|
_buildUploadContentCard(context),
|
||||||
|
_buildCreateClassCard(context),
|
||||||
|
_buildCreateQuizCard(context),
|
||||||
|
_buildViewAnalyticsCard(context),
|
||||||
|
];
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -33,29 +51,20 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
|
|||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 12),
|
||||||
|
SizedBox(
|
||||||
// Primary Actions Row
|
height: _scrollRowHeight,
|
||||||
Row(
|
child: ListView.separated(
|
||||||
children: [
|
scrollDirection: Axis.horizontal,
|
||||||
// Upload Content Card (Primary)
|
clipBehavior: Clip.none,
|
||||||
Expanded(flex: 2, child: _buildUploadContentCard(context)),
|
padding: const EdgeInsets.only(right: 16),
|
||||||
const SizedBox(width: 16),
|
itemCount: cards.length,
|
||||||
// Create Class Card
|
separatorBuilder: (_, __) => const SizedBox(width: 12),
|
||||||
Expanded(flex: 2, child: _buildCreateClassCard(context)),
|
itemBuilder: (context, index) => SizedBox(
|
||||||
const SizedBox(width: 16),
|
width: _scrollCardWidth,
|
||||||
// Create Quiz Card (Secondary)
|
child: cards[index],
|
||||||
Expanded(flex: 2, child: _buildCreateQuizCard(context)),
|
),
|
||||||
],
|
),
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Secondary Actions Row
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
// View Analytics Card
|
|
||||||
Expanded(child: _buildViewAnalyticsCard(context)),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -70,284 +79,77 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
|
|||||||
Widget _buildUploadContentCard(BuildContext context) {
|
Widget _buildUploadContentCard(BuildContext context) {
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child:
|
child: DashboardActionCard(
|
||||||
Container(
|
title: 'Upload Conteúdo',
|
||||||
height: 150,
|
subtitle: 'PDFs, textos, imagens',
|
||||||
decoration: BoxDecoration(
|
icon: Icons.upload_file,
|
||||||
gradient: LinearGradient(
|
useGradient: true,
|
||||||
begin: Alignment.topLeft,
|
minHeight: _cardMinHeight,
|
||||||
end: Alignment.bottomRight,
|
iconSize: _iconSize,
|
||||||
colors: [
|
iconPadding: _iconPadding,
|
||||||
Theme.of(context).colorScheme.primary,
|
titleFontSize: _titleFontSize,
|
||||||
Theme.of(context).colorScheme.primary.withOpacity(0.8),
|
subtitleFontSize: _subtitleFontSize,
|
||||||
],
|
padding: _cardPadding,
|
||||||
),
|
onTap: () => Navigator.push(
|
||||||
borderRadius: BorderRadius.circular(16),
|
context,
|
||||||
boxShadow: [
|
MaterialPageRoute(
|
||||||
BoxShadow(
|
builder: (_) => const TeacherMaterialsPage(),
|
||||||
color: Theme.of(
|
),
|
||||||
context,
|
),
|
||||||
).colorScheme.primary.withOpacity(0.3),
|
)
|
||||||
blurRadius: 15,
|
.animate()
|
||||||
offset: const Offset(0, 8),
|
.scale(
|
||||||
),
|
duration: const Duration(milliseconds: 600),
|
||||||
],
|
curve: Curves.elasticOut,
|
||||||
),
|
)
|
||||||
child: Material(
|
.then(delay: const Duration(milliseconds: 100)),
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
onTap: () => Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (_) => const TeacherMaterialsPage(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
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: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 6,
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: const [
|
|
||||||
Text(
|
|
||||||
'Upload Conteúdo',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 2),
|
|
||||||
Text(
|
|
||||||
'PDFs, textos, imagens',
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 11,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.animate()
|
|
||||||
.scale(
|
|
||||||
duration: const Duration(milliseconds: 600),
|
|
||||||
curve: Curves.elasticOut,
|
|
||||||
)
|
|
||||||
.then(delay: const Duration(milliseconds: 100)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCreateQuizCard(BuildContext context) {
|
Widget _buildCreateQuizCard(BuildContext context) {
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child:
|
child: DashboardActionCardSurface(
|
||||||
Container(
|
title: 'Criar Quiz',
|
||||||
height: 150,
|
subtitle: 'Avaliações interativas',
|
||||||
decoration: BoxDecoration(
|
icon: Icons.quiz,
|
||||||
color: Theme.of(context).colorScheme.surface,
|
minHeight: _cardMinHeight,
|
||||||
borderRadius: BorderRadius.circular(16),
|
titleFontSize: _titleFontSize,
|
||||||
border: Border.all(
|
subtitleFontSize: _subtitleFontSize,
|
||||||
color: Theme.of(
|
iconSize: _iconSize,
|
||||||
context,
|
padding: _cardPadding,
|
||||||
).colorScheme.outline.withOpacity(0.2),
|
onTap: () => context.go('/teacher/quiz/create'),
|
||||||
width: 1,
|
)
|
||||||
),
|
.animate()
|
||||||
boxShadow: [
|
.scale(
|
||||||
BoxShadow(
|
duration: const Duration(milliseconds: 600),
|
||||||
color: Theme.of(
|
curve: Curves.elasticOut,
|
||||||
context,
|
)
|
||||||
).colorScheme.shadow.withOpacity(0.05),
|
.then(delay: const Duration(milliseconds: 200)),
|
||||||
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(12),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
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: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Criar Quiz',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onSurface,
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
Text(
|
|
||||||
'Avaliações interativas',
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
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: 200)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildViewAnalyticsCard(BuildContext context) {
|
Widget _buildViewAnalyticsCard(BuildContext context) {
|
||||||
return Container(
|
final cs = Theme.of(context).colorScheme;
|
||||||
height: 120,
|
return DashboardActionCardSurface(
|
||||||
decoration: BoxDecoration(
|
title: 'Analytics',
|
||||||
color: Theme.of(context).colorScheme.surface,
|
subtitle: 'Desempenho da turma',
|
||||||
borderRadius: BorderRadius.circular(16),
|
icon: Icons.analytics,
|
||||||
border: Border.all(
|
minHeight: _cardMinHeight,
|
||||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
|
titleFontSize: _titleFontSize,
|
||||||
width: 1,
|
subtitleFontSize: _subtitleFontSize,
|
||||||
),
|
iconSize: _iconSize,
|
||||||
boxShadow: [
|
padding: _cardPadding,
|
||||||
BoxShadow(
|
iconColor: cs.primary,
|
||||||
color: Theme.of(context).colorScheme.shadow.withOpacity(0.05),
|
leadingWidget: Container(
|
||||||
blurRadius: 10,
|
padding: const EdgeInsets.all(_iconPadding),
|
||||||
offset: const Offset(0, 4),
|
decoration: BoxDecoration(
|
||||||
),
|
color: cs.primary.withOpacity(0.1),
|
||||||
],
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
|
||||||
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: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.primary.withOpacity(0.8),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
Icons.analytics,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
child: Icon(Icons.analytics, color: cs.primary, size: _iconSize),
|
||||||
),
|
),
|
||||||
|
onTap: () => context.go('/teacher/analytics'),
|
||||||
)
|
)
|
||||||
.animate()
|
.animate()
|
||||||
.scale(
|
.scale(
|
||||||
@@ -358,110 +160,45 @@ class _TeacherQuickActionsWidgetState extends State<TeacherQuickActionsWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCreateClassCard(BuildContext context) {
|
Widget _buildCreateClassCard(BuildContext context) {
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
return ClipRRect(
|
return ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
child:
|
child: DashboardActionCardSurface(
|
||||||
Container(
|
title: 'Criar Turma',
|
||||||
height: 150,
|
subtitle: 'Gerar código de acesso',
|
||||||
decoration: BoxDecoration(
|
icon: Icons.school,
|
||||||
color: Theme.of(context).colorScheme.surface,
|
minHeight: _cardMinHeight,
|
||||||
borderRadius: BorderRadius.circular(16),
|
titleFontSize: _titleFontSize,
|
||||||
border: Border.all(
|
subtitleFontSize: _subtitleFontSize,
|
||||||
color: Theme.of(
|
iconSize: _iconSize,
|
||||||
context,
|
padding: _cardPadding,
|
||||||
).colorScheme.outline.withOpacity(0.2),
|
iconColor: cs.primary,
|
||||||
width: 1,
|
onTapDisabled: _isCreatingClass,
|
||||||
),
|
onTap: () => _showCreateClassDialog(context),
|
||||||
boxShadow: [
|
leadingWidget: Container(
|
||||||
BoxShadow(
|
padding: const EdgeInsets.all(_iconPadding),
|
||||||
color: Theme.of(
|
decoration: BoxDecoration(
|
||||||
context,
|
color: cs.primary.withOpacity(0.1),
|
||||||
).colorScheme.shadow.withOpacity(0.05),
|
borderRadius: BorderRadius.circular(10),
|
||||||
blurRadius: 10,
|
),
|
||||||
offset: const Offset(0, 4),
|
child: _isCreatingClass
|
||||||
),
|
? SizedBox(
|
||||||
],
|
width: _iconSize,
|
||||||
),
|
height: _iconSize,
|
||||||
child: Material(
|
child: CircularProgressIndicator(
|
||||||
color: Colors.transparent,
|
strokeWidth: 2,
|
||||||
child: InkWell(
|
color: cs.primary,
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
onTap: _isCreatingClass
|
|
||||||
? null
|
|
||||||
: () => _showCreateClassDialog(context),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.primary.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: _isCreatingClass
|
|
||||||
? SizedBox(
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
strokeWidth: 2,
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.primary,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Icon(
|
|
||||||
Icons.school,
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.primary,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Criar Turma',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onSurface,
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
Text(
|
|
||||||
'Gerar código de acesso',
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(
|
|
||||||
context,
|
|
||||||
).colorScheme.onSurfaceVariant,
|
|
||||||
fontSize: 11,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
),
|
: Icon(Icons.school, color: cs.primary, size: _iconSize),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.animate()
|
.animate()
|
||||||
.scale(
|
.scale(
|
||||||
duration: const Duration(milliseconds: 600),
|
duration: const Duration(milliseconds: 600),
|
||||||
curve: Curves.elasticOut,
|
curve: Curves.elasticOut,
|
||||||
)
|
)
|
||||||
.then(delay: const Duration(milliseconds: 150)),
|
.then(delay: const Duration(milliseconds: 150)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
|||||||
Text(
|
Text(
|
||||||
'Erro ao carregar materiais:\n${snapshot.error}',
|
'Erro ao carregar materiais:\n${snapshot.error}',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF2D3748),
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -134,28 +134,28 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
|||||||
final materials = snapshot.data?.docs ?? [];
|
final materials = snapshot.data?.docs ?? [];
|
||||||
|
|
||||||
if (materials.isEmpty) {
|
if (materials.isEmpty) {
|
||||||
return const Center(
|
return Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.folder_open,
|
Icons.folder_open,
|
||||||
color: Color(0xFF718096),
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
size: 64,
|
size: 64,
|
||||||
),
|
),
|
||||||
SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
'Nenhum material enviado ainda.',
|
'Nenhum material enviado ainda.',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF718096),
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
'Os materiais enviados aparecerão aqui.',
|
'Os materiais enviados aparecerão aqui.',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF9CA3AF),
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -226,12 +226,12 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
const Text(
|
Text(
|
||||||
'Adicionar Material',
|
'Adicionar Material',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Color(0xFF2D3748),
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
@@ -318,10 +318,10 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Color(0xFF2D3748),
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
@@ -786,12 +786,12 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
fileName,
|
fileName,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: Color(0xFF2D3748),
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
subtitle: classId != null
|
subtitle: classId != null
|
||||||
@@ -805,8 +805,8 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
formattedDate,
|
formattedDate,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF718096),
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -814,21 +814,21 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
|||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
Icon(
|
||||||
Icons.school_outlined,
|
Icons.school_outlined,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: Color(0xFF82C9BD),
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
className,
|
className,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF82C9BD),
|
color: Theme.of(context).colorScheme.primary,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -841,8 +841,8 @@ class _TeacherMaterialsPageState extends State<TeacherMaterialsPage> {
|
|||||||
)
|
)
|
||||||
: Text(
|
: Text(
|
||||||
formattedDate,
|
formattedDate,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Color(0xFF718096),
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -646,7 +646,7 @@ class _InteractiveQuizSheetState extends State<_InteractiveQuizSheet> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
widget.title,
|
widget.title,
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: cs.onSurface),
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: cs.onSurface),
|
||||||
),
|
),
|
||||||
@@ -994,7 +994,7 @@ class _TeacherQuizInteractiveSheetState extends State<_TeacherQuizInteractiveShe
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(widget.title,
|
Text(widget.title,
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: cs.onSurface)),
|
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: cs.onSurface)),
|
||||||
if (!_submitted)
|
if (!_submitted)
|
||||||
|
|||||||
@@ -504,6 +504,7 @@ class _QuizEditorPageState extends State<_QuizEditorPage> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
widget.materialName.replaceAll('.pdf', '').replaceAll('_', ' '),
|
widget.materialName.replaceAll('.pdf', '').replaceAll('_', ' '),
|
||||||
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
backgroundColor: cs.surface,
|
backgroundColor: cs.surface,
|
||||||
@@ -693,7 +694,7 @@ class _QuestionEditorState extends State<_QuestionEditor> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.question.question.isEmpty ? 'Pergunta ${widget.index + 1}' : widget.question.question,
|
widget.question.question.isEmpty ? 'Pergunta ${widget.index + 1}' : widget.question.question,
|
||||||
maxLines: 1,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: cs.onSurface),
|
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: cs.onSurface),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../../core/theme/app_colors.dart';
|
import '../../../../core/theme/app_colors.dart';
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
import '../../../../core/services/theme_service.dart';
|
import '../../../../core/services/theme_service.dart';
|
||||||
import '../../../../core/providers/theme_provider.dart';
|
import '../../../../core/providers/theme_provider.dart';
|
||||||
|
|
||||||
@@ -34,16 +35,12 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
|
|||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
colors: themeMode == ThemeMode.light
|
colors: themeMode == ThemeMode.light
|
||||||
? [
|
? const [
|
||||||
const Color(0xFFD4E8E8),
|
Color(0xFFD4E8E8),
|
||||||
const Color(0xFFE8D4C0),
|
Color(0xFFE8D4C0),
|
||||||
const Color(0xFFD8E0E8),
|
Color(0xFFD8E0E8),
|
||||||
]
|
]
|
||||||
: [
|
: AppThemeExtras.of(context).authBackgroundGradient,
|
||||||
Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
|
||||||
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
|
|
||||||
Theme.of(context).colorScheme.background,
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
@@ -55,7 +52,10 @@ class _SettingsPageState extends ConsumerState<SettingsPage> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
icon: Icon(
|
||||||
|
Icons.arrow_back,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
onPressed: () => context.go('/student-dashboard'),
|
onPressed: () => context.go('/student-dashboard'),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter_animate/flutter_animate.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../../core/services/auth_service.dart';
|
import '../../../../core/services/auth_service.dart';
|
||||||
import '../../../../core/services/session_service.dart';
|
import '../../../../core/services/session_service.dart';
|
||||||
|
import '../../../../core/theme/app_theme_extension.dart';
|
||||||
import '../../../../l10n/app_localizations.dart';
|
import '../../../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
class SplashPage extends StatefulWidget {
|
class SplashPage extends StatefulWidget {
|
||||||
@@ -117,12 +118,14 @@ class _SplashPageState extends State<SplashPage> {
|
|||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
colors: [
|
colors: Theme.of(context).brightness == Brightness.dark
|
||||||
Theme.of(context).colorScheme.background,
|
? AppThemeExtras.of(context).authBackgroundGradient
|
||||||
Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
: [
|
||||||
Theme.of(context).colorScheme.secondary.withOpacity(0.05),
|
Theme.of(context).colorScheme.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,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
|
|||||||
Reference in New Issue
Block a user