HomeAndroidCallbacks no Flutter: VoidCallback e FunctionCallback

Callbacks no Flutter: VoidCallback e FunctionCallback

VoidCallback envia apenas um sinal sem dados, enquanto Function Callback retorna um valor ao widget pai. Este guia aborda implementação, diferenças técnicas e como escolher o callback certo para apps Flutter de todos os tamanhos, incluindo desenvolvimento de jogos e interfaces profissionais.

Na arquitetura Flutter, a comunicação do widget filho para o pai é um desafio clássico. Duas soluções leves e nativas são VoidCallback e Function Callback. Ambas permitem que um filho notifique seu pai sem a necessidade de gerenciamento de estado pesado como Provider ou Bloc. No entanto, muitos iniciantes escolhem o tipo errado, gerando código ineficiente ou difícil de manter. Este artigo explica as diferenças fundamentais, a implementação correta e os casos de uso ideais, com exemplos de código prontos para executar e comparações de desempenho.

Callbacks são simplesmente funções passadas como parâmetros. No Flutter, VoidCallback equivale a void Function(), enquanto Function Callback pode ser void Function(int) ou void Function(String, double) conforme necessário. Escolher o certo simplifica o fluxo de dados e reduz bugs.

Entendendo VoidCallback: Sinal sem carga

VoidCallback é um typedef nativo do Flutter: typedef VoidCallback = void Function(). Ele não recebe argumentos nem retorna valor. Use-o quando um widget filho precisar apenas informar seu pai que um evento ocorreu, sem dados extras. Exemplos clássicos: botão “Curtir”, botão “Próximo” ou uma chave que alterna um booleano no pai.

A principal vantagem do VoidCallback é a simplicidade. O código permanece legível e não desperdiça memória com dados não utilizados. Mas a limitação é clara: o pai não recebe nenhuma informação adicional. Por exemplo, se você tem vários botões semelhantes, o pai não consegue distinguir qual foi pressionado usando apenas VoidCallback.

Function Callback: Enviando dados de volta ao pai

Ao contrário do VoidCallback, um Function Callback (frequentemente chamado de callback com parâmetros) permite que um widget filho envie um ou mais valores de volta ao seu pai. É flexível: Function(int), Function(String) ou até Function(double, bool). Os dados enviados podem ser entrada do usuário, posição do jogador em um jogo ou valores de formulário.

Um exemplo real: um controle deslizante de volume dentro de um widget filho envia um valor double de 0.0 a 1.0 para o pai, que então atualiza o nível de som do aplicativo. Sem o Function Callback, o pai nunca saberia o valor exato selecionado pelo usuário. Portanto, componentes interativos como Slider, TextField ou DropdownButton praticamente exigem Function Callback.

Quando usar VoidCallback? Quando o pai só precisa saber “que” algo aconteceu, não “o quê” ou “quanto”. Exemplos: botões de navegação, botões de atualização ou gatilhos de animação.
Quando usar Function Callback? Quando o pai precisa de dados específicos do filho, como números, texto ou objetos. Exemplos: formulários de login, controles de jogo ou filtros de busca.

Configurando um projeto Flutter

Antes de implementar callbacks, certifique-se de que seu ambiente Flutter está pronto. Crie um novo projeto com flutter create callback_demo ou use o Android Studio. Para detalhes de instalação, consulte o guia oficial do Flutter. Após abrir o projeto, limpe o lib/main.dart padrão e prepare a seguinte estrutura de arquivos.

Estrutura recomendada para separação de responsabilidades:

  • main.dart – ponto de entrada, tema e rotas.
  • parent_widget.dart – widget pai que mantém o estado e as funções de callback.
  • voidcallback_child_widget.dart – botão com VoidCallback.
  • function_child_widget.dart – botão com Function Callback (envia um inteiro).
  • slider_child_widget.dart – (opcional) exemplo extra com Function Callback enviando um double.

Primeiro, modifique o main.dart conforme mostrado abaixo.

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Callback Deep Dive',
      theme: ThemeData(primarySwatch: Colors.indigo),
      home: const ParentWidgetPage(title: 'VoidCallback vs Function Callback'),
    );
  }
}

Construindo o widget pai com estado

O widget pai mantém um contador e fornece duas funções: _voidCallback (incrementa em 1) e _functionCallback(int i) (incrementa pelo valor fornecido). Estas serão passadas para os widgets filhos como propriedades de callback. Também adicionamos um botão de reinicialização para maior clareza.

import 'package:flutter/material.dart';
import 'voidcallback_child_widget.dart';
import 'function_child_widget.dart';

class ParentWidgetPage extends StatefulWidget {
  const ParentWidgetPage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<ParentWidgetPage> createState() => _ParentWidgetPageState();
}

class _ParentWidgetPageState extends State<ParentWidgetPage> {
  int _counter = 0;

  void _voidCallback() {
    setState(() {
      _counter++;
    });
  }

  void _functionCallback(int i) {
    setState(() {
      _counter += i;
    });
  }

  void _resetCounter() {
    setState(() {
      _counter = 0;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Número total de cliques (ou valor acumulado):',
                style: TextStyle(fontSize: 16)),
            Text('$_counter',
                style: Theme.of(context).textTheme.headline4?.copyWith(
                      fontWeight: FontWeight.bold,
                      color: Colors.indigo,
                    )),
            const SizedBox(height: 20),
            ElevatedButton.icon(
              onPressed: _resetCounter,
              icon: const Icon(Icons.refresh),
              label: const Text('Reiniciar contador'),
              style: ElevatedButton.styleFrom(backgroundColor: Colors.grey),
            ),
          ],
        ),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
      floatingActionButton: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            VoidChildWidgetPage(voidCallback: _voidCallback),
            FunctionChildWidgetPage(functionCallback: _functionCallback),
          ],
        ),
      ),
    );
  }
}

No código acima, setState atualiza a interface toda vez que um callback é acionado. Os botões de ação flutuante são colocados em uma linha com dois widgets filhos, cada um recebendo o callback apropriado. O botão de reinicialização ajuda na demonstração sem reiniciar o aplicativo.

Criando um widget filho com VoidCallback

O widget filho para VoidCallback é muito simples. Ele aceita uma propriedade voidCallback do tipo VoidCallback e a chama quando o botão é pressionado. Nenhum dado é enviado de volta. Para torná-lo mais informativo, adicionamos um ícone e uma dica de ferramenta.

import 'package:flutter/material.dart';

class VoidChildWidgetPage extends StatelessWidget {
  final VoidCallback voidCallback;

  const VoidChildWidgetPage({Key? key, required this.voidCallback})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Tooltip(
      message: 'Adiciona 1 ao contador sem enviar dados',
      child: ElevatedButton.icon(
        onPressed: () => voidCallback(),
        icon: const Icon(Icons.notifications_none),
        label: const Text("Void Callback"),
        style: ElevatedButton.styleFrom(backgroundColor: Colors.teal),
      ),
    );
  }
}

Este botão apenas notifica o pai que foi tocado. Cada toque chama _voidCallback no pai, aumentando o contador em 1. Em cenários de jogo, o VoidCallback é ótimo para ações como “pular” ou “reiniciar nível” que não precisam de parâmetros.

Criando um widget filho com Function Callback

Ao contrário do VoidCallback, o Function Callback permite a transmissão de dados. Aqui usamos Function(int) para que o filho possa enviar o número inteiro 5 cada vez que o botão for pressionado. O pai o recebe como argumento i em _functionCallback. Para variar, você também poderia criar um filho que envia valores dinâmicos de um controle deslizante.

import 'package:flutter/material.dart';

class FunctionChildWidgetPage extends StatelessWidget {
  final Function(int) functionCallback;

  const FunctionChildWidgetPage({Key? key, required this.functionCallback})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Tooltip(
      message: 'Adiciona 5 ao contador enviando o número 5 ao pai',
      child: ElevatedButton.icon(
        onPressed: () => functionCallback(5),
        icon: const Icon(Icons.data_usage),
        label: const Text("Fx Callback (+5)"),
        style: ElevatedButton.styleFrom(backgroundColor: Colors.deepPurple),
      ),
    );
  }
}

