В каком порядке вызываются методы жизненного цикла State?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Порядок вызова методов жизненного цикла State
Жизненный цикл State в Flutter — это чёткая последовательность методов, которые вызываются в определённом порядке от создания до удаления. Понимание этого критично для правильного управления ресурсами и инициализацией данных.
Полный порядок вызова методов
1. createState()
Первый вызываемый метод. Он находится в StatefulWidget и создаёт экземпляр State:
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState(); // Первый
}
2. initState()
Вызывается сразу после создания State. Это лучшее место для инициализации:
class _MyAppState extends State<MyApp> {
late List<String> items;
@override
void initState() {
super.initState(); // ВСЕГДА должен быть первой строкой
print("initState called"); // Второй
items = [];
loadData(); // Инициализирующий код
}
}
Важно: всегда вызывайте super.initState() первой строкой!
3. didChangeDependencies()
Вызывается сразу после initState() и каждый раз при изменении dependency (например, InheritedWidget):
@override
void didChangeDependencies() {
print("didChangeDependencies called"); // Третий
super.didChangeDependencies();
// Например, получение темы из контекста
final theme = Theme.of(context);
}
4. build()
Вызывается сразу после didChangeDependencies() и каждый раз при setState():
@override
Widget build(BuildContext context) {
print("build called"); // Четвёртый (и многие разы)
return Container();
}
Полная схема жизненного цикла
┌─────────────────────────────────────────┐
│ StatefulWidget (createState) │ 1. createState()
└──────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ State (initState) │ 2. initState()
└──────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ didChangeDependencies() │ 3. didChangeDependencies()
└──────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ build() ◄─ setState() ─ build() │ 4. build() (многие разы)
└──────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ didUpdateWidget(oldWidget) │ 5. didUpdateWidget() (если родитель изменился)
└──────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ deactivate() / dispose() │ 6. deactivate() / 7. dispose()
└─────────────────────────────────────────┘
Детальное описание каждого метода
5. didUpdateWidget()
Вызывается, когда родительский widget перестроился и передал новые параметры:
@override
void didUpdateWidget(covariant MyApp oldWidget) {
super.didUpdateWidget(oldWidget);
print("didUpdateWidget called"); // Пятый
// Сравнить старые и новые параметры
if (widget.title != oldWidget.title) {
loadNewData();
}
}
6. deactivate()
Вызывается перед удалением State из дерева виджетов. Это может быть временно (например, при навигации):
@override
void deactivate() {
print("deactivate called"); // Шестой
// Очистить временные ресурсы
super.deactivate();
}
7. dispose()
Вызывается после deactivate(). Это последний метод. Здесь нужно очищать ресурсы:
@override
void dispose() {
print("dispose called"); // Седьмой - ПОСЛЕДНИЙ
// Обязательно очищайте ресурсы
_controller?.dispose();
_streamSubscription?.cancel();
super.dispose(); // ВСЕГДА в конце
}
Практический пример
class CounterApp extends StatefulWidget {
@override
State<CounterApp> createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
late AnimationController _controller;
int _counter = 0;
@override
void initState() {
print("1. initState");
super.initState();
_controller = AnimationController(duration: Duration(seconds: 1), vsync: this);
_loadInitialData();
}
@override
void didChangeDependencies() {
print("2. didChangeDependencies");
super.didChangeDependencies();
}
@override
void didUpdateWidget(CounterApp oldWidget) {
print("3. didUpdateWidget");
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
print("4. build (called multiple times)");
return Scaffold(
body: Center(
child: Text(_counter.toString()),
),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _counter++),
child: Icon(Icons.add),
),
);
}
@override
void deactivate() {
print("5. deactivate");
super.deactivate();
}
@override
void dispose() {
print("6. dispose");
_controller.dispose();
super.dispose();
}
Future<void> _loadInitialData() async {
// Загрузка данных
}
}
Важные моменты
✅ Что делать в каждом методе:
- initState() — инициализировать переменные, подписаться на потоки, запустить таймеры
- didChangeDependencies() — использовать контекст, получать данные из InheritedWidget
- build() — ТОЛЬКО возвращать UI, не делать тяжёлые вычисления
- didUpdateWidget() — реагировать на изменения параметров из родителя
- dispose() — очищать контроллеры, отписываться от потоков, отменять таймеры
❌ Что НЕ делать:
- Не вызывайте setState() в initState() (это приведёт к двойной перестройке)
- Не используйте async/await напрямую в initState(), используйте Future
- Не забывайте вызывать super методы
- Не создавайте тяжёлые объекты в build()
Вывод
Понимание порядка вызова методов жизненного цикла State критично для:
- Правильной инициализации данных
- Эффективного управления ресурсами
- Предотвращения утечек памяти
- Написания стабильного и надёжного кода
Запомните: initState → didChangeDependencies → build → (setState) → didUpdateWidget → deactivate → dispose