Architecture
MVVM Pattern
Same architecture as the Parents app — each feature module follows Model-View-ViewModel:
modules/
└── {feature}/
├── model/ # Data classes
├── view/ # Screens
├── viewmodel/ # ChangeNotifier providers
└── services/ # API calls
GetIt Service Locator
// service_locator.dart
final getIt = GetIt.instance;
Future<void> setupServiceLocator() async {
// Network
getIt.registerLazySingleton<DioClient>(() => DioClient());
// Services
getIt.registerLazySingleton<AuthService>(() => AuthService(getIt<DioClient>()));
getIt.registerLazySingleton<AttendanceService>(() => AttendanceService(getIt<DioClient>()));
getIt.registerLazySingleton<ExamService>(() => ExamService(getIt<DioClient>()));
// ... more services
// ViewModels
getIt.registerFactory<AuthViewModel>(() => AuthViewModel(getIt<AuthService>()));
getIt.registerFactory<AttendanceViewModel>(() => AttendanceViewModel(getIt<AttendanceService>()));
getIt.registerFactory<ExamViewModel>(() => ExamViewModel(getIt<ExamService>()));
// ... more ViewModels
}
BaseProvider
Same BaseProvider mixin used as in the Parents app:
abstract class BaseProvider extends ChangeNotifier {
bool _isLoading = false;
String? _errorMessage;
bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage;
bool get hasError => _errorMessage != null;
void setLoading(bool value) {
_isLoading = value;
notifyListeners();
}
void setError(String? message) {
_errorMessage = message;
notifyListeners();
}
Future<T> requestHandler<T>(Future<T> Function() apiCall) async {
setLoading(true);
clearError();
try {
return await apiCall();
} catch (e) {
setError(e.toString());
rethrow;
} finally {
setLoading(false);
}
}
}
API Client
Dio client configured in lib/core/network/dio_client.dart with base URL https://dev-api.metaonus.in/teacher. Same 4 interceptors as Parents app: Logger, Auth, TokenRefresh, Evaluation.