Когда вызывается state dispose?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда вызывается State.dispose()?
Метод dispose() — это критически важный метод в жизненном цикле StatefulWidget'а. Понимание его вызова необходимо для предотвращения утечек памяти и правильного управления ресурсами.
Основное назначение dispose()
Метод dispose() вызывается, когда State удаляется из дерева виджетов и никогда больше не будет перестроено. Это последняя возможность очистить ресурсы.
class MyScreen extends StatefulWidget {
@override
State<MyScreen> createState() => MyScreenState();
}
class MyScreenState extends State<MyScreen> {
late AnimationController _animationController;
late StreamSubscription _subscription;
@override
void initState() {
super.initState();
// Инициализация ресурсов
_animationController = AnimationController(
duration: Duration(seconds: 1),
vsync: this,
);
_subscription = someStream.listen((event) {
print('Event: $event');
});
}
@override
void dispose() {
// Очистка ресурсов
_animationController.dispose();
_subscription.cancel();
super.dispose(); // Всегда вызывай super.dispose() в конце!
}
@override
Widget build(BuildContext context) {
return Container();
}
}
Когда именно вызывается dispose()
1. Когда виджет удаляется из дерева
Самый очевидный случай — когда пользователь навигирует с экрана, на котором находится StatefulWidget:
// В первом экране
class FirstScreen extends StatefulWidget {
@override
State<FirstScreen> createState() => FirstScreenState();
}
class FirstScreenState extends State<FirstScreen> {
@override
void dispose() {
print('FirstScreen disposed'); // Выполнится при навигации
super.dispose();
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => SecondScreen()),
);
// dispose() вызовется здесь
},
child: Text('Go to Second'),
);
}
}
2. Когда родитель перестраивается и удаляет дочерний виджет
Если родитель условно включает/исключает виджет:
class ParentWidget extends StatefulWidget {
@override
State<ParentWidget> createState() => ParentWidgetState();
}
class ParentWidgetState extends State<ParentWidget> {
bool showChild = true;
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: () => setState(() => showChild = !showChild),
child: Text('Toggle Child'),
),
if (showChild) ChildWidget(), // dispose() вызовется при удалении
],
);
}
}
class ChildWidget extends StatefulWidget {
@override
State<ChildWidget> createState() => ChildWidgetState();
}
class ChildWidgetState extends State<ChildWidget> {
@override
void dispose() {
print('ChildWidget disposed'); // Выполнится при showChild = false
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text('Child');
}
}
3. При замене типа виджета
Если виджет заменяется другим типом виджета:
class DynamicWidget extends StatefulWidget {
final bool useWidget1;
const DynamicWidget({required this.useWidget1});
@override
State<DynamicWidget> createState() => DynamicWidgetState();
}
class DynamicWidgetState extends State<DynamicWidget> {
@override
void didUpdateWidget(DynamicWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.useWidget1 != widget.useWidget1) {
// dispose() не вызывается здесь
}
}
@override
Widget build(BuildContext context) {
return widget.useWidget1 ? WidgetOne() : WidgetTwo();
}
}
4. При завершении приложения
Когда приложение закрывается, все активные State'ы получают dispose():
@override
void dispose() {
print('App is closing'); // Выполнится при выходе из приложения
super.dispose();
}
Что необходимо очищать в dispose()
1. Controllers (Animation, TextEdit, ScrollView)
late AnimationController _animationController;
late TextEditingController _textController;
late ScrollController _scrollController;
@override
void dispose() {
_animationController.dispose();
_textController.dispose();
_scrollController.dispose();
super.dispose();
}
2. Streams и Subscriptions
late StreamSubscription _subscription;
@override
void initState() {
super.initState();
_subscription = Firestore.instance.collection('users').snapshots().listen((_) {});
}
@override
void dispose() {
_subscription.cancel();
super.dispose();
}
3. Таймеры
late Timer _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(Duration(seconds: 1), (_) {
print('Tick');
});
}
@override
void dispose() {
_timer.cancel();
super.dispose();
}
4. Listeners
late FocusNode _focusNode;
@override
void initState() {
super.initState();
_focusNode = FocusNode();
_focusNode.addListener(_onFocusChange);
}
void _onFocusChange() {
print('Focus changed');
}
@override
void dispose() {
_focusNode.removeListener(_onFocusChange);
_focusNode.dispose();
super.dispose();
}
5. Providers (для пакета provider)
@override
void dispose() {
context.read<UserProvider>().removeListener(_onUserChanged);
super.dispose();
}
Последствия неправильного использования dispose()
Утечка памяти
// Плохо — нет очистки
class BadWidget extends StatefulWidget {
@override
State<BadWidget> createState() => BadWidgetState();
}
class BadWidgetState extends State<BadWidget> {
late Timer _timer;
@override
void initState() {
super.initState();
_timer = Timer.periodic(Duration(seconds: 1), (_) {}); // Таймер никогда не отменяется
}
@override
Widget build(BuildContext context) => Container();
// dispose() отсутствует — УТЕЧКА ПАМЯТИ!
}
Ошибки при биндинге к disposed объекту
// Плохо
_controller.addListener(() {
// После dispose может быть вызвано
setState(() {}); // "setState called after dispose" error
});
Лучшие практики
1. Всегда вызывай super.dispose()
@override
void dispose() {
_animationController.dispose();
_textController.dispose();
super.dispose(); // ОБЯЗАТЕЛЬНО в конце!
}
2. Используй mounted для проверки
@override
void dispose() {
if (mounted) {
// Выполни только если State ещё активен
setState(() {});
}
super.dispose();
}
3. Избегай async операций в dispose()
// Плохо
@override
void dispose() {
await repository.saveData(); // Может не завершиться
super.dispose();
}
// Хорошо — используй didChangeAppLifecycleState
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.detached) {
repository.saveData();
}
}
4. Создавай dispose-able классы
class MyService {
void dispose() {
// Очистка
}
}
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
late MyService _service;
@override
void initState() {
super.initState();
_service = MyService();
}
@override
void dispose() {
_service.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => Container();
}
Итог
dispose() вызывается когда:
- StatefulWidget удаляется из дерева виджетов
- Родитель перестраивается и исключает виджет
- Приложение завершается
В dispose() нужно:
- Отменять все таймеры
- Закрывать потоки и subscription'ы
- Вызывать dispose() на controllera
- Удалять listener'ы
- Освобождать другие ресурсы
Всегда вызывай super.dispose() в конце! Это критически важно для правильной очистки ресурсов и предотвращения утечек памяти.