Skip to content

Instantly share code, notes, and snippets.

@tiagolpadua
Last active January 29, 2026 00:58
Show Gist options
  • Select an option

  • Save tiagolpadua/6b406c43097da3486bf48f25c40be6f2 to your computer and use it in GitHub Desktop.

Select an option

Save tiagolpadua/6b406c43097da3486bf48f25c40be6f2 to your computer and use it in GitHub Desktop.
Flutter - Material Complementar

Flutter Cheatsheet - Aula 01

Introducao ao Flutter e Dart

Objetivo: Referencia rapida para acompanhamento da aula e consulta posterior.


Indice

  1. Comandos Essenciais
  2. Estrutura do Projeto
  3. Widgets Fundamentais
  4. Layout Basico
  5. Estilizacao
  6. Navegacao
  7. Recursos e Documentacao

Comandos Essenciais

Setup e Verificacao

# Verificar instalacao do Flutter
flutter doctor
flutter doctor -v              # Versao detalhada

# Criar novo projeto
flutter create nome_projeto
flutter create --org com.empresa nome_projeto

# Listar dispositivos disponiveis
flutter devices

Documentacao: Get Started

Executar Aplicacao

# Rodar app
flutter run
flutter run -d chrome          # Web
flutter run -d macos           # macOS
flutter run -d <device-id>     # Dispositivo especifico

# Atalhos durante execucao
r    # Hot Reload (recarrega codigo)
R    # Hot Restart (reinicia app)
q    # Sair

Documentacao: flutter run

Gerenciamento de Dependencias

flutter pub get                # Instalar dependencias
flutter pub upgrade            # Atualizar dependencias
flutter pub outdated           # Ver desatualizadas
flutter pub add nome_pacote    # Adicionar pacote

Documentacao: Using packages

Build e Analise

flutter build apk              # Android APK
flutter build ios              # iOS
flutter build web              # Web
flutter analyze                # Analisar codigo
flutter test                   # Rodar testes
flutter clean                  # Limpar cache

Estrutura do Projeto

pubspec.yaml

name: meu_app
description: Descricao do app
version: 1.0.0+1

environment:
  sdk: ">=3.0.0 <4.0.0"

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0

flutter:
  uses-material-design: true
  assets:
    - assets/images/

Documentacao: pubspec file

Estrutura de Pastas Recomendada

lib/
├── main.dart           # Ponto de entrada
├── screens/            # Telas do app
├── widgets/            # Widgets reutilizaveis
├── models/             # Classes de dados
├── services/           # Logica de negocio
└── utils/              # Utilitarios

Widgets Fundamentais

Estrutura Minima do App

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Meu App',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: HomePage(),
    );
  }
}

Documentacao: MaterialApp

Scaffold

Scaffold(
  appBar: AppBar(
    title: Text('Titulo'),
    actions: [
      IconButton(icon: Icon(Icons.search), onPressed: () {}),
    ],
  ),
  body: Center(child: Text('Conteudo')),
  floatingActionButton: FloatingActionButton(
    onPressed: () {},
    child: Icon(Icons.add),
  ),
  bottomNavigationBar: BottomNavigationBar(...),
)

Documentacao: Scaffold

StatelessWidget

class MeuWidget extends StatelessWidget {
  final String titulo;

  const MeuWidget({required this.titulo});

  @override
  Widget build(BuildContext context) {
    return Text(titulo);
  }
}

Documentacao: StatelessWidget

StatefulWidget

class Contador extends StatefulWidget {
  @override
  State<Contador> createState() => _ContadorState();
}

class _ContadorState extends State<Contador> {
  int _count = 0;

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

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

Documentacao: StatefulWidget


Layout Basico

Container

Container(
  width: 100,
  height: 100,
  padding: EdgeInsets.all(16),
  margin: EdgeInsets.symmetric(vertical: 8),
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(8),
    boxShadow: [
      BoxShadow(color: Colors.black26, blurRadius: 4),
    ],
  ),
  child: Text('Conteudo'),
)

Documentacao: Container

Column e Row

// Column (vertical)
Column(
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text('Item 1'),
    Text('Item 2'),
  ],
)

// Row (horizontal)
Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  children: [
    Icon(Icons.star),
    Text('5.0'),
  ],
)

Documentacao: Column | Row

ListView

// ListView simples
ListView(
  children: [
    ListTile(title: Text('Item 1')),
    ListTile(title: Text('Item 2')),
  ],
)

// ListView.builder (eficiente para listas longas)
ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(title: Text(items[index]));
  },
)

Documentacao: ListView

Stack

Stack(
  alignment: Alignment.center,
  children: [
    Container(width: 200, height: 200, color: Colors.blue),
    Text('Sobreposto'),
  ],
)

Documentacao: Stack


Estilizacao

Text e TextStyle

Text(
  'Hello Flutter',
  style: TextStyle(
    fontSize: 24,
    fontWeight: FontWeight.bold,
    color: Colors.blue,
  ),
  textAlign: TextAlign.center,
  maxLines: 2,
  overflow: TextOverflow.ellipsis,
)

Documentacao: Text

Cores

// Cores predefinidas
Colors.red
Colors.blue[500]       // Com variacao

// Cores customizadas
Color(0xFF42A5F5)                      // Hex
Color.fromRGBO(66, 165, 245, 1.0)      // RGBA
Colors.blue.withOpacity(0.5)           // Com opacidade

Documentacao: Colors

EdgeInsets (Padding/Margin)

EdgeInsets.all(16)                              // Todos iguais
EdgeInsets.symmetric(horizontal: 16, vertical: 8)
EdgeInsets.only(left: 16, top: 8)
EdgeInsets.fromLTRB(16, 8, 16, 8)               // L, T, R, B

Documentacao: EdgeInsets

SizedBox (Espacamento)

SizedBox(height: 16)   // Espaco vertical
SizedBox(width: 16)    // Espaco horizontal

Navegacao

Navegacao Basica

// Ir para nova tela
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => NovaTela()),
);

// Voltar
Navigator.pop(context);

// Passar dados
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => DetalhesScreen(id: 123),
  ),
);

Documentacao: Navigation

Rotas Nomeadas

MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => HomeScreen(),
    '/detail': (context) => DetailScreen(),
  },
)

// Navegar
Navigator.pushNamed(context, '/detail');

Recursos e Documentacao

Documentacao Oficial

Recurso Link
Documentacao Flutter docs.flutter.dev
API Reference api.flutter.dev
Catalogo de Widgets Widget Catalog
Cookbook (Receitas) Cookbook
Codelabs codelabs.developers.google.com

Ferramentas

Ferramenta Link
DartPad (Playground) dartpad.dev
pub.dev (Pacotes) pub.dev
Flutter Gallery gallery.flutter.dev

Canais Oficiais

Canal Link
YouTube Flutter youtube.com/@flutterdev
Twitter/X @FlutterDev
Discord Discord Flutter

Linguagem Dart

Recurso Link
Dart Language Tour dart.dev/language
Effective Dart dart.dev/effective-dart
Dart Cheatsheet dart.dev/codelabs/dart-cheatsheet

Atalhos VS Code

Atalho Acao
stless Criar StatelessWidget
stful Criar StatefulWidget
Cmd/Ctrl + . Quick Fix / Refactor
Cmd/Ctrl + Shift + P Command Palette
F5 Iniciar Debug

Checklist da Aula

  • Flutter Doctor sem erros
  • Criar e rodar projeto Flutter
  • Entender StatelessWidget vs StatefulWidget
  • Usar setState() para atualizar UI
  • Criar layouts com Column, Row, Container
  • Navegar entre telas
  • Usar Hot Reload durante desenvolvimento

Proxima Aula: Widgets Basicos e Layout

Flutter Cheatsheet - Aula 02

Widgets Basicos e Layout

Objetivo: Referencia rapida para acompanhamento da aula e consulta posterior.


Indice

  1. StatelessWidget
  2. Widgets de Estrutura
  3. Widgets de Texto e Imagem
  4. Layout com Container
  5. Row e Column
  6. Stack e Positioned
  7. Recursos e Documentacao

StatelessWidget

Template Basico

class MeuWidget extends StatelessWidget {
  final String titulo;  // Propriedades SEMPRE final

  const MeuWidget({required this.titulo});  // const constructor

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(titulo),
    );
  }
}

// Uso
MeuWidget(titulo: 'Ola')
const MeuWidget(titulo: 'Constante')  // Mais eficiente

Documentacao: StatelessWidget

Quando Usar StatelessWidget

  • UI que nao muda apos criacao
  • Componentes puramente visuais
  • Widgets que dependem apenas de parametros

Widgets de Estrutura

MaterialApp

MaterialApp(
  title: 'Meu App',
  debugShowCheckedModeBanner: false,
  theme: ThemeData(
    primarySwatch: Colors.orange,
    brightness: Brightness.light,
  ),
  darkTheme: ThemeData.dark(),
  themeMode: ThemeMode.system,
  home: HomeScreen(),
)

Documentacao: MaterialApp

Scaffold

Scaffold(
  appBar: AppBar(title: Text('Titulo')),
  body: Center(child: Text('Conteudo')),
  floatingActionButton: FloatingActionButton(
    onPressed: () {},
    child: Icon(Icons.add),
  ),
  drawer: Drawer(...),
  bottomNavigationBar: BottomNavigationBar(...),
  backgroundColor: Colors.grey[100],
)

Documentacao: Scaffold

AppBar

AppBar(
  leading: IconButton(icon: Icon(Icons.menu), onPressed: () {}),
  title: Text('Titulo'),
  centerTitle: true,
  actions: [
    IconButton(icon: Icon(Icons.search), onPressed: () {}),
    IconButton(icon: Icon(Icons.settings), onPressed: () {}),
  ],
  elevation: 0,
  backgroundColor: Colors.orange,
  foregroundColor: Colors.white,
)

Documentacao: AppBar


Widgets de Texto e Imagem

Text

Text(
  'Ola, Mundo!',
  style: TextStyle(
    fontSize: 24,
    fontWeight: FontWeight.bold,
    color: Colors.blue,
  ),
  textAlign: TextAlign.center,
  maxLines: 2,
  overflow: TextOverflow.ellipsis,
)

Documentacao: Text

FontWeight

FontWeight.w100  // Thin
FontWeight.w300  // Light
FontWeight.w400  // Normal (regular)
FontWeight.w500  // Medium
FontWeight.w600  // SemiBold
FontWeight.w700  // Bold
FontWeight.w900  // Black

Image

// Imagem da rede
Image.network(
  'https://example.com/image.jpg',
  width: 200,
  height: 200,
  fit: BoxFit.cover,
)

// Imagem local (assets)
Image.asset('assets/images/logo.png')

Documentacao: Image

BoxFit

BoxFit.cover     // Preenche, pode cortar
BoxFit.contain   // Cabe inteira
BoxFit.fill      // Estica
BoxFit.fitWidth  // Ajusta largura
BoxFit.fitHeight // Ajusta altura
BoxFit.scaleDown // Diminui se necessario

Documentacao: BoxFit

CircleAvatar

CircleAvatar(
  radius: 40,
  backgroundImage: NetworkImage('url'),
  backgroundColor: Colors.grey,
  child: Text('AB'),  // Fallback
)

Documentacao: CircleAvatar

Icon

Icon(
  Icons.favorite,
  size: 32,
  color: Colors.red,
)

Documentacao: Icon | Material Icons


Layout com Container

Container Completo

Container(
  width: 200,
  height: 100,
  padding: EdgeInsets.all(16),
  margin: EdgeInsets.symmetric(horizontal: 20),
  alignment: Alignment.center,
  decoration: BoxDecoration(
    color: Colors.white,
    borderRadius: BorderRadius.circular(12),
    border: Border.all(color: Colors.grey),
    boxShadow: [
      BoxShadow(
        color: Colors.black12,
        blurRadius: 8,
        offset: Offset(0, 4),
      ),
    ],
  ),
  child: Text('Conteudo'),
)

Documentacao: Container

BoxDecoration Comum

// Card
BoxDecoration(
  color: Colors.white,
  borderRadius: BorderRadius.circular(8),
  boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 4)],
)

// Circulo
BoxDecoration(
  shape: BoxShape.circle,
  color: Colors.orange,
)

// Gradiente
BoxDecoration(
  gradient: LinearGradient(
    colors: [Colors.orange, Colors.deepOrange],
  ),
)

Documentacao: BoxDecoration


Row e Column

Column (Vertical)

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.start,
  mainAxisSize: MainAxisSize.min,
  children: [
    Widget1(),
    Widget2(),
    Widget3(),
  ],
)

Documentacao: Column

Row (Horizontal)

Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Widget1(),
    Widget2(),
    Widget3(),
  ],
)

Documentacao: Row

MainAxisAlignment

MainAxisAlignment.start        // Inicio
MainAxisAlignment.end          // Final
MainAxisAlignment.center       // Centro
MainAxisAlignment.spaceBetween // Espaco entre
MainAxisAlignment.spaceAround  // Espaco ao redor
MainAxisAlignment.spaceEvenly  // Espaco igual

