← Назад к вопросам
Когда вызывается state deactivate?
1.0 Junior🔥 141 комментариев
#Flutter виджеты#State Management
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда вызывается State.deactivate()?
Метод deactivate() — это важная часть lifecycle хука StatefulWidget в Flutter. Это часто спрашивают на собеседованиях, потому что многие разработчики его путают с dispose().
Жизненный цикл State
Полный жизненный цикл State выглядит так:
1. createState() — создание State
2. initState() — инициализация
3. build() — рендер UI
4. deactivate() — виджет выходит из дерева
5. dispose() — удаление State из памяти
Когда вызывается deactivate()?
1. Виджет удалён из дерева (основной случай)
class CounterPage extends StatefulWidget {
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int counter = 0;
@override
void initState() {
super.initState();
print('initState вызван');
}
@override
void deactivate() {
print('deactivate вызван — виджет выходит из дерева');
super.deactivate();
}
@override
void dispose() {
print('dispose вызван — очистка ресурсов');
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(child: Text('Counter: $counter')),
);
}
}
// Навигация вызывает deactivate()
void navigateAway() {
// 1. deactivate() вызывается
// 2. dispose() вызывается после
Navigator.push(context, MaterialPageRoute(
builder: (context) => OtherPage(),
));
}
Вывод консоли:
initState вызван
deactivate вызван — виджет выходит из дерева
dispose вызван — очистка ресурсов
2. Когда parent переестраивает дерево
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(showChild ? 'Hide Child' : 'Show Child'),
),
// Когда showChild = false:
// 1. deactivate() вызывается
// 2. dispose() вызывается
if (showChild) ChildWidget(),
],
);
}
}
class ChildWidget extends StatefulWidget {
@override
State<ChildWidget> createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
@override
void deactivate() {
print('Child vidjeta vyhodit iz dereva');
super.deactivate();
}
@override
void dispose() {
print('Child polnost'yu udalena');
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(child: Text('Child Widget'));
}
}
3. Переход на другую страницу через Navigator
class PageA extends StatefulWidget {
@override
State<PageA> createState() => _PageAState();
}
class _PageAState extends State<PageA> {
@override
void initState() {
super.initState();
print('PageA initState');
}
@override
void deactivate() {
// Вызывается когда pushes к PageB
print('PageA deactivate — user нажал кнопку');
super.deactivate();
}
@override
void dispose() {
print('PageA dispose — очистка ресурсов');
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Page A')),
body: Center(
child: ElevatedButton(
onPressed: () {
// Переход вызывает deactivate() у PageA
Navigator.push(context, MaterialPageRoute(
builder: (context) => PageB(),
));
},
child: Text('Go to Page B'),
),
),
);
}
}
Важное различие: deactivate() vs dispose()
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late StreamSubscription subscription;
late Timer timer;
@override
void initState() {
super.initState();
// Инициализация ресурсов
subscription = eventStream.listen((event) {
print('Event: $event');
});
timer = Timer.periodic(Duration(seconds: 1), (t) {
print('Tick');
});
}
@override
void deactivate() {
// ⚠️ НЕ вызывай dispose здесь!
// deactivate может быть временным (например при перестройке)
// виджет может остаться живым
print('deactivate вызван, но виджет может вернуться');
super.deactivate();
}
@override
void dispose() {
// ✅ ЭТО правильное место для cleanup
// Вызывается только когда State полностью удаляется
subscription.cancel(); // Остановить stream
timer.cancel(); // Остановить timer
print('dispose вызван — все ресурсы очищены');
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text('My Widget');
}
}
Таблица: deactivate() vs dispose()
| Аспект | deactivate() | dispose() |
|---|---|---|
| Когда вызывается | Виджет выходит из дерева | State полностью удаляется |
| Может ли вернуться | Да, иногда | Нет, никогда |
| Что тут делать | Временная пауза, если нужна | Cleanup, отмена подписок |
| Вызов super | Обязателен | Обязателен |
| Частота вызова | Может быть несколько раз | Один раз в жизни |
Реальный пример: Page Navigation
class HomePage extends StatefulWidget {
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
late StreamController<String> _messages;
@override
void initState() {
super.initState();
_messages = StreamController<String>();
print('❶ initState — страница открыта');
}
@override
void deactivate() {
// Пауза: пользователь ушел на другую страницу
print('❷ deactivate — user нажал "Details"');
super.deactivate();
}
@override
void dispose() {
// Полная очистка
_messages.close();
print('❹ dispose — очистка StreamController');
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
// ❸ Может быть вызван между deactivate и dispose
print('❸ didChangeAppLifecycleState: $state');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) => DetailsPage(),
));
},
child: Text('Go to Details'),
),
),
);
}
}
// Навигационный поток:
// 1. HomePage открывается → initState()
// 2. Пользователь нажимает кнопку
// 3. HomePage выходит из дерева → deactivate()
// 4. DetailsPage открывается
// 5. Если user вернулся назад — deactivate() НЕ вызовется
// 6. Когда HomePage удаляется → dispose()
Когда НЕ вызывается deactivate()
// ❌ deactivate() НЕ вызывается в следующих случаях:
// 1. Обновление параметров виджета (если ключи совпадают)
class MyStateful extends StatefulWidget {
final String title;
MyStateful({required this.title});
@override
State createState() => _MyStatefulState();
}
// parent переестраивается с новыми параметрами
// deactivate() может НЕ вызваться
MyStateful(title: 'New Title')
// 2. Просто rebuild родителя без изменения дерева
setState(() {}); // Не вызывает deactivate()
// 3. Горячая перезагрузка (hot reload) — deactivate() НЕ вызовется
Резюме
deactivate() вызывается когда:
- Виджет выходит из дерева (например, при навигации)
- Parent удаляет виджет из списка дочерних элементов
- Состояние переходит в неактивное (inactive) состояние
Главные отличия:
- deactivate() — временная пауза, виджет может вернуться
- dispose() — окончательное удаление, cleanup ресурсов
Best Practice:
- Используй initState() для инициализации
- Используй dispose() для cleanup (не deactivate!)
- Минимизируй логику в deactivate()
- Всегда вызывай super.deactivate() и super.dispose()