FlutterFlutter · Chapterبەش 11 · Lectureوانە 11.4

ChangeNotifier, ChangeNotifierProvider and MultiProviderChangeNotifier، ChangeNotifierProvider و MultiProvider

How a ChangeNotifier (a built-in mixin class) holds state and calls its listeners, notifyListeners versus setState, ValueNotifier, the Counter app end to end with ChangeNotifierProvider (and without a package), the lecture's dark-mode checkbox, and several providers nested or in one MultiProvider.

⁦ChangeNotifier (mixin class⁩ـێکی ناوەکی) چۆن هەڵدەگرێت و بانگ دەکات، notifyListeners بەرامبەر setState، ValueNotifier، Counter لە سەرەتاوە تا کۆتایی بە ChangeNotifierProvider (و بەبێ )، وانەکە و چەند provider لەناو یەکدا یان لە یەک MultiProviderـدا.

19 min read18 خولەک خوێندنەوە · 15 stepsهەنگاو

What you will learnچی فێر دەبیت

  • Say which classes are built into Flutter and which come from the provider package.
  • Write a ChangeNotifier model with private state and notifyListeners, using extends or with.
  • Explain how notifyListeners differs from setState and when ValueNotifier is enough.
  • Build the Counter app with ChangeNotifierProvider, watch and read, and without a package.
  • Provide several values with MultiProvider and explain why it equals nesting.
  • بڵێیت کام لەناو Flutterـدان و کامیان لە providerـەوە دێن.
  • ChangeNotifier بە تایبەت و notifyListeners بنووسیت، بە extends یان with.
  • ڕوون بکەیتەوە notifyListeners چۆن لە setState جیاوازە و کەی ValueNotifier بەسە.
  • Counter بە ChangeNotifierProvider، watch و read دروست بکەیت، هەروەها بەبێ هیچ .
  • بە MultiProvider چەند نرخێک دابین بکەیت و ڕوون بکەیتەوە بۆچی یەکسانە بە لەناو یەکدانان.

Data that changes and tells everyoneدراوەیەک کە دەگۆڕێت و هەمووان ئاگادار دەکاتەوە

In State Management with Provider you saw a cart model that calls notifyListeners(), and in the last two lessons Consumers and Selectors that listen to such models. This lesson looks at the model side closely: ChangeNotifier and its small brother ValueNotifier, then ChangeNotifierProvider, which puts one into the tree, and MultiProvider, which gives an app several providers at once.

لە بەڕێوەبردنی دۆخ بە Provider سەبەتەت بینی کە بانگی notifyListeners() دەکات و لە دوو وانەی ڕابردوودا Consumer و Selectorـت بینی کە گوێ لەو جۆرە دەگرن. ئەم وانەیە بە وردی سەیری لایەنی دەکات: ChangeNotifier و برا بچووکەکەی ValueNotifier، پاشان ChangeNotifierProvider کە یەکێکیان دەخاتە نێو و MultiProvider کە لە یەک کاتدا چەند providerـێک دەدات بە .

Keep one fact clear from the start, because the course notes mix it up: some of these classes are built into Flutter and some come from the provider package:

لە سەرەتاوە یەک ڕاستی ڕوون بهێڵەرەوە، چونکە تێبینییەکانی کۆرسەکە تێکەڵی دەکەن: هەندێک لەم لەناو خودی Flutterـدان و هەندێکیان لە providerـەوە دێن:

ClassComes fromلە کوێوە دێتJobئەرک
ChangeNotifier, ValueNotifierFlutter itself (package:flutter/foundation.dart)خودی Flutter (package:flutter/foundation.dart)Hold state and notify listeners هەڵدەگرن و ئاگاداری دەکەنەوە
ListenableBuilder, ValueListenableBuilderFlutter itself (widgets library)خودی Flutter ( widgets)Rebuild a part of the screen when a notifier notifiesکاتێک notifierـێک ئاگادار دەکاتەوە بەشێک لە شاشەکە دووبارە دروست دەکەنەوە
ChangeNotifierProvider, MultiProvider, Consumer, context.watchThe provider package (flutter pub add provider) provider (flutter pub add provider)Put notifiers into the tree and let widgets find them by typenotifierـەکان دەخەنە نێو و ڕێگە دەدەن بەپێی جۆر بیاندۆزنەوە
Quick checkپشکنینی خێراWhich of these needs the provider package?کامیان پێویستی بە provider هەیە؟

ChangeNotifier: state plus a list of listenersChangeNotifier: دۆخ لەگەڵ لیستێک لە گوێگرەکان

A ChangeNotifier is a class that tells its listeners when its data changes. You do not use it alone: you write your own class that extends it, put the state inside it as private fields, and call notifyListeners() after every change. Here is the lecture's Counter, with a small program that adds two listeners by hand:

ChangeNotifier کە کاتێک دراوەکەی دەگۆڕێت ئاگاداری دەکاتەوە. بە تەنها بەکاری ناهێنیت: خۆت دەنووسیت کە extendsـی دەکات، دەخەیتە ناوی وەک تایبەت و دوای هەر گۆڕانێک بانگی notifyListeners() دەکەیت. ئەمە Counterـەکەی وانەکەیە، لەگەڵ بەرنامەیەکی بچووک کە بە دەست دوو زیاد دەکات:

A Counter and two listenersCounterـێک و دوو گوێگرDART
import 'package:flutter/foundation.dart';

class Counter extends ChangeNotifier {
  int _count = 0; // the state lives inside the notifier

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners(); // call every registered listener
  }
}