CrossAxisAlignment

CrossAxisAlignment.start    // Inicio
CrossAxisAlignment.end      // Final
CrossAxisAlignment.center   // Centro
CrossAxisAlignment.stretch  // Estica

Documentacao: MainAxisAlignment

Expanded e Flexible

Row(
  children: [
    Container(width: 50),  // Fixo
    Expanded(
      child: Container(),  // Preenche resto
    ),
    Expanded(
      flex: 2,             // 2x mais espaco
      child: Container(),
    ),
  ],
)

Documentacao: Expanded


Stack e Positioned

Stack

Stack(
  alignment: Alignment.center,
  children: [
    // Fundo (primeiro)
    Container(width: 200, height: 200, color: Colors.blue),
    // Topo (ultimo)
    Text('Overlay'),
  ],
)

Documentacao: Stack

Positioned

Stack(
  children: [
    Container(...),
    Positioned(
      top: 10,
      right: 10,
      child: Icon(Icons.star),
    ),
    Positioned(
      bottom: 0,
      left: 0,
      right: 0,
      child: Container(height: 50),
    ),
    Positioned.fill(
      child: Center(child: Text('Centro')),
    ),
  ],
)

Documentacao: Positioned


Espacamento

EdgeInsets

EdgeInsets.all(16)                               // Todos iguais
EdgeInsets.symmetric(horizontal: 20, vertical: 10)
EdgeInsets.only(left: 16, top: 8)                // Especificos
EdgeInsets.fromLTRB(16, 8, 16, 8)                // L, T, R, B
EdgeInsets.zero                                   // Zero

Documentacao: EdgeInsets

SizedBox

SizedBox(height: 16)  // Espaco vertical
SizedBox(width: 8)    // Espaco horizontal

Spacer

Row(
  children: [
    Text('Esquerda'),
    Spacer(),        // Empurra para extremidades
    Text('Direita'),
  ],
)

Documentacao: Spacer


Cores e Alinhamento

Colors

// Predefinidas
Colors.red
Colors.blue
Colors.orange

// Com variacao
Colors.red[100]   // Claro
Colors.red[500]   // Normal
Colors.red[900]   // Escuro

// Customizadas
Color(0xFF42A5F5)              // Hex
Color.fromRGBO(66, 165, 245, 1.0)
Colors.blue.withOpacity(0.5)

Documentacao: Colors

Alignment

Alignment.topLeft
Alignment.topCenter
Alignment.topRight
Alignment.centerLeft
Alignment.center
Alignment.centerRight
Alignment.bottomLeft
Alignment.bottomCenter
Alignment.bottomRight

BorderRadius

BorderRadius.circular(12)
BorderRadius.only(
  topLeft: Radius.circular(12),
  topRight: Radius.circular(12),
)
BorderRadius.vertical(top: Radius.circular(12))
BorderRadius.horizontal(left: Radius.circular(12))

Documentacao: BorderRadius


Recursos e Documentacao

Documentacao Oficial

Recurso Link
Widget Catalog docs.flutter.dev/ui/widgets
Layout Tutorial docs.flutter.dev/ui/layout
Box Constraints docs.flutter.dev/ui/layout/constraints

Widgets Essenciais

Widget Documentacao
Container api.flutter.dev/flutter/widgets/Container
Row api.flutter.dev/flutter/widgets/Row
Column api.flutter.dev/flutter/widgets/Column
Stack api.flutter.dev/flutter/widgets/Stack
ListView api.flutter.dev/flutter/widgets/ListView

Ferramentas

Ferramenta Link
DartPad dartpad.dev
Flutter Gallery gallery.flutter.dev
Material Icons fonts.google.com/icons

Comparacao CSS para Flutter

CSS Flutter
div Container
span Text
img Image
display: flex Row / Column
flex-direction Row vs Column
justify-content mainAxisAlignment
align-items crossAxisAlignment
flex: 1 Expanded
position: absolute Stack + Positioned
padding Padding / Container.padding
margin Container.margin
border-radius BorderRadius
box-shadow BoxShadow

Atalhos VS Code

Atalho Acao
stless StatelessWidget
cont Container
col Column
row Row
txt Text
Cmd/Ctrl + . Wrap with widget

Checklist da Aula

  • Criar StatelessWidget com propriedades final
  • Configurar MaterialApp com tema
  • Montar Scaffold com AppBar e body
  • Estilizar texto com TextStyle
  • Usar imagens (network e asset)
  • Criar layouts com Container e BoxDecoration
  • Organizar com Row e Column
  • Sobrepor elementos com Stack
  • Aplicar padding e margin com EdgeInsets

Proxima Aula: StatefulWidget e Ciclo de Vida

Flutter Cheatsheet - Aula 03

StatefulWidget e Ciclo de Vida

Objetivo: Referencia rapida para acompanhamento da aula e consulta posterior.


Indice

  1. StatefulWidget
  2. setState
  3. Ciclo de Vida
  4. Keys
  5. Padroes Comuns
  6. Recursos e Documentacao

StatefulWidget

Template Basico

class MeuWidget extends StatefulWidget {
  final String titulo;  // Props sao final

  const MeuWidget({required this.titulo});

  @override
  State<MeuWidget> createState() => _MeuWidgetState();
}

class _MeuWidgetState extends State<MeuWidget> {
  int _contador = 0;  // Estado mutavel

  @override
  Widget build(BuildContext context) {
    return Text('${widget.titulo}: $_contador');
    //          ^ Acessa props via widget.
  }
}

Documentacao: StatefulWidget

Diferenca Stateless vs Stateful

Aspecto StatelessWidget StatefulWidget
Estado Sem estado interno Com estado mutavel
Props Apenas final final + vars no State
Rebuild Quando pai reconstroi Quando chama setState()
Lifecycle Apenas build() Ciclo completo
Uso UI estatica UI dinamica

setState

Uso Correto

// Simples
setState(() {
  _count++;
});

// Multiplas variaveis
setState(() {
  _count++;
  _lastUpdate = DateTime.now();
});

// Modificar antes, setState vazio
_count++;
setState(() {});

Com Operacoes Async

// CORRETO - verificar mounted
void _loadData() async {
  final data = await fetchData();
  if (mounted) {  // SEMPRE verificar!
    setState(() {
      _data = data;
    });
  }
}

// ERRADO - async dentro do setState
setState(() async {
  _data = await fetchData();  // NUNCA fazer isso!
});

Documentacao: setState


