# ViewModel Communication Patterns

## ❌ The Problem: Direct ViewModel Dependencies

```dart
class NotificationViewModel {
  final HomeViewModel _homeViewModel; // ❌ BAD: Direct dependency
  
  void markAsRead() {
    _homeViewModel.setUnreadNotificationFalse(); // ❌ Tight coupling
  }
}
```

**Why is this bad?**
- Tight coupling between ViewModels
- Difficult to test in isolation
- Circular dependency risk
- Violates Single Responsibility Principle
- Hard to maintain and scale

---

## ✅ Solution 1: Shared Service Pattern (BEST for your case)

Create a dedicated service that manages shared state.

### Implementation:

**Service:**
```dart
// lib/data/services/notification_state_service.dart
class NotificationStateService extends ChangeNotifier {
  bool _hasUnreadNotification = false;
  
  bool get hasUnreadNotification => _hasUnreadNotification;
  
  void setHasUnreadNotification(bool value) {
    if (_hasUnreadNotification != value) {
      _hasUnreadNotification = value;
      notifyListeners();
    }
  }
}
```

**ViewModels depend on the service:**
```dart
class HomeViewModel {
  final NotificationStateService _notificationStateService;
  
  void checkUnreadNotifications() async {
    // ... fetch from API
    _notificationStateService.setHasUnreadNotification(hasUnread);
  }
}

class NotificationViewModel {
  final NotificationStateService _notificationStateService;
  
  void markAsRead() {
    // ... mark as read
    _notificationStateService.setHasUnreadNotification(false);
  }
}
```

**UI consumes the service directly:**
```dart
class NotificationBadge extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final hasUnread = context.watch<NotificationStateService>()
        .hasUnreadNotification;
    
    return Badge(showBadge: hasUnread);
  }
}
```

**Benefits:**
- ✅ No ViewModel-to-ViewModel dependency
- ✅ Single source of truth
- ✅ Easy to test
- ✅ Follows SOLID principles
- ✅ Scalable

---

## ✅ Solution 2: Repository Pattern

Move the shared logic to a repository that both ViewModels depend on.

```dart
// Repository manages the domain logic
class NotificationRepository {
  bool _hasUnread = false;
  
  Future<bool> checkUnreadNotifications() async {
    final result = await api.checkUnread();
    _hasUnread = result.hasUnread;
    return _hasUnread;
  }
  
  Future<void> markAllAsRead() async {
    await api.markAllAsRead();
    _hasUnread = false;
  }
}

// ViewModels only depend on repository
class HomeViewModel {
  final NotificationRepository _repo;
  
  void init() async {
    final hasUnread = await _repo.checkUnreadNotifications();
    notifyListeners();
  }
}
```

---

## ✅ Solution 3: Event Bus / Stream Pattern

ViewModels communicate through events without knowing about each other.

```dart
// Event bus service
class AppEventBus {
  final _controller = StreamController<AppEvent>.broadcast();
  
  Stream<AppEvent> get events => _controller.stream;
  void emit(AppEvent event) => _controller.add(event);
}

// Events
abstract class AppEvent {}
class NotificationReadEvent extends AppEvent {}

// ViewModel 1: Emits events
class NotificationViewModel {
  final AppEventBus _eventBus;
  
  void markAsRead() {
    // ... mark as read
    _eventBus.emit(NotificationReadEvent());
  }
}

// ViewModel 2: Listens to events
class HomeViewModel {
  final AppEventBus _eventBus;
  
  HomeViewModel() {
    _eventBus.events.listen((event) {
      if (event is NotificationReadEvent) {
        _handleNotificationRead();
      }
    });
  }
}
```

---

## ✅ Solution 4: UI Coordination

Let the UI layer coordinate between ViewModels.

```dart
class NotificationScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final notificationVM = context.watch<NotificationViewModel>();
    final homeVM = context.watch<HomeViewModel>();
    
    return Button(
      onPressed: () {
        // UI coordinates the interaction
        notificationVM.markAsRead();
        homeVM.refreshNotificationBadge();
      },
    );
  }
}
```

**When to use:**
- Simple interactions
- UI-specific coordination
- Few ViewModels involved

---

## ✅ Solution 5: Riverpod Pattern (Advanced)

Use Riverpod's provider dependencies for better state management.

```dart
// Shared state provider
final notificationStateProvider = StateNotifierProvider<NotificationState, bool>(
  (ref) => NotificationState(),
);

class NotificationState extends StateNotifier<bool> {
  NotificationState() : super(false);
  
  void setHasUnread(bool value) => state = value;
}

// ViewModels depend on the provider
final homeViewModelProvider = Provider((ref) {
  final notificationState = ref.read(notificationStateProvider.notifier);
  return HomeViewModel(notificationState);
});
```

---

## 📊 Comparison Table

| Pattern | Complexity | Testability | Scalability | Best For |
|---------|-----------|-------------|-------------|----------|
| Shared Service | Low | High | High | Shared state across app |
| Repository | Medium | High | High | Domain logic sharing |
| Event Bus | Medium | Medium | High | Loosely coupled events |
| UI Coordination | Low | Medium | Low | Simple interactions |
| Riverpod | Medium | High | Very High | New projects |

---

## 🎯 Recommendation for Your Project

Use **Solution 1: Shared Service Pattern** because:

1. ✅ Your codebase already uses Provider
2. ✅ The notification state is truly shared across the app
3. ✅ Simple to implement and understand
4. ✅ Easy to test
5. ✅ Follows your modular architecture principle
6. ✅ No major refactoring required

---

## 📝 Implementation Checklist

- [x] Create `NotificationStateService`
- [x] Add service to dependency injection
- [x] Update `HomeViewModel` to use service
- [x] Update `NotificationViewModel` to use service
- [x] Remove direct ViewModel dependencies
- [x] Test the implementation
- [ ] Remove the example files when done

---

## 🧪 Testing Example

```dart
test('NotificationViewModel updates shared state', () {
  final stateService = NotificationStateService();
  final repository = MockNotificationRepository();
  final viewModel = NotificationViewModel(
    notificationsRepository: repository,
    notificationStateService: stateService,
  );
  
  // Act
  viewModel.markAllAsRead();
  
  // Assert
  expect(stateService.hasUnreadNotification, false);
});
```

---

## 🚀 Key Takeaway

**ViewModels should NEVER depend on other ViewModels.**

Instead:
- Use a **shared service** for shared state
- Use **repositories** for domain logic
- Use **event buses** for loose coupling
- Let the **UI coordinate** simple interactions

