Skip to content

Repository files navigation

Abugida — Flutter MVVM App

A Flutter application that consolidates scattered educational resources for students preparing for high school, college, and international examinations to enhance learning efficiency.

About

Abugida is a mobile application built with the MVVM (Model-View-ViewModel) architecture pattern. It consolidates scattered educational resources for students preparing for high school, college, and international examinations to enhance learning efficiency.

Key highlights:

  • MVVM Architecture — Clean separation of UI, Data, and Domain layers
  • Unidirectional Data Flow — Predictable state management with provider + ChangeNotifier
  • Offline-First Data Layer — Local SQLite caching via drift & powersync with secure file storage
  • Optional Use Cases — Pure Dart business operations for orchestration & shared rules; simple flows can call Repositories directly
  • Comprehensive Testing — Fakes pattern, unit tests, and widget tests with 80%+ coverage
  • Feature-First Structure — Organized by feature in UI, by type in data and domain
  • Immutable Models — freezed-powered domain models with code generation

Architecture Overview

Abugida follows a strict 3-layer MVVM architecture with unidirectional data flow. The Domain layer sits at the core, containing pure Dart business logic with zero Flutter dependencies.

graph TD
    subgraph "UI Layer — lib/ui/"
        V[View / Screen] -->|Commands| VM[ViewModel<br/>ChangeNotifier]
        VM -->|State| V
    end

    subgraph "Domain Layer — lib/domain/"
        UC[Use Cases<br/>Business Operations]
        M[Domain Models<br/>@freezed]
        UC -->|reads/writes| M
    end

    subgraph "Data Layer — lib/data/"
        R[Repository<br/>Source of Truth] -->|delegates| S[Service<br/>Stateless API]
        S -->|returns Data Model| R
        R -->|reads/writes| DB[(Local DB<br/>Drift/PowerSync)]
        R -->|coordinates| FS[FileStorageService<br/>S3 + AES + SHA-256]
        FS -->|secure files| Disk[(Secure Disk<br/>Encrypted Files)]
    end

    VM -->|calls use case - default| UC
    UC -->|calls| R
    UC -->|returns Domain Models| VM
    VM -.->|optional: direct fetch for simple flows| R
    R -->|maps to Domain Models| M
    R -->|reads/writes| M
    VM -->|exposes| M
    V -->|renders| M
Loading

Data flow:

  1. User interacts with the View → emits a Command (e.g., onRefresh)
  2. ViewModel calls a Use Case → the Use Case executes the business operation and calls the Repository. Use cases are optional — they are the default path for business orchestration, but a simple screen-specific fetch that only delegates to one repository method can call the Repository directly.
  3. Repository coordinates Services & Local DB → Services return data-layer models (raw Map/DTO) — never domain models; the Repository maps them to domain models → returns via Either<Error, T> → notifies listeners
  4. View rebuilds via Consumer / context.watch

See ARCHITECTURE.md for the full architecture guide.

Quick Start

Prerequisites

  • Flutter SDK 3.22 or higher
  • Dart SDK 3.6 or higher
  • A code editor (VS Code with Flutter extension or Android Studio)

Setup & Run

# 1. Clone the repository
git clone https://github.com/mortymith/Abugida-Frontend.git
cd abugida-frontend

# 2. Install dependencies
flutter pub get

# 3. Generate freezed/json_serializable/drift code
flutter pub run build_runner build --delete-conflicting-outputs

# 4. Run the app
flutter run

# 5. Run tests
flutter test

# 6. Run with coverage
flutter test --coverage

Install and generate:

flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs

This produces the *.freezed.dart and *.g.dart part files referenced by every model. They are intentionally not included in the repository and are generated during the build step.

Documentation

Document Description Link
Architecture Guide Full MVVM architecture, layer definitions, diagrams ARCHITECTURE.md
AI Agent Instructions Rules for AI coding assistants working on this project AGENT.md
Contributing Guide How to propose features, submit PRs, code review checklist CONTRIBUTING.md
docs/ Detailed topic-specific guides docs/

Project Structure

