Introdução ao desenvolvimento mobile com Flutter

1. O que é Flutter e por que utilizá-lo?

Flutter é um framework de código aberto criado pelo Google e lançado oficialmente em 2017 para desenvolvimento de aplicativos mobile multiplataforma. Diferente de soluções que utilizam pontes JavaScript, Flutter compila diretamente para código nativo usando o motor gráfico Skia, garantindo desempenho próximo ao de aplicativos desenvolvidos com SDKs nativos.

Os principais diferenciais do Flutter incluem:

  • Hot Reload: permite visualizar alterações no código em menos de um segundo, sem perder o estado atual do aplicativo
  • Código único: uma base de código para Android, iOS e, recentemente, Web e Desktop
  • Desempenho nativo: compilação para código de máquina, sem interpretadores intermediários
  • Widgets personalizáveis: interface totalmente customizável sem depender de componentes nativos do sistema

Comparado ao React Native, Flutter oferece maior controle sobre a renderização e desempenho mais consistente. Já em relação ao Xamarin, o Flutter possui uma curva de aprendizado mais suave e documentação mais acessível.

2. Configuração do ambiente de desenvolvimento

Para iniciar com Flutter, siga os passos abaixo:

# Baixe o Flutter SDK em https://flutter.dev
# Extraia o arquivo em ~/development/flutter

# Configure o PATH no arquivo ~/.bashrc ou ~/.zshrc
export PATH="$PATH:$HOME/development/flutter/bin"

# Verifique a instalação
flutter doctor

O comando flutter doctor verifica se todos os componentes estão instalados corretamente: SDK Flutter, Android Studio, Xcode (para iOS), emuladores e conectividade com dispositivos.

Editores recomendados:
- VS Code: leve, com extensão oficial Flutter
- Android Studio: suporte completo para emuladores e depuração
- IntelliJ IDEA: similar ao Android Studio, mas mais genérico

Para testar, configure um emulador Android via Android Studio AVD Manager ou utilize um dispositivo físico com modo desenvolvedor ativado.

3. Estrutura de um projeto Flutter

Ao criar um projeto com flutter create meu_app, a estrutura gerada inclui:

meu_app/
  lib/
    main.dart          # Ponto de entrada do aplicativo
  android/             # Configurações específicas do Android
  ios/                 # Configurações específicas do iOS
  test/                # Testes unitários e de widget
  pubspec.yaml         # Gerenciamento de dependências e assets

O arquivo pubspec.yaml é fundamental:

name: meu_app
description: Um aplicativo Flutter de exemplo
version: 1.0.0

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  http: ^1.1.0          # Pacote para requisições HTTP
  provider: ^6.0.0      # Gerenciamento de estado

flutter:
  assets:
    - assets/images/     # Diretório de imagens

O main.dart inicializa o aplicativo:

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Meu App Flutter',
      home: const TelaInicial(),
    );
  }
}

4. Widgets: a base da interface em Flutter

No Flutter, tudo é widget. Cada elemento visual, desde um simples texto até layouts complexos, é representado por um widget.

Widgets Stateless vs Stateful:
- StatelessWidget: imutável, não mantém estado interno. Usado para componentes que não mudam após a construção.
- StatefulWidget: mutável, mantém estado que pode ser alterado durante a execução.

Exemplo de widgets comuns:

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Exemplo de Widgets')),
      body: Center(
        child: Container(
          padding: const EdgeInsets.all(16.0),
          decoration: BoxDecoration(
            color: Colors.blue,
            borderRadius: BorderRadius.circular(8.0),
          ),
          child: const Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Texto estilizado', style: TextStyle(fontSize: 20)),
              SizedBox(height: 10),
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.star, color: Colors.yellow),
                  SizedBox(width: 8),
                  Text('Avaliação: 5.0'),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

5. Gerenciamento de estado no Flutter

O gerenciamento de estado é crucial para aplicativos dinâmicos. O StatefulWidget oferece controle básico:

class ContadorWidget extends StatefulWidget {
  const ContadorWidget({super.key});

  @override
  State<ContadorWidget> createState() => _ContadorWidgetState();
}

class _ContadorWidgetState extends State<ContadorWidget> {
  int _contador = 0;

  @override
  void initState() {
    super.initState();
    // Inicialização de recursos
  }

  void _incrementar() {
    setState(() {
      _contador++;
    });
  }

  @override
  void dispose() {
    // Limpeza de recursos
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Contador: $_contador'),
        ElevatedButton(
          onPressed: _incrementar,
          child: const Text('Incrementar'),
        ),
      ],
    );
  }
}

Para projetos maiores, utilize Provider com ChangeNotifier:

class ContadorModel extends ChangeNotifier {
  int _valor = 0;
  int get valor => _valor;

  void incrementar() {
    _valor++;
    notifyListeners();
  }
}

Quando o aplicativo crescer, considere migrar para Bloc (para fluxos complexos) ou Riverpod (mais moderno e seguro).

6. Navegação e roteamento entre telas

A navegação básica utiliza Navigator:

// Navegar para nova tela
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => const SegundaTela()),
);

// Retornar à tela anterior
Navigator.pop(context);

Para passar parâmetros:

// Na tela de origem
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => DetalhesTela(item: itemSelecionado),
  ),
);

// Na tela de destino
class DetalhesTela extends StatelessWidget {
  final Item item;
  const DetalhesTela({super.key, required this.item});
  // ...
}

Roteamento nomeado com onGenerateRoute:

MaterialApp(
  onGenerateRoute: (settings) {
    if (settings.name == '/detalhes') {
      final item = settings.arguments as Item;
      return MaterialPageRoute(
        builder: (context) => DetalhesTela(item: item),
      );
    }
    return MaterialPageRoute(builder: (context) => const TelaInicial());
  },
);

7. Consumo de APIs e persistência local

Para consumir APIs REST, utilize o pacote http:

import 'dart:convert';
import 'package:http/http.dart' as http;

Future<List<Usuario>> buscarUsuarios() async {
  final response = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/users'),
  );

  if (response.statusCode == 200) {
    List<dynamic> dados = json.decode(response.body);
    return dados.map((json) => Usuario.fromJson(json)).toList();
  } else {
    throw Exception('Falha ao carregar usuários');
  }
}

class Usuario {
  final int id;
  final String nome;
  final String email;

  Usuario({required this.id, required this.nome, required this.email});

  factory Usuario.fromJson(Map<String, dynamic> json) {
    return Usuario(
      id: json['id'],
      nome: json['name'],
      email: json['email'],
    );
  }
}

Para persistência local:

import 'package:shared_preferences/shared_preferences.dart';

Future<void> salvarPreferencia() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('nome_usuario', 'João');
}

Future<String?> carregarPreferencia() async {
  final prefs = await SharedPreferences.getInstance();
  return prefs.getString('nome_usuario');
}

Para dados mais complexos, utilize sqflite para bancos SQLite.

8. Publicação e próximos passos

Para publicar seu aplicativo:

  1. Configurar ícones e splash screen: utilize o pacote flutter_launcher_icons
  2. Assinar o aplicativo: gere keystore para Android e configure perfis de distribuição para iOS
  3. Gerar APK: flutter build apk --release
  4. Gerar IPA: flutter build ios --release

Recursos para aprofundamento:
- Documentação oficial do Flutter
- Cursos na plataforma Dart Academy
- Comunidades no Discord e Stack Overflow

O Flutter continua evoluindo rapidamente, com suporte crescente para Web, Desktop e plataformas embarcadas. Dominar seus fundamentos abre portas para desenvolvimento multiplataforma eficiente e de alta qualidade.

Referências