Ответ
InheritedWidget — это особый виджет в Flutter, предназначенный для эффективной передачи данных вниз по дереву виджетов. Он является фундаментом для таких механизмов, как Theme, MediaQuery и State Management решений вроде Provider.
Его «жизненный цикл» проще, чем у StatefulWidget, и сводится к ключевым моментам вставки, обновления и удаления из дерева.
1. Создание и Вставка в дерево
InheritedWidget создается как любой другой виджет и помещается в дерево, обычно ближе к корню, чтобы быть доступным для многих потомков.
class MyAppState extends InheritedWidget {
final int counter; // Данные, которые мы хотим распространить.
const MyAppState({
Key? key,
required this.counter,
required Widget child,
}) : super(key: key, child: child);
// ...
}
2. Получение данных потомками (Наиболее важная часть)
Потомки получают доступ к данным через статический метод (часто называемый of), который использует context.dependOnInheritedWidgetOfExactType<T>().
static MyAppState of(BuildContext context) {
// Этот метод устанавливает зависимость.
final result = context.dependOnInheritedWidgetOfExactType<MyAppState>();
assert(result != null, 'No MyAppState found in context');
return result!;
}
Ключевой момент: Вызов dependOnInheritedWidgetOfExactType:
- Находит ближайший
InheritedWidgetтипаMyAppStateвыше по дереву. - Регистрирует зависимость текущего
BuildContext(виджета-потомка) от этогоInheritedWidget. ЕслиInheritedWidgetобновится, все зависящие от него виджеты будут перестроены.
3. Обновление данных
InheritedWidget неизменяем (immutable). Чтобы обновить данные, необходимо создать новый экземпляр InheritedWidget с новыми данными и заменить старый в дереве (например, через setState в родительском StatefulWidget).
Перед перестроением зависимых виджетов Flutter вызывает метод updateShouldNotify.
@override
bool updateShouldNotify(MyAppState oldWidget) {
// Сравниваем старые и новые данные.
// Возвращаем true, только если данные изменились и зависимые виджеты должны перестроиться.
return counter != oldWidget.counter;
}
Это критично для производительности. Если counter не изменился, Flutter не будет тратить ресурсы на перерисовку всех подписанных виджетов.
4. Удаление из дерева
Когда InheritedWidget удаляется из дерева (например, при навигации), все зависимости автоматически очищаются.
Полный пример использования:
class MyAppState extends InheritedWidget {
final int counter;
const MyAppState({Key? key, required this.counter, required Widget child})
: super(key: key, child: child);
@override
bool updateShouldNotify(MyAppState oldWidget) => counter != oldWidget.counter;
static MyAppState of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyAppState>()!;
}
}
// Родительский виджет, управляющий состоянием.
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _counter = 0;
@override
Widget build(BuildContext context) {
return MyAppState(
counter: _counter,
child: MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Потомок, который зависит от MyAppState и перестроится при изменении counter.
Builder(
builder: (context) {
final appState = MyAppState.of(context);
return Text('Counter: ${appState.counter}');
},
),
ElevatedButton(
onPressed: () => setState(() => _counter++),
child: Text('Increment'),
),
],
),
),
),
),
);
}
}
Итог: InheritedWidget — это эффективный механизм «подписки» на данные. Его жизненный цикл управляется Flutter'ом на основе зависимостей, установленных через dependOnInheritedWidgetOfExactType, и оптимизируется методом updateShouldNotify.