Ciclo de Vida

Ordem de Execucao

createState() -> initState() -> didChangeDependencies() -> build()
                     |                                        ^
               didUpdateWidget() <----------------------------|
                     |
                deactivate() -> dispose()

Template Completo

class _MeuWidgetState extends State<MeuWidget> {
  late Timer _timer;
  late ScrollController _controller;

  @override
  void initState() {
    super.initState();  // SEMPRE primeiro!
    _controller = ScrollController();
    _timer = Timer.periodic(Duration(seconds: 1), (_) {
      if (mounted) setState(() => _seconds++);
    });
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // Seguro usar Theme.of(context) aqui
  }

  @override
  void didUpdateWidget(MeuWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.prop != oldWidget.prop) {
      // Reagir a mudancas nas props
    }
  }

  @override
  void dispose() {
    _timer.cancel();
    _controller.dispose();
    super.dispose();  // SEMPRE ultimo!
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

Documentacao: State Lifecycle

Tabela de Metodos

Metodo Quando e chamado Uso comum
initState() State criado Timers, controllers, listeners
didChangeDependencies() Apos initState, InheritedWidget mudou Theme.of, MediaQuery
build() Apos setState, initState, didUpdate Construir UI
didUpdateWidget() Props mudaram Reagir a novas props
dispose() Removido definitivamente Cleanup!

Keys

ValueKey

// Para valores unicos
ListView.builder(
  itemBuilder: (ctx, i) => ListTile(
    key: ValueKey(items[i].id),  // ID unico
    title: Text(items[i].name),
  ),
)

Documentacao: ValueKey

ObjectKey

// Para objetos como identificador
ListTile(
  key: ObjectKey(item),
  title: Text(item.name),
)

GlobalKey

// Acessar state de fora
final _formKey = GlobalKey<FormState>();

Form(
  key: _formKey,
  child: Column(...),
)

// Usar
_formKey.currentState!.validate();
_formKey.currentState!.save();

Documentacao: GlobalKey

Quando Usar Keys

// SEMPRE em listas dinamicas
items.map((item) => TodoItem(
  key: ValueKey(item.id),
  item: item,
)).toList()

// Em ReorderableListView (obrigatorio)
ReorderableListView(
  children: items.map((item) => ListTile(
    key: ValueKey(item.id),
    title: Text(item.name),
  )).toList(),
)

// NAO usar index como key
key: ValueKey(index)  // Ruim!

Documentacao: When to use Keys


Padroes Comuns

Loading State

class _MyWidgetState extends State<MyWidget> {
  bool _isLoading = true;
  List<Item>? _items;
  String? _error;

  @override
  void initState() {
    super.initState();
    _loadData();
  }

  Future<void> _loadData() async {
    try {
      final items = await api.fetch();
      if (mounted) {
        setState(() {
          _items = items;
          _isLoading = false;
        });
      }
    } catch (e) {
      if (mounted) {
        setState(() {
          _error = e.toString();
          _isLoading = false;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) return CircularProgressIndicator();
    if (_error != null) return Text('Erro: $_error');
    return ListView(...);
  }
}

Timer Pattern

class _TimerState extends State<TimerWidget> {
  int _seconds = 0;
  Timer? _timer;
  bool _isRunning = false;

  void _start() {
    _timer = Timer.periodic(Duration(seconds: 1), (_) {
      if (mounted) setState(() => _seconds++);
    });
    setState(() => _isRunning = true);
  }

  void _pause() {
    _timer?.cancel();
    setState(() => _isRunning = false);
  }

  void _reset() {
    _timer?.cancel();
    setState(() {
      _seconds = 0;
      _isRunning = false;
    });
  }

  @override
  void dispose() {
    _timer?.cancel();  // IMPORTANTE!
    super.dispose();
  }
}

Documentacao: Timer

Checklist de Dispose

@override
void dispose() {
  // Controllers
  _textController.dispose();
  _scrollController.dispose();
  _animationController.dispose();
  _tabController.dispose();

  // Timers
  _timer?.cancel();

  // Streams
  _subscription?.cancel();
  _streamController?.close();

  // Focus
  _focusNode.dispose();

  super.dispose();
}

Recursos e Documentacao

Documentacao Oficial

Recurso Link
StatefulWidget api.flutter.dev/flutter/widgets/StatefulWidget
State Lifecycle api.flutter.dev/flutter/widgets/State
Keys api.flutter.dev/flutter/foundation/Key
Timer api.dart.dev/stable/dart-async/Timer

Tutoriais

Recurso Link
State Management Intro docs.flutter.dev/data-and-backend/state-mgmt/intro
Lifecycle Cookbook docs.flutter.dev/cookbook

Videos Recomendados

Video Link
Flutter Widget Lifecycle youtube.com/watch?v=FL_U8ORv-2Q
When to Use Keys youtube.com/watch?v=kn0EOS-ZiIc

Comparacao Flutter vs React

Flutter React Hooks
initState() useEffect(() => {}, [])
dispose() Cleanup do useEffect
didUpdateWidget() useEffect(() => {}, [deps])
setState() setX()
widget.prop props.prop
mounted - (hooks cancelam auto)

Atalhos VS Code

Atalho Acao
stful StatefulWidget completo
stless StatelessWidget
Cmd/Ctrl + . Quick fixes
F5 Debug

Checklist da Aula

  • Usar super.initState() primeiro
  • Usar super.dispose() ultimo
  • Verificar mounted antes de setState async
  • Dispose todos os controllers
  • Cancelar todos os timers
  • Usar Keys em listas dinamicas
  • Nao usar index como key
  • Nao fazer async dentro de setState

Proxima Aula: Formularios e Navegacao

Flutter Cheatsheet - Aula 04

Formularios, Navegacao e BuildContext

Objetivo: Referencia rapida para acompanhamento da aula e consulta posterior.


Indice

  1. Formularios
  2. TextEditingController
  3. Validacao
  4. Navegacao
  5. BuildContext
  6. InheritedWidget
  7. Recursos e Documentacao

Formularios

Template Basico

class MyForm extends StatefulWidget {
  @override
  State<MyForm> createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
  final _formKey = GlobalKey<FormState>();
  final _controller = TextEditingController();

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void _submit() {
    if (_formKey.currentState!.validate()) {
      // Processar dados
    }
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            controller: _controller,
            validator: (v) => v!.isEmpty ? 'Obrigatorio' : null,
          ),
          ElevatedButton(
            onPressed: _submit,
            child: Text('Enviar'),
          ),
        ],
      ),
    );
  }
}

Documentacao: Form

