InheritedWidget — шта је то, пренос података у стаблу и како ради

Аутор: IT Sectr Објављено: 2026-07-02 Време читања: 9 мин

InheritedWidget — је посебан виџет у Flutter-у који преноси податке низ стабло виџета без експлицитног прослеђивања кроз конструкторе. Дете виџети добијају приступ подацима кроз BuildContext и аутоматски се претплаћују на ажурирања. Када се подаци у InheritedWidget-у промене, сви зависни виџети се поново изграђују. Према Flutter API Reference, 2025, InheritedWidget лежи у основи Theme, MediaQuery, Localizations и већине библиотека за управљање стањем.

Главне ствари

  • InheritedWidget преноси податке низ Widget Tree без експлицитног провлачења кроз сваки виџет.
  • Аутоматска претплата — виџети који користе dependOnInheritedWidgetOfExactType се поново изграђују при промени података.
  • Theme и MediaQuery — уграђени примери InheritedWidget-а доступни у свакој Flutter апликацији.
  • Provider и Riverpod су изграђени на InheritedWidget-у и проширују његове могућности управљања стањем.
  • Правилна имплементација захтева преоптерећење updateShouldNotify ради спречавања непотребних поновних изградњи.

Шта је InheritedWidget у Flutter-у?

InheritedWidget — је виџет који чини своје податке доступним свим потомцима у Widget Tree. За разлику од обичног виџета, који преноси податке само кроз конструктор дете елементима, InheritedWidget омогућава било ком виџету у подстаблу приступ подацима без ланца параметара. Ово решава проблем "prop drilling" — пренос података кроз више посредних виџета који сами не користе ове податке.

Уграђени InheritedWidget

Flutter укључује неколико уграђених InheritedWidget: Theme (шема боја и стилови), MediaQuery (величина екрана, оријентација, густина пиксела), Localizations (локализовани стрингови), Directionality (смер текста), DefaultTextStyle (подразумевани стил текста). Ови виџети се постављају коренским виџетима попут MaterialApp и доступни су у целој апликацији.

Животни циклус InheritedWidget-а

InheritedWidget нема сопствено стање — чува податке прослеђене кроз конструктор. Када се родитељ InheritedWidget-а поново изгради са новим подацима, позива се метода updateShouldNotify ради поређења старих и нових података. Ако метода врати true, сви зависни виџети се означавају за поновну изградњу. Ово је једноставан, али ефикасан механизам реактивног ажурирања.

Како функционише пренос података кроз InheritedWidget

Механизам преноса података кроз InheritedWidget заснива се на Element Tree. Када виџет позове dependOnInheritedWidgetOfExactType, одговарајући елемент региструје зависност од InheritedElement-а. При промени InheritedWidget-а, InheritedElement обавештава све зависне елементе, који се поново изграђују у следећем кадру.

Регистрација зависности

Метода dependOnInheritedWidgetOfExactType не само да проналази InheritedWidget у стаблу — она претплаћује тренутни елемент на обавештења. Ако бисте користили findAncestorWidgetOfExactType уместо dependOn, виџет би добио податке, али се не би поново изградио при њиховој промени. Ово је важна разлика: dependOn је претплата, а findAncestor — једнократна претрага.

Пролазак кроз стабло InheritedWidget-а

Када виџет захтева InheritedWidget, Flutter се подиже уз Element Tree од тренутног елемента до корена, проверавајући сваки InheritedElement за поклапање типа. Први пронађени InheritedElement се враћа. То значи да најближи InheritedWidget у стаблу има приоритет — можете премостити податке на одређеном нивоу постављањем InheritedWidget-а ближе потомцима.

dart
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-а састоји се од два корака: дефинисање класе која наслеђује InheritedWidget и имплементација статичке методе of за приступ из потомака. Подаци се преносе кроз конструктор, а метода updateShouldNotify одређује када се зависни виџети требају поново изградити.

Корак 1: Дефинисање класе InheritedWidget

Класа мора да наслеђује InheritedWidget и да прима податке кроз конструктор са обавезним параметром child. Подаци могу бити било ког типа: примитиви, објекти, функције. Главно правило — подаци морају бити непроменљиви (immutable) како би се поуздано могла поредити стара и нова вредност.

Корак 2: Статичка метода of

Статичка метода of прима BuildContext и враћа податке InheritedWidget-а. Унутра се позива dependOnInheritedWidgetOfExactType, која тражи најближи InheritedWidget наведеног типа у стаблу. Ако InheritedWidget није пронађен, метода баца изузетак или враћа подразумевану вредност у зависности од имплементације.

Корак 3: Коришћење у виџетима

За приступ подацима, виџет позива MyWidget.of(context) унутар методе build. Flutter аутоматски претплаћује виџет на ажурирања. Ако се подаци промене, виџет ће се поново изградити у следећем кадру. Ово омогућава стварање чистог и декларативног кода без непотребних параметара.

dart
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 и спречавање непотребних поновних изградњи

updateShouldNotify — је кључна метода InheritedWidget-а која одређује да ли треба обавестити зависне виџете о промени података. Ако метода врати false, зависни виџети се не поново изграђују, чак и ако је сам InheritedWidget добио нову инстанцу са истим подацима. Ово је критично за перформансе.

Правилна имплементација updateShouldNotify