abugida-frontend/
├── lib/                                   # Application Source Code
│   ├── ui/                                # UI Layer — Presentation
│   │   ├── core/                          # Global UI Components
│   │   │   ├── widgets/                   # Shared Widgets Library
│   │   │   └── themes/                    # Theme Configuration
│   │   └── <feature_name>/                # Feature Modules
│   │       ├── view_models/               # Feature Logic Layer
│   │       │   └── <view_model_class>.dart
│   │       └── widgets/                   # Feature UI Components
│   │           ├── <feature_name>_screen.dart
│   │           └── <other_widgets>
│   ├── domain/                            # Domain Layer — Business Logic Core
│   │   ├── models/                        # Core Business Entities (@freezed)
│   │   │   └── <model_name>.dart
│   │   └── use_cases/                     # Single-Purpose Business Operations
│   │       └── <operation_name>_use_case.dart
│   ├── data/                              # Data Layer — Data Access & Local Storage
│   │   ├── local/                         # Local Database (Drift / PowerSync)
│   │   │   ├── tables/                    # Table Definitions (split by domain)
│   │   │   ├── daos/                      # Data Access Objects (Complex queries & joins)
│   │   │   └── app_database.dart          # Main AppDatabase class & migration strategy
│   │   ├── repositories/                  # Repository Interfaces (Source of Truth)
│   │   ├── services/                      # External Service Integration & Storage
│   │   │   ├── api_service.dart           # Dio HTTP wrapper
│   │   │   ├── sync_service.dart          # PowerSync / remote sync orchestration
│   │   │   └── file_storage_service.dart  # Secure offline file storage (S3, AES, SHA-256)
│   │   └── models/                        # Data Layer Models (DTOs)
│   │       └── <api_model_class>.dart
│   ├── config/                            # Application Configuration
│   ├── utils/                             # Shared Utilities
│   ├── routing/                           # Navigation Management
│   ├── main_staging.dart                  # Staging Environment Entry Point
│   ├── main_development.dart              # Development Environment Entry Point
│   └── main.dart                          # Production Entry Point
├── test/                                  # Test Suite
│   ├── data/                              # Data Layer Tests
│   ├── domain/                            # Domain Layer Tests
│   ├── ui/                                # Presentation Layer Tests
│   └── utils/                             # Utility Tests
├── testing/                               # Testing Infrastructure
│   ├── fakes/                             # Fake Implementations
│   └── models/                            # Test Data Models
├── assets/                                # Static Resources
│   ├── animations/                        # Motion Design Assets
│   ├── fonts/                             # Custom Typography
│   └── images/                            # Visual Assets
│       ├── icons/                         # Iconography
│       └── illustrations/                 # Decorative Graphics
├── docs/                                  # Documentation
│   ├── architecture/                      # Architecture Guides (01-08)
│   ├── guides/                            # Practical How-to Guides
│   ├── templates/                         # Dart Code Templates
│   ├── cheat-sheets/                      # Quick Reference Sheets
│   ├── adr/                               # Architecture Decision Records
│   └── onboarding/                        # Developer Onboarding Docs
├── UI/                                    # HTML Design Mockups
│   ├── course.html                        # Course Page Wireframe
│   ├── courseDetailes.html                # Course Details Wireframe
│   ├── examtype.html                      # Exam Type Selection Wireframe
│   ├── export.html                        # Export Data Wireframe
│   ├── LearnTab.html                      # Learn Tab Wireframe
│   ├── profile.html                       # Profile Page Wireframe
│   ├── progress.html                      # Progress Tracking Wireframe
│   └── search.html                        # Search Page Wireframe
├── ARCHITECTURE.md                        # Architecture Guide
├── AGENT.md                               # AI Agent Instructions
├── CONTRIBUTING.md                        # Contribution Guidelines
├── LICENSE                                # MIT License
├── README.md                              # Project Documentation
└── pubspec.yaml                           # Dependencies & Metadata

Layer Responsibilities

Layer Directory Responsibility
UI lib/ui/ Screens (widgets), ViewModels (state + commands), and shared components. Views are pure StatelessWidget that compose widgets and bind to ViewModel state. ViewModels hold transient UI state, expose commands, and coordinate domain use cases (or call Repositories directly for simple flows).
Data lib/data/ Repositories (source of truth with caching and retry logic) map data-layer models to domain models; Local DB (Drift tables, DAOs, and migrations for offline-first data); Services (stateless wrappers returning data-layer models — never domain models); ApiModels (DTOs for JSON serialization).
Domain lib/domain/ Pure Dart models (immutable via @freezed) and single-purpose use cases for business orchestration. No Flutter imports allowed. This is the core of the application — all other layers depend on it, but it depends on nothing. Use cases are optional; simple flows may bypass them.

