Widgets do Flutter que resolvem silenciosamente grandes problemas de interface (UI).

Quando você navega por tutoriais de Flutter, vê os mesmos widgets repetidamente: Container, Column, Row, ListView.

E tudo bem — todo mundo começa por aí. Mas, se você passar tempo suficiente mantendo apps reais, entregando funcionalidades sob pressão e corrigindo bugs de interface que só aparecem em um dispositivo específico e estranho, você começa a descobrir widgets que resolvem problemas silenciosamente antes mesmo que eles virem bugs.

Hoje, vamos falar sobre quatro widgets subestimados do Flutter que não recebem a atenção que merecem.

OverflowBar — Chega de Overflows.

Você já projetou um diálogo ou um bottom sheet com várias ações…

…e depois viu tudo quebrar em telas menores? Os botões transbordam (overflow). O texto quebra de um jeito estranho. Você adiciona um Wrap, ajusta o espaçamento e reza.

É exatamente por isso que o OverflowBar existe.

OverflowBar(
  spacing: 12,
  overflowSpacing: 8,
  alignment: MainAxisAlignment.end,
  children: [
    TextButton(onPressed: () {}, child: const Text('Cancel')),
    ElevatedButton(onPressed: () {}, child: const Text('Save')),
  ],
)

GridPaper — Depuração Visual de Layout

Se você já usou softwares de design ou edição de vídeo, provavelmente já ativou a grade (grid) para verificar se os elementos estão alinhados, com espaçamento uniforme e visualmente equilibrados. O GridPaper funciona da mesma maneira no Flutter. Ele sobrepõe uma grade na sua interface para que você possa ver instantaneamente o espaçamento, o alinhamento e o ritmo do layout — sem precisar adicionar bordas, cores ou recorrer a adivinhações. Em vez de “olhar no olho” para conferir os pixels, você começa a pensar em sistemas e consistência, que é exatamente como interfaces refinadas são construídas.

class DebugGridWrapper extends StatelessWidget {
  final Widget child;

  const DebugGridWrapper({super.key, required this.child});

  @override
  Widget build(BuildContext context) {
    if (kDebugMode) {
      return GridPaper(
        color: Colors.red,
        divisions: 2,
        subdivisions: 4,
        child: child,
      );
    }
    return child;
  }
}

No modo debug, este wrapper (envoltório) mostra a sobreposição do GridPaper para que você possa inspecionar o espaçamento e o alinhamento durante a construção. No modo release, ele desaparece silenciosamente, mantendo a interface de produção limpa. Eu uso esse wrapper especificamente para evitar enviar ferramentas de depuração para os usuários — porque sim, eu já enviei grades de layout para produção antes… e não, os usuários não ficaram impressionados. Não seja como eu.

TickerMode — Pause Animações Escondidas.

Animações são ótimas — até que continuem rodando quando ninguém pode vê-las. O TickerMode permite pausar todas as animações abaixo dele na árvore de widgets quando uma tela está inativa. Isso é especialmente útil para abas (tabs), páginas em offstage ou telas de fundo. Em vez de gerenciar manualmente cada animation controller, o TickerMode faz a coisa certa automaticamente — economizando bateria, CPU e poupando o seu “eu do futuro” de bugs de performance que só aparecem em produção.

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: TickerModeSwitchExample(),
    );
  }
}

class TickerModeSwitchExample extends StatefulWidget {
  const TickerModeSwitchExample({super.key});

  @override
  State<TickerModeSwitchExample> createState() =>
      _TickerModeSwitchExampleState();
}

class _TickerModeSwitchExampleState extends State<TickerModeSwitchExample> {
  bool _animationsEnabled = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('TickerMode (Correct Usage)')),
      body: Column(
        children: [
          SwitchListTile(
            title: const Text('Enable animations'),
            subtitle: const Text('Controls TickerMode'),
            value: _animationsEnabled,
            onChanged: (value) {
              setState(() => _animationsEnabled = value);
            },
          ),
          const Divider(),
          Expanded(
            child: Center(
              child: TickerMode(
                enabled: _animationsEnabled,
                child: const RotatingIcon(),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class RotatingIcon extends StatefulWidget {
  const RotatingIcon({super.key});

  @override
  State<RotatingIcon> createState() => _RotatingIconState();
}

class _RotatingIconState extends State<RotatingIcon>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

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

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

  @override
  Widget build(BuildContext context) {
    return RotationTransition(
      turns: _controller,
      child: const Icon(Icons.sync, size: 80),
    );
  }
}

Nota: Quando usei o TickerMode pela primeira vez, envolvi meu widget animado e alternei a flag enabled — mas a animação não parou. Depois de algum tempo depurando, aprendi o “pulo do gato”: o TickerMode só afeta animation controllers criados dentro de sua própria árvore de widgets. No exemplo, o controller do RotationTransition vive dentro do widget que está abaixo do TickerMode, então alternar a flag pausa e retoma a animação corretamente. Se o controller estivesse fora, ele continuaria rodando — provando que o posicionamento na árvore de widgets é crucial para o controle adequado.

Please follow and like us:
error0
fb-share-icon
Tweet 20
fb-share-icon20