120 lines
3.6 KiB
Markdown
120 lines
3.6 KiB
Markdown
# MVVM Pattern
|
|
|
|
Model-View-ViewModel architectural pattern for Flutter applications.
|
|
|
|
## Overview
|
|
|
|
MVVM separates application features into three parts:
|
|
- **Model**: Data and business logic (Repositories, Services)
|
|
- **View**: UI presentation (Widgets)
|
|
- **ViewModel**: UI logic and state management
|
|
|
|
## Component Relationships
|
|
|
|
Every screen or user flow usually contains:
|
|
- One **View** (UI)
|
|
- One **ViewModel** (UI logic)
|
|
- One or more **Repositories** (data sources)
|
|
- Zero or more **Services** (external API access)
|
|
- Zero or more **Use-cases** for complex or reused business logic
|
|
|
|
Views and ViewModels normally have a **one-to-one relationship** at the screen
|
|
or flow level. A View may be composed of many smaller widgets.
|
|
|
|
## View Layer
|
|
|
|
### Views (Widgets)
|
|
- Compose widgets to display UI
|
|
- Pass events to ViewModel via commands
|
|
- Receive data from ViewModel
|
|
- Contain minimal logic:
|
|
- Simple if-statements for conditional rendering
|
|
- Animation logic
|
|
- Layout logic based on device info
|
|
- Simple routing logic
|
|
|
|
**Important**: A View is not a single widget. Views are collections of widgets. One view may contain many widgets. ViewModels have one-to-one relationship with views, not individual widgets.
|
|
|
|
### ViewModels
|
|
- Transform repository data into UI state
|
|
- Maintain current UI state for rebuilds
|
|
- Expose commands (callback functions) for user actions
|
|
- Hold state that survives configuration changes
|
|
|
|
Responsibilities:
|
|
- Retrieve application data from repositories
|
|
- Filter, sort, aggregate data for presentation
|
|
- Track UI state (flags, carousel positions, etc.)
|
|
- Expose commands for button presses, form submissions, etc.
|
|
- Call repositories directly for simple operations, or call use-cases when the
|
|
logic is complex, reused, or spans multiple repositories.
|
|
|
|
## Model Layer
|
|
|
|
### Repositories
|
|
Single source of truth for model data. Each data type has one repository class.
|
|
|
|
Responsibilities:
|
|
- Poll data from services
|
|
- Transform raw data into domain models
|
|
- Handle business logic:
|
|
- Caching
|
|
- Error handling
|
|
- Retry logic
|
|
- Refreshing data (polling, user actions)
|
|
|
|
Output: Domain models as Streams or Futures
|
|
|
|
Relationships:
|
|
- Many-to-many with ViewModels
|
|
- One ViewModel can use multiple Repositories
|
|
- One Repository can be used by multiple ViewModels
|
|
- Repositories should never be aware of each other
|
|
|
|
### Use-cases (Optional)
|
|
Use-cases sit between ViewModels and Repositories only when they reduce
|
|
duplication or isolate complex business rules. Do not add them to simple CRUD
|
|
flows just to fill a layer.
|
|
|
|
### Services
|
|
Lowest layer, wrap API endpoints and expose async response objects.
|
|
|
|
Responsibilities:
|
|
- Isolate data-loading
|
|
- Stateless (no state held)
|
|
- One service per data source
|
|
|
|
Examples:
|
|
- Platform APIs (iOS, Android)
|
|
- REST endpoints
|
|
- Local files
|
|
- Databases
|
|
|
|
Relationships:
|
|
- Many-to-many with Repositories
|
|
- One Repository can use multiple Services
|
|
- One Service can be used by multiple Repositories
|
|
|
|
## Data Flow
|
|
|
|
### User Interaction Flow
|
|
1. View: User interaction triggers event
|
|
2. View: Event handler calls ViewModel command
|
|
3. ViewModel: Command calls Repository directly, or a Use-case when justified
|
|
4. Repository: Updates data and returns new data
|
|
5. ViewModel: Saves new state
|
|
6. View: UI rebuilds with new state
|
|
|
|
### Data-Originated Flow
|
|
1. Repository: Polls service for new data
|
|
2. Repository: Updates data
|
|
3. ViewModel: Receives new data from Repository
|
|
4. View: UI rebuilds with new state
|
|
|
|
## Benefits
|
|
|
|
- **Testability**: Test ViewModel logic by mocking Repositories
|
|
- **Maintainability**: Clear separation of concerns
|
|
- **Scalability**: Easy to add features without breaking existing code
|
|
- **Reusability**: Components have well-defined interfaces
|