Files
LearnIT/lib/features/dashboard/presentation/widgets/quick_access_widget.dart
2026-05-17 22:21:23 +01:00

362 lines
12 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:go_router/go_router.dart';
import '../../../classes/presentation/pages/join_class_page.dart';
import 'dashboard_action_card.dart';
/// Quick access cards for Student Dashboard with horizontal scrollable row
class QuickAccessWidget extends StatelessWidget {
const QuickAccessWidget({super.key});
/// Mesmas dimensões dos cards em "Ações Rápidas" do professor.
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
Widget build(BuildContext context) {
final cards = [
_buildTutorIACard(context),
_buildQuizCard(context),
_buildAchievementsCard(context),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
onTap: () => _showQuickAccessList(context),
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Acesso Rápido',
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 8),
Icon(
Icons.expand_more,
color: Theme.of(context).colorScheme.onSurfaceVariant,
size: 20,
),
],
),
),
),
const SizedBox(height: 12),
IntrinsicHeight(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
clipBehavior: Clip.none,
padding: const EdgeInsets.only(right: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(width: _scrollCardWidth, child: cards[0]),
const SizedBox(width: 12),
SizedBox(width: _scrollCardWidth, child: cards[1]),
const SizedBox(width: 12),
SizedBox(width: _scrollCardWidth, child: cards[2]),
],
),
),
),
const SizedBox(height: 16),
// Entrar numa Disciplina (full width)
_buildJoinClassCard(context),
],
)
.animate()
.fadeIn(
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
)
.then(delay: const Duration(milliseconds: 100));
}
Widget _buildTutorIACard(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child:
DashboardActionCard(
title: 'Tutor IA',
subtitle: 'Assistente de estudos',
icon: Icons.psychology,
useGradient: true,
minHeight: _cardMinHeight,
iconSize: _iconSize,
iconPadding: _iconPadding,
titleFontSize: _titleFontSize,
subtitleFontSize: _subtitleFontSize,
padding: _cardPadding,
onTap: () => context.go('/ai-tutor'),
)
.animate()
.fadeIn(
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
)
.then(delay: const Duration(milliseconds: 100)),
);
}
Widget _buildQuizCard(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child:
DashboardActionCardSurface(
title: 'Quiz',
subtitle: 'Testa os teus conhecimentos',
icon: Icons.quiz,
minHeight: _cardMinHeight,
titleFontSize: _titleFontSize,
subtitleFontSize: _subtitleFontSize,
iconSize: _iconSize,
padding: _cardPadding,
onTap: () => context.go('/quiz'),
)
.animate()
.fadeIn(
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
)
.then(delay: const Duration(milliseconds: 150)),
);
}
Widget _buildAchievementsCard(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(16),
child:
DashboardActionCardSurface(
title: 'Conquistas',
subtitle: 'Ver medals',
icon: Icons.emoji_events,
minHeight: _cardMinHeight,
titleFontSize: _titleFontSize,
subtitleFontSize: _subtitleFontSize,
iconSize: _iconSize,
padding: _cardPadding,
iconColor: Colors.amber,
onTap: () => context.go('/student/achievements'),
)
.animate()
.fadeIn(
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
)
.then(delay: const Duration(milliseconds: 200)),
);
}
Widget _buildJoinClassCard(BuildContext context) {
return DashboardActionCard(
title: 'Entrar numa Disciplina',
subtitle: 'Junta-te a uma disciplina com o código',
icon: Icons.group_add,
layout: DashboardActionCardLayout.horizontal,
minHeight: 0,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const JoinClassPage()),
);
},
)
.animate()
.fadeIn(
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
)
.then(delay: const Duration(milliseconds: 300));
}
void _showQuickAccessList(BuildContext context) {
final items = [
_QuickAccessItem(
title: 'Tutor IA',
subtitle: 'Assistente de estudos',
icon: Icons.psychology,
useGradient: true,
onTap: () {
Navigator.pop(context);
context.go('/ai-tutor');
},
),
_QuickAccessItem(
title: 'Quiz',
subtitle: 'Testa os teus conhecimentos',
icon: Icons.quiz,
onTap: () {
Navigator.pop(context);
context.go('/quiz');
},
),
_QuickAccessItem(
title: 'Conquistas',
subtitle: 'Ver medals',
icon: Icons.emoji_events,
iconColor: Colors.amber,
onTap: () {
Navigator.pop(context);
context.go('/student/achievements');
},
),
_QuickAccessItem(
title: 'Entrar numa Disciplina',
subtitle: 'Junta-te a uma disciplina com o código',
icon: Icons.group_add,
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const JoinClassPage()),
);
},
),
];
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
),
builder: (context) => DraggableScrollableSheet(
initialChildSize: 0.6,
minChildSize: 0.3,
maxChildSize: 0.9,
expand: false,
builder: (context, scrollController) {
return Column(
children: [
Container(
margin: const EdgeInsets.only(top: 8),
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(2),
),
),
Padding(
padding: const EdgeInsets.all(20),
child: Text(
'Acesso Rápido',
style: TextStyle(
color: Theme.of(context).colorScheme.onSurface,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Expanded(
child: ListView.builder(
controller: scrollController,
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Card(
margin: const EdgeInsets.only(bottom: 12),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(
color: Theme.of(
context,
).colorScheme.outline.withOpacity(0.2),
),
),
child: ListTile(
leading: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: item.useGradient
? Theme.of(
context,
).colorScheme.primary.withOpacity(0.1)
: (item.iconColor ??
Theme.of(
context,
).colorScheme.secondary)
.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
item.icon,
color: item.useGradient
? Theme.of(context).colorScheme.primary
: (item.iconColor ??
Theme.of(context).colorScheme.secondary),
size: 24,
),
),
title: Text(
item.title,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onSurface,
),
),
subtitle: Text(
item.subtitle,
style: TextStyle(
color: Theme.of(
context,
).colorScheme.onSurfaceVariant,
fontSize: 13,
),
),
trailing: Icon(
Icons.arrow_forward_ios,
color: Theme.of(context).colorScheme.primary,
size: 16,
),
onTap: item.onTap,
),
);
},
),
),
const SizedBox(height: 20),
],
);
},
),
);
}
}
class _QuickAccessItem {
final String title;
final String subtitle;
final IconData icon;
final bool useGradient;
final Color? iconColor;
final VoidCallback onTap;
_QuickAccessItem({
required this.title,
required this.subtitle,
required this.icon,
this.useGradient = false,
this.iconColor,
required this.onTap,
});
}