›Series summary
- 1. O que acontece quando você usa um ListView?
- 2. O que é um Sliver, afinal?
- 3. Quem realmente controla os Slivers?
- 4. Construindo uma tela inteira usando apenas Slivers
- 5. Criando seu próprio Sliver

Construindo uma tela inteira usando apenas Slivers
Do cabeçalho ao rodapé: descubra como combinar Slivers para criar interfaces flexíveis, performáticas e com um único fluxo de scroll.
Índice
Até agora entendemos três coisas importantes:
- o
ListViewé apenas uma abstração; - Slivers representam pedaços da área rolável;
- o
Viewportcoordena todos eles durante o scroll.
Mas isso levanta uma pergunta natural.
Se praticamente tudo no Flutter é baseado em Slivers… por que continuamos usando
ListView,GridVieweSingleChildScrollView?
A resposta é simples: Na maioria das vezes, esses widgets resolvem muito bem problemas específicos.
Mas quando a tela começa a crescer, combinar vários widgets roláveis diferentes costuma gerar limitações, perda de performance ou soluções improvisadas.
É justamente nesse momento que o CustomScrollView mostra seu verdadeiro potencial.
Neste artigo vamos construir uma tela inteira utilizando apenas Slivers e, mais importante do que decorar APIs, vamos entender o papel que cada Sliver desempenha dentro da arquitetura do Flutter.
Nossa tela
Imagine uma tela de e-commerce.
Ela possui:
- uma AppBar expansível;
- uma lista de categorias;
- uma grade de produtos;
- um espaçamento entre seções;
- uma mensagem quando não houver mais conteúdo.
Visualmente:
┌──────────────────────────────┐
│ SliverAppBar │
├──────────────────────────────┤
│ Lista de categorias │
├──────────────────────────────┤
│ Grid de produtos │
├──────────────────────────────┤
│ Espaçamento │
├──────────────────────────────┤
│ "Fim dos resultados" │
└──────────────────────────────┘
Tudo isso será uma única área rolável.
Começando pelo CustomScrollView
Toda tela baseada em Slivers começa da mesma forma.
CustomScrollView(
slivers: [],
)
Diferente do ListView, aqui não adicionamos filhos (children).
Adicionamos slivers.
Esse detalhe parece pequeno.
Mas muda completamente a forma como pensamos uma tela.
Agora não estamos mais montando uma árvore de widgets. Estamos montando uma sequência de pedaços da área rolável.
Primeiro Sliver: SliverAppBar
Vamos começar pelo cabeçalho.
CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 220,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('Produtos'),
),
),
],
)
Esse Sliver possui um comportamento que um AppBar comum não consegue oferecer.
Ele sabe responder ao scroll.
Quando o usuário começa a deslizar a tela, o próprio SliverAppBar recebe novas SliverConstraints, recalcula seu layout e informa ao Viewport quanto espaço ainda ocupa.
É exatamente o mecanismo que estudamos no artigo anterior.
Adicionando uma lista
Agora queremos mostrar categorias.
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ListTile(
title: Text(categories[index]),
);
},
childCount: categories.length,
),
),
Perceba que o código lembra bastante o ListView.builder.
Isso não é coincidência.
O ListView.builder utiliza exatamente esse Sliver internamente.
A diferença é que agora podemos combiná-lo livremente com qualquer outro tipo de Sliver.
Adicionando um Grid
Depois das categorias queremos mostrar produtos.
SliverGrid(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ProductCard(
product: products[index],
);
},
childCount: products.length,
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
),
Agora temos dois tipos diferentes de layout.
Primeiro uma lista.
Depois um grid.
Mas existe apenas um scroll.
Nenhum NestedScrollView.
Nenhum SingleChildScrollView.
Nenhum widget tentando disputar gestos. Tudo faz parte da mesma superfície rolável.
Organizando o layout com SliverPadding
Em widgets tradicionais fazemos isso:
Padding(
padding: EdgeInsets.all(16),
child: GridView(...),
)
Com Slivers existe uma versão específica.
SliverPadding(
padding: const EdgeInsets.all(16),
sliver: SliverGrid(
delegate: ...,
gridDelegate: ...,
),
),
O detalhe importante é que o padding também participa do cálculo do layout.
Ele não é apenas um widget visual.
Ele altera a geometria daquele trecho da área rolável.
Isso permite que o Viewport continue conhecendo exatamente quanto espaço cada Sliver ocupa.
Preenchendo o espaço restante
Imagine que existam poucos produtos.
A tela ficaria assim.
┌────────────────────┐
│ Produto │
│ Produto │
│ │
│ │
│ │
│ │
└────────────────────┘
Talvez queiramos mostrar uma mensagem centralizada.
Para isso existe:
SliverFillRemaining(
hasScrollBody: false,
child: Center(
child: Text(
'Você chegou ao fim.',
),
),
),
Esse Sliver possui um comportamento muito interessante.
Se ainda existir espaço livre no viewport, ele o ocupa.
Caso contrário, comporta-se como qualquer outro Sliver.
É muito mais elegante do que calcular alturas manualmente.
O resultado
Agora nossa tela ficou assim.
CustomScrollView(
slivers: [
SliverAppBar(...),
SliverList(...),
SliverPadding(
padding: const EdgeInsets.all(16),
sliver: SliverGrid(...),
),
SliverFillRemaining(
hasScrollBody: false,
child: Center(
child: Text('Fim dos resultados'),
),
),
],
)
Cada componente possui sua própria responsabilidade.
O Viewport coordena todos eles.
Durante o scroll:
- O
SliverAppBarencolhe; - A
SliverListcria novos itens; SliverGridcria novos cards;SliverPaddingajusta os espaçamentos;FillRemainingocupa o espaço restante quando necessário.

