Всегда ли вызывается didChangeDependency?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
didChangeDependencies() в Flutter
didChangeDependencies() — это жизненный цикл метод StatefulWidget, который вызывается при изменении зависимостей виджета. Это очень полезный, но часто неправильно понимаемый метод.
Что такое didChangeDependencies?
didChangeDependencies() — это callback, который вызывается в следующих случаях:
- После initState() — вызывается один раз после создания State
- При изменении InheritedWidget выше в дереве — если виджет зависит от InheritedWidget и тот изменился
- При изменении dependencies через context — если используется context.dependOnInheritedWidgetOfExactType() или аналог
Жизненный цикл State
createState() → initState() → didChangeDependencies() → build()
↓
[Если зависимости изменились]
didChangeDependencies() → build()
↓
[При изменении state]
setState() → build()
↓
[При удалении]
deactivate() → dispose()
Основной пример
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late ThemeData _theme;
@override
void initState() {
super.initState();
print('initState called');
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('didChangeDependencies called');
// Получить тему из контекста
_theme = Theme.of(context);
}
@override
Widget build(BuildContext context) {
print('build called');
return Container(
color: _theme.primaryColor,
child: const Text('Hello'),
);
}
@override
void dispose() {
print('dispose called');
super.dispose();
}
}
void main() {
runApp(const MyApp());
}
// Вывод консоли:
// initState called
// didChangeDependencies called
// build called
Случай 1: InheritedWidget (обычное использование)
didChangeDependencies() вызывается когда InheritedWidget выше в дереве изменяется.
class ThemeProvider extends InheritedWidget {
final ThemeData theme;
final Widget child;
const ThemeProvider({
required this.theme,
required this.child,
}) : super(child: child);
@override
bool updateShouldNotify(ThemeProvider oldWidget) {
return theme != oldWidget.theme; // Изменилась ли тема?
}
static ThemeData of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<ThemeProvider>()
?.theme ??
ThemeData.light();
}
}
class MyColoredBox extends StatefulWidget {
@override
State<MyColoredBox> createState() => _MyColoredBoxState();
}
class _MyColoredBoxState extends State<MyColoredBox> {
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('Theme changed! Rebuilding...');
// Получить новую тему
final theme = ThemeProvider.of(context);
print('New primary color: ${theme.primaryColor}');
}
@override
Widget build(BuildContext context) {
final theme = ThemeProvider.of(context);
return Container(
color: theme.primaryColor,
child: const Text('Colored Box'),
);
}
}
class App extends StatefulWidget {
@override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
bool isDarkMode = false;
@override
Widget build(BuildContext context) {
final theme = isDarkMode ? ThemeData.dark() : ThemeData.light();
return ThemeProvider(
theme: theme,
child: Column(
children: [
ElevatedButton(
onPressed: () {
setState(() => isDarkMode = !isDarkMode);
// Это вызовет didChangeDependencies() в MyColoredBox
},
child: const Text('Toggle Theme'),
),
MyColoredBox(),
],
),
);
}
}
Случай 2: Когда didChangeDependencies НЕ вызывается
didChangeDependencies() НЕ вызывается если:
- Виджет не зависит от InheritedWidget
class SimpleWidget extends StatefulWidget {
@override
State<SimpleWidget> createState() => _SimpleWidgetState();
}
class _SimpleWidgetState extends State<SimpleWidget> {
@override
void didChangeDependencies() {
// Эта функция вызовется только один раз после initState()
// Больше не будет вызвана, так как нет зависимостей
print('Called only once');
}
@override
Widget build(BuildContext context) {
return const Text('Simple');
}
}
- Используется setState() — это НЕ вызывает didChangeDependencies()
class CounterWidget extends StatefulWidget {
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int counter = 0;
@override
void didChangeDependencies() {
print('didChangeDependencies called');
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Counter: $counter'),
ElevatedButton(
onPressed: () {
setState(() => counter++); // didChangeDependencies НЕ вызовется
},
child: const Text('Increment'),
),
],
);
}
}
// Вывод:
// didChangeDependencies called (один раз после initState)
// При нажатии на кнопку: только build() вызывается
Сравнение: initState vs didChangeDependencies
| Параметр | initState | didChangeDependencies |
|---|---|---|
| Вызывается | Один раз при создании | При создании + при изменении зависимостей |
| Может использовать context | Может, но осторожно | Да, безопасно |
| Для получения InheritedWidget | Неправильное место | Правильное место |
| Доступ к Theme, Locale | Неправильно | Правильно |
Практический пример: Локализация
class LocalizedWidget extends StatefulWidget {
@override
State<LocalizedWidget> createState() => _LocalizedWidgetState();
}
class _LocalizedWidgetState extends State<LocalizedWidget> {
late Locale _locale;
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('Locale changed!');
// Получить текущую локаль
_locale = Localizations.localeOf(context);
print('Current locale: $_locale');
}
@override
Widget build(BuildContext context) {
return Text('Locale: $_locale');
}
}
Best Practices
1. Используй didChangeDependencies() для получения InheritedWidget
// ❌ Неправильно в initState
@override
void initState() {
super.initState();
// Это может не работать правильно
final theme = Theme.of(context);
}
// ✅ Правильно в didChangeDependencies
@override
void didChangeDependencies() {
super.didChangeDependencies();
final theme = Theme.of(context);
}
2. Всегда вызывай super.didChangeDependencies()
@override
void didChangeDependencies() {
super.didChangeDependencies(); // Обязательно!
// Твой код
}
3. Используй для дорогих операций при изменении зависимостей
@override
void didChangeDependencies() {
super.didChangeDependencies();
// Перезагрузить данные если локаль изменилась
final locale = Localizations.localeOf(context);
_loadTranslations(locale);
}
Когда НЕ использовать didChangeDependencies()
- Для простых операций используй
initState() - Для реакции на setState() используй обычный build()
- Для сложной логики рассмотри Provider или BLoC
Вывод
didChangeDependencies() вызывается один раз после initState() и каждый раз, когда изменяется InheritedWidget, от которого зависит текущий виджет. Это правильное место для получения InheritedWidget (Theme, Locale, MediaQuery) и для выполнения операций при их изменении. Если виджет не зависит от InheritedWidget, этот метод будет вызван только один раз.