InheritedWidget — је посебан виџет у Flutter-у који преноси податке низ стабло виџета без експлицитног прослеђивања кроз конструкторе. Дете виџети добијају приступ подацима кроз BuildContext и аутоматски се претплаћују на ажурирања. Када се подаци у InheritedWidget-у промене, сви зависни виџети се поново изграђују. Према Flutter API Reference, 2025, InheritedWidget лежи у основи Theme, MediaQuery, Localizations и већине библиотека за управљање стањем.
Главне ствари
InheritedWidget — је виџет који чини своје податке доступним свим потомцима у Widget Tree. За разлику од обичног виџета, који преноси податке само кроз конструктор дете елементима, InheritedWidget омогућава било ком виџету у подстаблу приступ подацима без ланца параметара. Ово решава проблем "prop drilling" — пренос података кроз више посредних виџета који сами не користе ове податке.
Flutter укључује неколико уграђених InheritedWidget: Theme (шема боја и стилови), MediaQuery (величина екрана, оријентација, густина пиксела), Localizations (локализовани стрингови), Directionality (смер текста), DefaultTextStyle (подразумевани стил текста). Ови виџети се постављају коренским виџетима попут MaterialApp и доступни су у целој апликацији.
InheritedWidget нема сопствено стање — чува податке прослеђене кроз конструктор. Када се родитељ InheritedWidget-а поново изгради са новим подацима, позива се метода updateShouldNotify ради поређења старих и нових података. Ако метода врати true, сви зависни виџети се означавају за поновну изградњу. Ово је једноставан, али ефикасан механизам реактивног ажурирања.
Механизам преноса података кроз InheritedWidget заснива се на Element Tree. Када виџет позове dependOnInheritedWidgetOfExactType, одговарајући елемент региструје зависност од InheritedElement-а. При промени InheritedWidget-а, InheritedElement обавештава све зависне елементе, који се поново изграђују у следећем кадру.
Метода dependOnInheritedWidgetOfExactType не само да проналази InheritedWidget у стаблу — она претплаћује тренутни елемент на обавештења. Ако бисте користили findAncestorWidgetOfExactType уместо dependOn, виџет би добио податке, али се не би поново изградио при њиховој промени. Ово је важна разлика: dependOn је претплата, а findAncestor — једнократна претрага.
Када виџет захтева InheritedWidget, Flutter се подиже уз Element Tree од тренутног елемента до корена, проверавајући сваки InheritedElement за поклапање типа. Први пронађени InheritedElement се враћа. То значи да најближи InheritedWidget у стаблу има приоритет — можете премостити податке на одређеном нивоу постављањем InheritedWidget-а ближе потомцима.
class ThemeData {
final Color primaryColor;
final TextTheme textTheme;
const ThemeData({required this.primaryColor, required this.textTheme});
}
class MyTheme extends InheritedWidget {
final ThemeData data;
const MyTheme({required this.data, required Widget child}) : super(child: child);
static MyTheme of(BuildContext context) {
final widget = context.dependOnInheritedWidgetOfExactType<MyTheme>();
assert(widget != null, "MyTheme not found in tree");
return widget!;
}
@override
bool updateShouldNotify(MyTheme oldWidget) => oldWidget.data != data;
}
У овом примеру MyTheme користи статичку методу of за пружање података потомцима. Метода dependOnInheritedWidgetOfExactType региструје зависност, а updateShouldNotify пореди старе и нове податке ради утврђивања потребе за поновном изградњом зависних виџета.
Креирање сопственог InheritedWidget-а састоји се од два корака: дефинисање класе која наслеђује InheritedWidget и имплементација статичке методе of за приступ из потомака. Подаци се преносе кроз конструктор, а метода updateShouldNotify одређује када се зависни виџети требају поново изградити.
Класа мора да наслеђује InheritedWidget и да прима податке кроз конструктор са обавезним параметром child. Подаци могу бити било ког типа: примитиви, објекти, функције. Главно правило — подаци морају бити непроменљиви (immutable) како би се поуздано могла поредити стара и нова вредност.
Статичка метода of прима BuildContext и враћа податке InheritedWidget-а. Унутра се позива dependOnInheritedWidgetOfExactType, која тражи најближи InheritedWidget наведеног типа у стаблу. Ако InheritedWidget није пронађен, метода баца изузетак или враћа подразумевану вредност у зависности од имплементације.
За приступ подацима, виџет позива MyWidget.of(context) унутар методе build. Flutter аутоматски претплаћује виџет на ажурирања. Ако се подаци промене, виџет ће се поново изградити у следећем кадру. Ово омогућава стварање чистог и декларативног кода без непотребних параметара.
class UserPreferences extends InheritedWidget {
final String languageCode;
final bool darkMode;
const UserPreferences({
required this.languageCode,
required this.darkMode,
required Widget child,
}) : super(child: child);
static UserPreferences of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<UserPreferences>()!;
}
@override
bool updateShouldNotify(UserPreferences oldWidget) =>
oldWidget.languageCode != languageCode || oldWidget.darkMode != darkMode;
}
У овом примеру UserPreferences чува подешавања корисника. Метода updateShouldNotify пореди свако поље појединачно, што спречава непотребне поновне изградње при промени само једног параметра. Користите сличан приступ за сопствене InheritedWidget-е са више поља.
updateShouldNotify — је кључна метода InheritedWidget-а која одређује да ли треба обавестити зависне виџете о промени података. Ако метода врати false, зависни виџети се не поново изграђују, чак и ако је сам InheritedWidget добио нову инстанцу са истим подацима. Ово је критично за перформансе.
Поредите само она поља која су се стварно променила и утичу на приказ. Ако InheritedWidget садржи 10 поља, али само једно од њих утиче на UI, проверавајте само то поље. За колекције користите дубинско поређење или непроменљиве структуре података. Немојте користити == за List или Map, јер се оне пореде по референци.
Најчешћа грешка — враћање true без поређења. Ово доводи до поновне изградње свих зависних виџета при сваком ажурирању родитеља, чак и ако се подаци нису променили. Друга грешка — враћање false када су се подаци променили, што доводи до застарелог UI. Трећа — сложено поређење које се извршава сваког кадра и успорава рад.
InheritedWidget и колбекови (прослеђивање функција кроз конструктор) решавају различите задатке. InheritedWidget је погодан за податке који су потребни многим виџетима на различитим нивоима стабла. Колбекови су згодни за једносмерни пренос догађаја од родитеља до одређеног потомка или обрнуто. Избор зависи од архитектуре апликације и учесталости промена.
Користите InheritedWidget када су подаци потребни многим виџетима на различитим нивоима угнежђења: тема апликације, подешавања корисника, информације о уређају, подаци тренутне сесије. InheritedWidget је посебно ефикасан за "глобалне" податке који се ретко мењају, али су потребни у различитим деловима UI.
Колбекови (повратне функције) су погодни за пренос догађаја од дете виџета ка родитељском: притисак на дугме, избор ставке листе, слање форме. Колбекови експлицитно указују које акције потомак може да изврши и не стварају скривене зависности. За пренос података низ стабло на мали број нивоа такође је једноставније користити параметре конструктора.
| Критеријум | InheritedWidget | Колбекови |
|---|---|---|
| Смер | Одозго надоле (родитељ → потомци) | Одоздо нагоре (потомак → родитељ) или директно |
| Област | Цело подстабло | Одређени виџет |
| Поновна изградња | Аутоматска при промени података | Захтева ручни setState |
| Сложеност | Средња (потребна класа InheritedWidget) | Ниска (само функција) |
Provider и Riverpod — популарне библиотеке за управљање стањем у Flutter-у, изграђене на InheritedWidget-у. Оне проширују његове могућности: додају подршку за ChangeNotifier, аутоматско уклањање при демонтажи, лењу иницијализацију и поједностављену синтаксу преко генерика.
Provider користи InheritedWidget за пренос објекта било ког типа низ стабло. ChangeNotifierProvider прати промене кроз ChangeNotifier и позива updateShouldNotify при позиву notifyListeners. Ово ослобађа програмера од ручног креирања InheritedWidget-а и имплементације updateShouldNotify.
Директни InheritedWidget даје више контроле и не захтева спољне зависности. Provider пружа готову инфраструктуру: Consumer, Selector, MultiProvider, ProxyProvider. Избор зависи од сложености апликације. За једноставне пројекте директни InheritedWidget је довољан, за велике — Provider или Riverpod смањују шаблонски код.
// Директни InheritedWidget
class UserProvider extends InheritedWidget {
final UserData userData;
const UserProvider({required this.userData, required Widget child}) : super(child: child);
static UserData of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<UserProvider>()!.userData;
@override
bool updateShouldNotify(UserProvider old) => old.userData != userData;
}
// Provider еквивалент
return ChangeNotifierProvider<UserData>(
create: (_) => UserData(),
child: MyApp(),
);
Оба приступа у примеру решавају исти задатак — пренос UserData низ стабло. Provider смањује количину кода, али скрива механику InheritedWidget-а. Директни InheritedWidget даје потпуну контролу и разумевање процеса, што је посебно важно при учењу Flutter-а и отклањању сложених проблема са поновном изградњом.
Често постављана питања
InheritedWidget чини податке доступним свим потомцима кроз BuildContext, а обичан виџет преноси податке само кроз конструктор. InheritedWidget такође претплаћује потомке на ажурирања података.
Зависни виџети се поново изграђују само када updateShouldNotify врати true. Ако је метода правилно имплементирана, поновна изградња се дешава само при стварној промени података, а не при сваком rebuild родитеља.
Да, можете користити било који број InheritedWidget-а у истом стаблу. Сваки пружа податке одређеног типа, а виџети могу примати податке из више InheritedWidget-а истовремено.
dependOn претплаћује виџет на ажурирања — при промени података виџет ће се поново изградити. findAncestor обавља једнократну претрагу без претплате и виџет неће сазнати за промене података.
За једноставно стање (тема, подешавања) InheritedWidget је довољан. За сложено стање са пословном логиком користите Provider, Riverpod или BLoC — они су изграђени на InheritedWidget-у и додају потребну инфраструктуру.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође