Skip to content

Architecture Overview

Pedro Monteiro edited this page Nov 27, 2025 · 1 revision

Architecture Overview

So, you've opened the repo and found a bunch of folders. Don't panic.

We organize the project as a monorepo containing multiple packages. This helps us separate the "app logic" from the "design system" and the "linting rules."

1. The High-Level Structure

All the code that matters lives in the packages/ directory.

packages/uni_app

This is the main Flutter application. It contains 99% of the business logic, screens, and data handling. If you're fixing a bug or adding a feature, you'll be working here.

packages/uni_ui

This is our internal design system. It contains reusable widgets (cards, buttons, icons) and theme definitions.

  • Why separate it? It forces us to build generic, reusable UI components that aren't tightly coupled to the app's specific logic.

  • Rule of Thumb: If you are building a generic "Card" that just displays text, it probably belongs here. If it fetches data from an API, it belongs in uni_app.

packages/uni_lint

This package contains our custom linting rules. It ensures we all write code the same way.

2. Inside uni_app: The MVC Pattern

We loosely follow the Model-View-Controller (MVC) pattern.

If you look inside packages/uni_app/lib/, you'll see the structure reflects this:

lib/model (Data & State)

This folder defines what our data looks like and holds the application state.

  • entities/: Plain Dart classes that represent things like Exam, Course, or Restaurant. Many of these are annotated for the database (ObjectBox).

  • providers/: The glue that holds the app together. We use Riverpod here to manage state and expose data to the UI.

lib/view (UI)

This is what the user sees.

  • Organized by feature (e.g., schedule/, exams/, profile/).
  • Each folder usually contains the main page file (e.g., schedule_page.dart) and a widgets/ folder for parts specific to that page.

lib/controller (Logic)

This is where we handle fetching data, parsing it, and saving it.

  • fetchers/: Classes responsible for getting data. They decide whether to pull from the network or local storage.
  • parsers/: Our app often has to scrape HTML from the university portal. This folder contains the messy logic that turns raw HTML strings into beautiful Dart objects.
  • local_storage/: Interfaces for our ObjectBox database.

3. Specialized Folders

You'll also see a few other important directories in lib/:

lib/sigarra

Sigarra is the name of the university's information system. This folder contains the specific logic for interacting with its endpoints (both API and HTML scraping).

lib/session

Handling authentication with the university system is complex. This folder manages the login flows, session persistence, and cookies.

lib/generated

DO NOT EDIT FILES HERE. These are files created automatically by build_runner and intl_utils. They handle things like JSON serialization, database adapters, and translation strings.

4. Typical Data Flow

Here is how data usually moves through the app:

  1. View: A user opens the Schedule Page. The widget asks the schedule_provider for data.
  2. Model (Provider): The provider checks if it has data. If not, it calls the ScheduleFetcher.
  3. Controller (Fetcher):
    • The ScheduleFetcher checks local_storage for cached data.
    • If the cache is empty or expired, it calls the NetworkRouter.
  4. Network: The app requests the schedule from Sigarra (HTML or API).
  5. Controller (Parser): The raw response is passed to a Parser, which converts it into a list of Lecture objects.
  6. Controller (Storage): The fetcher saves these new objects to the database for next time.
  7. Model (Provider): The provider updates its state with the new list.
  8. View: The UI rebuilds to show the schedule.

Clone this wiki locally