FalaDevs – Artigos e Tutoriais de Programação | Dicas e Técnicas Avançadas

Desenvolvedor frustrado(a) em frente a um monitor exibindo código Flutter com erros, simbolizando os desafios iniciais. Ao lado, uma xícara de café e um notebook com o logo do Flutter.

Flutter: Os 3 Erros Mais Comuns de Iniciantes

Fala devs! 🚀

O Flutter é sinônimo de agilidade e beleza no desenvolvimento mobile. Com uma única base de código, ele permite criar apps multiplataforma com interfaces incríveis e desempenho nativo.

Por mais madura e robusta que a plataforma seja, começar em qualquer tecnologia sempre traz seus desafios. E no Flutter não é diferente. É super normal tropeçar em alguns conceitos ou cair em certas armadilhas no início. Eu mesmo já passei por isso e sei o quanto pode ser frustrante.

Mas a boa notícia é que, com um pouco de conhecimento e as dicas certas, você pode evitar os erros mais comuns e acelerar sua jornada com o Flutter, construindo apps mais robustos e eficientes desde o começo.

Hoje, vamos mergulhar nos 3 erros mais frequentes de quem está começando com Flutter e, o mais importante, como evitá-los para construir apps de alta qualidade. Bora desmistificar isso na prática!

1. Ignorar o Gerenciamento de Estado (O Calcanhar de Aquiles Inicial)

Este é, sem dúvida, o erro número um. Muitos iniciantes com Flutter vêm de outras plataformas onde o gerenciamento de estado pode ser mais “implícito” ou menos centralizado. No Flutter, entender como e quando o estado do seu aplicativo muda é fundamental para evitar bugs e reconstruções de widgets desnecessárias.

O Problema: Sem uma estratégia clara, você pode acabar com:

  • setState espalhados por todo lado, causando rebuilds em cascata.
  • Dados sendo passados de widget em widget, mesmo que um widget intermediário não precise delas.
  • Dificuldade em depurar quando o app não se comporta como o esperado.

A Solução: Adote um Gerenciador de Estado! Existem várias opções excelentes, como Provider, BLoC, Cubit e Riverpod. Hoje, vamos explorar uma das minhas favoritas pela sua simplicidade e poder: o MobX. Ele usa um sistema reativo que atualiza a UI “magicamente” quando o estado muda, com o mínimo de código.

Vamos ver como fica um exemplo simples com MobX:

Primeiro, adicione as dependências ao seu pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
	flutter_mobx: ^2.3.0
  mobx: ^2.5.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^2.9.0
  mobx_codegen: ^2.7.4

Agora, vamos criar nossa “Store”, que é a classe que vai conter nosso estado e a lógica de negócio.

// counter_store.dart
import 'package:mobx/mobx.dart';

// Este é o arquivo que será gerado automaticamente.
part 'counter_store.g.dart';

class CounterStore = _CounterStore with _$CounterStore;

abstract class _CounterStore with Store {
  // @observable: Marca uma propriedade como reativa.
  // A UI irá reagir a qualquer mudança nela.
  @observable
  int value = 0;

  // @action: Marca um método que modifica o estado.
  // É a forma correta de alterar um @observable.
  @action
  void increment() {
    value++;
  }
}

Importante: Após criar esse arquivo, você precisa rodar o gerador de código no terminal para criar o arquivo counter_store.g.dart:

flutter packages pub run build_runner build --delete-conflicting-outputs

Com a nossa Store pronta, vamos usá-la na UI:

// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'counter_store.dart'; // Importe sua Store

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Contador com MobX',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // Instanciamos nossa Store
  final CounterStore counterStore = CounterStore();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Contador MobX'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Você pressionou o botão:'),
            // O Observer "escuta" as mudanças nos @observables
            // e reconstrói apenas este widget quando 'counterStore.value' muda.
            Observer(
              builder: (_) => Text(
                '${counterStore.value}',
                style: Theme.of(context).textTheme.headlineMedium,
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        // Chamamos a @action para modificar o estado
        onPressed: counterStore.increment,
        tooltip: 'Incrementar',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Explicação do Código:

  • CounterStore: Nossa classe de estado. A mágica do MobX acontece com as anotações (@observable, @action).
  • @observable: Transforma a variável value em um “estado observável”. Qualquer widget que estiver “escutando” essa variável será notificado quando ela mudar.
  • @action: A função increment é marcada como uma ação. Isso informa ao MobX que este método vai alterar o estado, permitindo que ele gerencie as atualizações de forma otimizada.
  • Observer: Este é o widget-chave do flutter_mobx. Você o utiliza apenas na parte da interface que depende de um estado observável. Ele é inteligente o suficiente para se reconstruir sozinho sempre que o counterStore.value for alterado, sem a necessidade de um setState().

2. Estruturar Mal os Widgets (A Bagunça na Árvore)

O Flutter é todo sobre widgets, e a forma como você os aninha e os organiza é crucial para a legibilidade, manutenção e performance do seu código. Um erro comum é criar árvores de widgets gigantes e monolíticas, dificultando a localização de problemas e a reutilização de componentes.

O Problema:

  • Funções build enormes, com centenas de linhas de código.
  • Dificuldade em encontrar a origem de um bug visual.
  • Reutilização de código quase impossível.
  • Impacto negativo na performance, pois um pequeno setState pode reconstruir uma parte muito maior da tela.

A Solução: Quebre seus Widgets em Partes Menores e Reutilizáveis! Pense em componentes menores e mais focados. Cada StatelessWidget ou StatefulWidget deve ter uma única responsabilidade.

Exemplo Prático:

Antes: Um único widget gigante.

//tela_cadastro.dart

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Cadastro Monolítico')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            const Text('Formulário de Cadastro', style: TextStyle(fontSize: 24)),
            const SizedBox(height: 20),
            TextFormField(
              decoration: const InputDecoration(
                labelText: 'Nome',
                prefixIcon: Icon(Icons.person),
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 10),
            TextFormField(
              decoration: const InputDecoration(
                labelText: 'Email',
                prefixIcon: Icon(Icons.email),
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 10),
            TextFormField(
            	obscureText: true,
              decoration: const InputDecoration(
                labelText: 'Senha',
                prefixIcon: Icon(Icons.lock),
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 10),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: () {},
                style: ElevatedButton.styleFrom(
                  padding: const EdgeInsets.symmetric(vertical: 15),
                ),
                child: const Text('Cadastrar'),
              ),
            )
            // ... muito mais código aqui
          ],
        ),
      ),
    );
  }
}

Depois: widgets menores e focados

//campo_texto_personalizado.dart

class CampoDeTextoPersonalizado extends StatelessWidget {
  final String label;
  final IconData? icon;
  final bool obscureText;

  const CampoDeTextoPersonalizado({
  	super.key, 
  	required this.label, 
  	this.icon, 
  	this.obscureText = false
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 8.0),
      child: TextFormField(
	      obscureText: obscureText,
        decoration: InputDecoration(
          labelText: label,
          prefixIcon: icon != null ? Icon(icon) : null,
          border: const OutlineInputBorder(),
        ),
      ),
    );
  }
}

//botao_primario.dart

class BotaoPrimario extends StatelessWidget {
  final String texto;
  final VoidCallback onPressed;

  const BotaoPrimario({super.key, required this.texto, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: double.infinity,
      child: ElevatedButton(
        onPressed: onPressed,
        style: ElevatedButton.styleFrom(
          padding: const EdgeInsets.symmetric(vertical: 15),
        ),
        child: Text(texto, style: const TextStyle(fontSize: 18)),
      ),
    );
  }
}

