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

ProxyProvider and ChangeNotifierProxyProviderProxyProvider و ChangeNotifierProxyProvider

Values that depend on other providers: the lecture's to-do count badge with a ProxyProvider (no state, an update callback that runs whenever its sources change), ProxyProvider2 for several sources, the course's AuthService examples, ChangeNotifierProxyProvider with the corrected update pattern, and a table of when to use what.

ئەو نرخانەی پشت بە providerـەکانی تر دەبەستن: نیشانەی ژمارەی ئەرکەکانی وانەکە بە ProxyProvider (بەبێ ، update کە هەر کاتێک سەرچاوەکانی بگۆڕێن جێبەجێ دەبێت)، ⁦ProxyProvider2⁩ بۆ چەند سەرچاوەیەک، نموونەکانی AuthServiceـی کۆرسەکە، ChangeNotifierProxyProvider بە شێوازی چاککراوی update و خشتەیەک بۆ ئەوەی کەی کامیان بەکار بهێنرێت.

20 min read19 خولەک خوێندنەوە · 14 stepsهەنگاو

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

  • Explain what a ProxyProvider is: no state of its own, a value built in update from other providers.
  • Build the lecture's to-do count badge and predict when update runs and when widgets rebuild.
  • Use ChangeNotifierProxyProvider correctly: create once, pass new values in update.
  • Find and fix the update bug in the course's example.
  • Choose between ChangeNotifier, ChangeNotifierProvider, ProxyProvider and ChangeNotifierProxyProvider.
  • ڕوون بکەیتەوە ProxyProvider چییە: بەبێ خۆی، نرخێک کە لە updateـدا لە providerـەکانی ترەوە دروست دەکرێت.
  • نیشانەی ژمارەی ئەرکەکانی وانەکە دروست بکەیت و پێشبینی بکەیت update کەی جێبەجێ دەبێت و کەی دووبارە دروست دەکرێنەوە.
  • ChangeNotifierProxyProvider بە دروستی بەکار بهێنیت: یەک جار دروستی بکەیت، لە updateـدا نرخی نوێ بدەیت.
  • هەڵەی update لە نموونەکەی کۆرسەکەدا بدۆزیتەوە و چاکی بکەیت.
  • لە نێوان ChangeNotifier، ChangeNotifierProvider، ProxyProvider و ChangeNotifierProxyProviderـدا هەڵبژێریت.

A value that depends on another providerنرخێک کە پشت بە providerـێکی تر دەبەستێت

In the lecture's to-do app, the app bar shows a small red badge with the number of to-dos. The list already comes from a provider (FutureProvider). The badge needs a second value, the count, and that value is not new data: it is calculated from the list. When the list changes, the count must change with it.

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

Left: a phone showing 'My To-Do List' with a red badge 5 and five to-dos. Middle: a service locator box with TodoDataService and getTodoList(): Future<List<Todo>>, with an arrow into a FutureProvider. Right: a MultiProvider box holding FutureProvider (List<Todo>) and ProxyProvider (int); an arrow labelled 'update: todos.length' goes from the FutureProvider to the ProxyProvider. Below: MaterialApp, TodoListScreen, Scaffold, ListView, ListTile. Two dashed arrows go from TodoListScreen up to the providers: 'get the to-do list' and 'get the count'.لای چەپ: مۆبایلێک کە 'My To-Do List' پیشان دەدات لەگەڵ نیشانەیەکی سووری ٥ و پێنج ئەرک. ناوەڕاست: سندووقی دۆزەرەوەی خزمەتگوزاری لەگەڵ TodoDataService و getTodoList(): Future<List<Todo>>، تیرێک دەچێتە ناو FutureProvider. لای ڕاست: سندووقی MultiProvider کە FutureProvider (List<Todo>) و ProxyProvider (int) لەخۆ دەگرێت؛ تیرێک بە ناوی 'update: todos.length' لە FutureProviderـەوە دەچێت بۆ ProxyProvider. لە خوارەوە: MaterialApp، TodoListScreen، Scaffold، ListView، ListTile. دوو تیری پچڕپچڕ لە TodoListScreenـەوە بەرەو providerـەکان دەچن: 'لیستی ئەرکەکان وەردەگرێت' و 'ژمارەکە وەردەگرێت'.
The lecture's slide, redrawn: the count is a second provider that is built from the first one.سلایدەکەی وانەکە، دووبارە کێشراوە: ژمارەکە providerـێکی دووەمە کە لە یەکەمەوە دروست دەکرێت.CêIT

The slide's service locator holds a data service with one method, getTodoList(): Future<List<Todo>>. In code it is a small file with the Todo model and that service. The service answers after one second, like a server would. The badge examples of this lesson share this file:

سلایدەکە دراوە هەڵدەگرێت کە یەک هەیە: getTodoList(): Future<List<Todo>>. لە کۆددا ئەمە بچووکە کە Todo و ئەو لەخۆ دەگرێت. دوای چرکەیەک وەڵام دەداتەوە، وەک . نموونەکانی نیشانەی ئەم وانەیە ئەم بەکار دەهێنن:

The to-do model and the data serviceمۆدێلی ئەرک و خزمەتگوزاریی دراوەtodo_data.dartDART
// The lecture's to-do model and data service, shared by this lesson's examples.
class Todo {
  const Todo({required this.id, required this.title, this.completed = false});

  final int id;
  final String title;
  final bool completed;
}

