← Назад к вопросам

Всегда ли вызывается didChangeDependency?

1.3 Junior🔥 151 комментариев
#Flutter виджеты

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

didChangeDependencies() в Flutter

didChangeDependencies() — это жизненный цикл метод StatefulWidget, который вызывается при изменении зависимостей виджета. Это очень полезный, но часто неправильно понимаемый метод.

Что такое didChangeDependencies?

didChangeDependencies() — это callback, который вызывается в следующих случаях:

  1. После initState() — вызывается один раз после создания State
  2. При изменении InheritedWidget выше в дереве — если виджет зависит от InheritedWidget и тот изменился
  3. При изменении 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() НЕ вызывается если:

  1. Виджет не зависит от 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');
  }
}
  1. Используется 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

ПараметрinitStatedidChangeDependencies
ВызываетсяОдин раз при созданииПри создании + при изменении зависимостей
Может использовать 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, этот метод будет вызван только один раз.

Всегда ли вызывается didChangeDependency? | PrepBro