//tela_cadastro.dart

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Cadastro Refatorado')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            const Text('Formulário de Cadastro', style: TextStyle(fontSize: 24)),
            const SizedBox(height: 20),
            const CampoDeTextoPersonalizado(label: 'Nome Completo', icon: Icons.person),
            const CampoDeTextoPersonalizado(label: 'Email', icon: Icons.email),
            const CampoDeTextoPersonalizado(label: 'Senha', icon: Icons.lock, obscureText: true),
            const SizedBox(height: 20),
            BotaoPrimario(texto: 'Cadastrar', onPressed: () {
              // Lógica de cadastro
            }),
          ],
        ),
      ),
    );
  }
}

Benefícios da Refatoração:

  • Legibilidade: O código fica muito mais fácil de ler e entender.
  • Reusabilidade: CampoDeTextoPersonalizado e BotaoPrimario podem ser usados em várias telas.
  • Manutenibilidade: Se houver um bug no campo de texto, você sabe exatamente onde procurar.
  • Performance: Widgets menores podem ser reconstruídos independentemente, otimizando o ciclo de vida.

3. Não Usar const para Otimização (O Pequeno Detalhe que Faz a Diferença)

Este é um erro sutil, mas que pode ter um impacto significativo na performance, especialmente em aplicativos complexos. O Flutter é extremamente eficiente, e o uso do const é uma das ferramentas que ele nos dá para otimizar a reconstrução da UI.

O Problema:

  • Se você não usa const em widgets que não mudam, o Flutter os reconstrói desnecessariamente a cada vez que o widget pai é reconstruído.
  • Isso adiciona trabalho extra de processamento, consumindo mais recursos e deixando o app mais lento.

A Solução: Use const em todo widget que não muda! Sempre que um widget e todos os seus filhos são imutáveis (seus valores não mudarão após a criação), declare como const.

Exemplo:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Este Text nunca muda, então pode ser const
        title: const Text('Minha Tela'), 
      ),
      body: Center(
        child: Column(
          children: [
            // Este texto também é estático
            const Text('Boas-vindas ao Flutter!'), 
            const SizedBox(height: 20), // SizedBox é sempre imutável
            ElevatedButton(
              onPressed: () {
                // ... alguma lógica que muda o estado
              },
              // O Text dentro do botão também pode ser const
              child: const Text('Clique aqui'), 
            ),
          ],
        ),
      ),
    );
  }
}

Por que const importa? Quando o Flutter encontra um widget marcado como const, ele sabe que não precisa reconstruí-lo ou até mesmo compará-lo com a versão anterior na próxima vez que o build do pai for chamado. Ele simplesmente reutiliza a instância existente na memória. Isso economiza recursos e torna seu aplicativo mais rápido e fluido. O próprio VS Code e o Android Studio geralmente te avisam com um “lint” quando você pode (e deve) adicionar const.

Resumo Final

  • Gerenciamento de Estado: Não fuja dele! Escolha um método (Mobx, Provider, BLoC, Cubit, Riverpod).
  • Estrutura de Widgets: Quebre widgets grandes em componentes menores, focados e reutilizáveis. Pense em modularidade e legibilidade.
  • Otimização com const: Use const em todos os widgets imutáveis. É um detalhe simples que faz uma grande diferença na performance do seu app.

Evitar esses três erros desde o início vai te poupar muitas dores de cabeça e te colocar no caminho certo para construir aplicativos Flutter de alta qualidade. Lembre-se, a prática leva à perfeição, então bora codar! 🚀

Curtiu as dicas? Já cometeu algum desses erros? Compartilhe sua experiência nos comentários e vamos juntos construir uma comunidade Flutter ainda mais forte!

Até a próxima, devs!

Picture of Daniel Albuquerque

Daniel Albuquerque

Sou um desenvolvedor apaixonado por tecnologia, com foco em Node.js, TypeScript, Flutter e PostgreSQL. Criei o FalaDevs pra compartilhar experiências reais de código, boas práticas e tudo o que aprendo no dia a dia construindo aplicações modernas.

0 0 votos
Classificação do artigo
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários
0
Adoraria saber sua opinião, comente.x