class TodoDataService {
  // One answer, later: the list arrives after a short wait, like from a server.
  Future<List<Todo>> getTodoList() async {
    await Future<void>.delayed(const Duration(seconds: 1));
    return const [
      Todo(id: 1, title: 'Prepare proposal for the new project'),
      Todo(id: 2, title: 'Replace light bulb'),
      Todo(id: 3, title: 'Buy Flutter eBook'),
      Todo(id: 4, title: 'Subscribe to fibre-optic internet'),
      Todo(id: 5, title: 'Set up online meeting room', completed: true),
    ];
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 12

The slide's getTodoList(): Future<List<Todo>>: a list that arrives later.

getTodoList(): Future<List<Todo>>ـی سلایدەکە: لیستێک کە دواتر دەگات.

The slide's note says it plainly: the count is to be implemented with a ProxyProvider, and a ProxyProvider depends on other providers. This lesson teaches that provider, its big brother ChangeNotifierProxyProvider, and when to use each one.

تێبینیی سلایدەکە بە ڕوونی دەیڵێت: ژمارەکە دەبێت بە ProxyProvider دروست بکرێت و ProxyProvider پشت بە providerـەکانی تر دەبەستێت. ئەم وانەیە ئەو providerـە فێر دەکات، لەگەڵ براگەورەکەی ChangeNotifierProxyProvider و کەی هەریەکەیان بەکار بهێنرێت.

Quick checkپشکنینی خێراWhy is the to-do count not simply stored as a second, separate piece of state?بۆچی ژمارەی ئەرکەکان وەک دووەمی جیاواز هەڵناگیرێت؟

ProxyProvider: a provider with no state of its own⁦ProxyProvider: provider⁩ـێک بەبێ دۆخێکی خۆی

The lecture describes a ProxyProvider in three points. Each one is worth understanding:

وانەکە ProxyProvider لە سێ خاڵدا وەسف دەکات. هەریەکەیان شایەنی تێگەیشتنە:

  • It holds no state. It never stores a value that you change later. Everything it gives is worked out again from its sources.
  • Its value comes from its update callback. You write a function update: (context, source, previous) => …; whatever it returns is the provided value. previous is the value it returned last time (null the first time).
  • Its source is other providers. ProxyProvider<A, R> reads the nearest A above it, and runs update again whenever that A changes.
  • هیچ هەڵناگرێت. هەرگیز نرخێک هەڵناگرێت کە دواتر بیگۆڕیت. هەموو ئەوەی دەیدات دیسان لە سەرچاوەکانیەوە دەردەهێنرێت.
  • نرخەکەی لە updateـەوە دێت. دەنووسیت update: (context, source, previous) => …؛ هەرچی بگەڕێنێتەوە ئەوە نرخە دابینکراوەکەیە. previous ئەو نرخەیە کە جاری پێشوو گەڕاندیەوە (یەکەم جار nullـە).
  • سەرچاوەکەی providerـەکانی ترن. ProxyProvider<A, R> نزیکترین A لە سەرووی خۆیەوە دەخوێنێتەوە و هەر کاتێک ئەو Aـیە بگۆڕێت update دیسان جێبەجێ دەکات.
Quick checkپشکنینی خێراWhat does a ProxyProvider's update callback return? updateـی ProxyProvider چی دەگەڕێنێتەوە؟

ProxyProvider: a provider with no state of its own⁦ProxyProvider: provider⁩ـێک بەبێ دۆخێکی خۆی

The badge as a complete appنیشانەکە وەک ئەپێکی تەواو

Here is the slide as a complete app. The FutureProvider gives the list; the ProxyProvider turns the list into its length; the screen watches both:

ئەمە سلایدەکەیە وەک تەواو. FutureProvider لیستەکە دەدات؛ ProxyProvider لیستەکە دەگۆڕێت بۆ درێژییەکەی؛ شاشەکە هەردووکیان watch دەکات:

The to-do count with a ProxyProviderژمارەی ئەرکەکان بە ProxyProviderbadge_app.dartDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'todo_data.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        // The source: the to-do list from the data service.
        FutureProvider<List<Todo>>(
          create: (_) => TodoDataService().getTodoList(),
          initialData: const [],
        ),
        // A value that depends on the source: the number of to-dos.
        ProxyProvider<List<Todo>, int>(
          update: (context, todos, previous) => todos.length,
        ),
      ],
      child: const MaterialApp(home: TodoListScreen()),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final todos = context.watch<List<Todo>>(); // get the to-do list
    final count = context.watch<int>(); // get the count
    return Scaffold(
      appBar: AppBar(
        title: const Text('My To-Do List (ProxyProvider)'),
        actions: [
          Padding(
            padding: const EdgeInsets.only(right: 16),
            child: Badge.count(count: count, child: const Icon(Icons.checklist)),
          ),
        ],
      ),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, i) => ListTile(title: Text(todos[i].title)),
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 16

The source: the list, [] until the Future completes.

سەرچاوەکە: لیستەکە، [] تا تەواو دەبێت.

Lineدێڕی 21

Read a List<Todo>, provide an int.

List<Todo>ـێک دەخوێنێتەوە، intـێک دابین دەکات.

Lineدێڕی 22

No create, no stored state: just a calculation.

نە create، نە هەڵگیراو: تەنها هەژمارێک.

Lineدێڕی 36

The badge's number comes from the ProxyProvider.

ژمارەی نیشانەکە لە ProxyProviderـەوە دێت.

At first the list is the initialData [], so the count is 0. One second later the list arrives, the FutureProvider notifies, the ProxyProvider runs update again, and the badge shows 5. Nobody had to remember to update the count.

سەرەتا لیستەکە initialDataـی []ـە، بۆیە ژمارەکە ٠ـە. چرکەیەک دواتر لیستەکە دەگات، FutureProvider ئاگادار دەکاتەوە، ProxyProvider دیسان update جێبەجێ دەکات و نیشانەکە ٥ پیشان دەدات. کەس پێویستی نەبوو بیری بێت ژمارەکە نوێ بکاتەوە.

Quick checkپشکنینی خێراRight after the app starts, the badge shows 0. Why?یەکسەر دوای دەستپێکردنی ، نیشانەکە ٠ پیشان دەدات. بۆچی؟

ProxyProvider: a provider with no state of its own⁦ProxyProvider: provider⁩ـێک بەبێ دۆخێکی خۆی

When the source changes, update runs againکاتێک سەرچاوەکە دەگۆڕێت، update دووبارە جێبەجێ دەبێت

A FutureProvider gives its list only once. To see the real power of ProxyProvider, keep the list in a ChangeNotifier that can grow, and let the ProxyProvider depend on it. Every time the list calls notifyListeners(), the ProxyProvider runs update again and the badge gets the new count:

FutureProvider تەنها یەک جار لیستەکەی دەدات. بۆ بینینی هێزی ڕاستەقینەی ProxyProvider، لیستەکە لە ChangeNotifierـێکدا دابنێ کە دەتوانێت گەورە ببێت و با ProxyProvider پشتی پێ ببەستێت. هەر جارێک لیستەکە notifyListeners() بانگ بکات، ProxyProvider دیسان update جێبەجێ دەکات و نیشانەکە ژمارە نوێیەکە وەردەگرێت:

The badge follows a list that can growنیشانەکە شوێنی لیستێک دەکەوێت کە دەتوانێت گەورە ببێتlive_count.dartDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'todo_data.dart';

// The list now lives in a ChangeNotifier, so it can grow.
class TodoListNotifier extends ChangeNotifier {
  final List<Todo> _todos = [
    const Todo(id: 1, title: 'Prepare proposal for the new project'),
    const Todo(id: 2, title: 'Replace light bulb'),
    const Todo(id: 3, title: 'Buy Flutter eBook'),
    const Todo(id: 4, title: 'Subscribe to fibre-optic internet'),
    const Todo(id: 5, title: 'Set up online meeting room'),
  ];

  List<Todo> get todos => List.unmodifiable(_todos);

