Skip to content

jads147/concepts_learning

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

8 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Flutter Concepts Learning - MVVM Template

Ein umfassendes Flutter-Lernprojekt, das wichtige Software-Engineering-Konzepte demonstriert.

🎯 Lernziele

Dieses Projekt zeigt Best Practices fΓΌr:

  • MVVM (Model-View-ViewModel) Architektur
  • Repository Pattern fΓΌr Datenabstraktion
  • Dependency Injection mit Provider
  • State Management mit ChangeNotifier
  • Testing (Unit Tests & Widget Tests)
  • Clean Architecture Prinzipien

πŸ“ Projektstruktur

lib/
β”œβ”€β”€ models/           # Datenmodelle (User)
β”œβ”€β”€ services/         # API-Services (HTTP-Kommunikation)
β”œβ”€β”€ repositories/     # Repository Pattern (Datenabstraktion + Caching)
β”œβ”€β”€ viewmodels/       # ViewModels (Business-Logik + State)
└── views/            # UI-Komponenten (Screens & Widgets)

test/
β”œβ”€β”€ models/           # Model Tests
β”œβ”€β”€ repositories/     # Repository Tests (mit Mocks)
β”œβ”€β”€ viewmodels/       # ViewModel Tests (mit Mocks)
└── views/            # Widget Tests

πŸ—οΈ Architektur-Übersicht

1. Model Layer (models/user.dart)

  • Datenstrukturen mit fromJson / toJson
  • Immutable mit final fields
  • copyWith fΓΌr Updates
  • Equality & HashCode
class User {
  final int id;
  final String name;
  final String email;

  factory User.fromJson(Map<String, dynamic> json) { ... }
  Map<String, dynamic> toJson() { ... }
  User copyWith({...}) { ... }
}

2. Service Layer (services/api_service.dart)

  • Abstrakte Interfaces fΓΌr Testbarkeit
  • HTTP-Kommunikation isoliert
  • Error Handling mit Custom Exceptions
  • Dependency Injection Ready
abstract class ApiService {
  Future<List<User>> fetchUsers();
}

class ApiServiceImpl implements ApiService {
  final http.Client client; // Injected!
  // ...
}

3. Repository Layer (repositories/user_repository.dart)

  • Abstrahiert Datenquellen (API, Cache, DB)
  • Implementiert Caching-Strategien
  • Business-Logik fΓΌr Datenzugriff
  • Kombiniert mehrere Services
abstract class UserRepository {
  Future<List<User>> getUsers();
  void clearCache();
}

class UserRepositoryImpl implements UserRepository {
  final ApiService apiService; // Injected!
  List<User>? _cachedUsers; // Caching
  // ...
}
  • Erweitert ChangeNotifier fΓΌr State Management
  • Kommuniziert mit Repositories
  • UI-unabhΓ€ngige Business-Logik
  • Verwaltung von Loading/Error/Success States
class UserListViewModel extends ChangeNotifier {
  final UserRepository repository; // Injected!

  ViewState _state = ViewState.idle;
  List<User> _users = [];

  Future<void> loadUsers() async {
    _state = ViewState.loading;
    notifyListeners(); // UI wird aktualisiert!

    _users = await repository.getUsers();
    _state = ViewState.success;
    notifyListeners();
  }
}
  • Stateless/Stateful Widgets
  • Consumer<T> fΓΌr reactive Updates
  • context.read<T>() fΓΌr Methoden-Aufrufe
  • Keine Business-Logik
Consumer<UserListViewModel>(
  builder: (context, viewModel, child) {
    if (viewModel.isLoading) return CircularProgressIndicator();
    if (viewModel.hasError) return ErrorWidget();
    return ListView(children: ...);
  },
)

πŸ”§ Dependency Injection Setup (main.dart)

MultiProvider erstellt eine Dependency-Hierarchie:

