Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Dispose в Flutter: Управление ресурсами
Определение
Dispose — это метод жизненного цикла StatefulWidget, который вызывается при удалении виджета из дерева виджетов. Это место, где мы должны очищать ресурсы, которые мы использовали: закрывать потоки, отписываться от событий, освобождать контроллеры и т.д.
В контексте управления памятью, dispose предотвращает утечки памяти (memory leaks).
Жизненный цикл StatefulWidget
Widget создан
↓
createState() — создание State
↓
initState() — инициализация ресурсов
↓
build() — построение UI (может вызваться множество раз)
↓
...
↓
Widget удалён из дерева
↓
dispose() — очистка ресурсов ← ВАЖНО!
↓
Widget полностью удалён
Практический пример
1. Базовый пример с AnimationController
class CounterWidget extends StatefulWidget {
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
int _counter = 0;
@override
void initState() {
super.initState();
// Создаём контроллер — это ресурс, требующий очистки
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this, // важно для производительности
);
print('Resource allocated');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter: $_counter')),
body: Center(
child: GestureDetector(
onTap: () {
setState(() => _counter++);
_controller.forward();
},
child: ScaleTransition(
scale: Tween<double>(begin: 1, end: 1.2).animate(
CurvedAnimation(parent: _controller, curve: Curves.bounceInOut),
),
child: Text('Tap me'),
),
),
),
);
}
@override
void dispose() {
// ✅ ОБЯЗАТЕЛЬНО очищаем ресурс
_controller.dispose();
print('Resource cleaned up');
super.dispose(); // Вызываем в конце!
}
}
Типичные случаи использования dispose
2. StreamController
class StreamListenerWidget extends StatefulWidget {
@override
State<StreamListenerWidget> createState() => _StreamListenerWidgetState();
}
class _StreamListenerWidgetState extends State<StreamListenerWidget> {
late StreamController<String> _streamController;
late StreamSubscription<String> _subscription;
@override
void initState() {
super.initState();
_streamController = StreamController<String>();
// Подписываемся на события потока
_subscription = _streamController.stream.listen((data) {
setState(() {
print('Received: $data');
});
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => _streamController.add('Event'),
child: Text('Send Event'),
);
}
@override
void dispose() {
// ✅ Закрываем подписку ПЕРЕД закрытием контроллера
_subscription.cancel();
_streamController.close();
super.dispose();
}
}
3. TextEditingController
class SearchWidget extends StatefulWidget {
@override
State<SearchWidget> createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
late TextEditingController _textController;
late FocusNode _focusNode;
@override
void initState() {
super.initState();
_textController = TextEditingController();
_focusNode = FocusNode();
// Слушаем изменения текста
_textController.addListener(() {
print('Text changed: ${_textController.text}');
});
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _textController,
focusNode: _focusNode,
decoration: InputDecoration(
hintText: 'Search...',
suffixIcon: _textController.text.isNotEmpty
? IconButton(
icon: Icon(Icons.clear),
onPressed: _textController.clear,
)
: null,
),
);
}
@override
void dispose() {
// ✅ Очищаем в обратном порядке
_textController.dispose();
_focusNode.dispose();
super.dispose();
}
}
4. Таймер
class TimerWidget extends StatefulWidget {
@override
State<TimerWidget> createState() => _TimerWidgetState();
}
class _TimerWidgetState extends State<TimerWidget> {
late Timer _timer;
int _seconds = 0;
@override
void initState() {
super.initState();
// Запускаем таймер каждую секунду
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() => _seconds++);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Timer: $_seconds sec')),
body: Center(child: Text('$_seconds')),
);
}
@override
void dispose() {
// ✅ Отменяем таймер — иначе будет утечка памяти
_timer.cancel();
super.dispose();
}
}
5. Слушатели системных событий
class LifecycleAwareWidget extends StatefulWidget {
@override
State<LifecycleAwareWidget> createState() => _LifecycleAwareWidgetState();
}
class _LifecycleAwareWidgetState extends State<LifecycleAwareWidget>
with WidgetsBindingObserver {
@override
void initState() {
super.initState();
// Подписываемся на события жизненного цикла приложения
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
print('App resumed');
break;
case AppLifecycleState.paused:
print('App paused');
break;
case AppLifecycleState.detached:
print('App detached');
break;
case AppLifecycleState.inactive:
print('App inactive');
break;
case AppLifecycleState.hidden:
print('App hidden');
break;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text('Lifecycle aware widget')),
);
}
@override
void dispose() {
// ✅ Удаляем себя из наблюдателей
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
Утечки памяти и проблемы
❌ НЕПРАВИЛЬНО: забыли dispose
class BadWidget extends StatefulWidget {
@override
State<BadWidget> createState() => _BadWidgetState();
}
class _BadWidgetState extends State<BadWidget> {
late StreamSubscription _subscription;
@override
void initState() {
super.initState();
_subscription = someStream.listen((_) {
// Слушаем evento
});
}
@override
Widget build(BuildContext context) => SizedBox();
// ❌ Забыли dispose — утечка памяти!
// Подписка будет оставаться активной даже после удаления виджета
}
✅ ПРАВИЛЬНО: правильно очищаем ресурсы
class GoodWidget extends StatefulWidget {
@override
State<GoodWidget> createState() => _GoodWidgetState();
}
class _GoodWidgetState extends State<GoodWidget> {
late StreamSubscription _subscription;
@override
void initState() {
super.initState();
_subscription = someStream.listen((_) {
// Слушаем события
});
}
@override
Widget build(BuildContext context) => SizedBox();
@override
void dispose() {
_subscription.cancel();
super.dispose(); // ✅ Всегда в конце!
}
}
Инструменты отладки
Проверка утечек памяти в DevTools
# Запускаем приложение с отладкой
flutter run --observe
# В другом терминале открываем DevTools
flutter pub global activate devtools
flutter pub global run devtools
# Идём на вкладку Memory и ищем утечки
Чеклист для dispose
// При создании ресурса в initState:
// ✓ AnimationController → _controller.dispose()
// ✓ StreamSubscription → _subscription.cancel()
// ✓ TextEditingController → _controller.dispose()
// ✓ FocusNode → _focusNode.dispose()
// ✓ Timer → _timer.cancel()
// ✓ StreamController → _controller.close()
// ✓ WidgetsBindingObserver → removeObserver(this)
@override
void dispose() {
_controller.dispose();
_subscription.cancel();
WidgetsBinding.instance.removeObserver(this);
super.dispose(); // ✅ ВСЕГДА в конце!
}
Заключение
Dispose — это критически важный метод для:
- Предотвращения утечек памяти — освобождение ресурсов
- Остановки фоновых операций — таймеры, потоки
- Отписки от событий — слушатели, подписки
- Правильной очистки контроллеров — animation, text, focus
Основное правило: каждому initState должен соответствовать dispose, где мы очищаем ровно те ресурсы, которые создали.