52 KiB
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:
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:
# 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 EPVC 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
import 'package:flutter/material.dart';
class AppColors {
// Primary Brand Colors (from EPVC)
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
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
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
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
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
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
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