Tudo utilizando exatamente o mesmo pipeline estudado no artigo anterior.
Um detalhe que poucos percebem
Observe que não existe nenhum código dizendo:
“Agora mostre a lista.”
Ou:
“Agora renderize o Grid.”
Quem toma essas decisões é o próprio Viewport.
Cada Sliver recebe um conjunto de SliverConstraints, calcula sua própria SliverGeometry e informa quanto espaço ocupa.
Ou seja, cada Sliver é completamente independente.
Esse desacoplamento é um dos motivos pelos quais o Flutter consegue construir interfaces tão complexas mantendo alta performance.
Quando vale a pena usar Slivers?
Nem toda tela precisa de um CustomScrollView.
Se você possui apenas uma lista simples, um ListView.builder continua sendo a melhor escolha.
Mas quando a interface começa a combinar diferentes comportamentos de scroll, Slivers tornam o código muito mais natural.
Alguns exemplos:
- Páginas iniciais com várias seções;
- Feeds com cabeçalhos expansíveis;
- Perfis de redes sociais;
- Marketplaces;
- Dashboards;
- Páginas de notícias;
- Qualquer tela que combine listas, grids, banners e cabeçalhos.
Nesses cenários, pensar em Slivers geralmente produz uma arquitetura mais simples do que tentar encaixar múltiplos widgets roláveis.
Conclusão
Ao longo desta série, deixamos de tratar Slivers como APIs “avançadas” e passamos a entendê-los como a base da arquitetura de scroll do Flutter.
Neste artigo vimos que uma tela inteira pode ser construída combinando Slivers especializados, cada um resolvendo um problema específico: cabeçalhos, listas, grades, espaçamentos e preenchimento do espaço restante.
Essa forma de pensar torna interfaces complexas mais previsíveis, evita problemas comuns com múltiplos widgets roláveis e aproxima seu código do funcionamento interno do framework.
Mas ainda existe uma peça importante faltando.
Até agora utilizamos apenas Slivers fornecidos pelo Flutter.
No próximo artigo vamos dar um passo além.
Vamos construir nosso próprio Sliver e descobrir como criar componentes que participam diretamente do pipeline de layout e renderização do Flutter.



