›Series summary

Criando seu próprio Sliver
Descubra como um Sliver conversa com o Viewport e entenda o mecanismo que move todo o sistema de scroll do Flutter.
Índice
Durante toda esta série desmontamos uma das maiores caixas-pretas do Flutter.
Descobrimos que:
- o
ListViewé apenas um wrapper; - uma tela rolável é formada por Slivers;
- o
Viewportcoordena todos eles; - cada Sliver calcula seu próprio layout.
Mas ainda existe uma pergunta.
Quem ensina um Sliver a fazer tudo isso?
Como um SliverAppBar sabe diminuir de tamanho?
Como um SliverList sabe quais itens construir?
Como um SliverGrid informa quanto espaço ocupa?
A resposta está em uma classe que a maioria dos desenvolvedores Flutter nunca chega a abrir: RenderSliver.
Neste artigo vamos construir nosso próprio Sliver. Não porque você fará isso todos os dias, mas porque entender esse mecanismo muda completamente a forma como você enxerga o Flutter.
Tudo começa no RenderSliver
Nos artigos anteriores vimos diversas classes:
SliverListSliverGridSliverPaddingSliverFillRemaining
Todas elas possuem algo em comum.
Em algum momento acabam delegando o trabalho para um RenderSliver.
É ele quem realmente participa do pipeline de layout.
Visualmente:
Widget
↓
Element
↓
RenderSliver
É aqui que o Flutter deixa de lidar com Widgets e passa a trabalhar com geometria.
O contrato de um Sliver
Todo RenderSliver possui duas responsabilidades.
Receber:
SliverConstraints
E responder com:
SliverGeometry
É literalmente uma conversa.
Viewport
↓
SliverConstraints
↓
RenderSliver
↓
SliverGeometry
↓
Viewport
Esse mesmo fluxo acontece dezenas de vezes durante cada segundo de scroll.
O método mais importante
Todo Sliver implementa um método.
@override
void performLayout() { }
Esse método é chamado sempre que o layout precisa ser recalculado.
É aqui que toda a mágica acontece.
O que recebemos?
Dentro de performLayout() temos acesso às restrições.
@override
void performLayout() {
final c = constraints;
}
Algumas informações disponíveis:
constraints.scrollOffset
constraints.remainingPaintExtent
constraints.viewportMainAxisExtent
constraints.crossAxisExtent
Ou seja… Nosso Sliver sabe exatamente:
- Quanto o usuário já rolou;
- Quanto ainda cabe no viewport;
- Qual a largura disponível;
- Qual a altura visível.
O Viewport entrega todas essas informações.
A resposta do Sliver
Depois dos cálculos, precisamos responder.
geometry = SliverGeometry(
scrollExtent: ...,
paintExtent: ...,
layoutExtent: ...,
);
É aqui que dizemos ao Flutter:
Eu ocupo X pixels.
ou
Ainda existe conteúdo abaixo.
ou
Posso desenhar apenas esta parte.
O Viewport usa essas respostas para montar a tela.
Vamos criar um SliverSpacing
Imagine que queremos um Sliver responsável apenas por criar um espaço de 48 pixels.
Hoje provavelmente escreveríamos:
SliverToBoxAdapter(
child: SizedBox(
height: 48,
),
)
Funciona.
Mas estamos criando um Widget inteiro apenas para representar um espaço.
Vamos construir um Sliver que faz isso diretamente.
Nosso RenderSliver
class RenderSliverSpacing extends RenderSliver {
RenderSliverSpacing(this.extent);
final double extent;
@override
void performLayout() {
final paintExtent = calculatePaintOffset(
constraints,
from: 0,
to: extent,
);
geometry = SliverGeometry(
scrollExtent: extent,
paintExtent: paintExtent,
layoutExtent: paintExtent,
maxPaintExtent: extent,
);
}
}
É muito menos código do que parece.
Na prática fazemos apenas duas coisas.
Calculamos quanto desse espaço realmente está visível.
Depois informamos essa geometria ao Viewport.
O que calculatePaintOffset faz?
Imagine que nosso espaço tenha 200 pixels.
Mas apenas 40 estejam visíveis.
O método:
calculatePaintOffset(...)
retorna exatamente isso.
Espaço total 200px
↓
Viewport
↓
40px visíveis
Assim não desenhamos pixels desnecessários.
O Viewport faz o restante
Depois que respondemos com uma SliverGeometry, nosso trabalho termina.
O Viewport continua percorrendo todos os Slivers.
SliverAppBar
↓
Geometr
↓
SliverList
↓
Geometry
↓
RenderSliverSpacing
↓
Geometry
↓
SliverGrid
↓
Geometry
Depois soma tudo.
Só então decide o que realmente será desenhado.
É assim que nasce qualquer Sliver
O mais interessante é perceber que exatamente esse mecanismo é utilizado para criar:
SliverListSliverGridSliverAppBarSliverFillRemainingSliverPersistentHeader
A diferença entre eles não é arquitetural. A diferença está apenas na lógica implementada dentro de performLayout().
Todos obedecem exatamente o mesmo contrato.
Você provavelmente nunca precisará fazer isso
E tudo bem.
A maioria das aplicações nunca implementará um RenderSliver.
Mas conhecer essa arquitetura traz vários benefícios.
Quando você encontrar:
- Um problema de layout;
- Um comportamento estranho de scroll;
- Uma AppBar expansível;
- Um Sliver personalizado de alguma biblioteca;
Você entenderá exatamente o que está acontecendo.
Esse tipo de conhecimento costuma separar quem apenas usa uma framework de quem realmente consegue raciocinar sobre ela.
Conclusão
Ao longo desta série, percorremos o caminho completo de uma interface rolável no Flutter.
Começamos com um simples ListView, descobrimos que ele é construído sobre Slivers, entendemos como o Viewport coordena o layout, montamos uma tela inteira utilizando CustomScrollView e, finalmente, chegamos ao nível mais baixo da abstração: um RenderSliver.
Mais importante do que aprender novas APIs foi construir um modelo mental sobre como o Flutter realmente funciona.
Esse conhecimento dificilmente aparecerá em tutoriais rápidos ou na documentação de uma única classe, mas faz diferença quando você precisa diagnosticar problemas complexos, tomar decisões arquiteturais ou simplesmente entender por que o framework se comporta da forma que se comporta.
Se esta série cumpriu seu objetivo, espero que da próxima vez que você escrever
ListView.builder(...), enxergue muito mais do que uma lista. Você enxergará toda a arquitetura que trabalha silenciosamente para entregar uma experiência de scroll rápida, eficiente e elegante.
🎉 You've completed the series Como o Flutter constrói uma tela rolável