Поредите само она поља која су се стварно променила и утичу на приказ. Ако InheritedWidget садржи 10 поља, али само једно од њих утиче на UI, проверавајте само то поље. За колекције користите дубинско поређење или непроменљиве структуре података. Немојте користити == за List или Map, јер се оне пореде по референци.

  • Примитиви — користите директна поређења: oldWidget.value != value.
  • Непроменљиви објекти — користите преоптерећени ==: oldWidget.data != data (ако data преоптерећује ==).
  • Колекције — користите listEquals, mapEquals из package:flutter/foundation.dart.

Грешке при имплементацији updateShouldNotify

Најчешћа грешка — враћање true без поређења. Ово доводи до поновне изградње свих зависних виџета при сваком ажурирању родитеља, чак и ако се подаци нису променили. Друга грешка — враћање false када су се подаци променили, што доводи до застарелог UI. Трећа — сложено поређење које се извршава сваког кадра и успорава рад.

InheritedWidget vs колбекови: шта одабрати?

InheritedWidget и колбекови (прослеђивање функција кроз конструктор) решавају различите задатке. InheritedWidget је погодан за податке који су потребни многим виџетима на различитим нивоима стабла. Колбекови су згодни за једносмерни пренос догађаја од родитеља до одређеног потомка или обрнуто. Избор зависи од архитектуре апликације и учесталости промена.

Када користити InheritedWidget

Користите InheritedWidget када су подаци потребни многим виџетима на различитим нивоима угнежђења: тема апликације, подешавања корисника, информације о уређају, подаци тренутне сесије. InheritedWidget је посебно ефикасан за "глобалне" податке који се ретко мењају, али су потребни у различитим деловима UI.

Када користити колбекове

Колбекови (повратне функције) су погодни за пренос догађаја од дете виџета ка родитељском: притисак на дугме, избор ставке листе, слање форме. Колбекови експлицитно указују које акције потомак може да изврши и не стварају скривене зависности. За пренос података низ стабло на мали број нивоа такође је једноставније користити параметре конструктора.

КритеријумInheritedWidgetКолбекови
СмерОдозго надоле (родитељ → потомци)Одоздо нагоре (потомак → родитељ) или директно
ОбластЦело подстаблоОдређени виџет
Поновна изградњаАутоматска при промени податакаЗахтева ручни setState
СложеностСредња (потребна класа InheritedWidget)Ниска (само функција)

InheritedWidget и библиотеке за управљање стањем

Provider и Riverpod — популарне библиотеке за управљање стањем у Flutter-у, изграђене на InheritedWidget-у. Оне проширују његове могућности: додају подршку за ChangeNotifier, аутоматско уклањање при демонтажи, лењу иницијализацију и поједностављену синтаксу преко генерика.

Provider заснован на InheritedWidget-у

Provider користи InheritedWidget за пренос објекта било ког типа низ стабло. ChangeNotifierProvider прати промене кроз ChangeNotifier и позива updateShouldNotify при позиву notifyListeners. Ово ослобађа програмера од ручног креирања InheritedWidget-а и имплементације updateShouldNotify.

Поређење са директним InheritedWidget-ом

Директни InheritedWidget даје више контроле и не захтева спољне зависности. Provider пружа готову инфраструктуру: Consumer, Selector, MultiProvider, ProxyProvider. Избор зависи од сложености апликације. За једноставне пројекте директни InheritedWidget је довољан, за велике — Provider или Riverpod смањују шаблонски код.

dart
// Директни 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 разликује од обичног виџета?

InheritedWidget чини податке доступним свим потомцима кроз BuildContext, а обичан виџет преноси податке само кроз конструктор. InheritedWidget такође претплаћује потомке на ажурирања података.

Колико често се поново изграђују зависни виџети?

Зависни виџети се поново изграђују само када updateShouldNotify врати true. Ако је метода правилно имплементирана, поновна изградња се дешава само при стварној промени података, а не при сваком rebuild родитеља.

Може ли се користити више InheritedWidget-а у истом стаблу?

Да, можете користити било који број InheritedWidget-а у истом стаблу. Сваки пружа податке одређеног типа, а виџети могу примати податке из више InheritedWidget-а истовремено.

Чим се dependOnInheritedWidgetOfExactType разликује од findAncestorWidgetOfExactType?

dependOn претплаћује виџет на ажурирања — при промени података виџет ће се поново изградити. findAncestor обавља једнократну претрагу без претплате и виџет неће сазнати за промене података.

Да ли је InheritedWidget погодан за управљање сложеним стањем?

За једноставно стање (тема, подешавања) InheritedWidget је довољан. За сложено стање са пословном логиком користите Provider, Riverpod или BLoC — они су изграђени на InheritedWidget-у и додају потребну инфраструктуру.

Резиме

  • InheritedWidget — посебан Flutter виџет за пренос података низ стабло са аутоматском претплатом на ажурирања.
  • Механизам рада се заснива на Element Tree: InheritedElement региструје зависне елементе и обавештава их о променама.
  • updateShouldNotify — кључна метода за спречавање непотребних поновних изградњи зависних виџета.
  • Уграђени InheritedWidget: Theme, MediaQuery, Localizations, Directionality, DefaultTextStyle.
  • Креирање сопственог InheritedWidget-а укључује наслеђивање класе, пренос података кроз конструктор и статичку методу of.
  • Provider и Riverpod су изграђени на InheritedWidget-у и додају ChangeNotifier, Consumer, Selector и поједностављену синтаксу.
  • InheritedWidget решава проблем prop drilling и представља темељ реактивног управљања стањем у Flutter-у.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође