← Назад к вопросам

Когда вызывается 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()