Flutter: Curso Intensivo da Linguagem Dart

Dart é a alma do Flutter — dominar Dart é como aprender a ler partituras antes de reger uma sinfonia.

📋 Pré-requisitos: Você precisa ter concluído o seguinte primeiro

1. O Que Você Vai Aprender


2. Uma História Real de um Desenvolvedor

(1) O Problema: Inferno de Callbacks e Confusão de Tipos

Bob é um desenvolvedor back-end em transição para o Flutter. Acostumado com a flexibilidade do JavaScript, ele continuava esbarrando em paredes no Dart: confundindo var e final, aninhando callbacks assíncronos 5 níveis de profundidade, e recebendo erros constantes de tipos genéricos em coleções. Durante um cálculo de preços no carrinho de compras, um bug de conversão implícita de int para double transformou 99.99 dólares em 99 dólares — resultando em uma enxurrada de reclamações de usuários após o lançamento.

(2) A Solução do Dart

Dart é uma linguagem fortemente tipada que detecta erros de tipo em tempo de compilação; async/await torna o código assíncrono tão claro quanto o síncrono; final/const previne mutações acidentais.

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

// Simplified class definition (for demonstration)
class Product {
  final int id;
  final String name;
  final double price;
  final String? imageUrl;
  const Product({required this.id, required this.name, required this.price, this.imageUrl});
  factory Product.fromJson(Map<String, dynamic> json) => Product(
    id: json['id'] as int,
    name: json['name'] as String,
    price: (json['price'] as num).toDouble(),
    imageUrl: json['image_url'] as String?,
  );
  Product copyWith({String? name, double? price}) => Product(
    id: id, name: name ?? this.name, price: price ?? this.price, imageUrl: imageUrl,
  );
  static List<Product> fromJsonList(List data) => data.map((j) => Product.fromJson(j as Map<String, dynamic>)).toList();
}

class CartItem {
  final Product product;
  final double price;
  final int quantity;
  CartItem({required this.product, required this.price, this.quantity = 1});
}

// Strong typing prevents price bugs
double calculateTotal(List<CartItem> items) {
  return items.fold(0.0, (sum, item) => sum + item.price * item.quantity);
}

// async/await eliminates callback hell
Future<List<Product>> fetchProducts() async {
  final response = await http.get(Uri.parse('/api/v1/products'));
  return Product.fromJsonList(jsonDecode(response.body));
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

(3) O Resultado: Segurança de Tipos + Assincronicidade Clara

Após mudar para Dart, erros de tipo são detectados em tempo de compilação, o código assíncrono é achatado, e o problema de precisão de preços no carrinho de compras nunca mais ocorreu.


3. Variáveis e Sistema de Tipos

Dart é uma linguagem fortemente tipada com inferência de tipos e Sound Null Safety.

100%
sequenceDiagram
    participant Bob
    participant ShopAPI
    Bob->>ShopAPI: fetchProducts() [async]
    ShopAPI-->>Bob: Future<List<Product>>
    Bob->>Bob: await parses JSON
    Bob->>Bob: Renders product list
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

(1) Tipos Básicos

Tipo Descrição Exemplo
int Inteiro de 64 bits int quantity = 3;
double Ponto flutuante de 64 bits double price = 99.99;
String String UTF-16 String name = "ShopApp";
bool Booleano bool inStock = true;
num Pai de int + double num value = 42;

(2) Palavras-chave de Declaração de Variáveis

Palavra-chave Mutabilidade Momento da Atribuição Caso de Uso
var Mutável Tempo de execução Variáveis locais com tipos inferíveis
final Imutável Tempo de execução Constantes de tempo de execução (ex.: respostas de API)
const Imutável Tempo de compilação Constantes de tempo de compilação (ex.: valores de cor)
late Inicialização adiada Tempo de execução Deve ser atribuído antes do uso

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Comparação de declaração de variáveis

DART
// var: type inferred, mutable
var productName = 'Flutter T-Shirt';
productName = 'Dart Hoodie'; // OK

// final: runtime constant, immutable
final double totalPrice = 29.99 * 3;
// totalPrice = 100.0; // Error: final variable

// const: compile-time constant
const double discountRate = 0.15;
const String storeName = 'ShopApp';

// late: deferred initialization
late String userToken;
void login() {
  userToken = 'eyJhbGciOiJIUzI1NiIs...';
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

4. Operações com Coleções

(1) List, Map, Set

Coleção Características Sintaxe Literal
List<E> Ordenada, permite duplicatas [1, 2, 3]
Map<K,V> Pares chave-valor {'key': 'value'}
Set<E> Não ordenada, única {1, 2, 3}

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Operações básicas com coleções

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

// Product class definition shown above

// List: ordered collection
List<Product> products = [
  Product(id: 1, name: 'Laptop', price: 1299.99),
  Product(id: 2, name: 'Phone', price: 899.99),
];

// Map: key-value pairs
Map<String, double> prices = {
  'Laptop': 1299.99,
  'Phone': 899.99,
  'Tablet': 599.99,
};

// Set: unique items
Set<String> categories = {'Electronics', 'Clothing', 'Books'};

// Access and modify
products.add(Product(id: 3, name: 'Tablet', price: 599.99));
prices['Headphones'] = 199.99;
categories.add('Electronics'); // Ignored, already exists
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

(2) Collection-if / Collection-for

A sintaxe única do Dart para condicionais e laços dentro de coleções simplifica grandemente a construção de UI.

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: collection-if e collection-for

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

// Product class definition shown above

// collection-if: conditional items
bool isAdmin = true;
var menuItems = [
  'Home',
  'Products',
  'Cart',
  if (isAdmin) 'Admin Panel',
];

// collection-for: expand items
var productNames = ['Laptop', 'Phone', 'Tablet'];
var descriptions = [
  for (var name in productNames) '$name - Best Price'
];
// ['Laptop - Best Price', 'Phone - Best Price', 'Tablet - Best Price']

// Combined in Widget list
Widget buildProductList(List<Product> products) {
  return Column(
    children: [
      for (var p in products)
        ListTile(title: Text(p.name), trailing: Text('\$${p.price}')),
    ],
  );
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

5. Recursos de Funções

(1) Tipos de Parâmetros

Tipo Sintaxe Obrigatório Valor Padrão
Posicional (a, b) Sim Nenhum
Posicional opcional ([a = 0]) Não Suportado
Nomeado ({required a}) Obrigatório quando required Suportado
Nomeado opcional ({a = 0}) Não Suportado

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Padrões de parâmetros de função

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

// Named parameters (recommended for Flutter)
Widget buildPriceTag({
  required double price,
  String currency = 'USD',
  double fontSize = 16.0,
}) {
  return Text(
    '$currency \$${price.toStringAsFixed(2)}',
    style: TextStyle(fontSize: fontSize),
  );
}

// Call with named params
buildPriceTag(price: 99.99);
buildPriceTag(price: 49.99, currency: 'EUR', fontSize: 20.0);
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

(2) Funções Anônimas e Closures

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Closures no Flutter

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

// Product class definition shown above

// Anonymous function (lambda)
var expensiveItems = products.where((p) => p.price > 500.0).toList();

// Closure: captures surrounding variables
double discountThreshold = 100.0;
var discountedItems = products.map((p) {
  // Closure captures discountThreshold
  if (p.price > discountThreshold) {
    return p.copyWith(price: p.price * 0.9);
  }
  return p;
}).toList();

// Callback in Flutter widget
ElevatedButton(
  onPressed: () {
    // Closure captures context variables
    addToCart(product);
  },
  child: const Text('Add to Cart'),
)
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

6. Programação Assíncrona

(1) Future e async/await

Conceito Descrição Analogia
Future<T> Espaço reservado para uma operação assíncrona Número do pedido no restaurante
async Marca uma função como assíncrona Diz ao sistema "eu vou esperar"
await Espera um Future ser concluído Esperar a comida antes de comer

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Requisição de rede com async/await

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

// Product class definition shown above

Future<List<Product>> fetchProducts({int page = 1}) async {
  try {
    final response = await http.get(
      Uri.parse('https://api.shopapp.com/v1/products?page=$page'),
    );
    if (response.statusCode == 200) {
      final List data = jsonDecode(response.body);
      return data.map((json) => Product.fromJson(json)).toList();
    } else {
      throw Exception('Failed to load products: ${response.statusCode}');
    }
  } catch (e) {
    // Handle network errors
    rethrow;
  }
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

(2) Stream: Fluxo de Dados Assíncrono

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Stream para atualizações em tempo real do carrinho

DART
import 'dart:async';

// Simplified class definition (for demonstration)
class Product {
  final String name;
  final double price;
  const Product({required this.name, required this.price});
}

class CartItem {
  final Product product;
  int quantity;
  CartItem({required this.product, this.quantity = 1});
}

// StreamController for cart updates
class CartBloc {
  final _cartStream = StreamController<List<CartItem>>.broadcast();

  Stream<List<CartItem>> get cartStream => _cartStream.stream;
  List<CartItem> _items = [];

  void addToCart(Product product) {
    _items.add(CartItem(product: product, quantity: 1));
    _cartStream.sink.add(_items);
  }

  void dispose() {
    _cartStream.close();
  }
}

// Listen to stream
cartBloc.cartStream.listen((items) {
  print('Cart updated: ${items.length} items, total: \$${calculateTotal(items)}');
});
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

7. Classes e Mixins

(1) Classes e Construtores

Tipo de Construtor Sintaxe Propósito
Padrão ClassName() Instanciação padrão
Nomeado ClassName.named() Múltiplos métodos de inicialização
Factory factory ClassName() Controlar criação de instâncias (ex.: cache/singleton)
Const const ClassName() Objetos constantes de tempo de compilação

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Modelo de dados Product

DART
class Product {
  final int id;
  final String name;
  final double price;
  final String? imageUrl;
  final double rating;

  // Main constructor with named params
  const Product({
    required this.id,
    required this.name,
    required this.price,
    this.imageUrl,
    this.rating = 0.0,
  });

  // Named constructor from JSON
  factory Product.fromJson(Map<String, dynamic> json) {
    return Product(
      id: json['id'] as int,
      name: json['name'] as String,
      price: (json['price'] as num).toDouble(),
      imageUrl: json['image_url'] as String?,
      rating: (json['rating'] as num?)?.toDouble() ?? 0.0,
    );
  }

  // Copy with pattern for immutable updates
  Product copyWith({String? name, double? price}) {
    return Product(
      id: id,
      name: name ?? this.name,
      price: price ?? this.price,
      imageUrl: imageUrl,
      rating: rating,
    );
  }
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

(2) Mixin: Reutilização de Código

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Mixin para funcionalidade compartilhada

DART
// Product class definition shown above

// Mixin: reusable behavior without inheritance
mixin PriceFormatter {
  String formatPrice(double price, {String currency = 'USD'}) {
    return '$currency \$${price.toStringAsFixed(2)}';
  }

  String formatDiscount(double original, double discounted) {
    double percent = ((original - discounted) / original * 100);
    return '${percent.toStringAsFixed(0)}% OFF';
  }
}

// Apply mixin to class
class ProductCard with PriceFormatter {
  final Product product;
  ProductCard(this.product);

  String get priceLabel => formatPrice(product.price);
  String get discountLabel => formatDiscount(100.0, product.price);
}

// Abstract class: cannot be instantiated
abstract class Repository<T> {
  Future<List<T>> getAll();
  Future<T> getById(int id);
  Future<T> create(T item);
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

8. Exemplo Completo: Camada de Dados do ShopApp

⚙️ Instalar dependência: flutter pub add http

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

// Model
class Product {
  final int id;
  final String name;
  final double price;
  final String category;

  const Product({
    required this.id,
    required this.name,
    required this.price,
    required this.category,
  });

  factory Product.fromJson(Map<String, dynamic> json) => Product(
        id: json['id'] as int,
        name: json['name'] as String,
        price: (json['price'] as num).toDouble(),
        category: json['category'] as String,
      );
}

// Repository with async/await
class ProductRepository {
  final String baseUrl = 'https://api.shopapp.com/v1';

  Future<List<Product>> fetchProducts({int page = 1, int limit = 20}) async {
    final response = await http.get(
      Uri.parse('$baseUrl/products?page=$page&limit=$limit'),
    );
    if (response.statusCode != 200) {
      throw Exception('API error: ${response.statusCode}');
    }
    final List data = jsonDecode(response.body)['items'] as List;
    return data.map((j) => Product.fromJson(j as Map<String, dynamic>)).toList();
  }

  Future<Product> fetchById(int id) async {
    final response = await http.get(Uri.parse('$baseUrl/products/$id'));
    return Product.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
  }
}

// Usage
void main() async {
  final repo = ProductRepository();
  final products = await repo.fetchProducts(page: 1);
  for (var p in products) {
    print('${p.name}: \$${p.price.toStringAsFixed(2)}');
  }
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

❓ Perguntas Frequentes

P: Qual a diferença real entre final e const? R: final é atribuído uma vez em tempo de execução e não pode ser alterado; const é determinado em tempo de compilação e deve ser um literal ou construtor const.

P: Dart tem operador ternário? R: Sim, condição ? valor1 : valor2, além de ?? (coalescência nula) e ?. (acesso seguro a nulo).

P: Uma função async deve retornar um Future? R: Não, mas é recomendado. Uma função async executa de forma assíncrona mesmo que retorne void.

P: Qual a diferença entre Stream e Future? R: Future é um resultado assíncrono único; Stream é uma sequência de dados assíncronos produzidos continuamente, como cotações de ações em tempo real.

P: Como escolher entre mixin e abstract class? R: Use mixin para adicionar comportamento (ex.: logging, formatação) sem uma relação é-um; use abstract class para definir um contrato de tipo que exige uma relação é-um.

P: Dart suporta null safety? R: Dart 2.12+ impõe Sound Null Safety. String não pode ser nulo, String? pode ser nulo. O compilador garante que não haverá erros de referência nula em tempo de execução.


📖 Resumo


📝 Exercícios

  1. Básico (dificuldade ⭐): Crie uma classe User com id (int), name (String), email (String?), e implemente um construtor factory fromJson.
  2. Intermediário (dificuldade ⭐⭐): Escreva uma função de requisição de rede simulada usando async/await que retorna Future<List<Product>>, usando Future.delayed para simular um atraso de 2 segundos.
  3. Desafio (dificuldade ⭐⭐⭐): Implemente uma classe CartManager que usa StreamController para transmitir mudanças no carrinho, com suporte a adicionar/remover itens e operações de limpeza.

← Anterior | Próximo →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%