Приведи пример использования InheritedWidget
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
InheritedWidget: использование и практические примеры
InheritedWidget — это один из ключевых механизмов Flutter для передачи данных вниз по дереву виджетов без необходимости передавать их через конструктор каждого промежуточного виджета (避免prop drilling).
Что такое InheritedWidget
InheritedWidget — это базовый класс в Flutter, позволяющий делать данные доступными для всех потомков в дереве виджетов. Когда данные в InheritedWidget изменяются, автоматически перестраиваются только те виджеты, которые используют эти данные через метод dependOnInheritedWidgetOfExactType().
Важно понимать: InheritedWidget сам по себе не управляет состоянием, он только распространяет его вниз по дереву.
Практический пример: тема приложения
Здесь я создам InheritedWidget для управления темой (светлая/тёмная):
// Модель для хранения данных темы
class ThemeData {
final Color primaryColor;
final Color backgroundColor;
final Brightness brightness;
ThemeData({
required this.primaryColor,
required this.backgroundColor,
required this.brightness,
});
}
// InheritedWidget для распространения темы
class ThemeProvider extends InheritedWidget {
final ThemeData theme;
final VoidCallback onThemeChanged;
const ThemeProvider({
required this.theme,
required this.onThemeChanged,
required super.child,
super.key,
});
// Удобный способ доступа к провайдеру из любого места в дереве
static ThemeProvider of(BuildContext context) {
final result = context.dependOnInheritedWidgetOfExactType<ThemeProvider>();
assert(
result != null,
'No ThemeProvider found in context',
);
return result!;
}
// Метод, определяющий, нужно ли перестраивать потомков
@override
bool updateShouldNotify(ThemeProvider oldWidget) {
return theme != oldWidget.theme;
}
}
// Виджет для переключения темы
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late ThemeData _theme = ThemeData(
primaryColor: Colors.blue,
backgroundColor: Colors.white,
brightness: Brightness.light,
);
void _toggleTheme() {
setState(() {
_theme = ThemeData(
primaryColor: _theme.brightness == Brightness.light
? Colors.deepPurple
: Colors.blue,
backgroundColor: _theme.brightness == Brightness.light
? Colors.grey[900]!
: Colors.white,
brightness: _theme.brightness == Brightness.light
? Brightness.dark
: Brightness.light,
);
});
}
@override
Widget build(BuildContext context) {
return ThemeProvider(
theme: _theme,
onThemeChanged: _toggleTheme,
child: MaterialApp(
title: 'InheritedWidget Example',
theme: ThemeData(
primaryColor: _theme.primaryColor,
brightness: _theme.brightness,
),
home: HomeScreen(),
),
);
}
}
Использование в виджетах-потомках
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Получаем доступ к провайдеру
final themeProvider = ThemeProvider.of(context);
return Scaffold(
appBar: AppBar(
title: const Text('InheritedWidget Example'),
backgroundColor: themeProvider.theme.primaryColor,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Current theme: ${themeProvider.theme.brightness}',
style: TextStyle(
fontSize: 18,
color: themeProvider.theme.primaryColor,
),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: themeProvider.onThemeChanged,
style: ElevatedButton.styleFrom(
backgroundColor: themeProvider.theme.primaryColor,
),
child: const Text('Toggle Theme'),
),
const SizedBox(height: 24),
// Этот виджет будет перестроен при изменении темы
CardWidget(),
],
),
),
);
}
}
class CardWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = ThemeProvider.of(context).theme;
return Container(
width: 200,
height: 100,
decoration: BoxDecoration(
color: theme.backgroundColor,
border: Border.all(color: theme.primaryColor, width: 2),
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'This card responds to theme changes',
textAlign: TextAlign.center,
style: TextStyle(color: theme.primaryColor),
),
),
);
}
}
Пример с пользовательскими данными (язык)
// InheritedWidget для управления языком приложения
class LocaleProvider extends InheritedWidget {
final String locale; // 'en', 'ru', 'es'
final Function(String) setLocale;
const LocaleProvider({
required this.locale,
required this.setLocale,
required super.child,
super.key,
});
static LocaleProvider of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<LocaleProvider>()!;
}
@override
bool updateShouldNotify(LocaleProvider oldWidget) {
return locale != oldWidget.locale;
}
}
// В виджете используем:
class LocalizedText extends StatelessWidget {
@override
Widget build(BuildContext context) {
final locale = LocaleProvider.of(context).locale;
final text = getTranslation(locale);
return Text(text);
}
String getTranslation(String locale) {
final translations = {
'en': 'Hello',
'ru': 'Привет',
'es': 'Hola',
};
return translations[locale] ?? 'Hello';
}
}
Когда использовать InheritedWidget
Оптимально для:
- Глобальные данные приложения (тема, язык, юзер)
- Данные, которые часто читаются, но редко меняются
- Данные, нужные глубоко вложенным виджетам
Не оптимально для:
- Часто меняющегося состояния (используйте Riverpod, BLoC, GetX)
- Сложной логики управления состоянием
- Когда нужны side effects (исползуйте провайдеры с logic)
Отличие от Provider пакета
Код выше показывает raw InheritedWidget. На практике в современном Flutter обычно используют пакет provider или riverpod, которые оборачивают InheritedWidget и делают его более удобным и мощным:
// С пакетом provider это выглядит проще
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => ThemeNotifier(),
child: Consumer<ThemeNotifier>(
builder: (context, theme, _) => MaterialApp(
theme: theme.themeData,
home: HomeScreen(),
),
),
);
}
}
Ключевые моменты
- dependOnInheritedWidgetOfExactType() — подписывает виджет на изменения
- updateShouldNotify() — определяет, нужно ли перестраивать потомков
- Нет нужды передавать данные через конструкторы — избегаем prop drilling
- Производительность — перестраиваются только нужные виджеты
InheritedWidget — фундамент для всех modern state management решений в Flutter (Provider, Riverpod, GetX).