Voltar para artigos

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.

5 min de leitura20 leituras
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 Viewport coordena 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:

  • SliverList
  • SliverGrid
  • SliverPadding
  • SliverFillRemaining

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:

  • SliverList
  • SliverGrid
  • SliverAppBar
  • SliverFillRemaining
  • SliverPersistentHeader

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.

Compartilhar:XLinkedInWhatsApp
Wellington Eugenio dos Santos

Escrito por Wellington Eugenio dos Santos

Engenheiro de Software focado em criar soluções robustas de alto desempenho para Web e Mobile.
Criador de bibliotecas open-source como signal_form e lucid_validation.

🎉 Você concluiu a série Como o Flutter constrói uma tela rolável