GlobalKey

final _formKey = GlobalKey<FormState>();

// Validar todos os campos
bool isValid = _formKey.currentState!.validate();

// Salvar (chama onSaved de cada campo)
_formKey.currentState!.save();

// Resetar formulario
_formKey.currentState!.reset();

Documentacao: FormState

InputDecoration

TextFormField(
  decoration: InputDecoration(
    labelText: 'Nome',
    hintText: 'Digite seu nome',
    prefixIcon: Icon(Icons.person),
    suffixIcon: Icon(Icons.clear),
    border: OutlineInputBorder(),
    filled: true,
    fillColor: Colors.grey[100],
    errorText: 'Mensagem de erro',
  ),
)

Documentacao: InputDecoration


TextEditingController

Uso Basico

final _controller = TextEditingController();

// Usar no campo
TextField(controller: _controller)

// Ler valor
String valor = _controller.text;

// Definir valor
_controller.text = 'Novo valor';

// Limpar
_controller.clear();

// SEMPRE dispose!
@override
void dispose() {
  _controller.dispose();
  super.dispose();
}

Documentacao: TextEditingController

Ouvir Mudancas

@override
void initState() {
  super.initState();
  _controller.addListener(() {
    print('Valor: ${_controller.text}');
  });
}

Tipos de Teclado

keyboardType: TextInputType.text,      // Padrao
keyboardType: TextInputType.number,    // Numeros
keyboardType: TextInputType.email,     // Email
keyboardType: TextInputType.phone,     // Telefone
keyboardType: TextInputType.url,       // URL
keyboardType: TextInputType.multiline, // Multiplas linhas

Validacao

Validacoes Comuns

// Campo obrigatorio
validator: (v) => v!.isEmpty ? 'Obrigatorio' : null

// Email
validator: (v) {
  if (v!.isEmpty) return 'Obrigatorio';
  if (!v.contains('@')) return 'Email invalido';
  return null;
}

// Senha (minimo 6)
validator: (v) {
  if (v!.isEmpty) return 'Obrigatorio';
  if (v.length < 6) return 'Minimo 6 caracteres';
  return null;
}

// Numero
validator: (v) {
  if (v!.isEmpty) return 'Obrigatorio';
  if (int.tryParse(v) == null) return 'Numero invalido';
  return null;
}

// Range numerico
validator: (v) {
  final n = int.tryParse(v!);
  if (n == null) return 'Numero invalido';
  if (n < 1 || n > 100) return 'Entre 1 e 100';
  return null;
}

Documentacao: Form Validation

DropdownButtonFormField

DropdownButtonFormField<String>(
  value: _selectedValue,
  decoration: InputDecoration(labelText: 'Opcao'),
  items: ['A', 'B', 'C'].map((item) {
    return DropdownMenuItem(value: item, child: Text(item));
  }).toList(),
  onChanged: (v) => setState(() => _selectedValue = v),
  validator: (v) => v == null ? 'Selecione' : null,
)

Documentacao: DropdownButtonFormField


Navegacao

Navegacao Basica

// Ir para nova tela (push)
Navigator.push(
  context,
  MaterialPageRoute(builder: (ctx) => NovaTela()),
);

// Voltar (pop)
Navigator.pop(context);

// Passar dados
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (ctx) => Detalhes(item: meuItem),
  ),
);

// Retornar dados
Navigator.pop(context, resultado);

// Aguardar resultado
final result = await Navigator.push<Tipo>(...);
if (result != null) {
  // usar result
}

Documentacao: Navigator

Rotas Nomeadas

// Configurar
MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (ctx) => HomeScreen(),
    '/profile': (ctx) => ProfileScreen(),
    '/settings': (ctx) => SettingsScreen(),
  },
)

// Navegar
Navigator.pushNamed(context, '/profile');

// Com argumentos
Navigator.pushNamed(
  context,
  '/details',
  arguments: {'id': 123},
);

// Receber argumentos
final args = ModalRoute.of(context)!.settings.arguments as Map;

Documentacao: Named Routes

Navegacao Avancada

// Substituir tela atual
Navigator.pushReplacement(context, route);
Navigator.pushReplacementNamed(context, '/home');

// Remover todas ate condicao
Navigator.pushAndRemoveUntil(
  context,
  route,
  (route) => false,  // Remove todas
);

// Pode voltar?
Navigator.canPop(context)

Documentacao: Navigation Cookbook


BuildContext

Acessando Dados

// Tema
Theme.of(context).primaryColor

// Tamanho da tela
MediaQuery.of(context).size.width
MediaQuery.of(context).size.height

// Navegacao
Navigator.of(context).push(...)

// Snackbar
ScaffoldMessenger.of(context).showSnackBar(...)

Documentacao: BuildContext

Cuidado com Async

// PERIGOSO - context pode estar invalido
void _load() async {
  await fetchData();
  Navigator.of(context).pop();  // Erro!
}

// SEGURO - capturar antes
void _load() async {
  final navigator = Navigator.of(context);
  await fetchData();
  navigator.pop();
}

// SEGURO - verificar mounted
void _load() async {
  await fetchData();
  if (mounted) {
    Navigator.of(context).pop();
  }
}

InheritedWidget

Criar

class AppSettings extends InheritedWidget {
  final bool isDarkMode;
  final VoidCallback toggle;

  const AppSettings({
    required this.isDarkMode,
    required this.toggle,
    required Widget child,
  }) : super(child: child);

  static AppSettings of(BuildContext context) {
    return context
      .dependOnInheritedWidgetOfExactType<AppSettings>()!;
  }

  @override
  bool updateShouldNotify(AppSettings old) {
    return isDarkMode != old.isDarkMode;
  }
}

Documentacao: InheritedWidget

Prover

class AppProvider extends StatefulWidget {
  final Widget child;
  const AppProvider({required this.child});

  @override
  State<AppProvider> createState() => _AppProviderState();
}

class _AppProviderState extends State<AppProvider> {
  bool _isDark = false;

  void _toggle() => setState(() => _isDark = !_isDark);

  @override
  Widget build(BuildContext context) {
    return AppSettings(
      isDarkMode: _isDark,
      toggle: _toggle,
      child: widget.child,
    );
  }
}

Consumir

Widget build(BuildContext context) {
  final settings = AppSettings.of(context);

  return Container(
    color: settings.isDarkMode ? Colors.black : Colors.white,
  );
}

Recursos e Documentacao

Documentacao Oficial