Domain Use Cases

lib/domain/use_cases/ contains single-purpose business operations that sit between the ViewModels (UI layer) and the Repositories (data layer). They allow the domain layer to expose application logic without leaking repository implementations to the UI. No Flutter imports allowed — like the models, they are pure Dart.

Use cases are optional, not mandatory. Reach for them when a flow carries real business logic — orchestration of multiple repositories, pure computation (e.g. filtering, grading), or rules shared across screens. For simple, screen-specific fetches/commands that only delegate one-for-one into a single repository method, the ViewModel may call the Repository directly. Never call a Service from a ViewModel, and never bypass the Repository.

Conventions

  • Name as verbs — <operation>_use_case.dart (e.g., grade_quiz_use_case.dart).
  • Single public entry point — expose one execute() method (plus thin helper methods when needed).
  • Immutability — inputs are never mutated; results are immutable data objects or domain models.
  • Injectable — repositories are injected via constructors (constructor injection), making use cases trivially faked in tests.
  • Pure actions — Filter* and validation use cases are plain, deterministic functions; Get*/coordinator use cases are the only ones touching repositories.

Development Setup

Requirements

Tool Version Install
Flutter 3.22+ flutter.dev
Dart 3.6+ Included with Flutter
IDE VS Code or Android Studio Install Flutter extension

IDE Configuration

  • VS Code: Install the "Flutter" and "Dart" extensions. Set dart.flutterSdkPath in settings.
  • Recommended: Enable "Save on format" — auto-runs dart format on save.

Code Generation

After modifying any @freezed, @JsonSerializable, or drift classes:

# One-time build
flutter pub run build_runner build --delete-conflicting-outputs

# Watch mode (auto-regenerate on save)
flutter pub run build_runner watch --delete-conflicting-outputs

Testing

Running Tests

# Run all tests
flutter test

# Run specific test file
flutter test test/ui/home/home_view_model_test.dart

# Run only unit tests (no widget tests)
flutter test test/ui/ test/data/

# Run only widget tests
flutter test test/ui/

# Run with verbose output
flutter test --reporter expanded

# Generate coverage report
flutter test --coverage
# Open coverage/lcov.html in a browser to view

Testing Architecture

testing/
├── fakes/                               # Shared Fake* classes
│   ├── fake_subject_repository.dart
│   └── fake_api_client.dart
└── models/
    └── test_data_models.dart           # Test data factories and fixtures

Coverage Requirements

  • Team standard coverage: 80% on all new code
  • Exclusions: Generated code (*.g.dart, *.freezed.dart) is excluded from coverage

Key Dependencies

Package Purpose Version
provider State management & dependency injection ^6.0.5
go_router Declarative navigation & deep linking ^17.3.0
freezed Immutable models with code generation ^4.0.0-dev.3
freezed_annotation Annotations for freezed models ^3.1.0
json_annotation JSON serialization annotations ^4.9.0
json_serializable JSON serialization code generation ^6.8.0
dartz Functional programming (Either for error handling) ^0.10.1
dio HTTP client for API calls ^5.3.2
drift Local SQLite database & offline-first data layer ^2.34.2
powersync Real-time sync & offline-first database engine ^2.3.2
crypto SHA-256 checksum verification for file integrity ^3.0.3
encrypt AES-256 encryption for secure offline file storage ^5.0.3
path_provider Access to sandboxed local file system directories ^2.1.1
logger Structured logging ^2.5.0
flutter_test Flutter testing framework SDK
flutter_lints Lint rules for Dart/Flutter ^6.0.0

Contributing

We welcome contributions! Please read our Contributing Guide before submitting a pull request.

Quick checklist:

  • Follow MVVM architecture (no logic in widgets)
  • Add unit tests for ViewModels & Repositories
  • Add widget tests for new Screens
  • Use Fakes (not Mocks) for test doubles
  • Run dart analyze . — zero issues
  • Run dart format . — clean formatting

License

This project is licensed under the MIT License. See the LICENSE file for the full license text.

About

No description, website, or topics provided.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages