Что такое InheritedWidget в Flutter?

«Что такое InheritedWidget в Flutter?» — вопрос из категории Flutter виджеты, который задают на 93% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

InheritedWidget — это специальный виджет в Flutter, предназначенный для эффективной передачи данных вниз по дереву виджетов. Он позволяет дочерним виджетам получать доступ к данным без необходимости явно прокидывать их через конструкторы каждого промежуточного виджета (проброс пропсов).

Ключевые особенности:

  • Эффективность: При изменении данных в InheritedWidget Flutter перестраивает только те виджеты, которые от этих данных зависят (вызвали context.dependOnInheritedWidgetOfExactType), а не всё поддерево.
  • Доступность: Любой виджет в поддереве ниже InheritedWidget может получить к нему доступ.
  • Использование: Исторически использовался для тем, локализации, аутентификации или любого другого «глобального» для поддерева состояния.

Пример реализации и использования:

// 1. Создаем InheritedWidget
class AppTheme extends InheritedWidget {
  final Color primaryColor;
  final Brightness brightness;

  const AppTheme({
    required this.primaryColor,
    required this.brightness,
    required Widget child,
    Key? key,
  }) : super(key: key, child: child);

  // 2. Статический метод для удобного доступа
  static AppTheme of(BuildContext context) {
    final AppTheme? result = context.dependOnInheritedWidgetOfExactType<AppTheme>();
    assert(result != null, 'No AppTheme found in context');
    return result!;
  }

  // 3. Метод, определяющий, нужно ли уведомлять зависимые виджеты об обновлении
  @override
  bool updateShouldNotify(AppTheme oldWidget) {
    return primaryColor != oldWidget.primaryColor || brightness != oldWidget.brightness;
  }
}

// 4. Использование в дочернем виджете
class ThemedButton extends StatelessWidget {
  const ThemedButton({super.key});

  @override
  Widget build(BuildContext context) {
    // Получаем данные из ближайшего AppTheme в дереве
    final theme = AppTheme.of(context);
    return ElevatedButton(
      style: ElevatedButton.styleFrom(primary: theme.primaryColor),
      onPressed: () {},
      child: const Text('Кнопка'),
    );
  }
}

Важно: InheritedWidget — это низкоуровневый примитив. В современных приложениях для управления состоянием чаще используются пакеты-надстройки, такие как Provider (который сам построен на InheritedWidget), Riverpod или Bloc, так как они предоставляют более удобный API и лучшие возможности для тестирования.