Files
LearnIT/docs/FRONTEND_MVP_TASKS.md
2026-05-06 20:16:11 +01:00

1683 lines
52 KiB
Markdown

# Frontend MVP Tasks - AI Study Assistant
## 📱 MVP FRONTEND ROADMAP (8-12 WEEKS)
---
## 🎯 WEEK 1-2: FOUNDATION & SETUP
### Task 1.1: Flutter Project Initialization
**Priority**: Critical
**Estimated Time**: 4 hours
**Dependencies**: None
#### Subtasks:
- [ ] Create new Flutter project: `flutter create teachit`
- [ ] Configure Flutter SDK version (3.41.9 or latest stable)
- [ ] Set up version control (Git repository)
- [ ] Create initial project structure
- [ ] Configure pubspec.yaml with initial dependencies
#### Dependencies to Add:
```yaml
dependencies:
flutter:
sdk: flutter
# State Management
flutter_riverpod: ^2.4.9
# Firebase
firebase_core: ^2.24.2
firebase_auth: ^4.15.3
cloud_firestore: ^4.13.6
firebase_storage: ^11.5.6
# UI Components
cupertino_icons: ^1.0.6
google_fonts: ^6.1.0
# Navigation
go_router: ^12.1.3
# HTTP & Networking
http: ^1.1.2
dio: ^5.4.0
# Local Storage
shared_preferences: ^2.2.2
# Utilities
intl: ^0.19.0
uuid: ^4.2.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.1
mockito: ^5.4.4
build_runner: ^2.4.7
```
#### Implementation Details:
```bash
# Create project
flutter create teachit
cd teachit
# Initialize git
git init
git add .
git commit -m "Initial Flutter project setup"
# Add dependencies
flutter pub add flutter_riverpod firebase_core firebase_auth cloud_firestore firebase_storage cupertino_icons google_fonts go_router http dio shared_preferences intl uuid
flutter pub add --dev flutter_test flutter_lints mockito build_runner
```
---
### Task 1.2: Project Structure Setup
**Priority**: Critical
**Estimated Time**: 6 hours
**Dependencies**: Task 1.1
#### Folder Structure to Create:
```
lib/
├── main.dart
├── app/
│ ├── app.dart
│ ├── router/
│ │ ├── app_router.dart
│ │ └── routes.dart
│ └── theme/
│ ├── app_theme.dart
│ ├── app_colors.dart
│ ├── app_text_styles.dart
│ └── app_spacing.dart
├── core/
│ ├── constants/
│ │ ├── app_constants.dart
│ │ └── firebase_constants.dart
│ ├── utils/
│ │ ├── logger.dart
│ │ ├── validators.dart
│ │ └── extensions.dart
│ ├── errors/
│ │ ├── exceptions.dart
│ │ └── failures.dart
│ └── services/
│ ├── storage_service.dart
│ └── notification_service.dart
├── features/
│ ├── auth/
│ │ ├── data/
│ │ │ ├── datasources/
│ │ │ │ ├── auth_remote_datasource.dart
│ │ │ │ └── auth_local_datasource.dart
│ │ │ ├── models/
│ │ │ │ ├── user_model.dart
│ │ │ │ └── auth_result_model.dart
│ │ │ └── repositories/
│ │ │ └── auth_repository_impl.dart
│ │ ├── domain/
│ │ │ ├── entities/
│ │ │ │ ├── user.dart
│ │ │ │ └── auth_result.dart
│ │ │ ├── repositories/
│ │ │ │ └── auth_repository.dart
│ │ │ └── usecases/
│ │ │ ├── sign_in.dart
│ │ │ ├── sign_up.dart
│ │ │ ├── sign_out.dart
│ │ │ └── get_current_user.dart
│ │ └── presentation/
│ │ ├── providers/
│ │ │ ├── auth_provider.dart
│ │ │ └── user_provider.dart
│ │ ├── screens/
│ │ │ ├── login_screen.dart
│ │ │ ├── signup_screen.dart
│ │ │ └── forgot_password_screen.dart
│ │ └── widgets/
│ │ ├── auth_form.dart
│ │ ├── social_login_button.dart
│ │ └── password_input_field.dart
│ ├── student/
│ │ ├── data/
│ │ │ ├── datasources/
│ │ │ │ ├── student_remote_datasource.dart
│ │ │ │ └── student_local_datasource.dart
│ │ │ ├── models/
│ │ │ │ ├── student_model.dart
│ │ │ │ ├── learning_state_model.dart
│ │ │ │ └── quiz_attempt_model.dart
│ │ │ └── repositories/
│ │ │ └── student_repository_impl.dart
│ │ ├── domain/
│ │ │ ├── entities/
│ │ │ │ ├── student.dart
│ │ │ │ ├── learning_state.dart
│ │ │ │ ├── concept_mastery.dart
│ │ │ │ └── quiz_attempt.dart
│ │ │ ├── repositories/
│ │ │ │ └── student_repository.dart
│ │ │ └── usecases/
│ │ │ ├── get_learning_state.dart
│ │ │ ├── update_learning_state.dart
│ │ │ ├── submit_quiz_attempt.dart
│ │ │ └── get_quiz_history.dart
│ │ └── presentation/
│ │ ├── providers/
│ │ │ ├── student_provider.dart
│ │ │ ├── learning_state_provider.dart
│ │ │ └── quiz_provider.dart
│ │ ├── screens/
│ │ │ ├── student_dashboard_screen.dart
│ │ │ ├── ask_tutor_screen.dart
│ │ │ ├── quiz_screen.dart
│ │ │ ├── progress_screen.dart
│ │ │ └── profile_screen.dart
│ │ └── widgets/
│ │ ├── mastery_progress_bar.dart
│ │ ├── concept_card.dart
│ │ ├── quiz_question_card.dart
│ │ ├── chat_bubble.dart
│ │ └── feedback_widget.dart
│ ├── teacher/
│ │ ├── data/
│ │ │ ├── datasources/
│ │ │ │ ├── teacher_remote_datasource.dart
│ │ │ │ └── teacher_local_datasource.dart
│ │ │ ├── models/
│ │ │ │ ├── teacher_model.dart
│ │ │ │ ├── content_model.dart
│ │ │ │ └── quiz_model.dart
│ │ │ └── repositories/
│ │ │ └── teacher_repository_impl.dart
│ │ ├── domain/
│ │ │ ├── entities/
│ │ │ │ ├── teacher.dart
│ │ │ │ ├── content.dart
│ │ │ │ └── quiz.dart
│ │ │ ├── repositories/
│ │ │ │ └── teacher_repository.dart
│ │ │ └── usecases/
│ │ │ ├── upload_content.dart
│ │ │ ├── create_quiz.dart
│ │ │ ├── get_class_analytics.dart
│ │ │ └── manage_students.dart
│ │ └── presentation/
│ │ ├── providers/
│ │ │ ├── teacher_provider.dart
│ │ │ ├── content_provider.dart
│ │ │ └── analytics_provider.dart
│ │ ├── screens/
│ │ │ ├── teacher_dashboard_screen.dart
│ │ │ ├── upload_content_screen.dart
│ │ │ ├── create_quiz_screen.dart
│ │ │ ├── class_analytics_screen.dart
│ │ │ └── manage_students_screen.dart
│ │ └── widgets/
│ │ ├── content_upload_card.dart
│ │ ├── quiz_builder.dart
│ │ ├── analytics_chart.dart
│ │ └── student_list_item.dart
│ └── shared/
│ ├── data/
│ │ ├── models/
│ │ │ ├── message_model.dart
│ │ │ ├── feedback_model.dart
│ │ │ └── notification_model.dart
│ │ └── datasources/
│ │ ├── shared_remote_datasource.dart
│ │ └── shared_local_datasource.dart
│ ├── domain/
│ │ ├── entities/
│ │ │ ├── message.dart
│ │ │ ├── feedback.dart
│ │ │ └── notification.dart
│ │ └── repositories/
│ │ └── shared_repository.dart
│ └── presentation/
│ ├── widgets/
│ │ ├── custom_button.dart
│ │ ├── custom_text_field.dart
│ │ ├── loading_widget.dart
│ │ ├── error_widget.dart
│ │ ├── empty_state_widget.dart
│ │ ├── confirmation_dialog.dart
│ │ └── bottom_sheet.dart
│ └── providers/
│ ├── theme_provider.dart
│ ├── connectivity_provider.dart
│ └── notification_provider.dart
└── widgets/
├── app_scaffold.dart
├── app_bottom_navigation.dart
└── app_drawer.dart
```
---
### Task 1.3: Theme & Design System Implementation
**Priority**: Critical
**Estimated Time**: 8 hours
**Dependencies**: Task 1.2
#### Subtasks:
- [ ] Implement AppColors class with EPVChat color palette
- [ ] Create AppTextStyles with typography system
- [ ] Set up AppTheme with light/dark mode support
- [ ] Implement custom widgets (buttons, cards, inputs)
- [ ] Create animation utilities
- [ ] Set up responsive design utilities
#### Implementation Files:
**lib/app/theme/app_colors.dart**
```dart
import 'package:flutter/material.dart';
class AppColors {
// Primary Brand Colors (from EPVChat)
static const Color primaryBlue = Color(0xFF4A90E2);
static const Color primaryTeal = Color(0xFF5AC8FA);
static const Color primaryOrange = Color(0xFFFF9500);
// Gradient Colors
static const Color gradientStart = Color(0xFF4A90E2);
static const Color gradientEnd = Color(0xFF5AC8FA);
// Neutral Colors
static const Color background = Color(0xFFF8F9FA);
static const Color surface = Color(0xFFFFFFFF);
static const Color cardBackground = Color(0xFFFFFFFF);
// Text Colors
static const Color textPrimary = Color(0xFF1A1A1A);
static const Color textSecondary = Color(0xFF6B7280);
static const Color textHint = Color(0xFF9CA3AF);
// Status Colors
static const Color success = Color(0xFF10B981);
static const Color warning = Color(0xFFF59E0B);
static const Color error = Color(0xFFEF4444);
static const Color info = Color(0xFF3B82F6);
// Interactive Colors
static const Color buttonPrimary = Color(0xFF4A90E2);
static const Color buttonSecondary = Color(0xFFE5E7EB);
static const Color iconActive = Color(0xFF4A90E2);
static const Color iconInactive = Color(0xFF9CA3AF);
// Chat Colors
static const Color chatBubbleStudent = Color(0xFF4A90E2);
static const Color chatBubbleAI = Color(0xFFF3F4F6);
static const Color chatInputBackground = Color(0xFFF8F9FA);
static const Color chatSendButton = Color(0xFF5AC8FA);
}
class AppDarkColors {
static const Color background = Color(0xFF121212);
static const Color surface = Color(0xFF1E1E1E);
static const Color cardBackground = Color(0xFF2D2D2D);
static const Color textPrimary = Color(0xFFFFFFFF);
static const Color textSecondary = Color(0xFFB3B3B3);
static const Color textHint = Color(0xFF808080);
}
```
**lib/app/theme/app_theme.dart**
```dart
import 'package:flutter/material.dart';
import 'app_colors.dart';
import 'app_text_styles.dart';
class AppTheme {
static ThemeData get lightTheme {
return ThemeData(
useMaterial3: true,
brightness: Brightness.light,
colorScheme: const ColorScheme.light(
primary: AppColors.primaryBlue,
secondary: AppColors.primaryTeal,
surface: AppColors.surface,
background: AppColors.background,
error: AppColors.error,
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: AppColors.textPrimary,
onBackground: AppColors.textPrimary,
onError: Colors.white,
),
textTheme: const TextTheme(
displayLarge: AppTextStyles.h1,
displayMedium: AppTextStyles.h2,
displaySmall: AppTextStyles.h3,
bodyLarge: AppTextStyles.bodyLarge,
bodyMedium: AppTextStyles.bodyMedium,
bodySmall: AppTextStyles.bodySmall,
labelLarge: AppTextStyles.buttonLarge,
labelMedium: AppTextStyles.buttonMedium,
labelSmall: AppTextStyles.caption,
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.buttonPrimary,
foregroundColor: Colors.white,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.surface,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: AppColors.primaryBlue.withOpacity(0.3),
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: AppColors.primaryBlue.withOpacity(0.3),
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(
color: AppColors.primaryBlue,
width: 2,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(
color: AppColors.error,
width: 1,
),
),
),
cardTheme: CardTheme(
color: AppColors.cardBackground,
elevation: 4,
shadowColor: Colors.black.withOpacity(0.1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
);
}
static ThemeData get darkTheme {
return ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: const ColorScheme.dark(
primary: AppColors.primaryBlue,
secondary: AppColors.primaryTeal,
surface: AppDarkColors.surface,
background: AppDarkColors.background,
error: AppColors.error,
onPrimary: Colors.white,
onSecondary: Colors.white,
onSurface: AppDarkColors.textPrimary,
onBackground: AppDarkColors.textPrimary,
onError: Colors.white,
),
// ... similar text theme and component themes for dark mode
);
}
}
```
---
### Task 1.4: Firebase Configuration
**Priority**: Critical
**Estimated Time**: 4 hours
**Dependencies**: Task 1.1
#### Subtasks:
- [ ] Create Firebase project
- [ ] Add Firebase configuration files
- [ ] Initialize Firebase in app
- [ ] Set up Firebase Auth configuration
- [ ] Configure Firestore security rules
- [ ] Set up Firebase Storage
#### Implementation:
**lib/core/constants/firebase_constants.dart**
```dart
class FirebaseConstants {
// Collections
static const String usersCollection = 'users';
static const String schoolsCollection = 'schools';
static const String learningStatesCollection = 'learning_states';
static const String contentChunksCollection = 'content_chunks';
static const String quizzesCollection = 'quizzes';
static const String quizAttemptsCollection = 'quiz_attempts';
static const String interactionsCollection = 'interactions';
static const String auditLogsCollection = 'audit_logs';
// Storage paths
static const String contentStoragePath = 'content/';
static const String profileImagesPath = 'profile_images/';
static const String tempFilesPath = 'temp/';
// User roles
static const String studentRole = 'student';
static const String teacherRole = 'teacher';
static const String adminRole = 'admin';
// Default values
static const int defaultQuizDuration = 30; // minutes
static const int maxChunkSize = 400; // tokens
static const int maxRetrievalChunks = 5;
}
```
---
## 🔐 WEEK 3-4: AUTHENTICATION & USER MANAGEMENT
### Task 2.1: Authentication System
**Priority**: Critical
**Estimated Time**: 12 hours
**Dependencies**: Task 1.4
#### Subtasks:
- [ ] Implement Firebase Auth service
- [ ] Create user models and entities
- [ ] Build authentication repository
- [ ] Implement sign in use case
- [ ] Implement sign up use case
- [ ] Implement password reset
- [ ] Create authentication providers
- [ ] Build login/signup screens
#### Implementation:
**lib/features/auth/data/datasources/auth_remote_datasource.dart**
```dart
import 'package:firebase_auth/firebase_auth.dart';
import '../models/user_model.dart';
abstract class AuthRemoteDataSource {
Future<UserModel> signInWithEmail(String email, String password);
Future<UserModel> signUpWithEmail(String email, String password, String name, String role);
Future<UserModel> signInWithGoogle();
Future<void> sendPasswordResetEmail(String email);
Future<void> signOut();
Future<UserModel?> getCurrentUser();
Stream<UserModel?> authStateChanges();
}
class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
final FirebaseAuth _firebaseAuth;
AuthRemoteDataSourceImpl(this._firebaseAuth);
@override
Future<UserModel> signInWithEmail(String email, String password) async {
try {
final result = await _firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
return UserModel.fromFirebaseUser(result.user!);
} catch (e) {
throw AuthException(e.toString());
}
}
@override
Future<UserModel> signUpWithEmail(
String email,
String password,
String name,
String role
) async {
try {
final result = await _firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
// Update user profile
await result.user!.updateDisplayName(name);
return UserModel.fromFirebaseUser(
result.user!,
role: role,
displayName: name,
);
} catch (e) {
throw AuthException(e.toString());
}
}
@override
Future<UserModel> signInWithGoogle() async {
// Implement Google Sign-In
throw UnimplementedError();
}
@override
Future<void> sendPasswordResetEmail(String email) async {
try {
await _firebaseAuth.sendPasswordResetEmail(email: email);
} catch (e) {
throw AuthException(e.toString());
}
}
@override
Future<void> signOut() async {
await _firebaseAuth.signOut();
}
@override
Future<UserModel?> getCurrentUser() async {
final user = _firebaseAuth.currentUser;
return user != null ? UserModel.fromFirebaseUser(user) : null;
}
@override
Stream<UserModel?> authStateChanges() {
return _firebaseAuth.authStateChanges().map((user) {
return user != null ? UserModel.fromFirebaseUser(user) : null;
});
}
}
```
**lib/features/auth/presentation/screens/login_screen.dart**
```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/auth_provider.dart';
import '../../../shared/widgets/custom_button.dart';
import '../../../shared/widgets/custom_text_field.dart';
class LoginScreen extends ConsumerStatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
@override
ConsumerState<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends ConsumerState<LoginScreen> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _obscurePassword = true;
bool _isLoading = false;
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
Future<void> _handleLogin() async {
if (!_formKey.currentState!.validate()) return;
setState(() => _isLoading = true);
try {
await ref.read(authProvider.notifier).signIn(
email: _emailController.text.trim(),
password: _passwordController.text,
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Login failed: ${e.toString()}'),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
} finally {
setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
AppColors.gradientStart,
AppColors.gradientEnd,
],
),
),
child: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 60),
// Logo and Title
Column(
children: [
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: const Icon(
Icons.school,
size: 40,
color: AppColors.primaryBlue,
),
),
const SizedBox(height: 16),
const Text(
'AI Study Assistant',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 8),
const Text(
'Escola Profissional de Vila do Conde',
style: TextStyle(
fontSize: 16,
color: Colors.white70,
),
),
],
),
const SizedBox(height: 60),
// Login Form
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'Welcome Back',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
CustomTextField(
controller: _emailController,
label: 'Email',
hint: 'Enter your email',
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email';
}
if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) {
return 'Please enter a valid email';
}
return null;
},
),
const SizedBox(height: 16),
CustomTextField(
controller: _passwordController,
label: 'Password',
hint: 'Enter your password',
obscureText: _obscurePassword,
suffixIcon: IconButton(
icon: Icon(
_obscurePassword ? Icons.visibility : Icons.visibility_off,
),
onPressed: () {
setState(() => _obscurePassword = !_obscurePassword);
},
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
if (value.length < 6) {
return 'Password must be at least 6 characters';
}
return null;
},
),
const SizedBox(height: 24),
CustomButton(
text: 'Sign In',
onPressed: _handleLogin,
isLoading: _isLoading,
),
const SizedBox(height: 16),
TextButton(
onPressed: () {
// Navigate to forgot password
},
child: const Text(
'Forgot Password?',
style: TextStyle(
color: AppColors.primaryBlue,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
const SizedBox(height: 32),
// Sign Up Link
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"Don't have an account? ",
style: TextStyle(color: Colors.white),
),
TextButton(
onPressed: () {
// Navigate to sign up
},
child: const Text(
'Sign Up',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
),
),
),
),
);
}
}
```
---
### Task 2.2: User Role Management
**Priority**: High
**Estimated Time**: 8 hours
**Dependencies**: Task 2.1
#### Subtasks:
- [ ] Implement role-based routing
- [ ] Create user profile screens
- [ ] Build role-specific navigation
- [ ] Implement user settings
- [ ] Create user management (for teachers/admins)
---
## 📚 WEEK 5-6: STUDENT FEATURES
### Task 3.1: Student Dashboard
**Priority**: High
**Estimated Time**: 16 hours
**Dependencies**: Task 2.2
#### Subtasks:
- [ ] Design dashboard layout
- [ ] Implement mastery progress cards
- [ ] Create concept review widgets
- [ ] Build misconception indicators
- [ ] Add learning streak tracker
- [ ] Implement quick action buttons
#### Implementation:
**lib/features/student/presentation/screens/student_dashboard_screen.dart**
```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/learning_state_provider.dart';
import '../../../shared/widgets/mastery_progress_bar.dart';
import '../../../shared/widgets/concept_card.dart';
import '../../../shared/widgets/custom_button.dart';
class StudentDashboardScreen extends ConsumerStatefulWidget {
const StudentDashboardScreen({Key? key}) : super(key: key);
@override
ConsumerState<StudentDashboardScreen> createState() => _StudentDashboardScreenState();
}
class _StudentDashboardScreenState extends ConsumerState<StudentDashboardScreen>
with TickerProviderStateMixin {
late AnimationController _fadeController;
late AnimationController _slideController;
@override
void initState() {
super.initState();
_fadeController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_slideController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_fadeController.forward();
_slideController.forward();
}
@override
void dispose() {
_fadeController.dispose();
_slideController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final learningState = ref.watch(learningStateProvider);
final recommendations = ref.watch(recommendationsProvider);
return Scaffold(
appBar: AppBar(
title: const Text('My Learning'),
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
elevation: 0,
),
body: RefreshIndicator(
onRefresh: () async {
await ref.refresh(learningStateProvider.future);
},
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Welcome Section
FadeTransition(
opacity: _fadeController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Welcome back, ${learningState.value?.profile.name ?? 'Student'}!',
style: AppTextStyles.h2,
),
const SizedBox(height: 8),
Text(
'Continue your learning journey',
style: AppTextStyles.bodyMedium.copyWith(
color: AppColors.textSecondary,
),
),
],
),
),
const SizedBox(height: 24),
// Summary Cards
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.3),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _slideController,
curve: Curves.easeOut,
)),
child: Row(
children: [
Expanded(
child: _buildSummaryCard(
title: 'Average Mastery',
value: '${(learningState.value?.averageMastery * 100 ?? 0).toInt()}%',
icon: Icons.trending_up,
color: AppColors.success,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildSummaryCard(
title: 'Learning Streak',
value: '${learningState.value?.learningStreak ?? 0} days',
icon: Icons.local_fire_department,
color: AppColors.primaryOrange,
),
),
],
),
),
const SizedBox(height: 24),
// Concepts to Review
_buildSectionTitle('Concepts to Review'),
const SizedBox(height: 12),
SizedBox(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: learningState.value?.spacedRepetition.length ?? 0,
itemBuilder: (context, index) {
final concept = learningState.value!.spacedRepetition[index];
return Container(
width: 200,
margin: const EdgeInsets.only(right: 12),
child: ConceptCard(
conceptId: concept.conceptId,
dueDate: concept.dueDate,
priority: concept.priority,
onTap: () {
// Navigate to concept details
},
),
);
},
),
),
const SizedBox(height: 24),
// Misconceptions
if (learningState.value?.misconceptions.isNotEmpty == true) ...[
_buildSectionTitle('Address Misconceptions'),
const SizedBox(height: 12),
...learningState.value!.misconceptions.map(
(misconception) => Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.warning.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: AppColors.warning.withOpacity(0.3),
),
),
child: Row(
children: [
Icon(
Icons.warning,
color: AppColors.warning,
size: 20,
),
const SizedBox(width: 12),
Expanded(
child: Text(
misconception.description,
style: AppTextStyles.bodyMedium,
),
),
CustomButton(
text: 'Fix',
onPressed: () {
// Navigate to remedial content
},
isSmall: true,
),
],
),
),
),
const SizedBox(height: 24),
],
// Recommended Actions
_buildSectionTitle('Recommended for You'),
const SizedBox(height: 12),
...recommendations.when(
data: (actions) => actions.map(
(action) => Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.cardBackground,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Icon(
_getActionIcon(action.type),
color: AppColors.primaryBlue,
size: 24,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
action.description,
style: AppTextStyles.bodyMedium,
),
if (action.estimatedTime != null) ...[
const SizedBox(height: 4),
Text(
'Est. ${action.estimatedTime} min',
style: AppTextStyles.caption,
),
],
],
),
),
CustomButton(
text: 'Start',
onPressed: () => _handleAction(action),
isSmall: true,
),
],
),
),
),
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (error, stack) => Center(
child: Text('Error: $error'),
),
),
const SizedBox(height: 24),
// Quick Actions
_buildSectionTitle('Quick Actions'),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: CustomButton(
text: 'Ask Tutor',
onPressed: () {
// Navigate to chat
},
icon: Icons.chat,
),
),
const SizedBox(width: 12),
Expanded(
child: CustomButton(
text: 'Take Quiz',
onPressed: () {
// Navigate to quiz
},
icon: Icons.quiz,
isSecondary: true,
),
),
],
),
],
),
),
),
);
}
Widget _buildSummaryCard({
required String title,
required String value,
required IconData icon,
required Color color,
}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: color.withOpacity(0.3),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, color: color, size: 24),
const SizedBox(height: 8),
Text(
title,
style: AppTextStyles.caption.copyWith(
color: color,
),
),
const SizedBox(height: 4),
Text(
value,
style: AppTextStyles.h3.copyWith(
color: color,
fontWeight: FontWeight.bold,
),
),
],
),
);
}
Widget _buildSectionTitle(String title) {
return Text(
title,
style: AppTextStyles.h3,
);
}
IconData _getActionIcon(String actionType) {
switch (actionType) {
case 'remedial':
return Icons.healing;
case 'review':
return Icons.refresh;
case 'new_learning':
return Icons.add_circle;
case 'exploration':
return Icons.explore;
default:
return Icons.lightbulb;
}
}
void _handleAction(RecommendedAction action) {
switch (action.type) {
case 'remedial':
// Navigate to remedial content
break;
case 'review':
// Navigate to review screen
break;
case 'new_learning':
// Navigate to new concept
break;
case 'exploration':
// Navigate to exploration
break;
}
}
}
```
---
### Task 3.2: AI Tutor Chat Interface
**Priority**: High
**Estimated Time**: 20 hours
**Dependencies**: Task 3.1
#### Subtasks:
- [ ] Design chat interface layout
- [ ] Implement message bubbles (student/AI)
- [ ] Create chat input with send button
- [ ] Add typing indicators
- [ ] Implement message history
- [ ] Add feedback collection
- [ ] Create smooth animations
#### Implementation:
**lib/features/student/presentation/screens/ask_tutor_screen.dart**
```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/chat_provider.dart';
import '../../../shared/widgets/chat_bubble.dart';
import '../../../shared/widgets/custom_text_field.dart';
class AskTutorScreen extends ConsumerStatefulWidget {
const AskTutorScreen({Key? key}) : super(key: key);
@override
ConsumerState<AskTutorScreen> createState() => _AskTutorScreenState();
}
class _AskTutorScreenState extends ConsumerState<AskTutorScreen> {
final _messageController = TextEditingController();
final _scrollController = ScrollController();
final _focusNode = FocusNode();
@override
void dispose() {
_messageController.dispose();
_scrollController.dispose();
_focusNode.dispose();
super.dispose();
}
void _scrollToBottom() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
}
});
}
Future<void> _sendMessage() async {
final message = _messageController.text.trim();
if (message.isEmpty) return;
_messageController.clear();
_focusNode.unfocus();
try {
await ref.read(chatProvider.notifier).sendMessage(message);
_scrollToBottom();
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to send message: $e'),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
}
}
@override
Widget build(BuildContext context) {
final messages = ref.watch(chatProvider);
return Scaffold(
appBar: AppBar(
title: const Text('AI Tutor'),
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
elevation: 0,
),
body: Column(
children: [
// Messages List
Expanded(
child: messages.when(
data: (messageList) => ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.all(16),
itemCount: messageList.length,
itemBuilder: (context, index) {
final message = messageList[index];
return ChatBubble(
message: message,
showAvatar: message.role == 'assistant',
onFeedback: (feedback) {
ref.read(chatProvider.notifier).submitFeedback(
messageId: message.id,
feedback: feedback,
);
},
);
},
),
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (error, stack) => Center(
child: Text('Error: $error'),
),
),
),
// Typing Indicator
Consumer(
builder: (context, ref, child) {
final isTyping = ref.watch(isTypingProvider);
return isTyping
? Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: AppColors.chatBubbleAI,
borderRadius: BorderRadius.circular(16),
),
child: Row(
children: [
SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation(AppColors.textSecondary),
),
),
const SizedBox(width: 8),
Text(
'AI is thinking...',
style: AppTextStyles.bodySmall.copyWith(
color: AppColors.textSecondary,
),
),
],
),
),
],
),
)
: const SizedBox.shrink();
},
),
// Input Area
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.chatInputBackground,
border: Border(
top: BorderSide(
color: AppColors.primaryBlue.withOpacity(0.1),
),
),
),
child: Row(
children: [
Expanded(
child: CustomTextField(
controller: _messageController,
hint: 'Ask a question...',
maxLines: 3,
minLines: 1,
focusNode: _focusNode,
onSubmitted: (_) => _sendMessage(),
),
),
const SizedBox(width: 12),
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: AppColors.chatSendButton,
borderRadius: BorderRadius.circular(24),
),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(24),
onTap: _sendMessage,
child: const Icon(
Icons.send,
color: Colors.white,
size: 24,
),
),
),
),
],
),
),
],
),
);
}
}
```
---
### Task 3.3: Quiz System
**Priority**: High
**Estimated Time**: 16 hours
**Dependencies**: Task 3.1
#### Subtasks:
- [ ] Create quiz question cards
- [ ] Implement multiple choice interface
- [ ] Add timer functionality
- [ ] Build progress tracking
- [ ] Create quiz results screen
- [ ] Implement quiz history
---
## 👨‍🏫 WEEK 7-8: TEACHER FEATURES
### Task 4.1: Teacher Dashboard
**Priority**: High
**Estimated Time**: 12 hours
**Dependencies**: Task 2.2
#### Subtasks:
- [ ] Design teacher dashboard layout
- [ ] Create class overview cards
- [ ] Implement analytics widgets
- [ ] Add quick action buttons
- [ ] Build student performance summary
---
### Task 4.2: Content Upload System
**Priority**: High
**Estimated Time**: 16 hours
**Dependencies**: Task 4.1
#### Subtasks:
- [ ] Create file upload interface
- [ ] Implement PDF parsing preview
- [ ] Add content chunking preview
- [ ] Build metadata editing
- [ ] Create content management list
---
### Task 4.3: Quiz Creation
**Priority**: Medium
**Estimated Time**: 12 hours
**Dependencies**: Task 4.1
#### Subtasks:
- [ ] Build quiz builder interface
- [ ] Add question type selection
- [ ] Implement question editor
- [ ] Create quiz settings
- [ ] Add preview functionality
---
## 🧪 WEEK 9-10: TESTING & POLISH
### Task 5.1: Unit Tests
**Priority**: High
**Estimated Time**: 16 hours
**Dependencies**: All previous tasks
#### Subtasks:
- [ ] Write tests for all use cases
- [ ] Test repository implementations
- [ ] Test provider logic
- [ ] Test widget rendering
- [ ] Test user interactions
---
### Task 5.2: Integration Tests
**Priority**: High
**Estimated Time**: 12 hours
**Dependencies**: Task 5.1
#### Subtasks:
- [ ] Test authentication flow
- [ ] Test complete user journeys
- [ ] Test Firebase integration
- [ ] Test API communication
- [ ] Test error handling
---
### Task 5.3: UI Polish
**Priority**: Medium
**Estimated Time**: 8 hours
**Dependencies**: Task 5.2
#### Subtasks:
- [ ] Refine animations
- [ ] Improve responsive design
- [ ] Add micro-interactions
- [ ] Optimize performance
- [ ] Fix UI inconsistencies
---
## 📱 WEEK 11-12: FINALIZATION
### Task 6.1: Performance Optimization
**Priority**: High
**Estimated Time**: 8 hours
**Dependencies**: Task 5.3
#### Subtasks:
- [ ] Profile app performance
- [ ] Optimize image loading
- [ ] Reduce bundle size
- [ ] Implement lazy loading
- [ ] Optimize Firebase queries
---
### Task 6.2: Documentation
**Priority**: Medium
**Estimated Time**: 8 hours
**Dependencies**: Task 6.1
#### Subtasks:
- [ ] Document API endpoints
- [ ] Create user guide
- [ ] Write deployment instructions
- [ ] Document architecture
- [ ] Create troubleshooting guide
---
## 🚀 DELIVERABLES
### Week 2 Deliverables
- [ ] Flutter project with complete structure
- [ ] Design system implementation
- [ ] Firebase configuration
- [ ] Basic authentication flow
### Week 4 Deliverables
- [ ] Complete authentication system
- [ ] User role management
- [ ] User profiles
- [ ] Navigation system
### Week 6 Deliverables
- [ ] Student dashboard
- [ ] AI tutor chat interface
- [ ] Quiz system
- [ ] Progress tracking
### Week 8 Deliverables
- [ ] Teacher dashboard
- [ ] Content upload system
- [ ] Quiz creation tools
- [ ] Basic analytics
### Week 10 Deliverables
- [ ] Complete test suite
- [ ] Polished UI
- [ ] Error handling
- [ ] Performance optimizations
### Week 12 Deliverables
- [ ] Production-ready app
- [ ] Complete documentation
- [ ] Deployment ready
- [ ] User testing feedback incorporated
---
## 📋 QUALITY CHECKLIST
### Code Quality
- [ ] All code follows Flutter/Dart conventions
- [ ] Proper error handling throughout
- [ ] No hardcoded values (use constants)
- [ ] Proper state management implementation
- [ ] Clean architecture maintained
### UI/UX Quality
- [ ] Design system consistently applied
- [ ] Responsive design works on all screen sizes
- [ ] Animations are smooth and purposeful
- [ ] Accessibility features implemented
- [ ] Dark mode support
### Performance
- [ ] App launches within 3 seconds
- [ ] Smooth 60 FPS animations
- [ ] Memory usage optimized
- [ ] Network requests efficient
- [ ] No memory leaks
### Testing
- [ ] Unit test coverage > 80%
- [ ] All critical paths tested
- [ ] Firebase integration tested
- [ ] Error scenarios tested
- [ ] Performance benchmarks met
---
*Last Updated: 2026-05-06*
*Version: 1.0.0*
*Frontend Lead: Flutter Development Team*