Pular para o conteúdo principal

Widgets com Scroll

SingleChildScrollView

Permite rolar conteúdo que excede o tamanho da tela:

SingleChildScrollView(
child: Column(
children: [
// Muitos widgets aqui
Text('Conteúdo 1'),
Text('Conteúdo 2'),
// ... mais conteúdo
],
),
)

ListView

ListView exibe uma lista rolável de widgets.

ListView com children

Usado quando você tem uma lista pequena e fixa de widgets (efeito similar ao uso de SingleChildScrollView):

ListView(
children: [
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
ListTile(title: Text('Item 3')),
],
)

ListView.builder

Usado para listas grandes ou dinâmicas. Mais eficiente em memória:

ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
);
},
)

ListView.separated

Similar ao builder, mas permite adicionar separadores entre itens:

ListView.separated(
itemCount: 10,
separatorBuilder: (context, index) => Divider(),
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
);
},
)

Diferenças importantes:

  • children: Cria todos os widgets de uma vez. Use para listas pequenas (< 50 itens)
  • builder: Cria widgets sob demanda (lazy loading). Use para listas grandes ou dinâmicas
  • separated: Como builder, mas com separadores customizados
dica

Quando usar SingleChildScrollView ou ListView?

  • SingleChildScrollView: Use quando você tem um conteúdo fixo e conhecido que precisa rolar, como um formulário longo, uma página de detalhes com múltiplas seções, ou qualquer layout customizado que não seja uma lista de itens repetidos. Cria todos os widgets de uma vez.

  • ListView: Use quando você tem uma lista de itens similares ou repetidos (como uma lista de produtos, mensagens, contatos). É mais eficiente para listas grandes porque cria widgets sob demanda (lazy loading) com ListView.builder.

GridView

Exibe widgets em uma grade:

GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: Center(
child: Text('Item $index'),
),
);
},
)

Exercícios

  1. ListView.builder: Crie uma lista usando ListView.builder que exiba números de 1 a 100. Cada item deve mostrar "Item [número]".

  2. ListView.separated: Crie uma lista usando ListView.separated com 10 itens. Adicione um Divider como separador entre cada item.

  3. SingleChildScrollView: Crie um formulário longo usando SingleChildScrollView com múltiplos campos de texto e botões. Demonstre que o conteúdo pode ser rolado.

  4. GridView: Crie um GridView com 20 itens em uma grade de 2 colunas. Cada item deve ser um Card com um número.

  5. ListTile interativo: Crie uma lista usando ListView.builder com ListTile que responda a toques. Ao tocar em um item, exiba um SnackBar com o nome do item.

Soluções

Ver solução do exercício 1
import 'package:flutter/material.dart';

void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('ListView.builder')),
body: ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item ${index + 1}'),
);
},
),
),
));
}
Ver solução do exercício 2
import 'package:flutter/material.dart';

void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('ListView.separated')),
body: ListView.separated(
itemCount: 10,
separatorBuilder: (context, index) => Divider(),
itemBuilder: (context, index) {
return ListTile(
title: Text('Item ${index + 1}'),
);
},
),
),
));
}
Ver solução do exercício 3
import 'package:flutter/material.dart';

void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Formulário')),
body: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: Column(
children: [
TextField(decoration: InputDecoration(labelText: 'Nome')),
TextField(decoration: InputDecoration(labelText: 'Email')),
TextField(decoration: InputDecoration(labelText: 'Telefone')),
TextField(decoration: InputDecoration(labelText: 'Endereço')),
TextField(decoration: InputDecoration(labelText: 'Cidade')),
TextField(decoration: InputDecoration(labelText: 'Estado')),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {},
child: Text('Enviar'),
),
],
),
),
),
));
}
Ver solução do exercício 4
import 'package:flutter/material.dart';

void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('GridView')),
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
padding: EdgeInsets.all(10),
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: Center(
child: Text('Item ${index + 1}'),
),
);
},
),
),
));
}
Ver solução do exercício 5
import 'package:flutter/material.dart';

void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Lista Interativa')),
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item ${index + 1}'),
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Item ${index + 1} selecionado')),
);
},
);
},
),
),
));
}