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
- Variáveis e sistema de tipos:
int,double,String,bool; diferenças entrevar/final/const - Operações com coleções:
List<E>,Map<K,V>,Set<E>; collection-if / collection-for - Recursos de funções: parâmetros opcionais, parâmetros nomeados, funções anônimas e closures
- Programação assíncrona:
Future<T>,async/await,Stream<T> - Classes e mixins: construtores, padrão factory,
abstract class,mixin
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.
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));
}
> 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.
sequenceDiagram
participant Bob
participant ShopAPI
Bob->>ShopAPI: fetchProducts() [async]
ShopAPI-->>Bob: Future<List<Product>>
Bob->>Bob: await parses JSON
Bob->>Bob: Renders product list
> 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
> 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
// 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...';
}
> 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
> 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
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
> 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
> 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
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}')),
],
);
}
> 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
> 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
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);
> 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
> 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
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'),
)
> 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
> 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
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;
}
}
> 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
> 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
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)}');
});
> 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
> 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
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,
);
}
}
> 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
> 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
// 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);
}
> 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
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)}');
}
}
> 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.
Stringnã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
- Dart é fortemente tipado com null safety, detectando erros de tipo em tempo de compilação
var/final/constdistinguem mutabilidade e momento da atribuição- collection-if/collection-for simplifica a construção de listas de UI
async/awaittorna o código assíncrono plano e claro- Mixin permite reutilização horizontal de código; abstract class define contratos de tipo
- O padrão
copyWithpermite atualizações em objetos imutáveis
📝 Exercícios
- Básico (dificuldade ⭐): Crie uma classe
Usercom id (int), name (String), email (String?), e implemente um construtor factoryfromJson. - Intermediário (dificuldade ⭐⭐): Escreva uma função de requisição de rede simulada usando
async/awaitque retornaFuture<List<Product>>, usandoFuture.delayedpara simular um atraso de 2 segundos. - Desafio (dificuldade ⭐⭐⭐): Implemente uma classe
CartManagerque usaStreamControllerpara transmitir mudanças no carrinho, com suporte a adicionar/remover itens e operações de limpeza.