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

Что такое deactivate?

1.0 Junior🔥 191 комментариев
#Flutter виджеты

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое deactivate?

deactivate() — это метод жизненного цикла в StatefulWidget, который вызывается, когда виджет удаляется из дерева виджетов, но до его окончательного уничтожения. Это критичный метод для корректной очистки ресурсов.

Жизненный цикл StatefulWidget

Полный цикл жизни выглядит так:

1. createState() → создает State объект
2. initState() → инициализация
3. build() → рендеринг
4. didUpdateWidget() → обновление при изменении properties
5. deactivate() ← УДАЛЕНо из дерева (но не уничтожено!)
6. dispose() → финальная очистка

Когда вызывается deactivate()

deactivate() вызывается в следующих ситуациях:

  1. Навигация: Переход на другой экран (поп/пуш)
  2. Удаление из списка: Если виджет в ListView удален скроллингом
  3. Условный рендеринг: Если условие сменилось и виджет скрыт
  4. Перестановка в дереве: Изменение порядка виджетов
class MyWidget extends StatefulWidget {
  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  void deactivate() {
    print('Виджет УДАЛЕН из дерева');
    super.deactivate();
  }

  @override
  void dispose() {
    print('Виджет УНИЧТОЖЕН (финальная очистка)');
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Жизненный цикл')),
    );
  }
}

Ключевое отличие: deactivate() vs dispose()

deactivate()           | dispose()
───────────────────────┼──────────────────────
Виджет УДАЛЕН из       | Виджет ОКОНЧАТЕЛЬНО
дерева, но может       | уничтожен
вернуться             |
Можно возобновить     | Нельзя возобновить
потоки/таймеры       | нельзя использовать
Виджет может быть     | State объект
включен обратно       | удаляется из памяти

Практические примеры

Пример 1: Таймер при навигации

class StopwatchScreen extends StatefulWidget {
  @override
  State<StopwatchScreen> createState() => _StopwatchScreenState();
}

class _StopwatchScreenState extends State<StopwatchScreen> {
  Timer? _timer;
  int _seconds = 0;

  @override
  void initState() {
    super.initState();
    _startTimer();
  }

  void _startTimer() {
    _timer = Timer.periodic(Duration(seconds: 1), (timer) {
      setState(() {
        _seconds++;
      });
    });
  }

  @override
  void deactivate() {
    // Таймер продолжает работать в фоне, если не остановить!
    _timer?.cancel(); // ← Обязательно останавливаем
    print('Таймер остановлен при удалении виджета');
    super.deactivate();
  }

  @override
  void dispose() {
    _timer?.cancel(); // На случай если deactivate() не сработал
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('$_seconds сек')),
    );
  }
}

Пример 2: Отмена HTTP запроса при навигации

class ProfileScreen extends StatefulWidget {
  @override
  State<ProfileScreen> createState() => _ProfileScreenState();
}

class _ProfileScreenState extends State<ProfileScreen> {
  late CancelToken _cancelToken;

  @override
  void initState() {
    super.initState();
    _cancelToken = CancelToken();
    _loadProfile();
  }

  Future<void> _loadProfile() async {
    try {
      final response = await dio.get(
        '/api/profile',
        cancelToken: _cancelToken,
      );
      setState(() {
        // обновление состояния
      });
    } on DioException catch (e) {
      if (e.type == DioExceptionType.cancel) {
        print('Запрос отменен (пользователь ушел со страницы)');
      }
    }
  }

  @override
  void deactivate() {
    // Если пользователь ушел на другой экран, отменяем запрос
    _cancelToken.cancel('Widget deactivated');
    print('HTTP запрос отменен');
    super.deactivate();
  }

  @override
  void dispose() {
    _cancelToken.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Профиль')),
    );
  }
}

Пример 3: Управление слушателями (Listeners)

class LocationTracker extends StatefulWidget {
  @override
  State<LocationTracker> createState() => _LocationTrackerState();
}

class _LocationTrackerState extends State<LocationTracker> {
  late StreamSubscription _locationSubscription;

  @override
  void initState() {
    super.initState();
    _startTracking();
  }

  void _startTracking() {
    _locationSubscription = Geolocator.getPositionStream(
      locationSettings: LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 10, // 10 метров
      ),
    ).listen((position) {
      setState(() {
        print('Новая локация: ${position.latitude}, ${position.longitude}');
      });
    });
  }

  @override
  void deactivate() {
    // Если пользователь свернул приложение,
    // остаавляем слушатель активным (опционально)
    // Или отменяем, если нужно экономить батарею:
    
    print('Экран скрыт - можно остановить отслеживание');
    // _locationSubscription.cancel(); // если нужно
    super.deactivate();
  }

  @override
  void dispose() {
    _locationSubscription.cancel(); // Окончательная очистка
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Трекер локации')),
    );
  }
}

Пример 4: Сохранение состояния при навигации

class FormScreen extends StatefulWidget {
  @override
  State<FormScreen> createState() => _FormScreenState();
}

class _FormScreenState extends State<FormScreen> {
  final _controller = TextEditingController();

  @override
  void initState() {
    super.initState();
    // Восстанавливаем ранее сохраненный текст
    _controller.text = _getSavedText();
  }

  String _getSavedText() {
    // Из SharedPreferences или Cache
    return '';
  }

  @override
  void deactivate() {
    // Сохраняем текст когда экран скрыт
    // (но может быть не удален!)
    _saveText(_controller.text);
    print('Текст сохранен: ${_controller.text}');
    super.deactivate();
  }

  void _saveText(String text) {
    // Сохраняем в SharedPreferences
  }

  @override
  void dispose() {
    _controller.dispose(); // Очищаем контроллер
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: TextField(controller: _controller),
    );
  }
}

Важные правила

  1. Всегда вызовите super.deactivate()

    @override
    void deactivate() {
      // ваш код
      super.deactivate(); // ОБЯЗАТЕЛЬНО!
    }
    
  2. Используйте для временной очистки

    • Остановка таймеров
    • Отмена HTTP запросов
    • Приостановка слушателей
  3. Не используйте для финальной очистки

    • Закрытие файлов
    • Очистку памяти
    • Это делается в dispose()

Когда deactivate() может не вызваться

  • При завершении приложения (dispose() вызывается напрямую)
  • При перезагрузке "Hot Reload" в разработке
  • При крахе приложения

Вывод

deactivate() — это мост между "активным" и "уничтоженным" состоянием виджета. Используйте его для:

  • Остановки дорогостоящих операций
  • Отмены незавершенных запросов
  • Приостановки слушателей без их полного удаления

Это критично для предотвращения утечек памяти и "зависаний" приложения.

Что такое deactivate? | PrepBro