Recurso Link
Forms docs.flutter.dev/cookbook/forms
Form Validation docs.flutter.dev/cookbook/forms/validation
Navigation docs.flutter.dev/ui/navigation
Named Routes docs.flutter.dev/cookbook/navigation/named-routes

API Reference

Widget Link
Form api.flutter.dev/flutter/widgets/Form
TextFormField api.flutter.dev/flutter/material/TextFormField
Navigator api.flutter.dev/flutter/widgets/Navigator
InheritedWidget api.flutter.dev/flutter/widgets/InheritedWidget

Videos Recomendados

Video Link
Forms in Flutter youtube.com/watch?v=2rn3XbBijy4
Navigation youtube.com/watch?v=nyvwx7o277U
InheritedWidget youtube.com/watch?v=1t-8rBCGBYw

Comparacao Flutter vs React

Flutter React
Form + GlobalKey <form> + useRef
TextEditingController useState
validator Custom validation
Navigator.push navigate()
Navigator.pop navigate(-1)
InheritedWidget Context
.of(context) useContext()

Quick Reference

Acao Codigo
Criar form Form(key: _formKey, child: ...)
Validar _formKey.currentState!.validate()
Resetar _formKey.currentState!.reset()
Ler texto _controller.text
Limpar _controller.clear()
Ir para tela Navigator.push(context, route)
Voltar Navigator.pop(context)
Voltar com dado Navigator.pop(context, data)
Rota nomeada Navigator.pushNamed(context, '/route')
Pegar tema Theme.of(context)
Pegar tela MediaQuery.of(context).size

Checklist da Aula

  • Criar Form com GlobalKey
  • Usar TextEditingController
  • Implementar validacoes
  • Fazer dispose dos controllers
  • Navegar entre telas com Navigator
  • Passar e receber dados na navegacao
  • Usar BuildContext corretamente com async
  • Entender InheritedWidget

Proxima Aula: Animacoes e Gerenciamento de Pacotes

Flutter Cheatsheet - Aula 05

Animacoes e Gerenciamento de Pacotes

Objetivo: Referencia rapida para acompanhamento da aula e consulta posterior.


Indice

  1. Animacoes Implicitas
  2. Animacoes Explicitas
  3. Tween e Curves
  4. Transicoes Prontas
  5. Gerenciamento de Pacotes
  6. Recursos e Documentacao

Animacoes Implicitas

AnimatedContainer

AnimatedContainer(
  duration: Duration(milliseconds: 300),
  curve: Curves.easeInOut,
  width: _expanded ? 200 : 100,
  height: _expanded ? 200 : 100,
  color: _expanded ? Colors.blue : Colors.red,
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(_expanded ? 20 : 10),
  ),
  child: Text('Conteudo'),
)

Documentacao: AnimatedContainer

AnimatedOpacity

AnimatedOpacity(
  duration: Duration(milliseconds: 500),
  opacity: _visible ? 1.0 : 0.0,
  child: Container(...),
)

Documentacao: AnimatedOpacity

Outros Widgets Animados

// Alinhamento
AnimatedAlign(
  duration: Duration(milliseconds: 300),
  alignment: _top ? Alignment.topCenter : Alignment.bottomCenter,
  child: Widget(),
)

// Padding
AnimatedPadding(
  duration: Duration(milliseconds: 300),
  padding: EdgeInsets.all(_big ? 50 : 10),
  child: Widget(),
)

// Escala
AnimatedScale(
  duration: Duration(milliseconds: 300),
  scale: _big ? 1.5 : 1.0,
  child: Widget(),
)

// Rotacao (0.5 = 180 graus)
AnimatedRotation(
  duration: Duration(milliseconds: 300),
  turns: _rotated ? 0.5 : 0,
  child: Widget(),
)

// Troca de widget
AnimatedSwitcher(
  duration: Duration(milliseconds: 300),
  child: _showFirst
      ? Text('Primeiro', key: ValueKey(1))
      : Text('Segundo', key: ValueKey(2)),
)

Documentacao: Implicit Animations


Animacoes Explicitas

AnimationController

class _MyState extends State<MyWidget>
    with SingleTickerProviderStateMixin {

  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: Duration(seconds: 1),
      vsync: this,  // Requer o mixin!
    );
  }

  @override
  void dispose() {
    _controller.dispose();  // OBRIGATORIO!
    super.dispose();
  }
}

Documentacao: AnimationController

Metodos do Controller

_controller.forward();              // Iniciar (0 -> 1)
_controller.reverse();              // Reverter (1 -> 0)
_controller.repeat();               // Loop infinito
_controller.repeat(reverse: true);  // Loop ida/volta
_controller.stop();                 // Parar
_controller.reset();                // Voltar ao inicio
_controller.animateTo(0.5);         // Ir para valor

// Status
_controller.value        // 0.0 a 1.0
_controller.isAnimating  // bool
_controller.isCompleted  // bool

AnimatedBuilder

AnimatedBuilder(
  animation: _controller,
  builder: (context, child) {
    return Opacity(
      opacity: _controller.value,
      child: child,
    );
  },
  child: ExpensiveWidget(),  // Nao reconstroi
)

Documentacao: AnimatedBuilder


Tween e Curves

Tween Basico

final sizeTween = Tween<double>(
  begin: 100,
  end: 200,
);

// Criar animation
late Animation<double> _sizeAnimation;

@override
void initState() {
  super.initState();
  _controller = AnimationController(
    duration: Duration(seconds: 1),
    vsync: this,
  );

  _sizeAnimation = sizeTween.animate(_controller);
}

// Usar
Container(width: _sizeAnimation.value)

Documentacao: Tween

Tipos de Tween

Tween<double>()      // Numeros
ColorTween()         // Cores
SizeTween()          // Tamanhos
RectTween()          // Retangulos
AlignmentTween()     // Alinhamentos
DecorationTween()    // BoxDecoration

Curves

Curve Efeito
Curves.linear Velocidade constante
Curves.easeIn Comeca devagar
Curves.easeOut Termina devagar
Curves.easeInOut Devagar-rapido-devagar
Curves.bounceOut Quica no final
Curves.elasticOut Efeito elastico
Curves.fastOutSlowIn Material Design

Documentacao: Curves

Aplicar Curve

_animation = Tween<double>(
  begin: 0,
  end: 1,
).animate(
  CurvedAnimation(
    parent: _controller,
    curve: Curves.bounceOut,
  ),
);

Transicoes Prontas

// Fade
FadeTransition(
  opacity: _animation,
  child: Widget(),
)

// Slide
SlideTransition(
  position: _offsetAnimation,  // Animation<Offset>
  child: Widget(),
)

