
In this article, we will clearly explain the basics of “MVVM,” a term often heard in app development.
We will also introduce code examples implemented in Flutter.
Overview of MVVM
MVVM (Model-View-ViewModel) is a software design pattern that separates an app’s logic and UI (User Interface), aiming to improve development efficiency and maintainability.
It is one of the methods for designing applications by roles such as “screen display,” “data manipulation,” and “data definition.” Similar concepts include MVC and other methodologies.
Components of MVVM
MVVM consists of three components: Model, View, and ViewModel.
View
The UI part displayed to the user.
The data to be displayed is obtained from the ViewModel.
In Flutter, it is represented by Widgets.
ViewModel
The part that handles the app’s complex logic.
Processes data (calculations, decisions, etc.) to be passed to the View.
Data is obtained from the Model.
Model
Defines data and performs CRUD operations on it.
Repository (Supplement)
Used for retrieving initial data and saving persistent data.
Performs CRUD processing of the database and data processing, and passes data to the ViewModel.
Relationships Between MVVM Elements
The relationships between the structural elements are as follows:

In Flutter, state changes are detected and implemented using Riverpod. By using Riverpod, state changes in the ViewModel and Model are detected in the View, creating a flow where the screen is redrawn.
Advantages of MVVM
The greatest advantage of MVVM is that since the display (View), behavior (ViewModel), and data (Model) are separated, each becomes simpler and easier to manage.
However, a drawback is that changes to the Model can potentially affect the ViewModel and View, increasing the range of changes required.
MVVM and Repository Code Example and Explanation
Below is an example of implementing the MVVM pattern using Riverpod in Flutter.
// User model
class User {
final String name;
final String email;
User(this.name, this.email);
}
// Repository
// In a real scenario, data would be fetched from a DB or API, but here we return fixed data.
class UserRepository {
Future<User> fetchUser() async {
return User('Goda Takeshi', 'Jaian@com');
}
}
// UserViewModel
final userProvider = FutureProvider<User>((ref) async {
final repository = UserRepository();
return repository.fetchUser();
});
// UserView
class UserView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider);
return Scaffold(
appBar: AppBar(title: Text('MVVM Example')),
body: user.when(
data: (user) => Text('User Name: ${user.name}'),
loading: () => CircularProgressIndicator(),
error: (e, stack) => Text('Error: $e'),
),
);
}
}
UserView monitors userProvider with “final user = ref.watch(userProvider).” userProvider defines the type using the User class and calls UserRepository. While UserRepository returns fixed data, in a real application, it would fetch and update data from a DB or API.
Conclusion
We introduced the MVVM design pattern. While MVVM is not necessarily the best design for all Flutter projects, it can be useful depending on the app or project. We hope this article helps you understand MVVM better and makes it easier to read applications using this design pattern.