Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое deactivate?
deactivate() — это метод жизненного цикла в StatefulWidget, который вызывается, когда виджет удаляется из дерева виджетов, но до его окончательного уничтожения. Это критичный метод для корректной очистки ресурсов.
Жизненный цикл StatefulWidget
Полный цикл жизни выглядит так:
1. createState() → создает State объект
2. initState() → инициализация
3. build() → рендеринг
4. didUpdateWidget() → обновление при изменении properties
5. deactivate() ← УДАЛЕНо из дерева (но не уничтожено!)
6. dispose() → финальная очистка
Когда вызывается deactivate()
deactivate() вызывается в следующих ситуациях:
- Навигация: Переход на другой экран (поп/пуш)
- Удаление из списка: Если виджет в ListView удален скроллингом
- Условный рендеринг: Если условие сменилось и виджет скрыт
- Перестановка в дереве: Изменение порядка виджетов
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),
);
}
}
Важные правила
-
Всегда вызовите super.deactivate()
@override void deactivate() { // ваш код super.deactivate(); // ОБЯЗАТЕЛЬНО! } -
Используйте для временной очистки
- Остановка таймеров
- Отмена HTTP запросов
- Приостановка слушателей
-
Не используйте для финальной очистки
- Закрытие файлов
- Очистку памяти
- Это делается в dispose()
Когда deactivate() может не вызваться
- При завершении приложения (dispose() вызывается напрямую)
- При перезагрузке "Hot Reload" в разработке
- При крахе приложения
Вывод
deactivate() — это мост между "активным" и "уничтоженным" состоянием виджета. Используйте его для:
- Остановки дорогостоящих операций
- Отмены незавершенных запросов
- Приостановки слушателей без их полного удаления
Это критично для предотвращения утечек памяти и "зависаний" приложения.