void main() {
  final counter = Counter();

  void listenerA() => debugPrint('A sees ${counter.count}');
  counter.addListener(listenerA);
  counter.addListener(() => debugPrint('B sees ${counter.count}'));

  counter.increment(); // both listeners run
  counter.removeListener(listenerA);
  counter.increment(); // only B runs now

  counter.dispose(); // finished: free the listener list
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 4

The state lives inside the notifier, private (_), so only its methods can change it.

لەناو notifierـەکەدایە و تایبەتە (_)، بۆیە تەنها خۆی دەتوانن بیگۆڕن.

Lineدێڕی 10

After the change, call every listener on the list.

دوای گۆڕانەکە، هەموو ناو لیستەکە بانگ بکە.

Lineدێڕی 18

addListener puts a function on the list. Widgets such as ListenableBuilder do this for you.

addListener دەخاتە ناو لیستەکەوە. وەک ListenableBuilder ئەمە بۆ تۆ دەکەن.

Lineدێڕی 25

dispose ends the notifier's life; it must not be used after this.

dispose ژیانی notifierـەکە کۆتایی پێ دەهێنێت؛ دوای ئەمە نابێت بەکار بهێنرێت.

When increment() runs, _count becomes 1 and every listener on the list is called: A and B. After A is removed, only B hears the second change. Each listener then reads counter.count itself; the notifier sends no data, only the message “I changed”.

کاتێک increment() جێبەجێ دەبێت، _count دەبێت بە 1 و هەموو ناو لیستەکە بانگ دەکرێت: A و B. دوای لابردنی A، تەنها B گۆڕانی دووەم دەبیستێت. پاشان هەر خۆی counter.count دەخوێنێتەوە؛ notifierـەکە هیچ دراوەیەک نانێرێت، تەنها پەیامی «گۆڕام».

Quick checkپشکنینی خێراIn the program above, listener A is removed after the first increment(). What does the second increment() print?لە بەرنامەکەی سەرەوەدا، دوای یەکەم increment()، A لادەبرێت. increment()ـی دووەم چی چاپ دەکات؟

ChangeNotifier (Technique of the Week)Flutter · Englishئینگلیزی · 14K viewsبینین · 710 likesلایک

Flutter's official two-minute clip: a ChangeNotifier keeps the state and calls notifyListeners(); then ListenableBuilder rebuilds the UI.

کلیپی فەرمیی : ChangeNotifier هەڵدەگرێت و ⁦notifyListeners()⁩ بانگ دەکات؛ پاشان ListenableBuilder ڕووکارەکە دووبارە دروست دەکاتەوە.

Whole clip (1:47). Careful: its increment() and decrement() write count++ and count--, but count is only a getter, so that code does not compile; write _count++ and _count-- as on this screen.

هەموو کلیپەکە ⁦(1:47)⁩. ئاگاداربە: ⁦increment()⁩ و ⁦decrement()⁩ تێیدا ⁦count++⁩ و count-- دەنووسن، بەڵام count تەنها ، بۆیە ئەو کۆدە کۆمپایل نابێت؛ وەک ئەم شاشەیە ⁦_count++⁩ و ⁦_count⁩-- بنووسە.

ChangeNotifier: state plus a list of listenersChangeNotifier: دۆخ لەگەڵ لیستێک لە گوێگرەکان

The listener list, picturedلیستی گوێگرەکان بە وێنە

A notice board titled 'Counter extends ChangeNotifier'. On it: a paper 'the state: count: 1', a sign-up list 'listeners' with 1. ListenableBuilder, 2. Consumer<Counter>, 3. CountText (watch), 4. Selector<Counter, …>, and the methods addListener(f), removeListener(f), notifyListeners() and dispose(). On the right, five steps: addListener writes a name on the list, removeListener crosses it out, notifyListeners rings every name, each listener then reads count itself, dispose takes the board down. Notes: built into Flutter (package:flutter/foundation.dart); adding a listener is fast, notifying takes longer the more names there are; setState wakes up one State object, notifyListeners wakes up everyone on the list.تەختەیەکی ئاگاداری بە ناونیشانی 'Counter extends ChangeNotifier'. لەسەری: کاغەزێک «دۆخەکە: count: 1»، لیستێکی ناونووسینی «گوێگرەکان» بە 1. ListenableBuilder، 2. Consumer<Counter>، 3. CountText (watch)، 4. Selector<Counter, …> و میتۆدەکانی addListener(f)، removeListener(f)، notifyListeners() و dispose(). لای ڕاست، پێنج هەنگاو: addListener ناوێک لە لیستەکەدا دەنووسێت، removeListener ڕەشی دەکاتەوە، notifyListeners زەنگ بۆ هەموو ناوێک لێ دەدات، پاشان هەر گوێگرێک خۆی count دەخوێنێتەوە، dispose تەختەکە لادەبات. تێبینییەکان: لەناو خودی Flutterـدایە (package:flutter/foundation.dart)؛ زیادکردنی گوێگرێک خێرایە، ئاگادارکردنەوە تا ناو زیاتر بێت کاتی زیاتر دەبات؛ setState یەک ئۆبجێکتی دۆخ ئاگادار دەکاتەوە، notifyListeners هەموو کەسێکی ناو لیستەکە.
Listeners are subscribers on a notice board: notifyListeners() calls everyone on the list. بەشداربووانی سەر تەختەیەکی ئاگادارین: ⁦notifyListeners()⁩ هەموو کەسێکی ناو لیستەکە بانگ دەکات.CêIT
The department notice boardتەختەی ئاگاداریی بەش

At the department, a sheet on the notice board says: “Write your phone number to hear about exam dates.” When the date changes, the secretary sends a short message to every number on the sheet, and each student then walks to the board and reads the new date. The sheet is the listener list, the message is notifyListeners(), and reading the board is each widget reading the new value.

لە بەشەکەدا، کاغەزێک لەسەر تەختەی ئاگاداری دەڵێت: «ژمارەی مۆبایلەکەت بنووسە بۆ ئەوەی ئاگاداری بەرواری تاقیکردنەوەکان بیت.» کاتێک بەروارەکە دەگۆڕێت، سکرتێرەکە پەیامێک بۆ هەموو ژمارەیەکی سەر کاغەزەکە دەنێرێت و پاشان هەر قوتابییەک دەچێتە لای تەختەکە و بەروارە نوێیەکە دەخوێنێتەوە. کاغەزەکە لیستی ، نامەکە notifyListeners()ـە و خوێندنەوەی تەختەکە ئەوەیە کە هەر نرخە نوێیەکە دەخوێنێتەوە.

Where the analogy stops workingسنووری ئەم نموونەیە

The secretary waits for nothing: the calls are made straight away, one after another, in the order the names were written. And a number that is never crossed out stays on the sheet, which is why widgets remove themselves and the owner calls dispose().

سکرتێرەکە چاوەڕێی هیچ ناکات: بانگکردنەکان یەکسەر و یەک لە دوای یەک دەکرێن، بە هەمان ڕیزبەندیی نووسینی ناوەکان. هەروەها ژمارەیەک کە هەرگیز ڕەش نەکرێتەوە لەسەر کاغەزەکە دەمێنێتەوە، بۆیە خۆیان لادەبەن و خاوەنەکەی بانگی dispose() دەکات.

Quick checkپشکنینی خێراA ChangeNotifier has three listeners. Your method changes a field but forgets notifyListeners(). What do the listeners see?ChangeNotifierـێک سێ هەیە. دەگۆڕێت بەڵام notifyListeners()ـی لەبیر دەچێت. چی دەبینن؟

ChangeNotifier: state plus a list of listenersChangeNotifier: دۆخ لەگەڵ لیستێک لە گوێگرەکان

extends or with: ChangeNotifier is a mixin classextends یان ⁦with: ChangeNotifier⁩ کلاسێکی mixinـە

The lecture says a custom ChangeNotifier is made with a mixin or by extending the class. Both work because Flutter declares it as mixin class ChangeNotifier implements Listenable: a mixin class can be used after extends or after with. Use with when your class already extends something else, because Dart allows only one superclass:

وانەکە دەڵێت ChangeNotifierـی تایبەت بە یان بە extendsکردنی دروست دەکرێت. هەردووکیان کار دەکەن چونکە Flutter بەم شێوەیە دەیناسێنێت: mixin class ChangeNotifier implements Listenable؛ ⁦mixin class⁩ دەتوانرێت دوای extends یان دوای with بەکار بێت. کاتێک پێشتر شتێکی تر extends دەکات with بەکار بهێنە، چونکە Dart تەنها یەک ڕێگە پێ دەدات:

A Player that is also a ChangeNotifierPlayerـێک کە ChangeNotifierـیشەDART
import 'package:flutter/foundation.dart';

class Person {
  Person(this.name);

  final String name;
}

// Player already extends Person, and Dart allows only one superclass,
// so the notifying ability comes in with `with`.
class Player extends Person with ChangeNotifier {
  Player(super.name);

  int _goals = 0;
  int get goals => _goals;

  void score() {
    _goals++;
    notifyListeners();
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 11

The superclass is Person; with mixes in the notifying ability.

Personـە؛ with توانای ئاگادارکردنەوە تێکەڵ دەکات.

Quick checkپشکنینی خێراclass Basket extends Shop must also notify listeners. What do you write?class Basket extends Shop دەبێت ئاگاداری بکاتەوە. چی دەنووسیت؟

notifyListeners() and setState(): alike, not the samenotifyListeners() و setState(): لە یەک دەچن، بەڵام وەک یەک نین

The lecture compares notifyListeners() with setState: both say “the state changed, update the screen”. That is a good first picture, but they differ in who is told:

وانەکە notifyListeners() بە setState بەراورد دەکات: هەردووکیان دەڵێن « گۆڕا، شاشەکە نوێ بکەرەوە». ئەمە وێنەیەکی سەرەتایی باشە، بەڵام جیاوازن لەوەدا کە کێ ئاگادار دەکرێتەوە:

PointخاڵsetState()notifyListeners()
Belongs toهی کێیەA State object (a stateful widget) ()Your ChangeNotifier class (no widget) ChangeNotifierـەکەت ( نییە)
Who rebuildsکێ دووبارە دروست دەبێتەوەOnly that one widgetتەنها ئەو یەک Every listener: ListenableBuilder, Consumer, context.watch, Selector (if its part changed)هەموو : ListenableBuilder، Consumer، context.watch، Selector (ئەگەر بەشەکەی گۆڕابێت)
Where the state lives لە کوێ دەژیInside the widget's Stateلەناو Stateـی Outside any widget, shared by manyلە دەرەوەی هەموو ، هاوبەش لە نێوان زۆرێکدا
Test it without widgets?بەبێ تاقی بکرێتەوە؟NoنەخێرYes: it is a plain Dart classبەڵێ: سادەی Dartـە
  • Call notifyListeners() after the change, once per change (not once per field if several change together).
  • Never call it from inside a build method: listeners would ask for a new build while one is running.
  • Never use a notifier after dispose(); in debug mode Flutter stops with an error.
  • بانگی notifyListeners() دوای گۆڕانەکە بکە، یەک جار بۆ هەر گۆڕانێک (نەک یەک جار بۆ هەر ئەگەر چەندێک پێکەوە بگۆڕێن).
  • هەرگیز لەناو buildـەوە بانگی مەکە: لە کاتێکدا buildـێک کار دەکات داوای buildـێکی نوێ دەکەن.
  • هەرگیز notifierـێک دوای dispose() بەکار مەهێنە؛ لە دۆخی debugـدا Flutter بە دەوەستێت.
Quick checkپشکنینی خێراA ThemeModel (a ChangeNotifier) changes from light to dark. Five widgets in different screens use it. With notifyListeners(), which of them are told?ThemeModelـێک (ChangeNotifierـێک) لە ڕووناکەوە دەگۆڕێت بۆ تاریک. پێنج لە شاشەی جیاوازدا بەکاری دەهێنن. بە notifyListeners()، کامیان ئاگادار دەکرێنەوە؟

ValueNotifier: a ChangeNotifier for one value⁦ValueNotifier: ChangeNotifier⁩ـێک بۆ یەک نرخ

ValueNotifier is a built-in child class of ChangeNotifier (ValueNotifier<T> extends ChangeNotifier). It holds one value in a field called value, and it calls notifyListeners() for you whenever you replace that value with a different one. For a single bool, int or String you need no class of your own. Flutter also has a matching widget, ValueListenableBuilder, that gives its builder the current value:

ValueNotifier ناوەکیی ChangeNotifierـە (ValueNotifier<T> extends ChangeNotifier). یەک نرخ لە بە ناوی value هەڵدەگرێت و هەر کاتێک ئەو نرخە بە نرخێکی جیاواز بگۆڕیتەوە، خۆی بۆ تۆ بانگی notifyListeners() دەکات. بۆ تەنها یەک bool، int یان String پێویستت بە تایبەت نییە. Flutter گونجاویشی هەیە، ValueListenableBuilder، کە نرخی ئێستا دەدات بە builderـەکەی:

Dark mode with only built-in classesدۆخی تاریک تەنها بە کلاسەکانی ناوەکیDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';

// Built into Flutter: one value, and it notifies when the value is replaced.
final darkMode = ValueNotifier<bool>(false);

void main() => runApp(const SettingsApp());

class SettingsApp extends StatelessWidget {
  const SettingsApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<bool>(
      valueListenable: darkMode,
      builder: (context, dark, child) => MaterialApp(
        theme: ThemeData(colorSchemeSeed: Colors.teal),
        darkTheme: ThemeData(
          colorSchemeSeed: Colors.teal,
          brightness: Brightness.dark,
        ),
        themeMode: dark ? ThemeMode.dark : ThemeMode.light,
        home: Scaffold(
          appBar: AppBar(title: const Text('Settings')),
          body: SwitchListTile(
            title: const Text('Dark mode'),
            value: dark,
            onChanged: (on) => darkMode.value = on, // the setter notifies
          ),
        ),
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 4

One bool, wrapped in a ValueNotifier.

یەک bool، لەناو ValueNotifierـێکدا پێچراوە.

Lineدێڕی 14

ValueListenableBuilder listens and passes the value as dark.

ValueListenableBuilder گوێ دەگرێت و نرخەکە وەک dark دەنێرێت.

Lineدێڕی 27

Replacing value notifies; there is no notifyListeners() to write.

گۆڕینەوەی value ئاگادار دەکاتەوە؛ هیچ notifyListeners()ـێک نییە بینووسیت.

ValueNotifier compares with ==. Setting the same value again notifies nobody, and changing an object inside the value (adding to a list in place) is not noticed either, because the value itself was not replaced:

ValueNotifier بە == بەراورد دەکات. دانانەوەی هەمان نرخ کەس ئاگادار ناکاتەوە. هەروەها گۆڕینی لەناو نرخەکەدا (زیادکردن بۆ لە شوێنی خۆیدا) نابینرێت، چونکە خودی نرخەکە نەگۆڕدراوەتەوە:

DART
final names = ValueNotifier<List<String>>(['Aram']);
names.addListener(() => debugPrint('notified: ${names.value}'));
names.value.add('Hana');                 // same list, changed inside
debugPrint('after add in place: ${names.value} (no notification above)');
names.value = [...names.value, 'Soran']; // a new list

final flag = ValueNotifier<bool>(false);
flag.addListener(() => debugPrint('flag notified: ${flag.value}'));
flag.value = false; // same value
flag.value = true;
Quick checkپشکنینی خێراfinal count = ValueNotifier<int>(3); Which line notifies the listeners?final count = ValueNotifier<int>(3); کام دێڕ ئاگاداری دەکاتەوە؟

The Counter, end to endCounter، لە سەرەتاوە تا کۆتایی

Now put the pieces together into a whole app. The lecture's Counter needs four steps: write the model, provide it with ChangeNotifierProvider above the app (so the Counter is available to the whole app), show it with context.watch, and change it with context.read:

ئێستا پارچەکان پێکەوە دابنێ بۆ تەواو. Counterـەکەی وانەکە پێویستی بە چوار هەنگاوە: بنووسە، بە ChangeNotifierProvider لە سەرووی دابینی بکە (بۆ ئەوەی Counter بۆ هەموو بەردەست بێت)، بە context.watch پیشانی بدە و بە context.read بیگۆڕە:

The Counter app with ChangeNotifierProviderئەپی Counter بە ChangeNotifierProviderDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

// 1. The model: built-in ChangeNotifier
class Counter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

// 2. Provide it: ChangeNotifierProvider from the provider package
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(), // made once, disposed for you
      child: const MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: CounterScreen());
  }
}

class CounterScreen extends StatelessWidget {
  const CounterScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: const Center(child: CountText()),
      floatingActionButton: FloatingActionButton(
        // 4. Change it: read, because a button only calls a method
        onPressed: () => context.read<Counter>().increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

class CountText extends StatelessWidget {
  const CountText({super.key});

  @override
  Widget build(BuildContext context) {
    // 3. Show it: watch, so this Text rebuilds after notifyListeners()
    final count = context.watch<Counter>().count;
    return Text('$count', style: Theme.of(context).textTheme.displayLarge);
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5

Step 1: the model, a built-in ChangeNotifier.

هەنگاوی ١: ، ChangeNotifierـێکی ناوەکی.

Lineدێڕی 19

Step 2: one Counter for the whole app, created once and disposed by the provider.

هەنگاوی ٢: یەک Counter بۆ هەموو ، یەک جار دروست دەکرێت و providerـەکە فڕێی دەدات.

Lineدێڕی 44

Step 4: a button only calls a method, so it reads without listening.

هەنگاوی ٤: تەنها بانگ دەکات، بۆیە بەبێ گوێگرتن دەخوێنێتەوە.

Lineدێڕی 57

Step 3: this small widget listens and is the only thing rebuilt.

هەنگاوی ٣: ئەم بچووکە گوێ دەگرێت و تاکە شتە کە دووبارە دروست دەبێتەوە.

Animation in three frames. Left: a phone with the title Counter, a big 0 and a + button. Right: a box 'Counter (ChangeNotifier)' with _count, and below it CounterScreen and CountText (watch). Frame 1: the + button is tapped, and an arrow labelled context.read<Counter>().increment() goes to the model. Frame 2: _count becomes 1 and notifyListeners() sends a dashed arrow to CountText. Frame 3: the phone shows 1 with an orange outline; CountText is marked 'rebuilt: shows 1' and CounterScreen 'not rebuilt'.
Animation in three frames. Left: a phone with the title Counter, a big 0 and a + button. Right: a box 'Counter (ChangeNotifier)' with _count, and below it CounterScreen and CountText (watch). Frame 1: the + button is tapped, and an arrow labelled context.read<Counter>().increment() goes to the model. Frame 2: _count becomes 1 and notifyListeners() sends a dashed arrow to CountText. Frame 3: the phone shows 1 with an orange outline; CountText is marked 'rebuilt: shows 1' and CounterScreen 'not rebuilt'.
From the tap to the new number: read, increment, notifyListeners, watch.لە دەستلێدانەوە بۆ ژمارە نوێیەکە: read، increment، notifyListeners، watch.CêIT

A widget test tapped the button twice and counted the builds:

تاقیکردنەوەیەکی دوو جار دەستی لە دا و buildـەکانی ژمارد:

Real output of a Flutter widget test (flutter test, October 2026)دەرخستەی ڕاستەقینەی تاقیکردنەوەیەکی ویجێتی ⁦Flutter (flutter test⁩، ئۆکتۆبەری ⁦2026⁩)TEXT
start:       1 "0", builds {CounterScreen: 1, CountText: 1}
after tap:   1 "1", builds {CounterScreen: 1, CountText: 2}
after 2 taps: 1 "2", builds {CounterScreen: 1, CountText: 3}
Quick checkپشکنینی خێراIn the Counter app, why does the button use context.read and not context.watch?لە Counterـدا، بۆچی context.read بەکار دەهێنێت نەک context.watch؟

Flutter Provider EASY TutorialFlutter Mapp · Englishئینگلیزی · 98K viewsبینین · 3.3K likesلایک · Partبەشی 4:37–9:43 (ofلە 9:43)

The difference between context.watch and context.read in Provider, shown on a small example.

جیاوازیی نێوان context.watch و context.read لە Providerـدا، بە نموونەیەکی بچووک.

Plays 'Step 2: Practical exercise' (4:37-9:43): a CounterProvider extends ChangeNotifier with increment and decrement, is added as a second ChangeNotifierProvider in MultiProvider, shown with context.watch and changed with context.read. Its value field is public; the lesson keeps state private.

بەشی Step 2: Practical exercise پەخش دەکات (⁦4:37⁩ تا ⁦9:43⁩): CounterProviderـێک (extends ChangeNotifier) بە increment و decrement، وەک دووەم ChangeNotifierProvider لە MultiProviderـدا زیاد دەکرێت، بە context.watch پیشان دەدرێت و بە context.read دەگۆڕدرێت. value تێیدا گشتییە (public)؛ وانەکە بە تایبەتی (private) دەهێڵێتەوە.

The Counter, end to endCounter، لە سەرەتاوە تا کۆتایی

The same Counter without a packageهەمان Counter بەبێ کتێبخانە

You met the built-in way before: Flutter's official learning pathway builds this kind of app with no package, using the same Counter class, owned by a stateful screen, and a ListenableBuilder around the number. Compare the two versions side by side:

ڕێگا ناوەکییەکە پێشتر ناسێنرا: ڕێڕەوی فێربوونی فەرمیی Flutter ئەم جۆرە بەبێ هیچ دروست دەکات، بە هەمان Counter، کە شاشەیەکی دۆخدار خاوەنیەتی و ListenableBuilderـێک بە دەوری ژمارەکەدا. هەردوو وەشانەکە لە تەنیشت یەک بەراورد بکە:

Counter with ChangeNotifier and ListenableBuilder onlyCounter تەنها بە ChangeNotifier و ListenableBuilderDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() => runApp(const MaterialApp(home: CounterScreen()));

class CounterScreen extends StatefulWidget {
  const CounterScreen({super.key});

  @override
  State<CounterScreen> createState() => _CounterScreenState();
}

class _CounterScreenState extends State<CounterScreen> {
  final _counter = Counter(); // this screen owns the model

  @override
  void dispose() {
    _counter.dispose(); // so it must dispose it too
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: Center(
        child: ListenableBuilder(
          listenable: _counter,
          builder: (context, child) => Text(
            '${_counter.count}',
            style: Theme.of(context).textTheme.displayLarge,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _counter.increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 23

The screen creates the model…

شاشەکە دروست دەکات…

Lineدێڕی 27

…so it must dispose it. ChangeNotifierProvider did this for us.

…بۆیە دەبێت فڕێیشی بدات. ChangeNotifierProvider ئەمەی بۆ ئێمە دەکرد.

Lineدێڕی 37

ListenableBuilder plays the role of context.watch.

ListenableBuilder ڕۆڵی context.watch دەبینێت.

StepهەنگاوBuilt in onlyتەنها ناوەکیWith providerبە provider
Modelclass Counter extends ChangeNotifierclass Counter extends ChangeNotifier
Create and disposeدروستکردن و فڕێدانYou, in a State objectخۆت، لەناو ChangeNotifierProvider(create: …)
Reach it from a widgetگەیشتن پێی لە Pass it through constructorsبە ناو بینێرەcontext.watch / context.read
Rebuild the numberدووبارە دروستکردنەوەی ژمارەکەListenableBuildercontext.watch, Consumer

Both are correct. The built-in way is enough when the model is used in one screen and its children; provider pays off when many screens need the same model.

هەردووکیان دروستن. ڕێگا ناوەکییەکە بەسە کاتێک لە یەک شاشە و منداڵەکانیدا بەکار دێت؛ provider کاتێک سوودی هەیە کە زۆر شاشە پێویستیان بە هەمان بێت.

Quick checkپشکنینی خێراIn the built-in version, who must call _counter.dispose()?لە وەشانە ناوەکییەکەدا، کێ دەبێت بانگی _counter.dispose() بکات؟

What ChangeNotifierProvider addsChangeNotifierProvider چی زیاد دەکات

A ChangeNotifierProvider is a widget that holds a ChangeNotifier and provides it to the widgets below it. Compared with a plain Provider, it does three extra jobs:

ChangeNotifierProvider کە ChangeNotifierـێک هەڵدەگرێت و دابینی دەکات بۆ ئەو لە خوارییەوەن. بە بەراورد لەگەڵ Providerـی سادە، سێ کاری زیاتر دەکات:

  • It listens to the notifier. When notifyListeners() runs, every widget that used watch, Provider.of, a Consumer or a Selector on it is rebuilt.
  • It creates the notifier lazily, the first time a widget asks for it, with your create function.
  • It disposes the notifier when the provider leaves the tree, so its listener list does not leak.
  • گوێ لە notifierـەکە دەگرێت. کاتێک notifyListeners() جێبەجێ دەبێت، هەموو ئەو watch، Provider.of، Consumer یان Selectorـیان لەسەری بەکار هێناوە دووبارە دروست دەبنەوە.
  • notifierـەکە بە دواخراوی دروست دەکات، یەکەم جار کە داوای دەکات، بە createـی تۆ.
  • notifierـەکە فڕێ دەدات کاتێک providerـەکە لە دەردەچێت، بۆیە لیستی بە بێسوودی نامێنێتەوە.

A plain Provider does none of the listening. Provider even stops you in debug mode if you give it a ChangeNotifier. A widget test that tried Provider<Counter>(create: (_) => Counter(), …) got:

Providerـی سادە هیچ گوێگرتنێک ناکات. Provider تەنانەت لە دۆخی debugـدا ڕێگەت لێ دەگرێت ئەگەر ChangeNotifierـێکی پێ بدەیت. تاقیکردنەوەیەکی کە Provider<Counter>(create: (_) => Counter(), …)ـی تاقی کردەوە ئەمەی وەرگرت:

Real output of a Flutter widget test (flutter test, October 2026)دەرخستەی ڕاستەقینەی تاقیکردنەوەیەکی ویجێتی ⁦Flutter (flutter test⁩، ئۆکتۆبەری ⁦2026⁩)TEXT
Tried to use Provider with a subtype of Listenable/Stream (Counter).

This is likely a mistake, as Provider will not automatically update dependents
Quick checkپشکنینی خێراYou provide a ChangeNotifier with a plain Provider and turn off the debug check. What happens when it calls notifyListeners()?ChangeNotifierـێک بە Providerـی سادە دابین دەکەیت و پشکنینی debug دەکوژێنیتەوە. کاتێک بانگی notifyListeners() دەکات چی ڕوو دەدات؟

The lecture's example: a dark-mode checkboxنموونەکەی وانەکە: چێک‌بۆکسی دۆخی تاریک

The lecture's ChangeNotifierProvider example adds dark mode to the to-do app. A ChangeNotifierProvider holding a ValueNotifier<bool> sits above MaterialApp. MaterialApp reads it to choose its theme. A checkbox in the app's drawer changes the value, the ValueNotifier calls notifyListeners(), and MaterialApp rebuilds in the new colours:

نموونەی ChangeNotifierProviderـی وانەکە زیاد دەکات بۆ ئەرکەکان. ChangeNotifierProviderـێک کە ValueNotifier<bool>ـێکی هەڵگرتووە لە سەرووی MaterialAppـەوەیە. MaterialApp دەیخوێنێتەوە بۆ هەڵبژاردنی . لە نرخەکە دەگۆڕێت، ValueNotifier بانگی notifyListeners() دەکات و MaterialApp بە ڕەنگە نوێیەکان دووبارە دروست دەبێتەوە:

A widget tree. At the top ChangeNotifierProvider with a ValueNotifier<bool> attached. Below it MaterialApp (blue), which listens (watch) and sets themeMode. MaterialApp has two branches: TodoListScreen, Scaffold, ListView and ListTiles; and Drawer with a CheckboxListTile (blue). A solid arrow from the checkbox to the ValueNotifier is labelled '.value = true, sets the value'; a dashed arrow from the ValueNotifier to MaterialApp is labelled notifyListeners(). A small phone shows the drawer with Settings and a ticked checkbox 'Turn on dark theme colour'.داری ویجێتەکان: لە سەرەوە ChangeNotifierProvider لەگەڵ ValueNotifier<bool>ـێک. لە خوارییەوە MaterialApp (شین)، کە گوێ دەگرێت (watch) و themeMode دیاری دەکات. MaterialApp دوو لقی هەیە: TodoListScreen، Scaffold، ListView و ListTileـەکان؛ و Drawer لەگەڵ CheckboxListTileـێک (شین). تیرێکی تەواو لە چێک‌بۆکسەکەوە بۆ ValueNotifier نووسراوی «.value = true، نرخەکە دادەنێت»ـی لەسەرە؛ تیرێکی پچڕپچڕ لە ValueNotifierـەوە بۆ MaterialApp نووسراوی notifyListeners()ـی لەسەرە. مۆبایلێکی بچووک لیستی لاتەنیشتەکە پیشان دەدات بە Settings و چێک‌بۆکسێکی نیشانکراو «Turn on dark theme colour».
The lecture's slide redrawn: the checkbox changes the value; the notifier tells MaterialApp.سلایدەکەی وانەکە بە شێوەیەکی نوێ: نرخەکە دەگۆڕێت؛ notifierـەکە MaterialApp ئاگادار دەکاتەوە.CêIT
Dark mode through a ChangeNotifierProviderدۆخی تاریک لە ڕێگەی ChangeNotifierProviderـەوەDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    // The lecture's tree: a ValueNotifier<bool> (a ChangeNotifier) above MaterialApp
    ChangeNotifierProvider(
      create: (_) => ValueNotifier<bool>(false), // dark mode starts off
      child: const TodoApp(),
    ),
  );
}

class TodoApp extends StatelessWidget {
  const TodoApp({super.key});

  @override
  Widget build(BuildContext context) {
    // MaterialApp listens: the theme changes when the checkbox changes
    final dark = context.watch<ValueNotifier<bool>>().value;
    return MaterialApp(
      theme: ThemeData(colorSchemeSeed: Colors.green),
      darkTheme: ThemeData(
        colorSchemeSeed: Colors.green,
        brightness: Brightness.dark,
      ),
      themeMode: dark ? ThemeMode.dark : ThemeMode.light,
      home: const TodoListScreen(),
    );
  }
}

class TodoListScreen extends StatelessWidget {
  const TodoListScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('My to-dos')),
      drawer: const SettingsDrawer(),
      body: ListView(
        children: const [
          ListTile(title: Text('Read chapter 8')),
          ListTile(title: Text('Practise Flutter layouts')),
          ListTile(title: Text('Call Hana about the project')),
        ],
      ),
    );
  }
}

class SettingsDrawer extends StatelessWidget {
  const SettingsDrawer({super.key});

  @override
  Widget build(BuildContext context) {
    final dark = context.watch<ValueNotifier<bool>>().value;
    return Drawer(
      child: ListView(
        children: [
          const DrawerHeader(child: Text('Settings')),
          CheckboxListTile(
            title: const Text('Turn on dark theme colour'),
            value: dark,
            // setting .value calls notifyListeners() inside ValueNotifier
            onChanged: (on) =>
                context.read<ValueNotifier<bool>>().value = on ?? false,
          ),
        ],
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 8

A ValueNotifier is a ChangeNotifier, so ChangeNotifierProvider can hold it.

⁦ValueNotifier ChangeNotifier⁩ـە، بۆیە ChangeNotifierProvider دەتوانێت هەڵیبگرێت.

Lineدێڕی 20

TodoApp, the small widget around MaterialApp, listens; the theme follows the value.

TodoApp، بچووکەکەی دەوری MaterialApp، گوێ دەگرێت؛ شوێن نرخەکە دەکەوێت.

Lineدێڕی 67

The checkbox replaces the value; ValueNotifier notifies by itself.

نرخەکە دەگۆڕێتەوە؛ ValueNotifier خۆی ئاگادار دەکاتەوە.

Quick checkپشکنینی خێراIn the dark-mode example, what makes MaterialApp switch to the dark theme?لە نموونەی ، چی وا دەکات MaterialApp بگۆڕێت بۆ تاریک؟

🎓📱 FULL Flutter Masterclass: Beginner to ProMitch Koko · Englishئینگلیزی · 562K viewsبینین · 17K likesلایک · Partبەشی 2:24:36–2:32:27 (ofلە 9:36:17)

Build a light/dark switch: a ThemeProvider with ChangeNotifier, a ChangeNotifierProvider above MaterialApp, and a tap that toggles the theme.

گۆڕین لە نێوان ڕووناک و تاریکدا: ThemeProviderـێک بە ChangeNotifier، ChangeNotifierProvider لە سەرووی MaterialAppـەوە و دەستلێدانێک کە دەگۆڕێت.

Plays only the chapter 'Themes: light vs dark mode' (about 8 minutes). The video swaps two ThemeData objects with a tap instead of a ValueNotifier<bool> and a checkbox.

تەنها بەشی Themes: light vs dark mode پەخش دەکات (نزیکەی 8 خولەک). ڤیدیۆکە بە دەستلێدانێک دوو ThemeData دەگۆڕێتەوە، لە جیاتی ValueNotifier<bool> و .

Several providers: nested or MultiProviderچەند provider: لەناو یەکدا یان MultiProvider

Real apps need several providers: a user, a cart, settings. The first way is to nest them: each provider is the child of the one above, and the app is at the bottom. It works, but every new provider adds one more level of brackets, and the code gets messy:

ڕاستەقینە پێویستیان بە چەند providerـێکە: ، سەبەتەیەک، ڕێکخستنەکان. ڕێگای یەکەم ئەوەیە کە لەناو یەکدا دایان بنێیت: هەر providerـێک منداڵی ئەوەی سەرووی خۆیەتی و لە خوارەوەیە. کار دەکات، بەڵام هەر providerـێکی نوێ ئاستێکی تری کەوانە زیاد دەکات و کۆدەکە تێکەڵ دەبێت:

Nested providersproviderـی لەناو یەکداDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
// Nested: each provider is the child of the one above it.
Widget nestedProviders() {
  return Provider<AppInfo>(
    create: (_) => const AppInfo('Class counter'),
    child: ChangeNotifierProvider<Counter>(
      create: (_) => Counter(),
      child: ChangeNotifierProvider<ValueNotifier<bool>>(
        create: (_) => ValueNotifier<bool>(false),
        child: const MyApp(),
      ),
    ),
  );
}

MultiProvider holds a list of providers in one widget. The same three providers become flat and easy to read:

MultiProvider لیستێک لە providerـەکان لە یەک هەڵدەگرێت. هەمان سێ provider تەخت و ئاسانی خوێندنەوە دەبن:

The same providers in a MultiProviderهەمان providerـەکان لە MultiProviderـێکداDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
// MultiProvider: the same three providers as one flat list.
Widget multiProvider() {
  return MultiProvider(
    providers: [
      Provider<AppInfo>(create: (_) => const AppInfo('Class counter')),
      ChangeNotifierProvider<Counter>(create: (_) => Counter()),
      ChangeNotifierProvider<ValueNotifier<bool>>(
        create: (_) => ValueNotifier<bool>(false),
      ),
    ],
    child: const MyApp(),
  );
}
Animation in three frames. Frame 1: three coloured boxes inside each other, Provider<AppInfo>, ChangeNotifierProvider<Counter> and ChangeNotifierProvider<ValueNotifier<bool>>, each with 'child:', and MyApp() in the middle: 'Nested: each provider is the child of the one above'. Frame 2: the three boxes are taken out of each other and lined up, with child: MyApp() below. Frame 3: one MultiProvider( providers: [ the three providers ], child: MyApp() ); under it, the tree from a real widget test: Scope<AppInfo?> > Scope<Counter?> > Scope<ValueNotifier<bool>?>, 'same tree, easier code'.
Animation in three frames. Frame 1: three coloured boxes inside each other, Provider<AppInfo>, ChangeNotifierProvider<Counter> and ChangeNotifierProvider<ValueNotifier<bool>>, each with 'child:', and MyApp() in the middle: 'Nested: each provider is the child of the one above'. Frame 2: the three boxes are taken out of each other and lined up, with child: MyApp() below. Frame 3: one MultiProvider( providers: [ the three providers ], child: MyApp() ); under it, the tree from a real widget test: Scope<AppInfo?> > Scope<Counter?> > Scope<ValueNotifier<bool>?>, 'same tree, easier code'.
MultiProvider changes how the code looks, not how the tree works.MultiProvider شێوەی کۆدەکە دەگۆڕێت، نەک چۆنیەتی کارکردنی .CêIT
Quick checkپشکنینی خێراWhat do you give to MultiProvider's providers: parameter?چی دەدەیت بە providers:ـی MultiProvider؟

Several providers: nested or MultiProviderچەند provider: لەناو یەکدا یان MultiProvider

Inside, it is the same treeلە ناوەوە، هەمان داری ویجێتەکانە

Is a MultiProvider really the same? A widget test built both versions and listed the provider scopes above MyApp: the hidden InheritedWidgets that hold the values (the test prints the class _InheritedProviderScope as Scope):

ئایا MultiProvider بە ڕاستی هەمانە؟ تاقیکردنەوەیەکی هەردوو وەشانەکەی دروست کرد و scopeـەکانی provider لە سەرووی MyAppـەوە ژمارد: ئەو InheritedWidgetـە شاراوانەی نرخەکان هەڵدەگرن (تاقیکردنەوەکە _InheritedProviderScope بە کورتی وەک Scope چاپ دەکات):

Real output of a Flutter widget test (flutter test, October 2026)دەرخستەی ڕاستەقینەی تاقیکردنەوەیەکی ویجێتی ⁦Flutter (flutter test⁩، ئۆکتۆبەری ⁦2026⁩)TEXT
nested:        Scope<AppInfo?> > Scope<Counter?> > Scope<ValueNotifier<bool>?>
MultiProvider: Scope<AppInfo?> > Scope<Counter?> > Scope<ValueNotifier<bool>?>
Quick checkپشکنینی خێراWhat changes when you replace three nested providers with one MultiProvider?کاتێک سێ providerـی لەناو یەکدا بە یەک MultiProvider دەگۆڕیتەوە چی دەگۆڕێت؟

Common mistakesهەڵە باوەکان

MistakeهەڵەWhat happensچی ڕوو دەداتFixچاکردنەوە
Changing a field and forgetting notifyListeners()گۆڕینی و لەبیرچوونی notifyListeners()The screen keeps the old valueشاشەکە نرخە کۆنەکە دەهێڵێتەوەCall it at the end of every method that changes stateلە کۆتایی هەموو کە دەگۆڕێت بانگی بکە
A ChangeNotifier in a plain ProviderChangeNotifierـێک لەناو Providerـی سادەداDebug error; without it, nothing rebuilds debug؛ بەبێ ئەو، هیچ شتێک دووبارە دروست نابێتەوەChangeNotifierProvider
Public fields (int count = 0;) changed from widgets گشتی (int count = 0;) کە لە دەگۆڕدرێنChanges without notification; state spread everywhereگۆڕان بەبێ ئاگادارکردنەوە؛ لە هەموو شوێنێکدا بڵاوەPrivate field + getter + methods that notify تایبەت + + کە ئاگادار دەکەنەوە
names.value.add('Hana') on a ValueNotifiernames.value.add('Hana') لەسەر ValueNotifierـێکNo notification: the list object is the sameئاگادارکردنەوە نییە: هەمانەnames.value = [...names.value, 'Hana']
Creating a notifier in a State and never disposing itدروستکردنی notifierـێک لە Stateـێکدا و هەرگیز فڕێنەدانیIts listener list stays in memoryلیستی لە دەمێنێتەوەdispose() it in the State's dispose, or let ChangeNotifierProvider own itلە disposeـی Stateـەکەدا dispose()ـی بکە، یان با ChangeNotifierProvider خاوەنی بێت
Two providers of the same type in one MultiProviderدوو providerـی هەمان جۆر لە یەک MultiProviderـداWidgets see only the inner one (the later in the list) تەنها ناوەکییەکە دەبینن (ئەوەی دواتر لە لیستەکەدایە)One provider per type; wrap values in different classesیەک provider بۆ هەر جۆرێک؛ نرخەکان لە جیاوازدا بپێچە
Quick checkپشکنینی خێراYour CartModel has List<Item> items = []; (public) and a widget does cart.items.add(tea). What is wrong?CartModelـەکەت List<Item> items = [];ـی (گشتی) هەیە و cart.items.add(tea) دەکات. چی هەڵەیە؟

Exercisesڕاهێنانەکان

Exerciseڕاهێنان1. A like button١. دوگمەی پەسەندکردن

Write a LikeCounter ChangeNotifier with a likes count and a like() method. Provide it above the app, show “… likes” in the middle of the screen, and add a heart button that likes the post.

ChangeNotifierـێکی LikeCounter بنووسە بە ژمارەی likes و like(). لە سەرووی دابینی بکە، «… likes» لە ناوەڕاستی شاشەکەدا پیشان بدە و دڵ زیاد بکە کە پۆستەکە پەسەند دەکات.

Hintئاماژە

Follow the four Counter steps: model, provide, show with watch, change with read.

شوێن چوار هەنگاوەکەی Counter بکەوە: ، دابینکردن، پیشاندان بە watch، گۆڕین بە read.

Show answerوەڵامەکە پیشان بدە
One possible answerوەڵامێکی گونجاوDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class LikeCounter extends ChangeNotifier {
  int _likes = 0;
  int get likes => _likes;

  void like() {
    _likes++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => LikeCounter(),
      child: const MaterialApp(home: PostScreen()),
    ),
  );
}

class PostScreen extends StatelessWidget {
  const PostScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Photo of the day')),
      body: const Center(child: LikesText()),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.read<LikeCounter>().like(),
        child: const Icon(Icons.favorite),
      ),
    );
  }
}

class LikesText extends StatelessWidget {
  const LikesText({super.key});

  @override
  Widget build(BuildContext context) {
    final likes = context.watch<LikeCounter>().likes;
    return Text('$likes likes');
  }
}

LikesText watches, so only it rebuilds; the button reads, because it only calls like().

LikesText watch دەکات، بۆیە تەنها ئەو دووبارە دروست دەبێتەوە؛ read دەکات، چونکە تەنها بانگی like() دەکات.

Exerciseڕاهێنان2. Predict the output٢. دەرخستەکە پێشبینی بکە

A Counter starts at 0. Listener A prints A sees … and listener B prints B sees …, both added in that order. The program calls increment(), removes A, then calls increment() again. Write the output.

Counterـێک لە 0ـەوە دەست پێ دەکات. A A sees … چاپ دەکات و B B sees …، هەردووکیان بەو ڕیزبەندییە زیاد کراون. بەرنامەکە بانگی increment() دەکات، A لادەبات، پاشان دووبارە بانگی increment() دەکات. دەرخستەکە بنووسە.

Show answerوەڵامەکە پیشان بدە
TEXT
A sees 1
B sees 1
B sees 2

This is the real output of the program at the start of this lesson. Listeners are called in the order they were added; after removeListener, A is no longer on the list.

ئەمە دەرخستەی ڕاستەقینەی بەرنامەکەی سەرەتای ئەم وانەیەیە. بە هەمان ڕیزبەندیی زیادکردنیان بانگ دەکرێن؛ دوای removeListener، A چیتر لە لیستەکەدا نییە.

Exerciseڕاهێنان3. Flatten the providers٣. providerـەکان تەخت بکە

Rewrite the nested providers of this lesson (AppInfo, Counter, dark mode) as one MultiProvider. Does the order in the list matter?

providerـە لەناو یەکەکانی ئەم وانەیە (AppInfo، Counter، ) وەک یەک MultiProvider بنووسەرەوە. ئایا ڕیزبەندی لە لیستەکەدا گرنگە؟

Show answerوەڵامەکە پیشان بدە
DARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
// MultiProvider: the same three providers as one flat list.
Widget multiProvider() {
  return MultiProvider(
    providers: [
      Provider<AppInfo>(create: (_) => const AppInfo('Class counter')),
      ChangeNotifierProvider<Counter>(create: (_) => Counter()),
      ChangeNotifierProvider<ValueNotifier<bool>>(
        create: (_) => ValueNotifier<bool>(false),
      ),
    ],
    child: const MyApp(),
  );
}

The list order is the nesting order: the first provider is the outermost. That matters only when a provider's create reads another provider; then the one it reads must come earlier in the list.

ڕیزبەندیی لیستەکە هەمان ڕیزبەندیی لەناو یەکدابوونە: یەکەم provider دەرەوەترینیانە. ئەمە تەنها کاتێک گرنگە کە createـی providerـێک providerـێکی تر بخوێنێتەوە؛ ئەو کاتە ئەوەی دەیخوێنێتەوە دەبێت پێشتر لە لیستەکەدا بێت.

Exerciseڕاهێنان4. Why does dark mode not switch?٤. بۆچی دۆخی تاریک ناگۆڕێت؟

Someone wrote Provider<ValueNotifier<bool>>(create: (_) => ValueNotifier(false), child: const TodoApp()). Explain what goes wrong and fix it.

کەسێک ئەمەی نووسیوە: Provider<ValueNotifier<bool>>(create: (_) => ValueNotifier(false), child: const TodoApp()). ڕوونی بکەرەوە چی هەڵە دەبێت و چاکی بکە.

Show answerوەڵامەکە پیشان بدە

A plain Provider does not listen to the notifier, so TodoApp would never rebuild; in debug mode provider stops at once with “Tried to use Provider with a subtype of Listenable/Stream”. Use ChangeNotifierProvider(create: (_) => ValueNotifier<bool>(false), child: const TodoApp()).

Providerـی سادە گوێ لە notifierـەکە ناگرێت، بۆیە TodoApp هەرگیز دووبارە دروست نابێتەوە؛ لە دۆخی debugـدا provider یەکسەر بە «⁦Tried to use Provider with a subtype of Listenable/Stream⁩» دەوەستێت. ChangeNotifierProvider(create: (_) => ValueNotifier<bool>(false), child: const TodoApp()) بەکار بهێنە.

Summaryپوختە

  • ChangeNotifier and ValueNotifier are built into Flutter; ChangeNotifierProvider and MultiProvider come from the provider package.
  • Your model extends (or mixes in) ChangeNotifier, keeps the state in private fields and calls notifyListeners() after each change.
  • setState updates one State object; notifyListeners calls every listener, wherever it is.
  • ValueNotifier holds one value and notifies when it is replaced by a different one; ValueListenableBuilder shows it.
  • ChangeNotifierProvider creates, listens to and disposes a notifier: provide → watch to show → read to change.
  • MultiProvider lists several providers; the tree inside is the same as nesting them, only the code is cleaner.
  • ChangeNotifier و ValueNotifier لەناو خودی Flutterـدان؛ ChangeNotifierProvider و MultiProvider لە providerـەوە دێن.
  • ⁦ChangeNotifier extends⁩ (یان تێکەڵ) دەکات، لە تایبەتدا هەڵدەگرێت و دوای هەر گۆڕانێک بانگی notifyListeners() دەکات.
  • setState یەک نوێ دەکاتەوە؛ notifyListeners هەموو بانگ دەکات، لە هەر شوێنێک بێت.
  • ValueNotifier یەک نرخ هەڵدەگرێت و کاتێک بە نرخێکی جیاواز دەگۆڕدرێتەوە ئاگادار دەکاتەوە؛ ValueListenableBuilder پیشانی دەدات.
  • ⁦ChangeNotifierProvider notifier⁩ـێک دروست دەکات، گوێی لێ دەگرێت و فڕێی دەدات: دابینکردن ← watch بۆ پیشاندان ← read بۆ گۆڕین.
  • MultiProvider چەند provider ڕیز دەکات؛ ناوەوە وەک لەناو یەکدانانیانە، تەنها کۆدەکە پاکترە.

Lesson complete!وانەکە تەواو بوو!

Well done. Review the key terms, then go on to the next lesson.دەستخۆش. سەیرێکی زاراوە سەرەکییەکان بکەرەوە، پاشان بڕۆ بۆ وانەی داهاتوو.

Key termsزاراوە سەرەکییەکان

Next lessonوانەی داهاتوو

FutureProvider and StreamProviderFutureProvider و StreamProvider

References and further readingسەرچاوەکان و خوێندنەوەی زیاتر

This lecture is a first edition. The Kurdish text and terminology are waiting for review by Kurdish IT teachers.ئەم وانەیە چاپی یەکەمە. دەقە کوردییەکە و زاراوەکانی چاوەڕێی پێداچوونەوەی مامۆستایانی کوردی بواری IT ن.