MVVM em Flutter: um guia para iniciantes

Tempo de leitura: 3 minutes

Model-View-ViewModel (MVVM) é um padrão de design popular no desenvolvimento de software, que separa o modelo de dados de um aplicativo, a interface do usuário e a lógica do aplicativo em camadas distintas. O padrão MVVM é amplamente utilizado em muitas plataformas, incluindo desenvolvimento móvel, para melhorar a organização, reutilização e manutenção do código.

Neste artigo, discutiremos como implementar o MVVM no Flutter, uma popular estrutura de desenvolvimento móvel que usa a linguagem de programação Dart. Também abordaremos as etapas necessárias para criar um aplicativo Flutter simples usando a arquitetura MVVM.

 

A arquitetura MVVM

Antes de mergulharmos na implementação do MVVM no Flutter, vamos dar uma olhada rápida nos três principais componentes da arquitetura MVVM:

  1. Model: O modelo de dados representa os dados e a lógica de negócios do aplicativo. Ele encapsula os dados e fornece métodos para manipulá-los.
  2. View: A visualização (view) é a interface do usuário (IU) do aplicativo. Ele exibe os dados e permite que o usuário interaja com eles.
  3. ViewModel: O ViewModel é responsável por conectar o Model e a View. Ele fornece dados para a exibição e comunica as interações do usuário ao modelo.

Implementando MVVM no Flutter

Para implementar o MVVM no Flutter, precisamos criar três componentes principais: o Model, o View e o ViewModel. Aqui estão as etapas para criar um aplicativo Flutter simples usando MVVM:

Etapa 1: criar o modelo

A primeira etapa é criar o modelo de dados para nosso aplicativo. No Flutter, podemos definir uma classe de modelo simples que contém os dados que queremos exibir na interface do usuário. Por exemplo, vamos criar uma classe Person com duas propriedades: nome e idade.

class Person {
  String name;
  int age;
  
  Person({required this.name, required this.age});
}

Etapa 2: criar o ViewModel

Em seguida, precisamos criar a classe ViewModel. No Flutter, podemos definir uma classe ViewModel simples que contém os dados e fornece métodos para manipulá-los. Por exemplo, vamos criar uma classe PersonViewModel que contenha uma lista de objetos Person e forneça métodos para adicionar, remover e atualizar pessoas.

import 'package:flutter/material.dart';

import 'person.dart';

class PersonViewModel extends ChangeNotifier {
  List<Person> _persons = [];

  List<Person> get persons => _persons;

  void addPerson(Person person) {
    _persons.add(person);
    notifyListeners();
  }

  void removePerson(Person person) {
    _persons.remove(person);
    notifyListeners();
  }

  void updatePerson(Person oldPerson, Person newPerson) {
    final index = _persons.indexOf(oldPerson);
    _persons[index] = newPerson;
    notifyListeners();
  }
}

A classe ViewModel estende a classe ChangeNotifier, que é uma classe interna do Flutter que fornece uma maneira de notificar a interface do usuário quando os dados são alterados.

Etapa 3: criar a exibição

A etapa final é criar a IU, que é o componente View. No Flutter, podemos criar a interface do usuário usando os widgets fornecidos pelo framework. Por exemplo, vamos criar um widget ListView simples que exibe uma lista de pessoas e permite ao usuário adicioná-las, removê-las e atualizá-las.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'person.dart';
import 'person_view_model.dart';

class PersonListView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Person List')),
      body: Consumer<PersonViewModel>(
        builder: (context, viewModel, child) {
          return ListView.builder(
            itemCount: viewModel.persons.length,
            itemBuilder: (context, index) {
              final person = viewModel.person;
              return ListTile(
                title: Text(person.name),
                subtitle: Text('Age: ${person.age}'),
                trailing: IconButton(
                  icon: const Icon(Icons.delete),
                  onPressed: () => viewModel.removePerson(person),
                ),
                onTap: () => _showEditDialog(context, viewModel, person),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () => _showAddDialog(context),
      ),
    );
  }

  void _showAddDialog(BuildContext context) {
    final nameController = TextEditingController();
    final ageController = TextEditingController();
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Add Person'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              controller: nameController,
              decoration: const InputDecoration(labelText: 'Name'),
            ),
            TextField(
              controller: ageController,
              decoration: const InputDecoration(labelText: 'Age'),
              keyboardType: TextInputType.number,
            ),
          ],
        ),
        actions: [
          TextButton(
            child: const Text('Cancel'),
            onPressed: () => Navigator.of(context).pop(),
          ),
          TextButton(
            child: const Text('Add'),
            onPressed: () {
              final name = nameController.text;
              final age = int.parse(ageController.text);
              final person = Person(name: name, age: age);
              final viewModel =
                  Provider.of<PersonViewModel>(context, listen: false);
              viewModel.addPerson(person);
              Navigator.of(context).pop();
            },
          ),
        ],
      ),
    );
  }

  void _showEditDialog(
      BuildContext context, PersonViewModel viewModel, Person person) {
    final nameController = TextEditingController(text: person.name);
    final ageController = TextEditingController(text: person.age.toString());
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Edit Person'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              controller: nameController,
              decoration: const InputDecoration(labelText: 'Name'),
            ),
            TextField(
              controller: ageController,
              decoration: const InputDecoration(labelText: 'Age'),
              keyboardType: TextInputType.number,
            ),
          ],
        ),
        actions: [
          TextButton(
            child: const Text('Cancel'),
            onPressed: () => Navigator.of(context).pop(),
          ),
          TextButton(
            child: const Text('Save'),
            onPressed: () {
              final name = nameController.text;
              final age = int.parse(ageController.text);
              final oldPerson = person;
              final newPerson = Person(name: name, age: age);
              viewModel.updatePerson(oldPerson, newPerson);
              Navigator.of(context).pop();
            },
          ),
        ],
      ),
    );
  }
}

O componente View usa o pacote Provider para ouvir as alterações no ViewModel e reconstruir a interface do usuário quando necessário. O widget ListView exibe uma lista de pessoas usando o método itemBuilder e cada item na lista é um widget ListTile. O widget floatingActionButton exibe um botão que permite ao usuário adicionar uma nova pessoa à lista.

 

Conclusão

Neste artigo, discutimos como implementar o MVVM no Flutter, uma estrutura popular de desenvolvimento móvel que usa a linguagem de programação Dart. Também abordamos as etapas necessárias para criar um aplicativo Flutter simples usando a arquitetura MVVM. Ao usar a arquitetura MVVM, podemos melhorar a organização, reutilização e capacidade de manutenção do código, tornando nosso aplicativo mais fácil de desenvolver e manter a longo prazo.