Flutter скрывает от разработчика ручное управление памятью. Кажется, что сборщик мусора все сделает сам. Но на практике утечки случаются регулярно. Экран закрыт, а какие-то объекты продолжают висеть в памяти. После нескольких переходов туда-сюда приложение начинает тормозить, а на слабых устройствах - вылетать. Это не баг фреймворка, это ошибки в коде, которые можно и нужно контролировать.
Откуда берутся утечки:
Чаще всего проблема в том, что объект создается, но не освобождается. Экран ушел, а контроллеры, подписки, таймеры или слушатели продолжают жить. Они держат ссылки на другие объекты, а те - на виджеты. В итоге память не отдается системе.
Контроллеры и dispose:
TextEditingController, ScrollController, AnimationController, FocusNode - все это нужно уничтожать в dispose(). Если этого не делать, каждый открытый экран будет оставлять после себя мусор. Правило простое: создал в initState - удали в dispose.
Подписки на Stream и слушатели:
StreamSubscription, addListener у ChangeNotifier и ValueNotifier - это классические источники утечек. Если подписался, но не отписался, поток будет слать события в уже мертвый экран. А заодно удерживать его в памяти. Решение: сохранять подписку и отменять ее в dispose.
BLoC и другие сущности с жизненным циклом:
Если BLoC создается прямо внутри State, нужно не забыть вызвать close(). Удобнее использовать BlocProvider, который сам управляет жизненным циклом. Главное правило: кто создал, тот и закрывает.
Таймеры:
Timer.periodic запускает повторяющиеся задачи. Если не отменить таймер при уходе с экрана, он будет продолжать тикать, вызывать setState и держать экран в памяти. Всегда сохраняйте ссылку на таймер и вызывайте cancel() в dispose.Сохраненный BuildContext:
BuildContext нельзя хранить в сервисах, синглтонах или долгоживущих объектах. Он привязан к дереву виджетов. Хранив его надолго, вы случайно удерживаете в памяти целую часть интерфейса. Лучший способ - передавать контекст только в момент вызова, не сохраняя его.
Кэши без ограничений:
Кэш - это тоже утечка, если он растет бесконтрольно. Изображения, модели, результаты запросов - все это копится в памяти, если вовремя не чистить. Простое решение: ограничить размер кэша или очищать его при выходе пользователя.
🔗 Читать подробнее
💡 Вывод:
Утечки памяти во Flutter - это не магия, а следствие забытых dispose, cancel и removeListener. Они не видны сразу, но накапливаются со временем. Самый надежный способ - профилактика: иметь чек-лист для код-ревью и периодически прогонять подозрительные сценарии через DevTools. Если относиться к dispose как к обязательной части работы с экраном, большинство проблем можно поймать еще до релиза.
Подписаться на канал:
➡️ Flutter & Dart | Мобильный трудоголик
Закрытый канал:
🚀 Мобильный трудоголик PRO