MultiProvider(
  providers: [
    // 1. Service Layer
    Provider<ApiService>(
      create: (_) => ApiServiceImpl(client: http.Client()),
    ),

    // 2. Repository Layer (nutzt ApiService)
    ProxyProvider<ApiService, UserRepository>(
      update: (_, apiService, _) => UserRepositoryImpl(apiService: apiService),
    ),

    // 3. ViewModel Layer (nutzt Repository)
    ChangeNotifierProxyProvider<UserRepository, UserListViewModel>(
      create: (ctx) => UserListViewModel(repository: ctx.read<UserRepository>()),
      update: (_, repo, vm) => vm ?? UserListViewModel(repository: repo),
    ),
  ],
  child: MaterialApp(...),
)

πŸ§ͺ Testing

Unit Tests

Model Tests (test/models/user_test.dart):

  • JSON Serialisierung/Deserialisierung
  • copyWith FunktionalitΓ€t
  • Equality & HashCode

Repository Tests (test/repositories/user_repository_test.dart):

@GenerateMocks([ApiService])
void main() {
  late MockApiService mockApiService;
  late UserRepositoryImpl repository;

  setUp(() {
    mockApiService = MockApiService();
    repository = UserRepositoryImpl(apiService: mockApiService);
  });

  test('should cache users', () async {
    when(mockApiService.fetchUsers()).thenAnswer((_) async => testUsers);

    await repository.getUsers(); // 1. API Call
    await repository.getUsers(); // Von Cache

    verify(mockApiService.fetchUsers()).called(1); // Nur 1x!
  });
}

ViewModel Tests (test/viewmodels/user_list_viewmodel_test.dart):

  • State Transitions (idle β†’ loading β†’ success)
  • Error Handling
  • Repository Interaktionen

Widget Tests

Screen Tests (test/views/user_list_screen_test.dart):

testWidgets('shows loading indicator when loading', (tester) async {
  when(mockViewModel.isLoading).thenReturn(true);

  await tester.pumpWidget(
    ChangeNotifierProvider<UserListViewModel>.value(
      value: mockViewModel,
      child: UserListScreen(),
    ),
  );

  expect(find.byType(CircularProgressIndicator), findsOneWidget);
});

Tests ausfΓΌhren

# Mocks generieren
flutter pub run build_runner build --delete-conflicting-outputs

# Alle Tests ausfΓΌhren
flutter test

# Mit Coverage
flutter test --coverage

πŸš€ App starten

# Dependencies installieren
flutter pub get

# App starten
flutter run

# Tests ausfΓΌhren
flutter test

πŸ“š Konzepte im Detail

MVVM vs. Erweiterte Architektur

Was ist "pures" MVVM?

MVVM im klassischen Sinne hat nur 3 Schichten:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  View (UI)              β”‚  ← UserListScreen
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ViewModel (UI-Logik)   β”‚  ← UserListViewModel
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Model (Daten)          β”‚  ← User-Klasse
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Pure MVVM: ViewModel wΓΌrde direkt HTTP-Calls machen

// ❌ Pure MVVM (nicht empfohlen für grâßere Apps)
class UserListViewModel extends ChangeNotifier {
  Future<void> loadUsers() async {
    final response = await http.get('https://api.com/users'); // Direkt im ViewModel!
    _users = jsonDecode(response.body);
    notifyListeners();
  }
}

Dieses Projekt: Erweiterte MVVM-Architektur

Dieses Projekt nutzt eine erweiterte MVVM-Architektur mit zusΓ€tzlichen Schichten:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  View (UI)                          β”‚  ← UserListScreen
β”‚  - Zeigt Daten an                   β”‚
β”‚  - Reagiert auf User-Input          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ViewModel (UI-Logik)               β”‚  ← UserListViewModel
β”‚  - Verwaltet UI-State               β”‚  βœ… Teil von MVVM
β”‚  - Holt Daten vom Repository        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Repository (Data-Logik)            β”‚  ← UserRepository
β”‚  - Caching                          β”‚  ⭐ ZUSΓ„TZLICHE SCHICHT
β”‚  - Daten kombinieren                β”‚  (Nicht in purem MVVM)
β”‚  - Business-Logik fΓΌr Daten         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Service (Externe Daten)            β”‚  ← ApiService
β”‚  - HTTP-Calls                       β”‚  ⭐ ZUSΓ„TZLICHE SCHICHT
β”‚  - Datenbank-Zugriff                β”‚  (Nicht in purem MVVM)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Model (Datenstruktur)              β”‚  ← User-Klasse
β”‚  - Nur Daten                        β”‚  βœ… Teil von MVVM
β”‚  - fromJson/toJson                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Ist "pure MVVM" normal?

