Рубрики
Flutter

Оптимизация списков во Flutter: ListView vs Sliver

Полное руководство по оптимизации списков во Flutter: ListView, Sliver, lazy loading и работа с большими данными (100K+ элементов).

Списки — один из самых важных компонентов в мобильных приложениях. В этой статье разберём как работать с большими списками во Flutter без потери производительности.

Проблема больших списков

Неправильная реализация списка из 10000 элементов может: — Убить приложение с OutOfMemory — Сделать UI медленным — Потреблять много батареи

ListView.builder

Правильный способ для динамических списков:

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(title: Text(items[index]));
  },
)

Почему это быстро? — Строит только видимые элементы — Переиспользует виджеты — Автоматически кеширует

ListView.separated

ListView.separated(
  itemCount: items.length,
  separatorBuilder: (context, index) => Divider(),
  itemBuilder: (context, index) {
    return ListTile(title: Text(items[index]));
  },
)

SliverListView для CustomScrollView

CustomScrollView(
  slivers: [
    SliverAppBar(
      title: Text('Sliver List'),
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        childCount: items.length,
        builder: (context, index) {
          return ListTile(title: Text(items[index]));
        },
      ),
    ),
  ],
)

Lazy loading

class _MyHomePageState extends State<MyHomePage> {
  final List<Item> items = [];
  final ScrollController _controller = ScrollController();
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _loadMore();
    _controller.addListener(_scrollListener);
  }

  void _scrollListener() {
    if (_controller.position.pixels >=
        _controller.position.maxScrollExtent - 200) {
      _loadMore();
    }
  }

  Future<void> _loadMore() async {
    if (_isLoading) return;

    setState(() => _isLoading = true);

    final newItems = await api.fetch(offset: items.length);
    setState(() {
      items.addAll(newItems);
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: _controller,
      itemCount: items.length + (_isLoading ? 1 : 0),
      itemBuilder: (context, index) {
        if (index == items.length) {
          return Center(child: CircularProgressIndicator());
        }
        return ListTile(title: Text(items[index].title));
      },
    );
  }
}

Optimised ListTile

// Используйте const конструкторы
const ListTile(
  leading: Icon(Icons.person),
  title: Text('Title'),
  trailing: Icon(Icons.arrow_forward),
)

Заключение

Для больших списков используйте ListView.builder или SliverListView с lazy loading. Избегайте ListView с большим количеством детей.