As the app grew, state logic scattered across widgets became hard to test and maintain, and it wasn't clear which state management approach best fit an MVVM structure.
Separate the View (widgets) from the ViewModel (business logic and state) explicitly, and commit to one state management tool consistently rather than mixing several.
Step-by-Step Guide
Define a ViewModel class per screen that holds state and exposes methods, with no Flutter widget imports
Use ChangeNotifier + Provider for simpler apps that need light structure
Use Bloc/Cubit for apps needing stricter separation and predictable event-driven testability
Use Riverpod for compile-time safety and easier testing without needing a BuildContext
Keep widgets 'dumb' - they only read state and call ViewModel methods, never mutate state directly
Write unit tests for ViewModels in isolation, without rendering any widgets
Avoid mixing Provider and Bloc within the same feature - pick one pattern per module
Use dependency injection (get_it or Riverpod providers) to supply ViewModels and repositories to widgets
Found an issue with this solution?