// Scale
ScaleTransition(
  scale: _animation,
  child: Widget(),
)

// Rotation
RotationTransition(
  turns: _animation,
  child: Widget(),
)

// Size
SizeTransition(
  sizeFactor: _animation,
  child: Widget(),
)

Documentacao: Transition Widgets

Staggered Animations

for (int i = 0; i < items.length; i++) {
  final start = i * 0.1;
  final end = start + 0.3;

  _animations[i] = Tween<double>(begin: 0, end: 1).animate(
    CurvedAnimation(
      parent: _controller,
      curve: Interval(
        start,
        end.clamp(0.0, 1.0),
        curve: Curves.easeOut,
      ),
    ),
  );
}

Documentacao: Staggered Animations


Gerenciamento de Pacotes

pub.dev

# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  nome_pacote: ^1.2.3

Site: pub.dev

Comandos

flutter pub get        # Instalar
flutter pub upgrade    # Atualizar
flutter pub outdated   # Ver desatualizados
flutter pub add pacote # Adicionar via CLI
flutter pub remove pkg # Remover
flutter pub deps       # Ver arvore

Documentacao: Using packages

Versionamento Semantico

MAJOR.MINOR.PATCH
  2  .  3  .  1

MAJOR = Breaking changes
MINOR = Novas features
PATCH = Bug fixes

Constraints de Versao

# Caret (recomendado)
pacote: ^1.2.3     # >=1.2.3 <2.0.0

# Range explicito
pacote: ">=1.2.0 <2.0.0"

# Versao exata (evitar)
pacote: 1.2.3

# Cuidado com 0.x
^0.2.3  =  >=0.2.3 <0.3.0  # Mais restritivo!

Documentacao: Semantic Versioning


Pacotes de Animacao

flutter_animate

// Instalacao
// flutter pub add flutter_animate

import 'package:flutter_animate/flutter_animate.dart';

Text('Hello')
  .animate()
  .fadeIn(duration: 500.ms)
  .slideX(begin: -0.2)

Pacote: pub.dev/packages/flutter_animate

Encadeamento

Widget()
  .animate()
  .fadeIn(duration: 600.ms)
  .then(delay: 200.ms)
  .slide()
  .scale()

Loop

Icon(Icons.favorite)
  .animate(onPlay: (c) => c.repeat(reverse: true))
  .scale(begin: Offset(1, 1), end: Offset(1.2, 1.2))

Recursos e Documentacao

Documentacao Oficial

Recurso Link
Animations Overview docs.flutter.dev/ui/animations
Implicit Animations docs.flutter.dev/ui/animations/implicit-animations
Explicit Animations docs.flutter.dev/ui/animations/tutorial
Hero Animations docs.flutter.dev/ui/animations/hero-animations

API Reference

Widget Link
AnimatedContainer api.flutter.dev/flutter/widgets/AnimatedContainer
AnimationController api.flutter.dev/flutter/animation/AnimationController
Tween api.flutter.dev/flutter/animation/Tween
Curves api.flutter.dev/flutter/animation/Curves

Pacotes Uteis

Pacote Descricao Link
flutter_animate Animacoes declarativas pub.dev/packages/flutter_animate
animations Transicoes Material pub.dev/packages/animations
lottie Animacoes After Effects pub.dev/packages/lottie
rive Animacoes interativas pub.dev/packages/rive

Videos Recomendados

Video Link
Animations Deep Dive youtube.com/watch?v=GXIJJkq_H8g
Implicit Animations youtube.com/watch?v=IVTjpW3W33s

Quick Reference

Acao Codigo
Fade simples AnimatedOpacity(opacity: x, duration: d)
Container animado AnimatedContainer(duration: d, ...)
Criar controller AnimationController(duration: d, vsync: this)
Iniciar animacao _controller.forward()
Loop _controller.repeat(reverse: true)
Parar _controller.stop()
Valor 0-1 _controller.value
Mapear valor Tween<T>(begin: a, end: b)
Aplicar curve CurvedAnimation(parent: c, curve: Curves.x)
Instalar pacote flutter pub add nome
Atualizar flutter pub upgrade

Checklist Animacao Implicita

AnimatedContainer(
  // 1. Duration (obrigatoria)
  duration: Duration(milliseconds: 300),

  // 2. Curve (opcional)
  curve: Curves.easeInOut,

  // 3. Propriedades para animar
  width: _expanded ? 200 : 100,
  color: _expanded ? Colors.blue : Colors.red,

  // 4. Child
  child: Content(),
)

Checklist Animacao Explicita

// 1. Mixin no State
with SingleTickerProviderStateMixin

// 2. Controller no initState
_controller = AnimationController(
  duration: Duration(seconds: 1),
  vsync: this,
);

// 3. Dispose (OBRIGATORIO)
@override
void dispose() {
  _controller.dispose();
  super.dispose();
}

// 4. AnimatedBuilder no build
AnimatedBuilder(
  animation: _controller,
  builder: (context, child) => ...,
  child: MyWidget(),
)

Checklist da Aula

  • Usar AnimatedContainer para animacoes simples
  • Usar AnimatedOpacity para fade effects
  • Criar AnimationController com vsync
  • Usar Tween para mapear valores
  • Aplicar Curves para suavizar animacoes
  • Dispose controllers corretamente
  • Navegar e avaliar pacotes no pub.dev
  • Entender versionamento semantico

Proxima Aula: Gerenciamento de Estado (Provider)

Flutter Cheatsheet - Aula 06

Gerenciamento de Estado (Provider)

Objetivo: Referencia rapida para acompanhamento da aula e consulta posterior.


Indice

  1. ValueNotifier
  2. Provider
  3. ChangeNotifier
  4. Consumer e Selector
  5. MultiProvider e Escopo
  6. Recursos e Documentacao

ValueNotifier

Criar e Usar

// Criar
final counter = ValueNotifier<int>(0);

// Ler valor
print(counter.value);  // 0

// Atualizar (notifica automaticamente)
counter.value = 5;
counter.value++;

// Dispose (importante!)
counter.dispose();

Documentacao: ValueNotifier

ValueListenableBuilder

ValueListenableBuilder<int>(
  valueListenable: counter,
  builder: (context, value, child) {
    return Text('Valor: $value');
  },
  child: Icon(Icons.star),  // Nao reconstroi
)

Documentacao: ValueListenableBuilder

Classe Customizada

class ThemeNotifier extends ValueNotifier<ThemeMode> {
  ThemeNotifier() : super(ThemeMode.light);

  void toggleTheme() {
    value = value == ThemeMode.light
        ? ThemeMode.dark
        : ThemeMode.light;
  }
}

