Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Всегда ли вызывается initState?
initState — это важный метод жизненного цикла StatefulWidget, который вызывается один раз после создания State объекта. Но есть важные нюансы, когда он вызывается, а когда нет.
Краткий ответ
Да, initState ВСЕГДА вызывается ровно один раз, если:
- Виджет является StatefulWidget
- У соответствующего State класса переопределён метод
initState() - Виджет успешно добавлен в дерево элементов (mounted)
Жизненный цикл StatefulWidget
// Порядок вызовов:
1. Конструктор MyStatefulWidget() — создание конфигурации
2. createState() — создание State объекта
3. initState() — инициализация State (ОДИН РАЗ!)
4. build() — отрисовка UI
5. didUpdateWidget() — если родитель перестроил виджет
6. build() — отрисовка заново
7. dispose() — очистка ресурсов перед удалением
Простой пример
class MyCounter extends StatefulWidget {
@override
State<MyCounter> createState() => _MyCounterState();
}
class _MyCounterState extends State<MyCounter> {
int counter = 0;
late StreamSubscription subscription;
@override
void initState() {
super.initState(); // ОБЯЗАТЕЛЬНО первая строка!
print('initState вызван');
// Инициализация:
// - Слушатели на потоки/сокеты
// - Загрузка данных из API
// - Инициализация контроллеров
// - Запуск таймеров
subscription = someStream.listen((_) {
setState(() => counter++);
});
}
@override
Widget build(BuildContext context) {
return Text('Counter: $counter');
}
@override
void dispose() {
print('dispose вызван');
subscription.cancel(); // очистка ресурсов
super.dispose(); // ОБЯЗАТЕЛЬНО последняя строка!
}
}
Когда initState НЕ вызывается?
1. Если виджет StatelessWidget
class MyStateless extends StatelessWidget { // StatelessWidget!
@override
void initState() { // ❌ ОШИБКА! нет этого метода
// это не работает
}
@override
Widget build(BuildContext context) {
return Text('Hello');
}
}
2. Если State объект не добавлен в дерево (unmounted)
var state = _MyCounterState(); // создали State
// initState() НЕ вызывается! потому что это не в дереве элементов
// initState вызывается только когда:
// - Виджет добавлен в Widget Tree
// - Создан Element
// - Вызван createState()
3. Если переопределить createState неправильно
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() {
// ❌ если здесь выбросить исключение
throw Exception('Ошибка!');
// то initState() не вызовется
}
}
initState вызывается ОДИН РАЗ
Это критично понимать:
class MyWidget extends StatefulWidget {
final String title;
MyWidget({required this.title});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
@override
void initState() {
super.initState();
print('initState: title = ${widget.title}');
}
@override
void didUpdateWidget(MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// Вызывается если РОДИТЕЛЬ перестроил виджет с другим title
print('didUpdateWidget: title = ${widget.title}');
// НО initState не вызывается снова!
}
@override
Widget build(BuildContext context) {
return Text(widget.title);
}
}
// Сценарий:
var myWidget = MyWidget(title: 'First');
// Вывод: initState: title = First
// (build вызывается)
// Родитель перестраивает: MyWidget(title: 'Second')
// Вывод: didUpdateWidget: title = Second
// (build вызывается ещё раз)
// initState НЕ вызывается снова!
Когда нужен initState
initState используй для инициализации, которая нужна один раз:
class ProfileScreen extends StatefulWidget {
final String userId;
ProfileScreen({required this.userId});
@override
State<ProfileScreen> createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
late Future<User> userFuture;
late StreamSubscription subscription;
@override
void initState() {
super.initState();
// 1. Инициализация Future для загрузки данных
userFuture = api.getUser(widget.userId);
// 2. Слушание потока данных
subscription = userService.userStream.listen((user) {
setState(() { /* обновляем UI */ });
});
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: userFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
return UserCard(user: snapshot.data!);
}
return CircularProgressIndicator();
},
);
}
@override
void dispose() {
subscription.cancel();
super.dispose();
}
}
Важные правила initState
1. Всегда вызывай super.initState() в начале
@override
void initState() {
super.initState(); // ✅ ВСЕГДА первым!
// остальной код
}
2. Всегда вызывай super.dispose() в конце
@override
void dispose() {
// очистка ресурсов
super.dispose(); // ✅ ВСЕГДА последним!
}
3. Нельзя вызывать setState в initState
@override
void initState() {
super.initState();
setState(() { // ❌ Не делай так!
value = 100;
});
// Вместо этого присвой значение напрямую:
value = 100; // ✅ Правильно
}
didUpdateWidget vs initState
class MyCounter extends StatefulWidget {
final int initialValue;
MyCounter({required this.initialValue});
@override
State<MyCounter> createState() => _MyCounterState();
}
class _MyCounterState extends State<MyCounter> {
late int counter;
@override
void initState() {
super.initState();
counter = widget.initialValue; // инициализируем один раз
print('initState: counter = $counter');
}
@override
void didUpdateWidget(MyCounter oldWidget) {
super.didUpdateWidget(oldWidget);
// Вызывается если initialValue изменился
if (oldWidget.initialValue != widget.initialValue) {
// Обновляем counter
counter = widget.initialValue;
print('didUpdateWidget: counter = $counter');
}
}
@override
Widget build(BuildContext context) => Text('$counter');
}
// Сценарий:
// MyCounter(initialValue: 10)
// Вывод: initState: counter = 10
// Родитель перестраивает: MyCounter(initialValue: 20)
// Вывод: didUpdateWidget: counter = 20
Пример с проверкой
class TestInitState extends StatefulWidget {
@override
State<TestInitState> createState() => _TestInitStateState();
}
class _TestInitStateState extends State<TestInitState> {
int initCallCount = 0;
@override
void initState() {
super.initState();
initCallCount++;
print('initState вызван (раз: $initCallCount)');
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('initState вызван $initCallCount раз'),
ElevatedButton(
onPressed: () => setState(() {}), // перестроение
child: Text('Rebuild'),
),
],
),
);
}
}
// Результат:
// Первая отрисовка: "initState вызван 1 раз"
// После нажатия кнопки (setState): "initState вызван 1 раз" (не изменилось!)
// initState действительно вызывается только один раз
Краткая таблица вызовов
| Событие | initState | didUpdateWidget | build | dispose |
|---|---|---|---|---|
| Первое создание | ✅ | ❌ | ✅ | ❌ |
| setState() | ❌ | ❌ | ✅ | ❌ |
| Новый конфиг от родителя | ❌ | ✅ | ✅ | ❌ |
| Удаление виджета | ❌ | ❌ | ❌ | ✅ |
Вывод: initState ВСЕГДА вызывается ровно один раз при создании State для StatefulWidget. Это идеальное место для инициализации ресурсов, которые не нужно повторять. Помни про super.initState() в начале и всегда соответствующий dispose() для очистки ресурсов.