Master Flutter MVVM: Easy Guide for Efficient App Development

 

Introduction to MVVM Architecture


Introduction to MVVM Architecture

MVVM (Model-View-ViewModel) is a software architectural pattern widely used in building user interfaces, particularly in frameworks like WPF, Xamarin, and Flutter. It's an evolution of the MVC (Model-View-Controller) pattern, aiming to improve separation of concerns and facilitate testability and maintainability of code.

In MVVM, the UI is divided into three components:

Model: Represents the data and business logic of the application. It encapsulates the data and operations that manipulate that data. The model notifies the ViewModel of any changes, but it's unaware of the UI.

View: Represents the user interface. It's responsible for displaying data and capturing user input. Unlike MVC, the view in MVVM is passive and doesn't directly interact with the model. Instead, it binds to properties and commands exposed by the ViewModel.

ViewModel: Acts as an intermediary between the view and the model. It exposes properties and commands that the view can bind to. It retrieves data from the model, processes it, and prepares it for display in the view. The ViewModel also handles user interactions and updates the model accordingly.

MVVM promotes separation of concerns by ensuring that each component has a specific responsibility. This separation makes the codebase easier to understand, maintain, and test. Additionally, MVVM facilitates UI testing by decoupling the UI logic from the business logic.

For a visual walkthrough of the code implementation, you can watch a detailed explanation on YouTube:



Now, let's analyze the provided Flutter code to understand how MVVM is implemented.

textfieldcontroller.dart

This file defines the TextFieldController class, which is responsible for managing a TextEditingController used for text input fields.

import 'package:flutter/material.dart';

class TextFieldController extends ChangeNotifier {
  late TextEditingController textEditingController;

  TextFieldController(String? initialText) {
    textEditingController = TextEditingController(text: initialText);
    textEditingController.addListener(() {
      notifyListeners();
    });
  }

  String get text => textEditingController.text;
  set text(String value) => textEditingController.text = value;

  @override
  void dispose() {
    textEditingController.dispose();
    super.dispose();
  }
}

  • The TextFieldController class extends ChangeNotifier, indicating that it can be listened to for changes.
  • It contains a TextEditingController instance to manage text input.
  • The TextFieldController constructor initializes the textEditingController with an initial text value and adds a listener to it.
  • It provides getter and setter methods for accessing and updating the text value.
  • The dispose method is overridden to dispose of the TextEditingController when it's no longer needed.

usermodel.dart

This file defines the UserModel class, representing a user entity with email and password attributes.

class UserModel {
  String email;
  String password;

  UserModel({required this.email, required this.password});
}
The UserModel class defines two properties: email and password, representing the user's credentials.
It has a constructor that requires both email and password parameters.

loginviewmodel.dart

This file defines the LoginViewModel class, which serves as the ViewModel for the login view.

import 'package:apitutorial/controller/textfieldcontroller.dart';
import 'package:apitutorial/model/usermodel.dart';
import 'package:flutter/material.dart';

class LoginViewModel extends ChangeNotifier {
  final emailController = TextFieldController('');
  final passwordController = TextFieldController('');

  Future<UserModel?> login() async {
    if (emailController.text.isNotEmpty && passwordController.text.isNotEmpty) {
      return UserModel(email: emailController.text, password: passwordController.text);
    } else {
      return null;
    }
  }
}

  • The LoginViewModel class extends ChangeNotifier, indicating that it can notify listeners of changes.
  • It contains two instances of TextFieldController, one for managing the email field and another for managing the password field.
  • The login method validates the email and password fields. If both are not empty, it returns a UserModel instance; otherwise, it returns null.

login.dart

This file contains the UI code for the login screen.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'controller/textfieldcontroller.dart';
import 'viewmodels/loginviewmodel.dart';

class Login extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final loginViewModel = Provider.of<LoginViewModel>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Login'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: loginViewModel.emailController.textEditingController,
              decoration: InputDecoration(
                labelText: 'Email',
              ),
            ),
            SizedBox(height: 20),
            TextField(
              controller: loginViewModel.passwordController.textEditingController,
              obscureText: true,
              decoration: InputDecoration(
                labelText: 'Password',
              ),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                final user = await loginViewModel.login();
                if (user != null) {
                  // Navigate to next screen or perform desired action
                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Login Successfully.')));
                } else {
                  // Show error message
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(
                      content: Text('Invalid email or password'),
                    ),
                  );
                }
              },
              child: Text('Login'),
            ),
          ],
        ),
      ),
    );
  }
}

  • This class defines a stateless widget representing the login screen.
  • It utilizes the Provider.of<LoginViewModel>(context) method to obtain an instance of LoginViewModel.
  • The build method returns a Scaffold widget containing an AppBar and a Padding with a column of UI components.
  • Two TextField widgets are used for email and password input. They are bound to the respective TextEditingController instances from the LoginViewModel.
  • An ElevatedButton triggers the login action. Upon pressing, it calls the login method of LoginViewModel and displays appropriate feedback using SnackBar.

main.dart

This file serves as the entry point for the application.

import 'package:apitutorial/login.dart';
import 'package:apitutorial/viewmodels/loginviewmodel.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: ChangeNotifierProvider(create: (context) => LoginViewModel(), child: Login()),
    );
  }
}

  • The main function invokes runApp with an instance of MyApp.
  • MyApp is a stateless widget representing the root of the application.
  • It wraps the Login widget with a ChangeNotifierProvider, providing an instance of LoginViewModel to the widget tree.
This concludes the explanation of the provided Flutter code implementing the MVVM architecture. Each component plays a specific role in adhering to the MVVM pattern, resulting in a clean and maintainable codebase. Let me know if you need further clarification on any part of the code!

Comments

Popular posts from this blog

Push Notifications in .NET MAUI: A Comprehensive Guide

Explore the UI libraries available for .NET MAUI at no cost.

Push Notification using Firebase in xamarin form (Android and IOS)