Provider

Instalacao

# pubspec.yaml
dependencies:
  provider: ^6.1.1
flutter pub add provider

Pacote: pub.dev/packages/provider

Import

import 'package:provider/provider.dart';

ChangeNotifier

Criar Provider

class CounterProvider extends ChangeNotifier {
  int _count = 0;

  // Getter publico
  int get count => _count;

  // Metodos que modificam estado
  void increment() {
    _count++;
    notifyListeners();  // IMPORTANTE!
  }

  void decrement() {
    _count--;
    notifyListeners();
  }

  void reset() {
    _count = 0;
    notifyListeners();
  }
}

Documentacao: ChangeNotifier

Fornecer Provider

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterProvider(),
      child: MyApp(),
    ),
  );
}

Documentacao: ChangeNotifierProvider


Acessar Provider

watch() vs read()

// watch() - Reconstroi quando muda (usar no build)
Widget build(BuildContext context) {
  final counter = context.watch<CounterProvider>();
  return Text('${counter.count}');
}

// read() - Sem rebuild (usar em callbacks)
void _increment() {
  context.read<CounterProvider>().increment();
}

Regra de Ouro

Metodo Onde Usar Rebuild?
watch() No build() Sim
read() Em callbacks Nao

Documentacao: Provider Usage


Consumer e Selector

Consumer

// Basico
Consumer<CounterProvider>(
  builder: (context, provider, child) {
    return Text('${provider.count}');
  },
)

// Com child (otimizado)
Consumer<CounterProvider>(
  builder: (context, provider, child) {
    return Row(
      children: [
        Text('${provider.count}'),
        child!,  // Nao reconstroi
      ],
    );
  },
  child: Icon(Icons.star),
)

Documentacao: Consumer

Consumer2 (dois providers)

Consumer2<ProviderA, ProviderB>(
  builder: (context, providerA, providerB, child) {
    return Text('${providerA.value} - ${providerB.value}');
  },
)

Selector

// So reconstroi quando count muda
Selector<CounterProvider, int>(
  selector: (_, provider) => provider.count,
  builder: (_, count, __) {
    return Text('$count');
  },
)

// Multiplos valores
Selector<Provider, ({int a, String b})>(
  selector: (_, p) => (a: p.valueA, b: p.valueB),
  builder: (_, data, __) {
    return Text('${data.a} - ${data.b}');
  },
)

Documentacao: Selector

context.select()

// Alternativa ao widget Selector
Widget build(BuildContext context) {
  final count = context.select<CounterProvider, int>(
    (provider) => provider.count,
  );
  return Text('$count');
}

MultiProvider e Escopo

MultiProvider

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => AuthProvider()),
        ChangeNotifierProvider(create: (_) => CartProvider()),
        ChangeNotifierProvider(create: (_) => ThemeProvider()),
      ],
      child: MyApp(),
    ),
  );
}

Documentacao: MultiProvider

Escopo Global (todo o app)

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => GlobalProvider(),
      child: MyApp(),
    ),
  );
}

Escopo Local (uma tela)

class MyScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => LocalProvider(),
      child: ScreenContent(),
    );
  }
}

Boas Praticas

Getters Imutaveis

// BOM - Retorna copia imutavel
List<Item> get items => List.unmodifiable(_items);

// RUIM - Expoe lista interna
List<Item> get items => _items;

notifyListeners() no Final

void addMultiple(List<Item> newItems) {
  for (final item in newItems) {
    _items.add(item);
  }
  notifyListeners();  // Uma vez no final
}

Dispose do ValueNotifier

@override
void dispose() {
  _counter.dispose();
  super.dispose();
}

Erros Comuns

read() no build

// ERRADO - nunca atualiza
Widget build(BuildContext context) {
  final count = context.read<Counter>().count;
  return Text('$count');
}

// CERTO
Widget build(BuildContext context) {
  final count = context.watch<Counter>().count;
  return Text('$count');
}

Esquecer notifyListeners()

// ERRADO - UI nao atualiza
void increment() {
  _count++;
}

// CERTO
void increment() {
  _count++;
  notifyListeners();
}

Recursos e Documentacao

Documentacao Oficial

Recurso Link
Provider Package pub.dev/packages/provider
State Management docs.flutter.dev/data-and-backend/state-mgmt
Simple State docs.flutter.dev/data-and-backend/state-mgmt/simple

API Reference

Classe Link
ValueNotifier api.flutter.dev/flutter/foundation/ValueNotifier
ChangeNotifier api.flutter.dev/flutter/foundation/ChangeNotifier
ValueListenableBuilder api.flutter.dev/flutter/widgets/ValueListenableBuilder

Videos Recomendados

Video Link
Provider Explained youtube.com/watch?v=L_QMsE2v6dw
State Management youtube.com/watch?v=3tm-R7ymwhc

Alternativas ao Provider

Pacote Descricao Link
Riverpod Provider mais robusto pub.dev/packages/riverpod
Bloc State management reativo pub.dev/packages/bloc
GetX Solucao completa pub.dev/packages/get

Quick Reference

Acao Codigo
Criar ValueNotifier ValueNotifier<T>(valor)
Atualizar ValueNotifier notifier.value = novo
Criar Provider class X extends ChangeNotifier
Notificar mudanca notifyListeners()
Fornecer provider ChangeNotifierProvider(create: ...)
Ler com rebuild context.watch<T>()
Ler sem rebuild context.read<T>()
Consumer basico Consumer<T>(builder: ...)
Selector otimizado Selector<T, V>(selector: ..., builder: ...)
Multiplos providers MultiProvider(providers: [...])

Quando Usar O Que?

Situacao Solucao
Estado local simples setState()
Estado local reativo ValueNotifier
Estado compartilhado Provider
Multiplos estados MultiProvider
Otimizacao de rebuilds Selector

Fluxo de Dados

User Action (tap, input)
         |
         v
context.read<Provider>().method()
         |
         v
Provider modifica estado
         |
         v
notifyListeners()
         |
         v
Widgets com watch()/Consumer rebuildam
         |
         v
UI atualizada

Checklist da Aula

  • Criar e usar ValueNotifier
  • Usar ValueListenableBuilder corretamente
  • Criar ChangeNotifier para Provider
  • Configurar ChangeNotifierProvider
  • Usar context.watch() e context.read() corretamente
  • Usar Consumer para rebuilds granulares
  • Configurar MultiProvider
  • Usar Selector para otimizacao
  • Entender quando usar cada abordagem

Proxima Aula: Persistencia de Dados

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment