DEV Community

Cover image for ListView.build vs ListView tem diferença?
Gustavo Guedes
Gustavo Guedes

Posted on

ListView.build vs ListView tem diferença?

Recentemente esbarrei em um post no LinkedIn que dizia: "O mercado de Flutter brasileiro está morto." No post ele descrevia que em entrevistas com candidatos que se intitulavam Sênior, perguntas de nível Junior ou inferiores não eram respondidas de maneira satisfatória.

Então resolvi criar uma série de artigos/posts sobre as perguntas/assuntos abordados nesse post para consolidar meus conhecimentos e ajudar a comunidade a responder de maneira assertivas essas perguntas.

Nesse artigo falaremos sobre a diferença entre ListView.build e ListView(unnamed constructor).

O são ListView e ListView.build?

Conforme a documentação do Flutter, uma ListView é: "Uma lista rolável de widgets organizados linearmente."

Enquanto a ListView.build: "Cria uma matriz linear e rolável de widgets criados sob demanda.

Este construtor é apropriado para visualizações de lista com um número grande (ou infinito) de filhos porque o construtor é chamado apenas para os filhos que são realmente visíveis."

Fica claro que a diferença principal entre elas está na forma como os widgets da lista são renderizados e gerenciados.

Um exemplo simples da utilização de uma ListView é:

ListView(
  children: [
    ListTile(title: Text('Item 1')),
    ListTile(title: Text('Item 2')),
    ListTile(title: Text('Item 3')),
  ],
);
Enter fullscreen mode Exit fullscreen mode

É uma maneira simples de habilitar o scroll em uma lista de itens. Uma abordagem semelhante seria:

SingleChildScrollView(
  child: Column(
    children: [
      ListTile(title: Text('Item 1')),
      ListTile(title: Text('Item 2')),
      ListTile(title: Text('Item 3')),
    ],
  )
);
Enter fullscreen mode Exit fullscreen mode

O comportamento seria o mesmo.

Fica claro que esse tipo de abordagem são para listagens simples no qual a quantidade de itens não é massiva ou para elementos que necessitem ser instanciados quando forem para a tela.

Exemplo: Imagine que você possua uma listagem de itens que serão mostrados para o usuário e o componente que você criou para exibi-los, envia um evento para seu serviço de Analytics quando esse elemento é criado. Não exibido ou tocado, mas criado. Perceba que se a gente usar o ListView.build alguns desses elementos poderão não disparar esse evento, logo, as informações no serviço não irão refletir o comportamento esperado.

E quanto a ListView.build? O que seria "widgets criados sob demanda"? Vamos falar um pouco sobre isso.

Renderizações sob demanda

Esse conceito é algo amplamente utilizado não só no Flutter, mas em muitas outras stacks. Como:

List - SwiftUI
RecyclerView - Android (Java/Kotlin)
LazyColumn - Jetpack Compose (Android/Kotlin)
UITableView - UIKit (iOS - Swift)
FlatList - React Native
VirtualScroller - VueJS
react-window ou react-virtualized - ReactJS

Praticamente toda tecnológica moderna possui esse conceito, renderização sob demanda. Quando estamos inteirando uma listagem com centenas ou até infinitos itens, levando tudo isso para memória irá causa lentidão, freezing ou até crashs da nossa aplicação.

A ideia é bem simples: Só coloco em memória aquilo que está sendo exibindo, alguns itens anteriores, se for o caso, e alguns itens posteriores, se for o caso também.

Exemplo:

Imagine uma listagem de 100 itens. E o usuário está vendo o intervalo entre os itens 10 e 15. A nossa ferramenta irá colocar em memória os itens da posição 5 a 20. Esses números são um exemplo, o cálculo para isso leva em consideração algumas informações como: tamanho de cada item, posição do scroll, se está próximo do início ou final da listagem e até tamanho da tela.

Falando um pouco mais sobre o ListView.build

Lendo a documentação desse Widget, o segredo dele está no itemCount. É uma prop opcional, mas auxilia no cálculo que mencionei acima. Se em algum cenário você não tiver como configurar essa propriedade, o itemBuilder fará o papel de dizer para o componente pai, ListView.build, quando não há mais itens a serem renderizados.

A tipagem desse do itemBuilder é: NullableIndexedWidgetBuilder, que por sua vez é:

NullableIndexedWidgetBuilder = Widget? Function(
  BuildContext context,
  int index
)
Enter fullscreen mode Exit fullscreen mode

Isso é importante entender porque se retornamos null em algum momento, mesmo com o itemCount configurado, irá interromper e exibição dos próximos itens.

Exemplo:

ListView.builder(
  itemCount: 100,
  itemBuilder: (context, index) {
    print(index);
    return ListTile(
      title: Text("Item $index"),
    );
  },
),
Enter fullscreen mode Exit fullscreen mode

Utilizando um simulador do iPhone 15 Pro Max, dessa listagem o console me printou até o index 19, logo, de 100 itens somente 20 deles estão em memória. E se fizermos a seguinte edição:

ListView.builder(
  itemCount: 100,
  itemBuilder: (context, index) {
    if (index == 50) {
      return null;
    }

    print(index);
    return ListTile(
      title: Text("Item $index"),
    );
  },
),
Enter fullscreen mode Exit fullscreen mode

A exibição irá exibir até o elemento 50, index 49.

gif

Fique atento!

Conclusão

A utilização do ListView.builder é sempre recomendado para amenizar o consumo de memória, principalmente quando falamos de dispositivos mais antigos, mas também vimos que há cenários onde ListView se encaixa melhor.

Ainda tem muita coisa legal para falar sobre esse assunto, como: ListView.custom e ListView.separated além das integrações do ListView com elementos SliverChildBuilder. Mas acho que com os conceitos falados nesse artigo, você irá conseguir responder qual a diferença entre esses dois Widgets e quando os usar.

E isso! Vlw pessoal.

Top comments (0)