Introdução
O Flutter, framework open-source da Google, permite o desenvolvimento de aplicativos multiplataforma de forma rápida e eficaz. Com a base em Dart, ele oferece uma experiência de desenvolvimento produtiva e com interfaces altamente responsivas. Neste tutorial, vamos explorar como construir uma aplicação utilizando as boas práticas da arquitetura MVVM (Model-View-ViewModel) que melhora a separação de responsabilidades no código. Além disso, vamos integrar ferramentas populares como Riverpod para gerenciamento de estados e Dio para chamadas HTTP. Se você é um desenvolvedor que quer se aprofundar no Flutter e implementar boas práticas de programação, este guia é para você. Vamos juntos construir uma aplicação completa do zero!
Etapas
Configuração do Ambiente de Desenvolvimento
Antes de tudo, você precisa configurar seu ambiente de desenvolvimento. Primeiro, instale o Flutter SDK em sua máquina através do site oficial do Flutter. Após a instalação, verifique se o flutter está acessível no terminal utilizando o comando `flutter doctor`. Este comando irá verificar a instalação e mostrar se algum componente está faltando.
commands# Verificar a instalação do Flutter
flutter doctorCriação do Projeto Flutter
Utilize o comando abaixo para criar um novo projeto Flutter. Substitua `my_flutter_app` pelo nome da sua aplicação. Este comando irá criar uma estrutura básica de diretórios para a aplicação.
commands# Criar um novo projeto Flutter
flutter create my_flutter_app
# Navegue para o diretório do projeto
cd my_flutter_appConfiguração de Dependências
Edite o arquivo `pubspec.yaml` para adicionar as dependências Riverpod e Dio. Essas dependências são necessárias para o gerenciamento de estado e requisições HTTP, respectivamente. Após adicionar as dependências, execute o comando `flutter pub get` para instalá-las.
pubspec.yamldependencies: flutter: sdk: flutter dio: ^5.0.0 flutter_riverpod: ^1.0.0
Criando a Estrutura MVVM
Para seguir a arquitetura MVVM, comece criando as pastas `model`, `view`, e `view_model` dentro do diretório `lib`. Em seguida, crie um modelo `User` simples que representará o usuário na aplicação.
User.dartclass User { final String name; final String email; User({required this.name, required this.email}); }
Implementação do ViewModel com Riverpod
Crie um arquivo chamado `user_view_model.dart` dentro da pasta `view_model`. Neste arquivo, implemente a classe `UserViewModel` que gerencia o estado do usuário utilizando Riverpod. A classe terá métodos para buscar os dados do usuário e notificar a view sobre alterações.
user_view_model.dartimport 'package:flutter_riverpod/flutter_riverpod.dart'; import 'User.dart'; import 'dio_client.dart'; final userViewModelProvider = StateNotifierProvider<UserViewModel, User?>((ref) => UserViewModel()); class UserViewModel extends StateNotifier<User?> { UserViewModel() : super(null); Future<void> fetchUser() async { final user = await DioClient().getUser(); state = user; } }
Implementando o Dio para Requisições HTTP
Crie um arquivo chamado `dio_client.dart` no diretório raiz do `lib` para encapsular chamadas HTTP. Neste exemplo, faremos uma requisição GET para uma API fictícia que retorna dados de um usuário.
dio_client.dartimport 'package:dio/dio.dart'; import 'User.dart'; class DioClient { final Dio _dio = Dio(); Future<User> getUser() async { final response = await _dio.get('https://api.example.com/user'); return User(name: response.data['name'], email: response.data['email']); } }
Construindo a View
Crie uma tela que exibirá as informações do usuário. Para isso, no diretório `view`, crie um arquivo chamado `user_screen.dart`. Utilize o `ConsumerWidget` para escutar as mudanças do `UserViewModel` e exibir as informações do usuário.
user_screen.dartimport 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'user_view_model.dart'; class UserScreen extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final user = ref.watch(userViewModelProvider); return Scaffold( appBar: AppBar(title: Text('User Info')), body: Center( child: user == null ? CircularProgressIndicator() : Text('Name: ${user.name}, Email: ${user.email}'), ), floatingActionButton: FloatingActionButton( onPressed: () { ref.read(userViewModelProvider.notifier).fetchUser(); }, child: Icon(Icons.refresh), ), ); } }
Implementação dos Testes
Para garantir que sua aplicação funcione conforme esperado, implemente testes para o ViewModel e a integração do Dio. Utilize o package `flutter_test` e o `mockito` para realizar os testes necessários.
user_view_model_test.dartimport 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'user_view_model.dart'; import 'dio_client.dart'; class MockDioClient extends Mock implements DioClient {} void main() { test('fetch user updates state', () async { final mockDioClient = MockDioClient(); when(mockDioClient.getUser()).thenAnswer((_) async => User(name: 'John Doe', email: 'john@example.com')); final userViewModel = UserViewModel(); await userViewModel.fetchUser(); expect(userViewModel.state?.name, 'John Doe'); }); }
Executando a Aplicação
Para executar a aplicação, utilize o comando abaixo. Certifique-se de que o emulador esteja ativo ou que um dispositivo físico esteja conectado. A aplicação estará disponível em seu emulador ou dispositivo.
commands# Executar a aplicação em um emulador ou dispositivo
flutter run
Conclusão
Neste tutorial, você aprendeu a criar uma aplicação Flutter utilizando a arquitetura MVVM com integração do Riverpod e Dio. Exploramos desde a configuração do ambiente até a implementação de testes unitários, cobrindo todos os aspectos necessários para desenvolver aplicações modernas e manuteníveis. A utilização de padrões arquiteturais e ferramentas de gerenciamento de estado permite uma melhor organização do código e facilita a escalabilidade das aplicações. Holisticamente, esses conhecimentos te preparam para criar aplicações mais complexas e robustas utilizando Flutter.