FlutterFlutter · Chapterبەش 11 · Lectureوانە 11.3
The Selector Widget: Fewer Rebuildsویجێتی Selector: دووبارە دروستکردنەوەی کەمتر
What a callback is, how Selector<T1, T2> uses a selector and a builder callback, how it compares the old and new selected values to skip rebuilds, the lecture's list-length, user-id and name-versus-age examples with real rebuild counts, context.select, and what may be selected.
چییە، Selector<T1, T2> چۆن selector و builder بەکار دەهێنێت، چۆن نرخی هەڵبژێردراوی کۆن و نوێ بەراورد دەکات بۆ ئەوەی دووبارە دروستکردنەوە بوەستێنێت، نموونەکانی وانەکە (درێژیی لیست، idـی ، ناو بەرامبەر تەمەن) بە ژمارەی ڕاستەقینەی دووبارە دروستکردنەوە، context.select و چی دەتوانرێت هەڵبژێردرێت.
What you will learnچی فێر دەبیت
- Explain what a callback is and name the two callbacks of a Selector.
- Set up Selector<T1, T2> with the right provider type and selected type.
- Describe how Selector compares the previous and new results to decide on a rebuild.
- Choose between Consumer, Selector and context.select for a given widget.
- Select immutable values, and several values with a record.
- ڕوون بکەیتەوە چییە و ناوی هەردوو Selector ببەیت.
Selector<T1, T2>بە جۆری دروستی provider و جۆری هەڵبژێردراو دابمەزرێنیت.- وەسفی ئەوە بکەیت کە Selector چۆن ئەنجامی پێشوو و نوێ بەراورد دەکات بۆ بڕیاردان لەسەر دووبارە دروستکردنەوە.
- بۆ دیاریکراو لە نێوان Consumer، Selector و
context.selectـدا هەڵبژێریت. - نرخی هەڵبژێریت و بۆ چەند نرخێک record بەکار بهێنیت.
When a Consumer rebuilds too oftenکاتێک Consumer زۆر جار دووبارە دروست دەبێتەوە
A Consumer rebuilds its builder every time its provider notifies, whatever changed. That is fine for a small object. But imagine a big Profile with a name, an age, a photo and settings, and a title that shows only the name. A birthday changes the age, the provider notifies, and the title is rebuilt for nothing.
Consumer هەر جارێک providerـەکەی ئاگادار بکاتەوە builderـەکەی دووبارە دروست دەکاتەوە، هەرچی گۆڕابێت. بۆ بچووک ئەمە کێشە نییە. بەڵام Profileـێکی گەورە بهێنە پێش چاوت کە ناو، تەمەن، وێنە و ڕێکخستنی هەیە و ناونیشانێک کە تەنها ناوەکە پیشان دەدات. ڕۆژی لەدایکبوون تەمەن دەگۆڕێت، providerـەکە ئاگادار دەکاتەوە و ناونیشانەکە بێهودە دووبارە دروست دەکرێتەوە.
The lecture's answer is the Selector widget: an equivalent of Consumer that can prevent widgets from being rebuilt when the part they use did not change. You tell it which part you care about, and it rebuilds only when that part is different.
وەڵامی وانەکە Selectorـە: هاوتای Consumerـە، بەڵام دەتوانێت ڕێگە بگرێت لە دووبارە دروستکردنەوەی کاتێک ئەو بەشەی بەکاری دەهێنن نەگۆڕاوە. تۆ پێی دەڵێیت گرنگیت بە کام بەشە و تەنها کاتێک ئەو بەشە جیاواز بێت دووبارە دروست دەبێتەوە.
At the tea house, one fan follows only Erbil's matches. The TV shows every result of the evening, but he looks up only when Erbil's score changes. A Selector is that fan: the provider may announce many changes, but the Selector reacts only to the value it picked.
لە چایخانەکەدا هاندەرێک تەنها یارییەکانی هەولێر بەدوادا دەچێت. تەلەفزیۆنەکە هەموو ئەنجامەکانی ئێوارەکە پیشان دەدات، بەڵام ئەو تەنها کاتێک ئەنجامی هەولێر دەگۆڕێت سەیر دەکات. Selector ئەو هاندەرەیە: لەوانەیە providerـەکە زۆر گۆڕان ڕابگەیەنێت، بەڵام Selector تەنها بەو نرخە کاردانەوەی هەیە کە خۆی هەڵیبژاردووە.
Where the analogy stops workingسنووری ئەم نموونەیە
The fan still hears every announcement; he just ignores most of them. A Selector is the same: its selector callback runs on every notification, and only the builder is skipped.
هاندەرەکە هێشتا هەموو ڕاگەیاندنێک دەبیستێت؛ تەنها زۆربەیان پشتگوێ دەخات. Selector هەروایە: selector لە هەموو ئاگادارکردنەوەیەکدا جێبەجێ دەبێت و تەنها builderـەکە دەوەستێنرێت.
Two callbacks: selector and builderدوو کرداری بانگکردنەوە: selector و builder
The lecture stops to explain one word first. A callback is a function you pass as an argument to other code, which that code calls later, when it needs it. You met the idea in functions as values: onPressed is a callback that Flutter calls when the button is tapped.
وانەکە سەرەتا بۆ ڕوونکردنەوەی یەک وشە دەوەستێت. کە وەک دەیدەیت بە کۆدێکی تر و ئەو کۆدە دواتر، کاتێک پێویستی پێی بوو، بانگی دەکات. ئەم بیرۆکەیەت لە کردارەکان وەک نرخ بینیوە: onPressed کە Flutter کاتێک دەست لە دەدرێت بانگی دەکات.
A Selector takes two callbacks, and the Selector itself decides when to call each one:
Selector دوو وەردەگرێت و خودی Selector بڕیار دەدات کەی هەر یەکێکیان بانگ بکات:
selectorpicks the part of the provided data that should be watched for changes. It runs every time the provider notifies.builderbuilds the widgets from that part, like a Consumer's builder. It runs only when the selected part is new.
selectorئەو بەشەی دراوە دابینکراوەکە هەڵدەبژێرێت کە دەبێت چاودێریی گۆڕانەکانی بکرێت. هەر جارێک providerـەکە ئاگادار بکاتەوە جێبەجێ دەبێت.builderلەو بەشەوە دروست دەکات، وەک builderـی Consumer. تەنها کاتێک بەشە هەڵبژێردراوەکە نوێ بێت جێبەجێ دەبێت.
Setting up a Selector<T1, T2>دامەزراندنی Selector<T1, T2>
A Selector has two types in its angle brackets: Selector<T1, T2>.
Selector دوو جۆری لەناو کەوانە تیژەکانیدا هەیە: Selector<T1, T2>.
| Typeجۆر | Meaningواتا | In the badge belowلە نیشانەکەی خوارەوەدا |
|---|---|---|
T1 | The type of the provider. The Selector uses it to climb up the tree and find the provider.جۆری providerـەکە. Selector بەکاری دەهێنێت بۆ ئەوەی بە سەر بکەوێت و providerـەکە بدۆزێتەوە. | TodoListModel |
T2 | The type of the part you watch: what selector returns and builder receives.جۆری ئەو بەشەی چاودێری دەکەیت: ئەوەی selector دەیگەڕێنێتەوە و builder وەری دەگرێت. | int |
The lecture's example rebuilds a list only when its length changes. Here is the same idea as a to-do counter badge for an app bar. Ticking a to-do as done changes the model but not the number of to-dos, so the badge stays as it is (the model, TodoListModel, is shown in What to select):
نموونەکەی وانەکە تەنها کاتێک درێژییەکەی دەگۆڕێت دووبارە دروست دەکاتەوە. ئەمە هەمان بیرۆکەیە وەک نیشانەیەکی ژمارەی ئەرکەکان بۆ . نیشانکردنی ئەرکێک وەک تەواوبوو دەگۆڕێت، بەڵام ژمارەی ئەرکەکان ناگۆڕێت، بۆیە نیشانەکە وەک خۆی دەمێنێتەوە (، TodoListModel، لە چی هەڵبژێرین پیشان دراوە):
// Rebuilds only when the NUMBER of to-dos changes.
class TodoCountBadge extends StatelessWidget {
const TodoCountBadge({super.key});
@override
Widget build(BuildContext context) {
return Selector<TodoListModel, int>(
selector: (_, list) => list.items.length, // T1 in, T2 out
builder: (context, count, child) => Badge(
label: Text('$count'),
child: const Icon(Icons.checklist),
),
);
}
}after toggle: {badge: 1}
after add: {badge: 2}Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 7
T1 = TodoListModel(the provider),T2 = int(the part we watch).T1 = TodoListModel(providerـەکە)،T2 = int(ئەو بەشەی چاودێری دەکەین).- Lineدێڕی 8
From the whole model, keep only the number of items.
لە هەموو ، تەنها ژمارەی دانەکان بهێڵەرەوە.
- Lineدێڕی 9
countis theintthe selector returned.countئەوintـەیە کە selector گەڕاندییەوە.
Badge is a Material 3 widget that puts a small label on an icon. The test shows the badge was built once at the start, not again after a to-do was ticked, and once more after a new to-do was added.
Badge Material 3ـە کە نیشانەیەکی بچووک دەخاتە سەر . تاقیکردنەوەکە پیشان دەدات کە نیشانەکە لە سەرەتادا یەک جار دروست کرا، دوای نیشانکردنی ئەرکێک دووبارە دروست نەکرایەوە و دوای زیادکردنی ئەرکێکی نوێ جارێکی تر دروست کرایەوە.
How Selector decides: the old result against the new oneSelector چۆن بڕیار دەدات: ئەنجامی کۆن بەرامبەر ئەنجامی نوێ
- The provider notifiesproviderـەکە ئاگادار دەکاتەوە
Something in the model changed and
notifyListeners()ran.شتێک لە گۆڕا و
notifyListeners()جێبەجێ بوو. - Selector reads the dataSelector دراوەکە دەخوێنێتەوە
Like a Consumer, it gets the whole value with
Provider.of, and passes it to the selector callback.وەک Consumer، هەموو نرخەکە بە
Provider.ofوەردەگرێت و دەیدات بە selector. - The selector makes new dataselector دراوەی نوێ دروست دەکات
The selector creates new data from it, usually one small part, for example
user.id.selector لەوەوە دراوەیەکی نوێ دروست دەکات، زۆرجار بەشێکی بچووک، بۆ نموونە
user.id. - Compareبەراوردکردن
The Selector compares this result with the previous result of the selector.
Selector ئەم ئەنجامە لەگەڵ ئەنجامی پێشووی selector بەراورد دەکات.
- Rebuild or keepدووبارە دروستکردنەوە یان هێشتنەوە
If they are different, it calls the builder with the new result. If they are equal, it keeps the widget it built last time; the builder is not called.
ئەگەر جیاواز بن، builder بە ئەنجامە نوێیەکە بانگ دەکات. ئەگەر یەکسان بن، ئەو جاری پێشوو دروستی کردبوو دەیهێڵێتەوە؛ builder بانگ ناکرێت.
In selector: (context, data) { return newData; }لە selector: (context, data) { return newData; }ـدا | Meaningواتا |
|---|---|
context | A BuildContext object BuildContext |
data | The whole value obtained from the provider (type T1)هەموو ئەو نرخەی لە providerـەکەوە وەرگیراوە (جۆری T1) |
newData | The result of the selector (type T2): passed to the builder and used for the comparisonئەنجامی selector (جۆری T2): دەدرێت بە builder و بۆ بەراوردکردن بەکار دێت |
The lecture's example: rebuild only for a new userنموونەکەی وانەکە: تەنها بۆ بەکارهێنەرێکی نوێ دووبارە دروست بکەرەوە
The lecture's second example rebuilds the whole Scaffold only if the user id has changed, that is, only when a different person signs in. Note that the builder reads the user with listen: false: the Selector already decides when the builder runs.
نموونەی دووەمی وانەکە هەموو Scaffoldـەکە تەنها ئەگەر idـی گۆڕابێت دووبارە دروست دەکاتەوە، واتە تەنها کاتێک کەسێکی تر دێتە ژوورەوە. ئاگاداری ئەوە بە کە builderـەکە بە listen: false دەخوێنێتەوە: خودی Selector بڕیار دەدات builder کەی جێبەجێ ببێت.
Scaffold تەنها کاتێک idـی بەکارهێنەر دەگۆڕێت دووبارە دروست بکەرەوەDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات// The lecture's example: rebuild the Scaffold only if the user id changes.
Widget buildMainScreen(BuildContext context) {
return Selector<UserModel, int>(
selector: (_, UserModel user) => user.id,
builder: (context, uid, child) {
// read without listening: the builder already runs when the id changes
final user = Provider.of<UserModel>(context, listen: false);
return Scaffold(
appBar: AppBar(
leading: CircleAvatar(child: Text(user.name[0])),
title: Text(user.name),
),
body: Center(child: Text('Signed in as user #$uid')),
);
},
);
}title at start: Aram after rename (id 1): Aram after switchUser (id 2): Hana
Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 4
The selector callback: from the whole
UserModel, return onlyid(anint).selector: لە هەموو
UserModel، تەنهاidبگەڕێنەرەوە (intـێک).- Lineدێڕی 7
Read the rest of the user without listening; the Selector already listens.
ئەوانی تری بەبێ گوێگرتن بخوێنەرەوە؛ Selector خۆی گوێ دەگرێت.
Look at the second line of the output. rename changed the name, the provider notified, but the id was still 1, so the builder did not run and the title still shows the old name. Only switchUser (a new id) rebuilt the screen. That is exactly what the code asks for: the widget rebuilds only if the user id changes, even when other properties such as the name or avatar change.
سەیری دێڕی دووەمی دەرخستەکە بکە. rename ناوەکەی گۆڕی، providerـەکە ئاگاداری کردەوە، بەڵام id هێشتا 1 بوو، بۆیە builder جێبەجێ نەبوو و ناونیشانەکە هێشتا ناوە کۆنەکە پیشان دەدات. تەنها switchUser (idـێکی نوێ) شاشەکەی دووبارە دروست کردەوە. ئەمە ڕێک ئەوەیە کە کۆدەکە داوای دەکات: تەنها ئەگەر idـی بگۆڕێت دووبارە دروست دەبێتەوە، تەنانەت ئەگەر تر وەک ناو یان وێنە بگۆڕێن.
Consumer or Selector: the name and the ageConsumer یان Selector: ناو و تەمەن
The lecture compares the two widgets with a model that has a name and an age. Here is that model, and a title written both ways:
وانەکە ئەم دوو بە بەراورد دەکات کە name و ageـی هەیە. ئەمە ئەو و ناونیشانێک کە بە هەردوو شێوە نووسراوە:
class Profile extends ChangeNotifier {
String _name = 'Dilan';
int _age = 20;
String get name => _name;
int get age => _age;
void rename(String newName) {
_name = newName;
notifyListeners();
}
void birthday() {
_age++;
notifyListeners();
}
}// Consumer: the Text is rebuilt after ANY change, even a birthday.
class NameWithConsumer extends StatelessWidget {
const NameWithConsumer({super.key});
@override
Widget build(BuildContext context) {
return Consumer<Profile>(
builder: (context, profile, child) => Text(profile.name),
);
}
}// Selector: the Text is rebuilt only when the name itself changes.
class NameWithSelector extends StatelessWidget {
const NameWithSelector({super.key});
@override
Widget build(BuildContext context) {
return Selector<Profile, String>(
selector: (context, profile) => profile.name,
builder: (context, name, child) => Text(name),
);
}
}Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 8
The selector takes only
nameout of the model.selector تەنها
nameلە دەردەهێنێت.


A widget test put both titles on one screen, then called birthday() and rename('Lana'):
تاقیکردنەوەیەکی هەردوو ناونیشانەکەی لە یەک شاشەدا دانا، پاشان بانگی birthday() و rename('Lana')ـی کرد:
start: {Consumer: 1, Selector: 1}
after birthday: {Consumer: 2, Selector: 1}
after rename: {Consumer: 3, Selector: 2}The birthday rebuilt the Consumer's Text, although it shows only the name; the Selector's Text stayed. The rename rebuilt both, because the name really changed. With one Text the saving is tiny. With a large widget, or a long list where every row would rebuild, it matters for performance: less work per change means smoother scrolling and less battery.
ڕۆژی لەدایکبوون Textـی Consumerـی دووبارە دروست کردەوە، هەرچەندە تەنها ناوەکە پیشان دەدات؛ Textـی Selector وەک خۆی مایەوە. گۆڕینی ناو هەردووکیانی دووبارە دروست کردەوە، چونکە ناوەکە بە ڕاستی گۆڕا. بە یەک Text قازانجەکە زۆر کەمە. بە گەورە، یان لیستێکی درێژ کە هەموو ڕیزێکی دووبارە دروست ببێتەوە، بۆ کارایی گرنگە: کاری کەمتر لە هەر گۆڕانێکدا واتە نەرمتر و باتریی کەمتر.
context.select: a Selector without the widgetcontext.select: Selector بەبێ ویجێت
You met context.select in State Management with Provider. It does the same filtering as a Selector, but inside a build method instead of as a widget of its own:
context.selectـت لە بەڕێوەبردنی دۆخ بە Provider بینی. هەمان پاڵاوتنی Selector دەکات، بەڵام لەناو buildـدا، نەک وەک سەربەخۆ:
context.selectDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات// The same filter without a Selector widget: context.select in build.
class NameText extends StatelessWidget {
const NameText({super.key});
@override
Widget build(BuildContext context) {
final name = context.select((Profile p) => p.name);
return Text(name);
}
}| Selector widget Selector | context.select |
|---|---|
Selector<Profile, String>(selector: (_, p) => p.name, builder: …) | final name = context.select((Profile p) => p.name); |
| A widget: wraps one part of a build method: بە دەوری بەشێکی buildـدا دەپێچێت | A call: the whole widget that calls it is rebuiltبانگکردنێکە: هەموو ئەو بانگی دەکات دووبارە دروست دەبێتەوە |
Can keep a static childدەتوانێت childـێکی نەگۆڕ بهێڵێتەوە | No child; keep the widget small insteadchild نییە؛ لە جیاتی ئەوە بچووک بهێڵەرەوە |
One rule: context.select works only inside build, and not straight inside a list's itemBuilder (there it would rebuild the whole list). Give each row its own small widget or a Builder:
یەک یاسا: context.select تەنها لەناو buildـدا کار دەکات و ڕاستەوخۆ لەناو itemBuilderـی کار ناکات (لەوێ هەموو دووبارە دروست دەکاتەوە). هەر ڕیزێک بچووکی خۆی یان Builderـێک بدەرێ:
// In a list, select inside a small widget per row, not in itemBuilder.
class NamesList extends StatelessWidget {
const NamesList({super.key});
@override
Widget build(BuildContext context) {
final count = context.select((ClassModel c) => c.names.length);
return ListView.builder(
itemCount: count,
itemBuilder: (context, index) => Builder(
builder: (context) {
// each row listens only to its own name
final name = context.select((ClassModel c) => c.names[index]);
return ListTile(title: Text(name));
},
),
);
}
}context.selectـی ڕاستەوخۆ لەناو itemBuilderـدا بانگ کرد، بەبێ Builder_AssertionError: … Failed assertion: … 'widget is! SliverWithKeepAliveWidget':
Tried to use context.select inside a SliverList/SliderGridView.
This is likely a mistake, as instead of rebuilding only the item that cares …What to select: small values that cannot change in placeچی هەڵبژێرین: نرخی بچووک کە لە شوێنی خۆیدا ناگۆڕێن
The comparison only works if the old result is kept as it was. If your selector returns an object that the model later changes in place, the old and new results are the very same object, so they always look equal. The package's documentation says the selected value must be immutable.
بەراوردکردنەکە تەنها کاتێک کار دەکات کە ئەنجامە کۆنەکە وەک خۆی بمێنێتەوە. ئەگەر selectorـەکەت بگەڕێنێتەوە کە دواتر لە شوێنی خۆیدا دەیگۆڕێت، ئەنجامی کۆن و نوێ ڕێک هەمان ، بۆیە هەمیشە یەکسان دەردەکەون. دەڵێن نرخی هەڵبژێردراو دەبێت بێت.
Here is the model behind the badge and this lesson's tests. items returns a new unmodifiable list on every call, but the Todo objects inside are always the same ones, and toggle changes done inside them:
ئەمە ئەو کە نیشانەکە و تاقیکردنەوەکانی ئەم وانەیە بەکاری دەهێنن. items لە هەر بانگکردنێکدا نوێ و نەگۆڕ دەگەڕێنێتەوە، بەڵام Todoـی ناوی هەمیشە هەمان ئەوانەن و toggle ئەو doneـەی لەناو خودی ئەواندایە دەگۆڕێت:
class Todo {
Todo(this.title);
final String title;
bool done = false; // can change in place
}
class TodoListModel extends ChangeNotifier {
final List<Todo> _items = [Todo('Buy bread'), Todo('Call Shilan')];
// A new unmodifiable list on every call, holding the SAME Todo objects.
List<Todo> get items => List.unmodifiable(_items);
void add(String title) {
_items.add(Todo(title));
notifyListeners(); // the length changes
}
void toggle(int index) {
_items[index].done = !_items[index].done; // changed in place
notifyListeners(); // the length stays the same
}
}Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 5
A mutable field:
togglechanges it inside the existing object.گۆڕاو:
toggleلەناو هەمان دەیگۆڕێت.- Lineدێڕی 12
A new list each time, but with the same
Todoobjects in it.هەر جارێک نوێ، بەڵام هەمان
Todoـی تێدایە.
A widget test selected list.items itself and then ticked a to-do. The new list held the very same Todo objects, now with done = true, so the deep comparison found no difference and the screen stayed wrong. Adding a to-do was seen, because the length changed:
تاقیکردنەوەیەکی خودی list.itemsـی هەڵبژارد و پاشان ئەرکێکی نیشان کرد. نوێیەکە ڕێک هەمان Todoـی تێدا بوو، کە ئێستا done = true بوون، بۆیە بەراوردە قووڵەکە هیچ جیاوازییەکی نەدۆزییەوە و شاشەکە هەڵە مایەوە. زیادکردنی ئەرکێک بینرا، چونکە درێژییەکە گۆڕا:
after toggle: {list: 1}, model says done=true
after add: {list: 2}What to select: small values that cannot change in placeچی هەڵبژێرین: نرخی بچووک کە لە شوێنی خۆیدا ناگۆڕێن
Small values, and records for severalنرخی بچووک و record بۆ چەند نرخێک
- Select small, simple values: an
int, aString, abool, a length, a total. - To watch several values, return a Dart 3 record: values in brackets that are compared field by field.
- If you select your own class, make it immutable (
finalfields, a new object for every change) and give it an==.
- نرخی بچووک و سادە هەڵبژێرە:
int،String،bool، درێژییەک، کۆیەک. - بۆ چاودێریکردنی چەند نرخێک، recordـێکی Dart 3 بگەڕێنەرەوە: چەند نرخێک لەناو کەوانەدا کە خانە بە خانە بەراورد دەکرێن.
- ئەگەر خۆت هەڵدەبژێریت، بیکە بە (
final، نوێ بۆ هەر گۆڕانێک) و==ـی بۆ بنووسە.
// Two values at once: a Dart 3 record compares field by field.
class NameAndAge extends StatelessWidget {
const NameAndAge({super.key});
@override
Widget build(BuildContext context) {
return Selector<Profile, ({String name, int age})>(
selector: (_, p) => (name: p.name, age: p.age),
builder: (context, data, child) => Text('${data.name}, ${data.age}'),
);
}
}record shows: 1 "Dilan, 21"
Consumer or Selector?Consumer یان Selector؟
| Pointخاڵ | Consumer<T> | Selector<T1, T2> |
|---|---|---|
| Rebuilds whenکەی دووبارە دروست دەبێتەوە | The provider notifies, whatever changedproviderـەکە ئاگادار دەکاتەوە، هەرچی گۆڕابێت | The selected part is different from last timeبەشە هەڵبژێردراوەکە لە جاری پێشوو جیاواز بێت |
| Callbacks | builder | selector + builder |
| Builder receivesbuilder وەردەگرێت | The whole valueهەموو نرخەکە | Only the selected partتەنها بەشە هەڵبژێردراوەکە |
| Best forباشترین بۆ | Widgets that use most of a small objectئەو زۆربەی بچووک بەکار دەهێنن | One part of a big object, in many places or in listsیەک بەشی گەورە، لە چەند شوێنێکدا یان لە لیستەکاندا |
| Short form in buildشێوەی کورت لە buildـدا | context.watch<T>() | context.select((T t) => t.part) |
Start with a Consumer (or context.watch): it is simpler. Switch to a Selector when a widget uses only a small part of a large object that changes often. That is how the lecture sums it up: if you have a large state object but only need a small part of it in a widget, a Selector prevents unnecessary rebuilds.
بە Consumer (یان context.watch) دەست پێ بکە: سادەترە. کاتێک تەنها بەشێکی بچووکی گەورە بەکار دەهێنێت کە زۆرجار دەگۆڕێت، بگۆڕە بۆ Selector. وانەکە بەم شێوەیە کورتی دەکاتەوە: ئەگەر گەورەت هەیە بەڵام لە تەنها بەشێکی بچووکیت پێویستە، Selector ڕێگە لە دووبارە دروستکردنەوەی نەپێویست دەگرێت.
Common mistakesهەڵە باوەکان
| Mistakeهەڵە | What happensچی ڕوو دەدات | Fixچاکردنەوە |
|---|---|---|
| Selecting a mutable object or listهەڵبژاردنی یان گۆڕاو | Changes in place are never seen; the screen shows old dataگۆڕانی لە شوێنی خۆیدا هەرگیز نابینرێت؛ شاشەکە دراوەی کۆن پیشان دەدات | Select simple values or a record; keep model objects immutableنرخی سادە یان record هەڵبژێرە؛ بهێڵەرەوە |
Reading other fields in the builder with listen: false, then expecting them to updateخوێندنەوەی تر لە builderـدا بە listen: false و چاوەڕوانکردنی نوێبوونەوەیان | They update only when the selected part changesتەنها کاتێک بەشە هەڵبژێردراوەکە دەگۆڕێت نوێ دەبنەوە | Select everything the builder shows (a record)هەموو ئەو شتانەی builder پیشانیان دەدات هەڵبژێرە (recordـێک) |
Mixing up T1 and T2تێکەڵکردنی T1 و T2 | Compile error or ProviderNotFoundException compile یان ProviderNotFoundException | T1 = the provider's type, T2 = what selector returnsT1 = جۆری providerـەکە، T2 = ئەوەی selector دەیگەڕێنێتەوە |
context.select straight in itemBuildercontext.select ڕاستەوخۆ لەناو itemBuilderـدا | Assertion error in debug mode assertion لە دۆخی debugـدا | Wrap each row in a Builder or its own widgetهەر ڕیزێک لە Builderـێک یان تایبەت بە خۆیدا بپێچە |
| A Selector for every tiny widgetSelector بۆ هەموو بچووک | More code, almost no gainکۆدی زیاتر، نزیکەی هیچ قازانجێک | Use it where a big object changes oftenلەو شوێنانە بەکاری بهێنە کە گەورە زۆرجار دەگۆڕێت |
Exercisesڕاهێنانەکان
A CartModel has totalPrice (an int) and a giftWrap switch that does not change the price. Write a TotalText widget that shows Total: … IQD and is not rebuilt when only giftWrap changes.
CartModelـێک totalPriceـی (intـێک) و سویچێکی giftWrapـی هەیە کە نرخ ناگۆڕێت. TotalText بنووسە کە Total: … IQD پیشان بدات و کاتێک تەنها giftWrap دەگۆڕێت دووبارە دروست نەبێتەوە.
Hintئاماژە
Which part does the Text use? Select exactly that.
Textـەکە کام بەش بەکار دەهێنێت؟ ڕێک ئەوە هەڵبژێرە.
Show answerوەڵامەکە پیشان بدە
class TotalText extends StatelessWidget {
const TotalText({super.key});
@override
Widget build(BuildContext context) {
return Selector<CartModel, int>(
selector: (_, cart) => cart.totalPrice,
builder: (context, total, child) => Text('Total: $total IQD'),
);
}
}T1 = CartModel, T2 = int. Toggling gift wrap leaves the total equal, so the builder is skipped. context.select((CartModel c) => c.totalPrice) in a small widget works just as well.
T1 = CartModel، T2 = int. گۆڕینی پێچانی دیاری کۆکە یەکسان دەهێڵێتەوە، بۆیە builder دەوەستێنرێت. context.select((CartModel c) => c.totalPrice) لە بچووکدا هەمان کار دەکات.
Selector<Profile, int>(selector: (_, p) => p.age, builder: …) is on screen. Then the app calls rename('Hana'), birthday(), rename('Soran'), birthday(). How many times has the builder run in total?
Selector<Profile, int>(selector: (_, p) => p.age, builder: …) لەسەر شاشەیە. پاشان بانگی rename('Hana')، birthday()، rename('Soran')، birthday() دەکات. builder بە گشتی چەند جار جێبەجێ بووە؟
Show answerوەڵامەکە پیشان بدە
3 times: once for the first build and once after each birthday(). The two renames make the provider notify, but the age stays equal, so the builder is skipped.
3 جار: یەک جار بۆ یەکەم build و یەک جار دوای هەر birthday()ـێک. دوو گۆڕینی ناوەکە وا دەکەن providerـەکە ئاگادار بکاتەوە، بەڵام تەمەن یەکسان دەمێنێتەوە، بۆیە builder دەوەستێنرێت.
A classmate writes Selector<TodoListModel, List<Todo>>(selector: (_, m) => m.items, …). Adding a to-do updates the screen, but ticking one as done does not. Explain and fix.
هاوپۆلێک دەنووسێت Selector<TodoListModel, List<Todo>>(selector: (_, m) => m.items, …). زیادکردنی ئەرکێک شاشەکە نوێ دەکاتەوە، بەڵام نیشانکردنی ئەرکێک وەک تەواوبوو نوێی ناکاتەوە. ڕوونی بکەرەوە و چاکی بکە.
Show answerوەڵامەکە پیشان بدە
Adding changes the length, so the deep comparison sees a difference. Ticking changes done inside the same Todo object, so old and new lists hold identical objects and compare equal. (Adding is seen only because items returns a new list each time; if it returned _items itself, the Selector would compare the list with itself and miss even an addition.) Fix: make Todo immutable (final fields and a constructor Todo(this.title, {this.done = false})) and replace the object when it changes, _items[i] = Todo(_items[i].title, done: !_items[i].done); or simply use a Consumer for this list.
زیادکردن درێژی دەگۆڕێت، بۆیە بەراوردە قووڵەکە جیاوازییەک دەبینێت. نیشانکردن done لەناو هەمان Todoـدا دەگۆڕێت، بۆیە لیستی کۆن و نوێ وەک یەکیان تێدایە و یەکسان دەردەچن. (زیادکردن تەنها لەبەر ئەوە دەبینرێت کە items هەر جارێک نوێ دەگەڕێنێتەوە؛ ئەگەر خودی _itemsـی بگەڕاندایەتەوە، Selector لەگەڵ خۆیدا بەراورد دەکرد و تەنانەت زیادکردنیش نەدەبینرا.) چاکردنەوە: Todo بکە بە ( final و Todo(this.title, {this.done = false})) و کاتێک دەگۆڕێت نوێی لە جێ دابنێ، _items[i] = Todo(_items[i].title, done: !_items[i].done)؛ یان بە سادەیی Consumer بۆ ئەم بەکار بهێنە.
Write the slide's idea in full: a provider of type List<Todo> sits above the screen. Build a ListView that is rebuilt only when the number of to-dos changes.
بیرۆکەکەی سلایدەکە بە تەواوی بنووسە: providerـێکی جۆری List<Todo> لە سەرووی شاشەکەوەیە. ListViewـێک دروست بکە کە تەنها کاتێک ژمارەی ئەرکەکان دەگۆڕێت دووبارە دروست ببێتەوە.
Show answerوەڵامەکە پیشان بدە
// The lecture's idea: rebuild the ListView only when the length changes.
// Here the List<Todo> comes from a provider of type List<Todo>.
class TodoListView extends StatelessWidget {
const TodoListView({super.key});
@override
Widget build(BuildContext context) {
return Selector<List<Todo>, int>(
selector: (_, list) => list.length,
builder: (context, length, child) {
final todos = Provider.of<List<Todo>>(context, listen: false);
return ListView.builder(
itemCount: length,
itemBuilder: (context, i) => ListTile(title: Text(todos[i].title)),
);
},
);
}
}Like the user-id example, the builder reads the list with listen: false. So if a new list arrives with the same length but different titles, the screen keeps the old titles. That is the price of selecting only the length.
وەک نموونەی idـی ، builder بە listen: false دەخوێنێتەوە. بۆیە ئەگەر نوێ بە هەمان درێژی بەڵام ناونیشانی جیاواز بگات، شاشەکە ناونیشانە کۆنەکان دەهێڵێتەوە. ئەمە نرخی هەڵبژاردنی تەنها درێژییەکەیە.
Summaryپوختە
- A Selector is a Consumer that rebuilds only when the part you select changes.
Selector<T1, T2>:T1is the provider's type (to find it),T2is the type of the selected part.- Two callbacks:
selectormakes new data from the provider's value;builderbuilds widgets from it. - After every notification the selector runs; the builder runs only if the new result differs from the previous one (deep equality).
- Whatever the builder reads with
listen: falseupdates only together with the selected part. context.selectis the same filter insidebuild. Select small, immutable values; use a record for several.
- Selector جۆرە Consumerـێکە کە تەنها کاتێک ئەو بەشەی هەڵیدەبژێریت دەگۆڕێت دووبارە دروست دەبێتەوە.
Selector<T1, T2>:T1جۆری providerـەکەیە (بۆ دۆزینەوەی)،T2جۆری بەشە هەڵبژێردراوەکەیە.- دوو :
selectorلە نرخی providerـەکە دراوەی نوێ دروست دەکات؛builderلەوەوە دروست دەکات. - دوای هەموو ئاگادارکردنەوەیەک selector جێبەجێ دەبێت؛ builder تەنها ئەگەر ئەنجامە نوێیەکە لە پێشوو جیاواز بێت جێبەجێ دەبێت (یەکسانیی قووڵ).
- هەرچی builder بە
listen: falseدەیخوێنێتەوە تەنها لەگەڵ بەشە هەڵبژێردراوەکەدا نوێ دەبێتەوە. context.selectهەمان پاڵاوتەیە لەناوbuildـدا. نرخی بچووک و هەڵبژێرە؛ بۆ چەند نرخێک record بەکار بهێنە.
Lesson complete!وانەکە تەواو بوو!
Well done. Review the key terms, then go on to the next lesson.دەستخۆش. سەیرێکی زاراوە سەرەکییەکان بکەرەوە، پاشان بڕۆ بۆ وانەی داهاتوو.
Key termsزاراوە سەرەکییەکان
ChangeNotifier, ChangeNotifierProvider and MultiProviderChangeNotifier، ChangeNotifierProvider و MultiProvider
Related lecturesوانە پەیوەندیدارەکان
References and further readingسەرچاوەکان و خوێندنەوەی زیاتر
This lecture is a first edition. The Kurdish text and terminology are waiting for review by Kurdish IT teachers.ئەم وانەیە چاپی یەکەمە. دەقە کوردییەکە و زاراوەکانی چاوەڕێی پێداچوونەوەی مامۆستایانی کوردی بواری IT ن.