Ответ
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.
Ответ 18+ 🔞
А, InheritedWidget! Ну это, блядь, классика жанра, ёпта. Типа такой виджет-дед, который сидит наверху дерева и кричит всем внукам: «Эй, пацаны, вот вам данные, берите, не стесняйтесь!». По сути, это основа для всяких Theme и MediaQuery — без него нихуя бы не работало.
Жизненный цикл у него, кстати, не такой замудрёный, как у StatefulWidget. Всё просто, как три копейки.
1. Рождение и посадка на дерево
Создаёшь ты его, как обычный виджет, и суёшь куда-нибудь повыше, чтобы всем потомкам было видно. Типа короля на трон.
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выше по дереву. - Он жёстко регистрирует зависимость текущего виджета от этого деда. Если дед обновится — все, кто от него зависят, перестроятся. Жёсткая связь, блядь!
3. Апдейт данных (Тут нужно не проебаться)
InheritedWidget — он неизменяемый, immutable, как скала. Чтобы что-то поменять, надо выкинуть старый экземпляр и воткнуть новый с обновлёнными данными (через setState где-то выше).
Перед тем как перестраивать всех подписчиков, Флаттер спрашивает у виджета: «А стоит ли, старик?». Это метод updateShouldNotify.
@override
bool updateShouldNotify(MyAppState oldWidget) {
// Сравниваем, поменялось ли что-то важное.
// Возвращаем true, только если данные реально изменились и всех надо тряхнуть.
return counter != oldWidget.counter;
}
Это, блядь, критично для производительности! Если counter тот же самый, зачем попусту дергать все виджеты? Экономия ресурсов, ёпта.
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++), // Обновляем состояние — создаётся новый InheritedWidget.
child: Text('Increment'),
),
],
),
),
),
),
);
}
}
Итог, ёпта: InheritedWidget — это такой эффективный механизм подписки на данные сверху. Всё завязано на зависимостях через dependOnInheritedWidgetOfExactType, а метод updateShouldNotify не даёт Флаттеру сойти с ума и перерисовывать всё подряд. Умная штука, если её правильно использовать, а не просто тыкать, куда попало.