Ответ
Метод dispose() вызывается, когда объект State окончательно и безвозвратно удаляется из дерева виджетов. Это точка очистки ресурсов, чтобы избежать утечек памяти.
Конкретные случаи вызова dispose():
- При удалении виджета из дерева навсегда (например,
Navigator.pushReplacementзаменяет текущий экран). - Когда родительский виджет перестраивается и больше не включает данный
StatefulWidget. - При использовании
GlobalKeyи перемещении виджета в другое место дерева с другим ключом.
Что обязательно нужно освобождать в dispose():
class _MyPlayerState extends State<MyPlayer> {
late AnimationController _animationController;
late StreamSubscription<int> _timerSubscription;
Timer? _periodicTimer;
final _scrollController = ScrollController();
@override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this, duration: Duration(seconds: 1));
_timerSubscription = Stream.periodic(Duration(seconds: 1)).listen((_) {});
_periodicTimer = Timer.periodic(Duration(seconds: 1), (_) {});
_scrollController.addListener(() {});
}
@override
void dispose() {
// 1. Контроллеры анимаций (обязательно, т.к. они требуют TickerProvider)
_animationController.dispose();
// 2. Подписки на стримы
_timerSubscription.cancel();
// 3. Таймеры
_periodicTimer?.cancel();
// 4. Контроллеры скролла, текста и т.п.
_scrollController.dispose();
// Вызов super.dispose() ВСЕГДА в конце
super.dispose();
}
}
Важно: После вызова dispose() объект State больше не может быть использован. Любая попытка вызвать setState() после этого выбросит исключение.
Ответ 18+ 🔞
А, ну это же классика, ёпта! Сейчас объясню, как не наступить на эти грабли. Метод dispose() — это такой последний выдох, когда твой State окончательно и навсегда отправляется в утиль. Насовсем, блядь. Не "ой, я его потом верну", а именно накрылся медным тазом. Точка очистки, чтобы память не текла, как решето.
Когда этот похоронный звонок раздаётся:
- Виджет выпиливают из дерева навсегда. Типа
Navigator.pushReplacement— старый экран в пизду, новый на его место. - Родитель перестроился и решил: "А этого сыночка я сегодня не хочу". И всё, state-объект — в утиль.
- Используешь
GlobalKeyи так намудрил с перемещениями, что виджет получает новый ключ — старый state летит в помойку.
А теперь, что там внутри dispose() нужно придушить, чтобы не бздело потом:
Смотри, вот живой пример, чувак:
class _MyPlayerState extends State<MyPlayer> {
late AnimationController _animationController;
late StreamSubscription<int> _timerSubscription;
Timer? _periodicTimer;
final _scrollController = ScrollController();
@override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this, duration: Duration(seconds: 1));
_timerSubscription = Stream.periodic(Duration(seconds: 1)).listen((_) {});
_periodicTimer = Timer.periodic(Duration(seconds: 1), (_) {});
_scrollController.addListener(() {});
}
@override
void dispose() {
// 1. Контроллеры анимаций — это святое! Они же тикеры жрут.
_animationController.dispose();
// 2. Подписки на стримы — иначе они так и будут висеть, как доверия ебать ноль.
_timerSubscription.cancel();
// 3. Таймеры — прибей, иначе будут тикать в пустоту.
_periodicTimer?.cancel();
// 4. Контроллеры скролла, текста — тоже ресурсы.
_scrollController.dispose();
// И ВСЕГДА, блядь, super.dispose() вызывай в самом конце! Иначе отец не упокоится.
super.dispose();
}
}
И главное, запомни раз и навсегда: после того как dispose() отработал, твой state — это труп. Мёртвенький. Любая попытка ткнуть в него пальцем, типа вызвать setState(), закончится дикой ошибкой. Сам от себя охуеешь, когда приложение крешнется на ровном месте. Так что чисти за собой, не будь распиздяем.