Dart e Flutter: desenvolvimento multiplataforma

1. Introdução ao ecossistema Dart e Flutter

1.1. O que é Dart? Características da linguagem

Dart é uma linguagem de programação moderna, criada pelo Google, projetada para desenvolvimento de aplicações client-side. Suas principais características incluem tipagem forte e opcional, compilação Just-In-Time (JIT) para desenvolvimento com hot reload e Ahead-Of-Time (AOT) para produção, resultando em código nativo rápido. Dart também oferece suporte a programação orientada a objetos, funções de primeira classe e null safety, que elimina erros comuns de referência nula.

// Exemplo de sintaxe Dart com null safety
void main() {
  String? nome; // Variável anulável
  nome = 'Flutter';
  print('Olá, $nome!');

  int idade = 30; // Variável não anulável
  // idade = null; // Erro de compilação
}

1.2. O que é Flutter? Arquitetura baseada em widgets

Flutter é um framework de código aberto do Google que utiliza Dart para construir interfaces de usuário nativas para múltiplas plataformas. Sua arquitetura é baseada em widgets, onde cada elemento visual é um widget imutável. Flutter possui seu próprio motor de renderização (Skia, sendo substituído por Impeller), que desenha diretamente na tela, garantindo consistência visual entre plataformas.

// Widget básico em Flutter
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Exemplo Flutter')),
        body: const Center(child: Text('Olá, Mundo!')),
      ),
    );
  }
}

1.3. Vantagens do desenvolvimento multiplataforma com Flutter

Flutter oferece desempenho próximo ao nativo, hot reload para iteração rápida, e uma única base de código para Android, iOS, Web e Desktop. Isso reduz custos de desenvolvimento e manutenção, mantendo alta qualidade visual e funcional.

2. Fundamentos da linguagem Dart para Flutter

2.1. Sintaxe básica: variáveis, funções, classes e null safety

// Classes e null safety
class Pessoa {
  final String nome;
  int? idade; // Pode ser nulo

  Pessoa(this.nome, {this.idade});

  void apresentar() {
    print('Meu nome é $nome');
    if (idade != null) {
      print('Tenho $idade anos');
    }
  }
}

void main() {
  var pessoa = Pessoa('Ana', idade: 28);
  pessoa.apresentar();
}

2.2. Programação assíncrona: Futures, async/await e Streams

// Exemplo de async/await
Future<String> buscarDados() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Dados carregados';
}

void main() async {
  print('Carregando...');
  String resultado = await buscarDados();
  print(resultado);
}

2.3. Gerenciamento de dependências e pacotes com pubspec.yaml

# pubspec.yaml
name: meu_app
description: Aplicação Flutter
version: 1.0.0

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  http: ^1.1.0
  provider: ^6.0.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.0

3. Construindo interfaces com Widgets

3.1. Widgets Stateless vs Stateful

// StatelessWidget - imutável
class SaudacaoWidget extends StatelessWidget {
  final String nome;
  const SaudacaoWidget({super.key, required this.nome});

  @override
  Widget build(BuildContext context) {
    return Text('Olá, $nome!');
  }
}

// StatefulWidget - com estado mutável
class ContadorWidget extends StatefulWidget {
  const ContadorWidget({super.key});

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

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

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Contagem: $_contador'),
        ElevatedButton(
          onPressed: () => setState(() => _contador++),
          child: const Text('Incrementar'),
        ),
      ],
    );
  }
}

3.2. Layouts fundamentais

// Exemplo de layout com Row e Column
class LayoutExemplo extends StatelessWidget {
  const LayoutExemplo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                Container(width: 50, height: 50, color: Colors.red),
                Container(width: 50, height: 50, color: Colors.green),
                Container(width: 50, height: 50, color: Colors.blue),
              ],
            ),
            const SizedBox(height: 20),
            Expanded(
              child: Container(color: Colors.amber),
            ),
          ],
        ),
      ),
    );
  }
}

3.3. Navegação entre telas

// Navegação com rotas nomeadas
void main() {
  runApp(MaterialApp(
    initialRoute: '/',
    routes: {
      '/': (context) => const HomePage(),
      '/detalhes': (context) => const DetalhesPage(),
    },
  ));
}

// Navegação programática
Navigator.pushNamed(context, '/detalhes');

4. Gerenciamento de estado em aplicações Flutter

4.1. StatefulWidget e setState

// Gerenciamento simples com setState
class ListaTarefas extends StatefulWidget {
  @override
  State<ListaTarefas> createState() => _ListaTarefasState();
}

class _ListaTarefasState extends State<ListaTarefas> {
  final List<String> _tarefas = [];

  void _adicionarTarefa(String tarefa) {
    setState(() => _tarefas.add(tarefa));
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: _tarefas.length,
      itemBuilder: (context, index) => ListTile(
        title: Text(_tarefas[index]),
      ),
    );
  }
}

4.2. Provider e Riverpod

// Provider para gerenciamento de estado
class ContadorProvider extends ChangeNotifier {
  int _contador = 0;
  int get contador => _contador;

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

// Uso no widget
class MeuWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final contador = context.watch<ContadorProvider>();
    return Text('${contador.contador}');
  }
}

4.3. Bloc/Cubit

// Cubit para gerenciamento de estado com streams
class ContadorCubit extends Cubit<int> {
  ContadorCubit() : super(0);

  void incrementar() => emit(state + 1);
  void decrementar() => emit(state - 1);
}

5. Acesso a recursos nativos e APIs do dispositivo

5.1. Plugins e pacotes

// Uso do pacote camera
import 'package:camera/camera.dart';

Future<void> iniciarCamera() async {
  final cameras = await availableCameras();
  final camera = cameras.first;
  // Controlador da câmera
}

5.2. Comunicação com APIs REST

// Requisição HTTP com Dio
import 'package:dio/dio.dart';

Future<Map<String, dynamic>> buscarUsuario(int id) async {
  final dio = Dio();
  final response = await dio.get('https://api.exemplo.com/usuarios/$id');
  return response.data;
}

5.3. Persistência de dados

// SharedPreferences para dados simples
import 'package:shared_preferences/shared_preferences.dart';

Future<void> salvarPreferencia(String chave, String valor) async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString(chave, valor);
}

6. Testes, depuração e otimização de desempenho

6.1. Testes unitários e de widget

// Teste unitário
import 'package:flutter_test/flutter_test.dart';

void main() {
  test('Contador deve incrementar', () {
    final contador = Contador();
    contador.incrementar();
    expect(contador.valor, 1);
  });
}

6.2. Ferramentas de depuração

Flutter DevTools oferece inspeção de widgets, timeline de desempenho, análise de memória e rede. Para acessar, execute flutter devtools no terminal.

6.3. Otimização de desempenho

// Evitar rebuilds desnecessários com const
class WidgetOtimizado extends StatelessWidget {
  const WidgetOtimizado({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Widget constante');
  }
}

7. Publicação e deploy multiplataforma

7.1. Configuração para Android

// build.gradle (app-level)
android {
    compileSdkVersion 34
    defaultConfig {
        applicationId "com.exemplo.meuapp"
        minSdkVersion 21
        targetSdkVersion 34
    }
}

7.2. Configuração para iOS

Para iOS, configure o Xcode com certificados de desenvolvimento e distribuição, e utilize o App Store Connect para publicação.

7.3. Publicação para Web e Desktop

// Configuração para web
flutter build web --release
// Configuração para desktop
flutter build windows --release
flutter build macos --release
flutter build linux --release

Referências