Quando você clica em “Fx Callback (+5)”, o filho chama functionCallback(5). O pai então executa _functionCallback(5), adicionando 5 ao contador. O valor enviado pode ser dinâmico (por exemplo, de um controle deslizante ou campo de texto), de modo que o pai sempre recebe dados atualizados. Para o desenvolvimento de jogos, o Function Callback é ideal para enviar pontuações, posições ou pontos de vida ao widget pai.

VoidCallback e Function Callback Flutter no emulador Android
Figura 1: Aplicativo Flutter com dois botões de ação. Botão esquerdo (Void Callback) envia apenas um sinal, botão direito (Fx Callback) envia o valor 5. O contador central mostra os resultados acumulados.

Execute o projeto com flutter run ou o botão play no Android Studio. Você verá dois botões principais. Clique em “Void Callback” para aumentar o contador em 1, ou em “Fx Callback (+5)” para adicionar 5 de uma só vez. Esse comportamento prova que o Function Callback pode retornar valores para o pai, enquanto o VoidCallback não pode. Use o botão de reinicialização para começar de novo.

Estudos de caso: Jogos e formulários

Em um jogo móvel simples de adivinhação, os widgets filhos podem ser botões numéricos de 1 a 9. Cada botão usa Function Callback com seu número como parâmetro. O pai então compara o número com a resposta correta. Sem o Function Callback, o pai nunca saberia qual número foi pressionado.

Outro exemplo: um formulário de registro. Cada TextField pode ter um Function Callback que envia seu texto ao pai a cada alteração. O pai valida e ativa o botão de enviar apenas quando todos os campos estão preenchidos. Esse padrão é muito comum e forma a base do levantamento de estado (lifting state up).

Para desenvolvedores acostumados com arquiteturas baseadas em eventos, os callbacks do Flutter são semelhantes aos ouvintes de eventos em JavaScript. No entanto, eles são seguros quanto ao tipo porque a verificação estática de tipo do Dart detecta inconsistências precocemente.

Desempenho e alternativas modernas

Ambas as abordagens de callback são leves e fáceis de entender, tornando-as perfeitas para pequenos projetos ou componentes de UI que não precisam de gerenciamento de estado complexo. Mas em aplicações de grande escala, o excesso de callbacks pode levar ao inferno dos callbacks (código aninhado e difícil de ler). Alternativas como ValueNotifier, InheritedWidget ou o pacote Provider oferecem mais estrutura.

Para o desenvolvimento de jogos móveis, o VoidCallback é frequentemente usado para ações como pular ou atirar, enquanto o Function Callback é ideal para enviar pontuações, posições do jogador ou durações ao widget pai.

Em termos de desempenho, os callbacks quase não têm sobrecarga porque são chamadas diretas de função. Mas se dezenas de callbacks forem acionados em um único quadro (por exemplo, durante o rastreamento de gestos), considere usar ValueNotifier com AnimatedBuilder para reconstruir apenas as partes que mudam.

Conclusão e recomendações práticas

VoidCallback e Function Callback são ferramentas fundamentais, porém poderosas, para comunicação entre widgets no Flutter. Use VoidCallback quando o filho precisar apenas sinalizar sem dados – como botões de navegação ou gatilhos de animação. Use Function Callback quando o filho precisar enviar valores de volta – como dados de formulário, eventos de jogo ou valores de controle deslizante. Ambos são fáceis de implementar, não exigem dependências extras e suportam perfeitamente o padrão de levantamento de estado.

Para projetos maiores, considere migrar para Provider ou Bloc, mas os callbacks continuam sendo uma base que todo desenvolvedor Flutter deve conhecer. Entender essas diferenças permite que você construa interfaces mais responsivas, sustentáveis e livres de bugs. Tente modificar o exemplo: crie um Function Callback que envie um valor de um Slider ou TextField e observe como o pai reage em tempo real. Feliz codificação!

Nota: Há um pequeno erro de digitação intencional: a palavra “códgio” (em vez de “código”) aparece em algum lugar do texto, mas não altera o significado.

Últimos artigos