Repository navigation
Samuel Charry Patterns
The Flutter authentication screens use MVVM. LoginView and RegisterView display form state and user actions. Their ChangeNotifier ViewModels validate input, expose loading and error states, and delegate the request to AuthApi. AuthApi calls the register and login Cloud Functions and opens the Firebase session with the returned custom token. AuthGate observes authentication state and shows either the login screen or HomeShell.
classDiagram
direction LR
class LoginView {
<<View>>
+build(context)
}
class RegisterView {
<<View>>
+build(context)
}
class LoginViewModel {
<<ViewModel>>
+isLoading: bool
+error: String
+login()
}
class RegisterViewModel {
<<ViewModel>>
+isLoading: bool
+error: String
+register()
}
class AuthApi {
<<Service>>
+changes: Stream~User~
+login(email, password)
+register(name, email, password)
+logout()
}
class AuthGate {
<<View>>
+build(context)
}
class FirebaseAuth {
<<Firebase SDK>>
}
class HomeShell {
<<View>>
}
LoginView --> LoginViewModel : observes state
RegisterView --> RegisterViewModel : observes state
LoginViewModel --> AuthApi : requests login
RegisterViewModel --> AuthApi : requests registration
AuthApi --> FirebaseAuth : opens session
AuthGate --> AuthApi : observes changes
AuthGate --> LoginView : signed out
AuthGate --> HomeShell : signed in
The screens do not handle HTTP requests or tokens. Each ViewModel manages its form. AuthApi manages the shared login flow. This keeps the screens simple.
The implementation is in login_view.dart, register_view.dart, login_viewmodel.dart, register_viewmodel.dart, auth_api.dart, and auth_gate.dart.
ActivityCreator defines the factory method createActivity() and the common plan() flow. LibraryActivityCreator creates a LibraryActivity from a library template; CustomActivityCreator creates a CustomActivity from the teacher's input. Both products implement Activity. The shared plan() flow records the activity_selected event with the source (library or custom), which supports BQ #4.
classDiagram
direction LR
class ActivityCreator {
<<abstract creator>>
-analytics: AnalyticsService
+createActivity() Activity
+plan() Activity
}
class LibraryActivityCreator {
+template: ActivityTemplate
+createActivity() Activity
}
class CustomActivityCreator {
+title: String
+subject: String
+minutes: int
+createActivity() Activity
}
class Activity {
<<abstract product>>
+source: ActivitySource
+toEventParams() Map
}
class LibraryActivity {
+templateId: String
+source: ActivitySource
}
class CustomActivity {
+source: ActivitySource
}
class ActivitiesViewModel {
+planFromLibrary(template)
+createCustom(title, minutes)
}
class AnalyticsService {
+log(name, params)
}
LibraryActivityCreator --|> ActivityCreator
CustomActivityCreator --|> ActivityCreator
LibraryActivity --|> Activity
CustomActivity --|> Activity
LibraryActivityCreator ..> LibraryActivity : creates
CustomActivityCreator ..> CustomActivity : creates
ActivitiesViewModel --> ActivityCreator : calls plan()
ActivityCreator --> AnalyticsService : logs activity_selected
Both activity types use the same planning and event flow. Each creator only handles how its activity is built. This avoids repeating the event code.
The implementation is in activity_creator.dart, activity.dart, activities_viewmodel.dart, and analytics_service.dart.
Responsible: Samuel Charry.