  void add(String title) {
    _todos.add(Todo(id: _todos.length + 1, title: title));
    notifyListeners(); // the ProxyProvider hears this and runs update again
  }
}

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => TodoListNotifier()),
        ProxyProvider<TodoListNotifier, int>(
          update: (context, list, previous) => list.todos.length,
        ),
      ],
      child: const MaterialApp(home: TodoListScreen()),
    ),
  );
}

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

  @override
  Widget build(BuildContext context) {
    final todos = context.watch<TodoListNotifier>().todos;
    return Scaffold(
      appBar: AppBar(
        title: const Text('My To-Do List'),
        actions: const [CountBadge()],
      ),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, i) => ListTile(title: Text(todos[i].title)),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.read<TodoListNotifier>().add('New task'),
        child: const Icon(Icons.add),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final count = context.watch<int>(); // only the derived value
    return Padding(
      padding: const EdgeInsets.only(right: 16),
      child: Badge.count(count: count, child: const Icon(Icons.checklist)),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 20

The list announces the change…

لیستەکە گۆڕانەکە ڕادەگەیەنێت…

Lineدێڕی 29

…the ProxyProvider depends on the notifier, listed after it…

…ProxyProvider پشت بە ChangeNotifierـەکە دەبەستێت و دوای ئەو نووسراوە…

Lineدێڕی 30

…and computes the count again.

…و ژمارەکە دیسان هەژمار دەکاتەوە.

Lineدێڕی 66

The badge watches only the count.

نیشانەکە تەنها ژمارەکە watch دەکات.

Animation in three frames. Left: a phone with 'My To-Do List', a red badge and a + button. Right: a chain of boxes: TodoListNotifier (todos: 5 items) in a ChangeNotifierProvider, then ProxyProvider<TodoListNotifier, int> with 'update: list.todos.length', then int 5, then CountBadge with context.watch<int>(). Frame 1: five to-dos, badge 5. Frame 2: + is tapped, a sixth to-do 'New task' appears, the notifier shows 6 items, an arrow labelled notifyListeners() goes to the ProxyProvider, 'update runs again', and the int becomes 6. Frame 3: the badge shows 6 with an orange ring; only the badge rebuilds.
Animation in three frames. Left: a phone with 'My To-Do List', a red badge and a + button. Right: a chain of boxes: TodoListNotifier (todos: 5 items) in a ChangeNotifierProvider, then ProxyProvider<TodoListNotifier, int> with 'update: list.todos.length', then int 5, then CountBadge with context.watch<int>(). Frame 1: five to-dos, badge 5. Frame 2: + is tapped, a sixth to-do 'New task' appears, the notifier shows 6 items, an arrow labelled notifyListeners() goes to the ProxyProvider, 'update runs again', and the int becomes 6. Frame 3: the badge shows 6 with an orange ring; only the badge rebuilds.
The ProxyProvider holds no count of its own: it asks the list again after every change.ProxyProvider هیچ ژمارەیەکی خۆی هەڵناگرێت: دوای هەر گۆڕانێک دیسان لە لیستەکە دەپرسێت.CêIT

One more detail matters for speed. By default a ProxyProvider tells its listeners only when the new value is different from the old one (!=). A widget test toggled something in the notifier without changing the length, then added a to-do:

وردەکارییەکی تریش بۆ خێرایی گرنگە. بە شێوەی بنەڕەتی ProxyProvider تەنها کاتێک ئاگادار دەکاتەوە کە نرخە نوێیەکە جیاواز بێت لە کۆنەکە (!=). تاقیکردنەوەیەکی شتێکی لە ChangeNotifierـەکەدا گۆڕی بەبێ ئەوەی درێژییەکە بگۆڕێت، پاشان ئەرکێکی زیاد کرد:

Real output of a Flutter widget test (flutter test, October 2026)دەرخستەی ڕاستەقینەی تاقیکردنەوەیەکی ویجێتی ⁦Flutter (flutter test⁩، ئۆکتۆبەری ⁦2026⁩)TEXT
start:              update ran 1 time(s), badge built 1 time(s)
notify, same count: update ran 2 time(s), badge built 1 time(s)
notify, count 6:    update ran 3 time(s), badge built 2 time(s)

update ran after every notification, but the badge was rebuilt only when the count really changed. So keep update cheap: it should only combine values, never download or save anything.

update دوای هەموو ئاگادارکردنەوەیەک جێبەجێ کرا، بەڵام نیشانەکە تەنها کاتێک دووبارە دروست کرایەوە کە ژمارەکە بە ڕاستی گۆڕا. بۆیە update سووک ڕابگرە: تەنها دەبێت نرخەکان تێکەڵ بکات، هەرگیز هیچ شتێک دانەبەزێنێت یان پاشەکەوت نەکات.

Quick checkپشکنینی خێراA ProxyProvider<TodoListNotifier, int> returns the length. The user marks a to-do as done (the length stays 5). What happens?ProxyProvider<TodoListNotifier, int> درێژییەکە دەگەڕێنێتەوە. ئەرکێک وەک تەواوکراو نیشان دەکات (درێژییەکە هەر ٥ دەمێنێتەوە). چی ڕوو دەدات؟

ProxyProvider: a provider with no state of its own⁦ProxyProvider: provider⁩ـێک بەبێ دۆخێکی خۆی

ProxyProvider2, 3, …: more than one source⁦ProxyProvider2⁩، 3، …: زیاتر لە یەک سەرچاوە

Sometimes a value needs two or more providers. The package has numbered versions for that: the digit after the name is the number of providers it depends on. ProxyProvider2<A, B, R> reads an A and a B and builds an R, and its update receives both values (the package goes up to ProxyProvider6):

هەندێک جار نرخێک پێویستی بە دوو provider یان زیاتر هەیە. وەشانی ژمارەدار بۆ ئەمە هەیە: ئەو ژمارەیەی دوای ناوەکە ژمارەی ئەو providerـانەیە کە پشتیان پێ دەبەستێت. ProxyProvider2<A, B, R> Aـێک و Bـێک دەخوێنێتەوە و Rـێک دروست دەکات و updateـەکەی هەردوو نرخەکە وەردەگرێت ( تا ProxyProvider6 دەچێت):

A summary built from a User and the listپوختەیەک کە لە User و لیستەکەوە دروست دەکرێتtwo_sources.dartDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'live_count.dart' show TodoListNotifier;

class User {
  const User(this.name);

  final String name;
}

// Built from TWO other providers, so it is a ProxyProvider2.
class TodoSummary {
  const TodoSummary(this.userName, this.count);

  final String userName;
  final int count;

  String get text => '$userName has $count to-dos';
}

Widget buildApp() {
  return MultiProvider(
    providers: [
      Provider<User>(create: (_) => const User('Aram')),
      ChangeNotifierProvider(create: (_) => TodoListNotifier()),
      ProxyProvider2<User, TodoListNotifier, TodoSummary>(
        update: (context, user, list, previous) =>
            TodoSummary(user.name, list.todos.length),
      ),
    ],
    child: const MaterialApp(home: SummaryScreen()),
  );
}

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

  @override
  Widget build(BuildContext context) {
    final summary = context.watch<TodoSummary>();
    return Scaffold(
      appBar: AppBar(title: Text(summary.text)),
      floatingActionButton: FloatingActionButton(
        onPressed: () => context.read<TodoListNotifier>().add('New task'),
        child: const Icon(Icons.add),
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 27

Two sources (User, TodoListNotifier), one result (TodoSummary).

دوو سەرچاوە (User، TodoListNotifier)، یەک ئەنجام (TodoSummary).

Lineدێڕی 28

One value per source, then the previous result.

یەک نرخ بۆ هەر سەرچاوەیەک، پاشان ئەنجامی پێشوو.

Quick checkپشکنینی خێراWhich class builds a Report from a User, a Settings object and a TodoListNotifier?کام Reportـێک لە User، Settings و TodoListNotifierـەوە دروست دەکات؟

The course example: a UserProfile built from AuthServiceنموونەکەی کۆرسەکە: UserProfile کە لە AuthServiceـەوە دروست دەکرێت

The course's summary pages give a second example. An AuthService manages sign-in and holds a token (the secret string a server gives after sign-in). A UserProfile needs that service to do its job. A ProxyProvider builds the profile from the service, so the profile never has to find or create the service itself. Here is the course's code, completed so that it runs:

لاپەڕەکانی پوختەی کۆرسەکە نموونەیەکی دووەم دەدەن. AuthService بەڕێوە دەبات و هەڵدەگرێت (ئەو نهێنییەی دوای دەیدات). UserProfile بۆ کارەکەی پێویستی بەو هەیە. ProxyProvider پرۆفایلەکە لە دروست دەکات، بۆیە پرۆفایلەکە هەرگیز پێویست ناکات خۆی بدۆزێتەوە یان دروستی بکات. ئەمە کۆدەکەی کۆرسەکەیە، تەواوکراو تا کار بکات:

The course's ProxyProvider exampleنموونەی ProxyProviderـی کۆرسەکەauth_course.dartDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

// The course's example, completed so that it runs.
class AuthService {
  String token = 'some_auth_token';
}

class UserProfile {
  UserProfile(this.authService);

  final AuthService authService;
}

Widget buildApp() {
  return MultiProvider(
    providers: [
      Provider<AuthService>(create: (context) => AuthService()),
      ProxyProvider<AuthService, UserProfile>(
        update: (context, auth, previousUser) => UserProfile(auth),
      ),
    ],
    child: const MaterialApp(home: ProfileScreen()),
  );
}

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

  @override
  Widget build(BuildContext context) {
    final profile = context.watch<UserProfile>();
    return Scaffold(
      body: Center(child: Text('Token: ${profile.authService.token}')),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 9

UserProfile depends on AuthService: it receives it in its constructor.

UserProfile پشت بە AuthService دەبەستێت: لە وەری دەگرێت.

Lineدێڕی 18

AuthService is provided normally (the course writes Provider(create: …); the type is clearer written out).

AuthService بە شێوەی ئاسایی دابین دەکرێت (کۆرسەکە Provider(create: …) دەنووسێت؛ نووسینی جۆرەکە ڕوونترە).

Lineدێڕی 19

UserProfile is created based on AuthService.

UserProfile لەسەر بنەمای AuthService دروست دەکرێت.

This works, but notice something: AuthService sits in a plain Provider, and a plain Provider never notifies (it never listens). If you change token later, nobody hears about it, so update runs only once. That is fine when AuthService never changes. When it can change (a user signs in as someone else), make it a ChangeNotifier and provide it with ChangeNotifierProvider. Then the ProxyProvider builds a new, up-to-date UserProfile after every sign-in:

ئەمە کار دەکات، بەڵام سەرنجی شتێک بدە: AuthService لەناو Providerـێکی ئاساییدایە و Providerـی ئاسایی هەرگیز ئاگادار ناکاتەوە (هەرگیز گوێ ناگرێت). ئەگەر دواتر token بگۆڕیت، کەس پێی نازانێت، بۆیە update تەنها یەک جار جێبەجێ دەبێت. ئەمە باشە کاتێک AuthService هەرگیز ناگۆڕێت. کاتێک دەکرێت بگۆڕێت ( وەک کەسێکی تر دەچێتە ژوورەوە)، بیکە بە ChangeNotifier و بە ChangeNotifierProvider دابینی بکە. ئەو کاتە ProxyProvider دوای هەر UserProfileـێکی نوێ و تازە دروست دەکات:

AuthService can change, so the profile follows itAuthService دەکرێت بگۆڕێت، بۆیە پرۆفایلەکە شوێنی دەکەوێتauth_changes.dartDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

// AuthService can now change (sign in), so it is a ChangeNotifier.
class AuthService extends ChangeNotifier {
  String _token = 'token-A';
  String get token => _token;

  void signIn(String newToken) {
    _token = newToken;
    notifyListeners();
  }
}

// A plain class that only combines values: a ProxyProvider builds it.
class UserProfile {
  const UserProfile(this.token);

  final String token;

  String get title => 'Profile for $token';
}

Widget buildApp() {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (_) => AuthService()),
      ProxyProvider<AuthService, UserProfile>(
        update: (context, auth, previous) => UserProfile(auth.token),
      ),
    ],
    child: const MaterialApp(home: ProfileScreen()),
  );
}

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

  @override
  Widget build(BuildContext context) {
    final profile = context.watch<UserProfile>();
    return Scaffold(
      appBar: AppBar(title: Text(profile.title)),
      body: Center(
        child: FilledButton(
          onPressed: () => context.read<AuthService>().signIn('token-B'),
          child: const Text('Sign in as another user'),
        ),
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 5

Signing in changes the token and notifies.

تۆکنەکە دەگۆڕێت و ئاگادار دەکاتەوە.

Lineدێڕی 16

A plain, immutable class: it only combines values, so a ProxyProvider fits.

ئاسایی و : تەنها نرخەکان تێکەڵ دەکات، بۆیە ProxyProvider گونجاوە.

Lineدێڕی 29

A new UserProfile after every change of AuthService.

دوای هەر گۆڕانێکی AuthService، UserProfileـێکی نوێ.

Quick checkپشکنینی خێراIn the course's version, AuthService is in a plain Provider. Your code later sets auth.token = 'new'. What happens to UserProfile?لە وەشانەکەی کۆرسەکەدا، AuthService لەناو Providerـێکی ئاساییدایە. کۆدەکەت دواتر auth.token = 'new' دادەنێت. چی بەسەر UserProfile دێت؟

ChangeNotifierProxyProvider: a notifier kept in sync⁦ChangeNotifierProxyProvider: ChangeNotifier⁩ـێک کە هاوکات ڕادەگیرێت

ProxyProvider builds a new, plain value every time. Sometimes that is not enough: the thing that depends on another provider is a ChangeNotifier with its own state, for example a to-do list that the user edits, which must also follow the current user. You cannot rebuild it from scratch on every change, or the user's edits would be lost. The lecture defines the answer in two sentences:

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

  • It is a ChangeNotifierProvider with the capability of a ProxyProvider: it holds a ChangeNotifier, listens to it and disposes it, like a ChangeNotifierProvider, and it also has an update callback that runs when its sources change, like a ProxyProvider.
  • It builds and synchronizes a ChangeNotifier with external values: the notifier is built once, then kept in step with values that come from other providers.
  • ChangeNotifierProviderـێکە بە توانای ProxyProviderـەوە: ChangeNotifierـێک هەڵدەگرێت، گوێی لێ دەگرێت و disposeـی دەکات، وەک ChangeNotifierProvider و هەروەها updateـی هەیە کە کاتێک سەرچاوەکانی دەگۆڕێن جێبەجێ دەبێت، وەک ProxyProvider.
  • ChangeNotifierـێک دروست دەکات و لەگەڵ نرخە دەرەکییەکاندا هاوکاتی دەکات: ChangeNotifierـەکە یەک جار دروست دەکرێت، پاشان لەگەڵ ئەو نرخانەدا ڕێک ڕادەگیرێت کە لە providerـەکانی ترەوە دێن.
On the left an AuthService box (token 'token-A', the source, changes when the user signs in). Two arrows go right. The upper one goes into ProxyProvider<AuthService, UserProfile> with 'update: (_, auth, previous) => UserProfile(auth.token)' and on to UserProfile, a plain class: 'a NEW object after every change'. The lower one goes into ChangeNotifierProxyProvider<AuthService, UserProfileNotifier> with 'update: (_, auth, notifier) => notifier!..updateAuth(auth)' and on to UserProfileNotifier extends ChangeNotifier: 'the SAME notifier, told about the change'.لای چەپ سندووقی AuthService (تۆکن 'token-A'، سەرچاوەی دراوەکە، کاتێک بەکارهێنەرەکە دەچێتە ژوورەوە دەگۆڕێت). دوو تیر بەرەو ڕاست دەچن. سەرەوە دەچێتە ناو ProxyProvider<AuthService, UserProfile> لەگەڵ update: (_, auth, previous) => UserProfile(auth.token) و پاشان بۆ UserProfile، کلاسێکی ئاسایی: 'دوای هەر گۆڕانێک ئۆبجێکتێکی نوێ'. خوارەوە دەچێتە ناو ChangeNotifierProxyProvider<AuthService, UserProfileNotifier> لەگەڵ update: (_, auth, notifier) => notifier!..updateAuth(auth) و پاشان بۆ UserProfileNotifier extends ChangeNotifier: 'هەمان ChangeNotifier، ئاگادار دەکرێتەوە لە گۆڕانەکە'.
Both providers depend on AuthService. ProxyProvider replaces its value; ChangeNotifierProxyProvider keeps one notifier and updates it.هەردوو providerـەکە پشت بە AuthService دەبەستن. ProxyProvider نرخەکەی دەگۆڕێتەوە؛ ChangeNotifierProxyProvider یەک ChangeNotifier دەهێڵێتەوە و نوێی دەکاتەوە.CêIT

Why not just a ChangeNotifierProvider whose create reads the other provider? Because create runs once. The notifier would be built from the first AuthService and would never hear about a later one. ChangeNotifierProxyProvider adds the missing piece: update runs again whenever a source changes.

بۆچی تەنها ChangeNotifierProviderـێک نا کە createـەکەی providerـەکەی تر بخوێنێتەوە؟ چونکە create یەک جار جێبەجێ دەبێت. ChangeNotifierـەکە لە یەکەم AuthServiceـەوە دروست دەکرا و هەرگیز ئاگای لە دواتر نەدەبوو. ChangeNotifierProxyProvider پارچە ونبووەکە زیاد دەکات: هەر کاتێک سەرچاوەیەک بگۆڕێت update دیسان جێبەجێ دەبێت.

Quick checkپشکنینی خێراA CartNotifier (the user adds and removes items) must also know the current Currency from another provider. Which provider fits?CartNotifierـێک ( کاڵا زیاد دەکات و لادەبات) دەبێت Currencyـی ئێستاش لە providerـێکی ترەوە بزانێت. کام provider گونجاوە؟

ChangeNotifierProxyProvider: a notifier kept in sync⁦ChangeNotifierProxyProvider: ChangeNotifier⁩ـێک کە هاوکات ڕادەگیرێت

The course's example and its update bugنموونەکەی کۆرسەکە و هەڵەی update تێیدا

The course's ChangeNotifierProxyProvider example has a UserProfileNotifier that fetches user data based on the token. Here it is as written in the notes:

نموونەی ChangeNotifierProxyProviderـی کۆرسەکە UserProfileNotifierـێکی هەیە کە بەپێی تۆکنەکە دراوەی دەهێنێت. ئەمە وەک لە تێبینییەکاندا نووسراوە:

The course's code (the notes say: updates when AuthService changes)کۆدەکەی کۆرسەکە (تێبینییەکان دەڵێن: کاتێک AuthService دەگۆڕێت نوێ دەبێتەوە)notes_version.dartDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'auth_course.dart' show AuthService;

// The course's ChangeNotifierProxyProvider example, as written in the notes.
class UserProfileNotifier extends ChangeNotifier {
  final AuthService authService;
  String userData = 'Loading...';

  UserProfileNotifier(this.authService) {
    fetchUserData();
  }

  void fetchUserData() {
    userData = 'User Data for ${authService.token}';
    notifyListeners();
  }
}

Widget buildApp() {
  return MultiProvider(
    providers: [
      Provider(create: (context) => AuthService()),
      ChangeNotifierProxyProvider<AuthService, UserProfileNotifier>(
        create: (context) => UserProfileNotifier(context.read<AuthService>()),
        update: (context, auth, previous) =>
            previous!..fetchUserData(), // the new `auth` is never used
      ),
    ],
    child: const MaterialApp(home: Scaffold()),
  );
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 8

The service is stored once, in the constructor, and is final.

یەک جار، لە ، هەڵدەگیرێت و finalـە.

Lineدێڕی 26

create reads AuthService once.

create یەک جار AuthService دەخوێنێتەوە.

Lineدێڕی 28

update receives the current auth… and ignores it.

update authـی ئێستا وەردەگرێت… و پشتگوێی دەخات.

Real output of a Flutter widget test (flutter test, October 2026)دەرخستەی ڕاستەقینەی تاقیکردنەوەیەکی ویجێتی ⁦Flutter (flutter test⁩، ئۆکتۆبەری ⁦2026⁩)TEXT
token-A  notes:  User Data for token-A
token-A  fixed:  User data for token-A
token-B  notes:  User Data for token-A
token-B  fixed:  User data for token-B

The course's version still shows token-A after the change; the fixed version (next screen) shows token-B.

وەشانەکەی کۆرسەکە دوای گۆڕانەکەش هێشتا ⁦token-A⁩ پیشان دەدات؛ وەشانە چاککراوەکە (شاشەی داهاتوو) ⁦token-B⁩ پیشان دەدات.

Quick checkپشکنینی خێراWhat is wrong with update: (context, auth, previous) => previous!..fetchUserData()?چی هەڵەیە لە update: (context, auth, previous) => previous!..fetchUserData()؟

ChangeNotifierProxyProvider: a notifier kept in sync⁦ChangeNotifierProxyProvider: ChangeNotifier⁩ـێک کە هاوکات ڕادەگیرێت

The right pattern: keep the notifier, pass the new value inشێوازی دروست: ChangeNotifierـەکە بهێڵەوە و نرخە نوێیەکەی بدەرێ

The provider package's own documentation shows the pattern. Create the notifier once in create, without the dependency. In update, reuse the previous notifier and give it the new value through a method of your own (here updateAuth):

بەڵگەنامەی خودی provider شێوازەکە پیشان دەدات. ChangeNotifierـەکە یەک جار لە createـدا دروست بکە، بەبێ . لە updateـدا، ChangeNotifierـە پێشووەکە دووبارە بەکار بهێنەوە و نرخە نوێیەکەی بە خۆت پێ بدە (لێرەدا updateAuth):

ChangeNotifierProxyProvider, done rightChangeNotifierProxyProvider، بە شێوەی دروستprofile_fixed.dartDARTRuns on a phone (or an emulator)لەسەر مۆبایل کار دەکات
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'auth_changes.dart' show AuthService;

class UserProfileNotifier extends ChangeNotifier {
  String? _token;
  String _userData = 'Loading...';

  String get userData => _userData;

  // ChangeNotifierProxyProvider calls this every time AuthService changes.
  void updateAuth(AuthService auth) {
    if (auth.token == _token) return; // nothing new: keep the data
    _token = auth.token;
    _fetchUserData(auth.token);
  }

  Future<void> _fetchUserData(String token) async {
    await Future<void>.delayed(const Duration(milliseconds: 300)); // a server
    if (token != _token) return; // the user changed while we waited
    _userData = 'User data for $token';
    notifyListeners();
  }
}

Widget buildApp() {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (_) => AuthService()),
      ChangeNotifierProxyProvider<AuthService, UserProfileNotifier>(
        create: (_) => UserProfileNotifier(), // made once
        update: (_, auth, notifier) => notifier!..updateAuth(auth), // kept
      ),
    ],
    child: const MaterialApp(home: ProfileScreen()),
  );
}

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

  @override
  Widget build(BuildContext context) {
    final data = context.watch<UserProfileNotifier>().userData;
    return Scaffold(
      appBar: AppBar(title: Text(data)),
      body: Center(
        child: FilledButton(
          onPressed: () => context.read<AuthService>().signIn('token-B'),
          child: const Text('Sign in as another user'),
        ),
      ),
    );
  }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 13

The new AuthService arrives here every time.

AuthServiceـی نوێ هەموو جارێک لێرەوە دەگات.

Lineدێڕی 14

Same token: keep the data (and the state) the notifier already has.

هەمان تۆکن: ئەو دراوەیە (و ) بهێڵەوە کە ChangeNotifierـەکە هەیەتی.

Lineدێڕی 21

A slow answer for an old token is thrown away.

وەڵامێکی خاو بۆ تۆکنێکی کۆن فڕێ دەدرێت.

Lineدێڕی 30

AuthService is listed first: a provider can only read providers above it.

AuthService یەکەم نووسراوە: provider تەنها دەتوانێت providerـەکانی سەرووی خۆی بخوێنێتەوە.

Lineدێڕی 33

Same notifier, told about the new auth.

هەمان ChangeNotifier، ئاگادارکراو لە authـە نوێیەکە.

Now the sentence from the notes is true: AuthService is provided first, and UserProfileNotifier depends on it and updates when it changes. While the new data loads, the old data stays on screen, and the notifier object itself is never replaced, so any other state inside it survives.

ئێستا ڕستەکەی تێبینییەکان ڕاستە: AuthService یەکەم دابین دەکرێت و UserProfileNotifier پشتی پێ دەبەستێت و کاتێک دەگۆڕێت نوێ دەبێتەوە. تا دراوە نوێیەکە دەگات، دراوە کۆنەکە لەسەر شاشە دەمێنێتەوە و خودی ChangeNotifierـەکە هەرگیز ناگۆڕدرێت، بۆیە هەر تری ناوی دەمێنێتەوە.

The documentation also warns against the tempting shortcut of building a new notifier inside update:

بەڵگەنامەکە ئاگاداریش دەکاتەوە لە ڕێگا کورتە سەرنجڕاکێشەکەی دروستکردنی ChangeNotifierـێکی نوێ لەناو updateـدا:

DON'T and DOمەیکە و بیکەdont_and_do.dartDART
import 'package:provider/provider.dart';

import 'auth_changes.dart' show AuthService;
import 'profile_fixed.dart' show UserProfileNotifier;

// DON'T: a new notifier on every update loses its state and listeners.
final wrong = ChangeNotifierProxyProvider<AuthService, UserProfileNotifier>(
  create: (_) => UserProfileNotifier(),
  update: (_, auth, previous) => UserProfileNotifier()..updateAuth(auth),
);

// DO: keep the previous notifier and pass the new value into it.
final right = ChangeNotifierProxyProvider<AuthService, UserProfileNotifier>(
  create: (_) => UserProfileNotifier(),
  update: (_, auth, previous) => previous!..updateAuth(auth),
);
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 9

A new notifier each time: its state is lost, the old one is disposed and the widgets subscribe again.

هەموو جارێک ChangeNotifierـێکی نوێ: لەدەست دەچێت، کۆنەکە dispose دەکرێت و دیسان بەشداری دەکەنەوە.

Lineدێڕی 15

The same notifier, updated.

هەمان ChangeNotifier، نوێکراوە.

Quick checkپشکنینی خێراIn a ChangeNotifierProxyProvider, where should the notifier object be created?لە ChangeNotifierProxyProviderـدا، ChangeNotifierـەکە لە کوێ دروست بکرێت؟

When to use whatکەی کامیان بەکار بهێنرێت

The course ends the topic with a table. Here it is, with the built-in classes marked and one row added for comparison:

کۆرسەکە بابەتەکە بە خشتەیەک کۆتایی پێ دەهێنێت. ئەمە خشتەکەیە، ناوخۆییەکان نیشان کراون و دێڕێک بۆ بەراوردکردن زیاد کراوە:

ClassComes fromسەرچاوەUse it whenکەی بەکاری بهێنە
ChangeNotifierFlutter itselfخودی FlutterYou manage simple state that changes: a counter, settings, user data. It holds the state and calls notifyListeners() (its lesson). سادە بەڕێوە دەبەیت کە دەگۆڕێت: ، ڕێکخستنەکان، دراوەی . هەڵدەگرێت و notifyListeners() بانگ دەکات (وانەکەی).
ChangeNotifierProviderprovider package providerYou give a ChangeNotifier to the widgets below. It can hold state (inside its notifier) and update widgets through notifyListeners().ChangeNotifierـێک دەدەیت بە خوارەوە. دەتوانێت هەڵبگرێت (لەناو ChangeNotifierـەکەیدا) و بە notifyListeners() نوێ بکاتەوە.
ProxyProviderprovider package providerA class depends on another provider but is not a ChangeNotifier: a count, a summary, a profile built from AuthService. پشت بە providerـێکی تر دەبەستێت بەڵام ChangeNotifier نییە: ژمارەیەک، پوختەیەک، پرۆفایلێک کە لە AuthServiceـەوە دروست دەکرێت.
ChangeNotifierProxyProviderprovider package providerA ChangeNotifier depends on another provider and must update when it changes, keeping its own state.ChangeNotifierـێک پشت بە providerـێکی تر دەبەستێت و دەبێت کاتێک ئەو دەگۆڕێت نوێ ببێتەوە، لە کاتێکدا خۆی دەهێڵێتەوە.
Providerprovider package providerA value that never changes (a service, a configuration).نرخێک کە هەرگیز ناگۆڕێت (، ).
Quick checkپشکنینی خێراWhich of these classes is built into Flutter itself, with no package?کام لەم لەناو خودی Flutterـدایە، بەبێ هیچ ؟

When to use whatکەی کامیان بەکار بهێنرێت

In one line eachهەریەکەیان لە یەک دێڕدا

  • ChangeNotifier → notifies widgets when its data changes.
  • ChangeNotifierProvider → provides a ChangeNotifier to widgets.
  • ProxyProvider → provides a class that depends on another provider.
  • ChangeNotifierProxyProvider → provides a ChangeNotifier that depends on another provider and updates when it changes.
  • ChangeNotifier ← کاتێک دراوەکەی دەگۆڕێت ئاگادار دەکاتەوە.
  • ChangeNotifierProvider → ChangeNotifierـێک دابین دەکات بۆ .
  • ProxyProvider ← دابین دەکات کە پشت بە providerـێکی تر دەبەستێت.
  • ChangeNotifierProxyProvider → ChangeNotifierـێک دابین دەکات کە پشت بە providerـێکی تر دەبەستێت و کاتێک ئەو دەگۆڕێت نوێ دەبێتەوە.
Quick checkپشکنینی خێراA screen title must read “Aram has 3 to-dos”, built from the current User and the to-do list. Nothing else is stored. Which provider?ناونیشانی شاشەیەک دەبێت «⁦Aram has 3 to-dos⁩» بێت، کە لە Userـی ئێستا و لیستی ئەرکەکانەوە دروست دەکرێت. هیچ شتێکی تر هەڵناگیرێت. کام provider؟

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

MistakeهەڵەWhat happensچی ڕوو دەداتFixچارەسەر
A ProxyProvider listed before the provider it readsProxyProvider پێش ئەو providerـەی دەیخوێنێتەوە نووسراوەProviderNotFoundException: Could not find the correct Provider<TodoListNotifier> above this _InheritedProviderScope<int?> Widget (real test output)ProviderNotFoundException: Could not find the correct Provider<TodoListNotifier> above this _InheritedProviderScope<int?> Widget (دەرخستەی ڕاستەقینەی تاقیکردنەوە)In a MultiProvider, put the sources first; the list order is the tree orderلە MultiProviderـدا، سەرچاوەکان یەکەم دابنێ؛ ڕیزبەندیی لیستەکە ڕیزبەندیی دارەکەیە
The source is a plain Provider but its data changesسەرچاوەکە Providerـی ئاساییە بەڵام دراوەکەی دەگۆڕێتupdate runs once; the dependent value stays oldupdate یەک جار جێبەجێ دەبێت؛ نرخە پشتبەستووەکە کۆن دەمێنێتەوەProvide changing data with a ChangeNotifierProvider (or Future/StreamProvider)دراوەی گۆڕاو بە ChangeNotifierProvider (یان ⁦Future/StreamProvider⁩) دابین بکە
update ignores its value argument (previous!..fetch())update نرخە ناردراوەکەی پشتگوێ دەخات (previous!..fetch())The notifier keeps using an old objectChangeNotifierـەکە هەر کۆن بەکار دەهێنێتprevious!..update(value) with a method that stores the new valueprevious!..update(value) لەگەڵ کە نرخە نوێیەکە هەڵدەگرێت
A new ChangeNotifier created inside updateChangeNotifierـێکی نوێ لەناو updateـدا دروست کراوەState lost on every change; old notifier disposed; extra workلە هەر گۆڕانێکدا لەدەست دەچێت؛ ChangeNotifierـە کۆنەکە dispose دەکرێت؛ کاری زیادەCreate it once in create, reuse previous in updateیەک جار لە createـدا دروستی بکە، لە updateـدا previous دووبارە بەکار بهێنەوە
Slow work (downloads, saving) inside a ProxyProvider's updateکاری خاو (دابەزاندن، پاشەکەوتکردن) لەناو updateـی ProxyProviderـداIt runs after every change of every sourceدوای هەر گۆڕانێکی هەر سەرچاوەیەک جێبەجێ دەبێتKeep update a quick calculation; put slow work in a notifier or serviceupdate وەک هەژمارێکی خێرا ڕابگرە؛ کاری خاو بخە ناو ChangeNotifier یان
Quick checkپشکنینی خێراYou moved ProxyProvider<TodoListNotifier, int> to the top of the MultiProvider list, above ChangeNotifierProvider<TodoListNotifier>. What happens?ProxyProvider<TodoListNotifier, int>ـت گواستەوە بۆ سەرەوەی لیستی MultiProvider، لە سەرووی ChangeNotifierProvider<TodoListNotifier>ـەوە. چی ڕوو دەدات؟

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

Exerciseڕاهێنان1. Open to-dos only١. تەنها ئەرکە تەواونەکراوەکان

Change the badge example so that the badge shows only the to-dos that are not completed. Which line changes?

نموونەی نیشانەکە بگۆڕە بە شێوەیەک کە نیشانەکە تەنها ئەو ئەرکانە پیشان بدات کە تەواو نەکراون. کام دێڕ دەگۆڕێت؟

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

Only the update callback. The rest of the tree stays the same:

تەنها update. هەموو بەشەکانی تری دارەکە وەک خۆیان دەمێننەوە:

DART
ProxyProvider<List<Todo>, int>(
  update: (context, todos, previous) =>
      todos.where((t) => !t.completed).length,
),

With the lecture's five to-dos the badge shows 4, because one is already completed.

لەگەڵ پێنج ئەرکەکەی وانەکە نیشانەکە ٤ پیشان دەدات، چونکە یەکێکیان پێشتر تەواو کراوە.

Exerciseڕاهێنان2. Choose the provider٢. providerـەکە هەڵبژێرە

For each case, choose Provider, ChangeNotifierProvider, ProxyProvider or ChangeNotifierProxyProvider: (a) an ApiClient that never changes; (b) a ThemeNotifier the user switches between light and dark; (c) a PriceText built from the cart total and the chosen currency (dinar or dollar); (d) an OrderNotifier the user edits, which must reload when the signed-in user changes.

بۆ هەر حاڵەتێک، Provider، ChangeNotifierProvider، ProxyProvider یان ChangeNotifierProxyProvider هەڵبژێرە: (أ) ApiClientـێک کە هەرگیز ناگۆڕێت؛ (ب) ThemeNotifierـێک کە لە نێوان ڕووناک و تاریکدا دەیگۆڕێت؛ (ج) PriceTextـێک کە لە کۆی نرخی سەبەتەکە و جۆری پارەی هەڵبژێردراو (دینار یان دۆلار) دروست دەکرێت؛ (د) OrderNotifierـێک کە دەستکاری دەکات و دەبێت کاتێک چووەژوورەوە دەگۆڕێت دووبارە بار بکرێتەوە.

Show answerوەڵامەکە پیشان بدە
  • (a) Provider: the value never changes.
  • (b) ChangeNotifierProvider: state that the app changes.
  • (c) ProxyProvider2: only a combination of two providers, no state of its own.
  • (d) ChangeNotifierProxyProvider: a notifier with its own state that depends on another provider.
  • (أ) Provider: نرخەکە هەرگیز ناگۆڕێت.
  • (ب) ChangeNotifierProvider: کە دەیگۆڕێت.
  • (ج) ⁦ProxyProvider2⁩: تەنها تێکەڵەیەکە لە دوو provider، بەبێ خۆی.
  • (د) ⁦ChangeNotifierProxyProvider: ChangeNotifier⁩ـێک بە خۆیەوە کە پشت بە providerـێکی تر دەبەستێت.
Exerciseڕاهێنان3. Fix the notifier٣. ChangeNotifierـەکە چاک بکە

This code compiles but the greeting never changes after a new Settings object arrives. Find both problems and fix them.

ئەم کۆدە compile دەبێت بەڵام دوای گەیشتنی Settingsـی نوێ، سڵاوەکە هەرگیز ناگۆڕێت. هەردوو کێشەکە بدۆزەرەوە و چاکیان بکە.

DART
class GreetingNotifier extends ChangeNotifier {
  GreetingNotifier(this.settings);
  final Settings settings;
  String get greeting => settings.language == 'ckb' ? 'Silaw' : 'Hello';
}

ChangeNotifierProxyProvider<Settings, GreetingNotifier>(
  create: (context) => GreetingNotifier(context.read<Settings>()),
  update: (_, settings, previous) => GreetingNotifier(settings),
),
Hintئاماژە

Compare with the right pattern: where is the notifier created, and how does the new value reach it?

بەراوردی بکە لەگەڵ شێوازە دروستەکە: ChangeNotifierـەکە لە کوێ دروست دەکرێت و نرخە نوێیەکە چۆن پێی دەگات؟

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

Two problems: the notifier keeps the Settings from its constructor, and update creates a new notifier, losing the old one's state. Fixed:

دوو کێشە: ChangeNotifierـەکە ئەو Settingsـەی لە وەریگرتووە دەیهێڵێتەوە و update ChangeNotifierـێکی نوێ دروست دەکات و کۆنەکە لەدەست دەدات. چاککراوە:

DART
class GreetingNotifier extends ChangeNotifier {
  String _language = 'en';
  String get greeting => _language == 'ckb' ? 'Silaw' : 'Hello';

  void updateSettings(Settings settings) {
    if (settings.language == _language) return;
    _language = settings.language;
    notifyListeners();
  }
}

// in the MultiProvider, after the Settings provider:
ChangeNotifierProxyProvider<Settings, GreetingNotifier>(
  create: (_) => GreetingNotifier(),
  update: (_, settings, notifier) => notifier!..updateSettings(settings),
),
Exerciseڕاهێنان4. Predict the output٤. دەرخستەکە پێشبینی بکە

A ProxyProvider<TodoListNotifier, int> returns list.todos.length and a badge watches the int. The list starts with 5 to-dos. The program (1) adds a to-do, (2) marks a to-do as done, (3) deletes a to-do. After each step: does update run, and is the badge rebuilt?

ProxyProvider<TodoListNotifier, int>ـێک list.todos.length دەگەڕێنێتەوە و نیشانەیەک intـەکە watch دەکات. لیستەکە بە ٥ ئەرک دەست پێ دەکات. بەرنامەکە ⁦(1)⁩ ئەرکێک زیاد دەکات، ⁦(2)⁩ ئەرکێک وەک تەواوکراو نیشان دەکات، ⁦(3)⁩ ئەرکێک دەسڕێتەوە. دوای هەر هەنگاوێک: ئایا update جێبەجێ دەبێت و نیشانەکە دووبارە دروست دەکرێتەوە؟

Show answerوەڵامەکە پیشان بدە
  • (1) update runs (5→65 \to 6), badge rebuilt.
  • (2) update runs (still 6), badge not rebuilt: the value did not change.
  • (3) update runs (6→56 \to 5), badge rebuilt.
  • ⁦(1)⁩ update جێبەجێ دەبێت (5→65 \to 6)، نیشانەکە دووبارە دروست دەکرێتەوە.
  • ⁦(2)⁩ update جێبەجێ دەبێت (هێشتا ٦)، نیشانەکە دووبارە دروست ناکرێتەوە: نرخەکە نەگۆڕا.
  • ⁦(3)⁩ update جێبەجێ دەبێت (6→56 \to 5)، نیشانەکە دووبارە دروست دەکرێتەوە.

Summaryپوختە

  • A ProxyProvider holds no state: its value comes from its update callback, which reads other providers and runs again whenever they change.
  • It notifies its own listeners only when the new value differs from the old one, so keep update a quick calculation.
  • ProxyProvider2 … ProxyProvider6 combine 2–6 sources; the last type is the result.
  • A ChangeNotifierProxyProvider builds a ChangeNotifier once and keeps it in sync: create makes it, update passes the new values in (notifier!..update(value)). Never create the notifier inside update.
  • A source that changes must notify (ChangeNotifierProvider, FutureProvider, StreamProvider); a plain Provider never does. List sources first.
  • ChangeNotifier is built into Flutter; all the …Provider classes come from the provider package.
  • ProxyProvider هیچ هەڵناگرێت: نرخەکەی لە updateـەوە دێت کە providerـەکانی تر دەخوێنێتەوە و هەر کاتێک بگۆڕێن دیسان جێبەجێ دەبێت.
  • تەنها کاتێک خۆی ئاگادار دەکاتەوە کە نرخە نوێیەکە جیاواز بێت لە کۆنەکە، بۆیە update وەک هەژمارێکی خێرا ڕابگرە.
  • ProxyProvider2 … ProxyProvider6 ٢ تا ٦ سەرچاوە تێکەڵ دەکەن؛ دوایین جۆر ئەنجامەکەیە.
  • ChangeNotifierProxyProvider ChangeNotifierـێک یەک جار دروست دەکات و هاوکاتی ڕادەگرێت: create دروستی دەکات، update نرخە نوێیەکانی دەداتێ (notifier!..update(value)). هەرگیز ChangeNotifierـەکە لەناو updateـدا دروست مەکە.
  • سەرچاوەیەک کە دەگۆڕێت دەبێت ئاگادار بکاتەوە (ChangeNotifierProvider، FutureProvider، StreamProvider)؛ Providerـی ئاسایی هەرگیز ئەوە ناکات. سەرچاوەکان یەکەم بنووسە.
  • ChangeNotifier لەناو خودی Flutterـدایە؛ هەموو …Provider لە providerـەوە دێن.

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

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

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

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

Project: A To-Do App with Providersپڕۆژە: ئەپێکی ئەرکەکان بە providerـەکان

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

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