Ответ
В Flutter есть несколько виджетов для отображения списков, каждый со своими особенностями производительности и использования:
1. ListView — базовый виджет для вертикальных/горизонтальных списков:
// Простой список с заранее созданными детьми
ListView(
padding: EdgeInsets.all(16),
children: [
ListTile(title: Text('Item 1'), leading: Icon(Icons.star)),
ListTile(title: Text('Item 2'), leading: Icon(Icons.star)),
ListTile(title: Text('Item 3'), leading: Icon(Icons.star)),
],
)
// С разделителями
ListView.separated(
itemCount: 20,
separatorBuilder: (context, index) => Divider(height: 1, color: Colors.grey[300]),
itemBuilder: (context, index) => ListTile(
title: Text('Item $index'),
subtitle: Text('Subtitle $index'),
trailing: Icon(Icons.chevron_right),
),
)
2. ListView.builder — для динамических списков (ленивая загрузка):
final List<Product> products = [...]; // 1000+ элементов
ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ProductCard(
product: product,
onTap: () => _openProductDetails(product),
);
},
// Оптимизация производительности
addAutomaticKeepAlives: true, // Сохраняет состояние
addRepaintBoundaries: true, // Изолирует перерисовку
cacheExtent: 500, // Предзагрузка вне viewport
)
3. GridView — для отображения в виде сетки:
// Фиксированное количество колонок
GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 0.8, // Соотношение сторон
children: List.generate(20, (index) =>
Card(
child: Column(
children: [
Image.network('https://picsum.photos/200?random=$index'),
Text('Item $index'),
],
),
),
),
)
// Динамическая сетка с builder
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: MediaQuery.of(context).size.width > 600 ? 4 : 2,
childAspectRatio: 1.0,
),
itemCount: products.length,
itemBuilder: (context, index) => ProductGridItem(product: products[index]),
)
4. CustomScrollView + Slivers — для сложных кастомных списков:
CustomScrollView(
slivers: [
// Заголовок (прилипающий)
SliverAppBar(
title: Text('Products'),
floating: true,
snap: true,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
background: Image.network('https://...', fit: BoxFit.cover),
),
),
// Фильтры (не прокручиваются)
SliverPersistentHeader(
delegate: _StickyHeaderDelegate(
child: FilterBar(onFilterChanged: _applyFilter),
),
pinned: true,
),
// Основной список продуктов
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ProductCard(product: filteredProducts[index]),
childCount: filteredProducts.length,
),
),
// Загрузка следующей страницы
if (_isLoadingMore)
SliverToBoxAdapter(
child: Center(child: CircularProgressIndicator()),
),
// Подвал
SliverToBoxAdapter(
child: Footer(),
),
],
)
5. ListView с различными типами элементов:
ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
final item = _items[index];
// Разные типы элементов в одном списке
switch (item.type) {
case 'header':
return _buildHeader(item);
case 'product':
return _buildProductCard(item);
case 'banner':
return _buildBanner(item);
case 'divider':
return Divider(height: 24);
default:
return SizedBox.shrink();
}
},
)
6. Оптимизация производительности для больших списков:
// 1. Использование const конструкторов где возможно
ListView.builder(
itemBuilder: (context, index) => const ProductCard(
// const параметры
),
)
// 2. AutomaticKeepAliveClientMixin для сохранения состояния
class ProductCardState extends State<ProductCard>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true; // Сохранять состояние при скролле
@override
Widget build(BuildContext context) {
super.build(context); // Обязательный вызов
return ...;
}
}
// 3. Кэширование сложных вычислений
ListView.builder(
itemBuilder: (context, index) {
final product = products[index];
// Дорогие вычисления кэшируем
final formattedPrice = _priceCache.putIfAbsent(
product.id,
() => _formatPrice(product.price),
);
return ProductCard(price: formattedPrice);
},
)
7. Пакеты для специфичных задач:
flutter_staggered_grid_view— кастомные раскладки сеткиinfinite_scroll_pagination— пагинация с подгрузкойreorderables— перетаскивание элементов в спискеscrollable_positioned_list— прокрутка к конкретной позиции
В production-приложениях я чаще всего использую комбинацию: CustomScrollView с разными Slivers для сложных интерфейсов и ListView.builder/GridView.builder для ленивой загрузки больших наборов данных. Ключевое правило — всегда использовать builder-конструкторы для списков с переменным или большим количеством элементов, чтобы Flutter создавал виджеты только для видимой области.