Nein! In der Praxis nutzen fast alle professionellen Apps erweiterte Architekturen:

Ansatz Wann verwendet? Beispiele
Pure MVVM Sehr kleine Apps, Prototypen Todo-App Demo, einfache Tutorials
MVVM + Repository Kleine bis mittlere Apps Die meisten Flutter Apps
MVVM + Repository + Service Mittlere bis große Apps
Clean Architecture Sehr große Enterprise Apps Banking Apps, E-Commerce

Vorteile der erweiterten Architektur

βœ… Vorteile:

  • Separation of Concerns: Jede Schicht hat genau eine Aufgabe
  • Testbarkeit: Jede Schicht kann isoliert getestet werden
  • Austauschbar: API β†’ lokale DB ohne ViewModel zu Γ€ndern
  • Caching: An einem Ort (Repository), nicht in jedem ViewModel
  • Skalierbarkeit: Einfach neue Features hinzufΓΌgen
  • Team-Arbeit: Verschiedene Entwickler an verschiedenen Schichten

❌ Nachteile:

  • Mehr Code: Mehr Dateien, mehr Boilerplate
  • KomplexitΓ€t: Steile Lernkurve fΓΌr AnfΓ€nger
  • Overhead: FΓΌr kleine Apps ΓΌbertrieben
  • Mehr Abstraktion: Schwieriger zu debuggen

Wann welchen Ansatz nutzen?

// Kleine App (< 5 Screens):
View β†’ ViewModel β†’ HTTP (direkt)

// Mittlere App (5-20 Screens):
View β†’ ViewModel β†’ Repository β†’ HTTP
                               β†˜ Cache

// Große App (20+ Screens):
View β†’ ViewModel β†’ Repository β†’ Service β†’ HTTP/DB
                               β†˜ Cache
                               β†˜ Offline-Sync

MVVM (Model-View-ViewModel)

Vorteile:

  • βœ… Klare Trennung von UI und Logik
  • βœ… Testbar ohne UI
  • βœ… Wiederverwendbare ViewModels
  • βœ… Reaktive UI-Updates

Datenfluss:

View ← notifyListeners() ← ViewModel ← Repository ← Service ← API
View β†’ Aktion β†’ ViewModel β†’ Repository β†’ Service β†’ API

Repository Pattern

Vorteile:

  • βœ… Abstrahiert Datenquellen
  • βœ… ErmΓΆglicht Caching
  • βœ… Austauschbare Implementierungen
  • βœ… Zentrale Datenzugriff-Logik

Dependency Injection

Vorteile:

  • βœ… Loose Coupling
  • βœ… Testbarkeit (Mocking)
  • βœ… FlexibilitΓ€t
  • βœ… Single Responsibility

Provider Pattern

Vorteile:

  • βœ… Built-in in Flutter
  • βœ… Reactive State Management
  • βœ… Scoped Dependencies
  • βœ… Efficient Rebuilds

πŸŽ“ Was du hier lernst

  1. Clean Architecture: Schichten-Trennung fΓΌr wartbaren Code
  2. SOLID Prinzipien: Besonders Dependency Inversion
  3. Testing: Unit Tests mit Mocks, Widget Tests
  4. State Management: ChangeNotifier & Provider
  5. Async Programming: Futures, async/await
  6. Error Handling: Try-catch, Custom Exceptions
  7. Caching: In-Memory Caching Strategien

πŸ” NΓ€chste Schritte zum Lernen

  1. Erweitere das User-Model: FΓΌge Address, Company hinzu
  2. Implementiere CRUD: Create, Update, Delete User
  3. Persistenz: Speichere Daten lokal (SharedPreferences, SQLite)
  4. Navigation: Implementiere komplexere Navigation
  5. Themes: Dark Mode mit Provider
  6. Error States: Besseres Error Handling
  7. Integration Tests: End-to-End Tests

πŸ“– Weitere Ressourcen

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published