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 و خشتەیەک بۆ ئەوەی کەی کامیان بەکار بهێنرێت.
What you will learnچی فێر دەبیت
- Explain what a ProxyProvider is: no state of its own, a value built in
updatefrom other providers. - Build the lecture's to-do count badge and predict when
updateruns 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). نیشانەکە پێویستی بە نرخێکی دووەمە: ژمارەکە. ئەو نرخە دراوەیەکی نوێ نییە: لە لیستەکەوە هەژمار دەکرێت. کاتێک لیستەکە دەگۆڕێت، ژمارەکەش دەبێت لەگەڵیدا بگۆڕێت.
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 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 و کەی هەریەکەیان بەکار بهێنرێت.
ProxyProvider: a provider with no state of its ownProxyProvider: 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
updatecallback. You write a functionupdate: (context, source, previous) => …; whatever it returns is the provided value.previousis the value it returned last time (nullthe first time). - Its source is other providers.
ProxyProvider<A, R>reads the nearestAabove it, and runsupdateagain whenever thatAchanges.
- هیچ هەڵناگرێت. هەرگیز نرخێک هەڵناگرێت کە دواتر بیگۆڕیت. هەموو ئەوەی دەیدات دیسان لە سەرچاوەکانیەوە دەردەهێنرێت.
- نرخەکەی لە
updateـەوە دێت. دەنووسیتupdate: (context, source, previous) => …؛ هەرچی بگەڕێنێتەوە ئەوە نرخە دابینکراوەکەیە.previousئەو نرخەیە کە جاری پێشوو گەڕاندیەوە (یەکەم جارnullـە). - سەرچاوەکەی providerـەکانی ترن.
ProxyProvider<A, R>نزیکترینAلە سەرووی خۆیەوە دەخوێنێتەوە و هەر کاتێک ئەوAـیە بگۆڕێتupdateدیسان جێبەجێ دەکات.
ProxyProvider: a provider with no state of its ownProxyProvider: 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 دەکات:
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)),
),
);
}
}at 0 s: rows=0, badge=0 after 1 s: rows=5, badge=5
Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 16
The source: the list,
[]until the Future completes.سەرچاوەکە: لیستەکە،
[]تا تەواو دەبێت.- Lineدێڕی 21
Read a
List<Todo>, provide anint.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 جێبەجێ دەکات و نیشانەکە ٥ پیشان دەدات. کەس پێویستی نەبوو بیری بێت ژمارەکە نوێ بکاتەوە.
ProxyProvider: a provider with no state of its ownProxyProvider: 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 جێبەجێ دەکات و نیشانەکە ژمارە نوێیەکە وەردەگرێت:
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)),
);
}
}start: rows=5, badge=5 after + : rows=6, badge=6
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 دەکات.


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ـەکەدا گۆڕی بەبێ ئەوەی درێژییەکە بگۆڕێت، پاشان ئەرکێکی زیاد کرد:
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 سووک ڕابگرە: تەنها دەبێت نرخەکان تێکەڵ بکات، هەرگیز هیچ شتێک دانەبەزێنێت یان پاشەکەوت نەکات.
ProxyProvider: a provider with no state of its ownProxyProvider: providerـێک بەبێ دۆخێکی خۆی
ProxyProvider2, 3, …: more than one sourceProxyProvider2، 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 دەچێت):
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),
),
);
}
}summary: Aram has 5 to-dos after + : Aram has 6 to-dos
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.
یەک نرخ بۆ هەر سەرچاوەیەک، پاشان ئەنجامی پێشوو.
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 پرۆفایلەکە لە دروست دەکات، بۆیە پرۆفایلەکە هەرگیز پێویست ناکات خۆی بدۆزێتەوە یان دروستی بکات. ئەمە کۆدەکەی کۆرسەکەیە، تەواوکراو تا کار بکات:
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}')),
);
}
}course example: Token: some_auth_token
Explain the codeڕوونکردنەوەی کۆدەکە
- Lineدێڕی 9
UserProfiledepends onAuthService: 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ـێکی نوێ و تازە دروست دەکات:
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'),
),
),
);
}
}before sign-in: Profile for token-A after sign-in: Profile for token-B
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ـێکی نوێ.
ChangeNotifierProxyProvider: a notifier kept in syncChangeNotifierProxyProvider: 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
updatecallback 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ـەکانی ترەوە دێن.
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 دیسان جێبەجێ دەبێت.
ChangeNotifierProxyProvider: a notifier kept in syncChangeNotifierProxyProvider: 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ـێکی هەیە کە بەپێی تۆکنەکە دراوەی دەهێنێت. ئەمە وەک لە تێبینییەکاندا نووسراوە:
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
createreads AuthService once.createیەک جار AuthService دەخوێنێتەوە.- Lineدێڕی 28
updatereceives the currentauth… and ignores it.updateauthـی ئێستا وەردەگرێت… و پشتگوێی دەخات.
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-BThe course's version still shows token-A after the change; the fixed version (next screen) shows token-B.
وەشانەکەی کۆرسەکە دوای گۆڕانەکەش هێشتا token-A پیشان دەدات؛ وەشانە چاککراوەکە (شاشەی داهاتوو) token-B پیشان دەدات.
ChangeNotifierProxyProvider: a notifier kept in syncChangeNotifierProxyProvider: 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):
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'),
),
),
);
}
}at 0 ms: Loading... after 300 ms: User data for token-A sign-in, 0 ms: User data for token-A sign-in + 300 ms: User data for token-B
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ـدا:
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، نوێکراوە.
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:
کۆرسەکە بابەتەکە بە خشتەیەک کۆتایی پێ دەهێنێت. ئەمە خشتەکەیە، ناوخۆییەکان نیشان کراون و دێڕێک بۆ بەراوردکردن زیاد کراوە:
| Class | Comes fromسەرچاوە | Use it whenکەی بەکاری بهێنە |
|---|---|---|
ChangeNotifier | Flutter itselfخودی Flutter | You manage simple state that changes: a counter, settings, user data. It holds the state and calls notifyListeners() (its lesson). سادە بەڕێوە دەبەیت کە دەگۆڕێت: ، ڕێکخستنەکان، دراوەی . هەڵدەگرێت و notifyListeners() بانگ دەکات (وانەکەی). |
ChangeNotifierProvider | provider package provider | You give a ChangeNotifier to the widgets below. It can hold state (inside its notifier) and update widgets through notifyListeners().ChangeNotifierـێک دەدەیت بە خوارەوە. دەتوانێت هەڵبگرێت (لەناو ChangeNotifierـەکەیدا) و بە notifyListeners() نوێ بکاتەوە. |
ProxyProvider | provider package provider | A class depends on another provider but is not a ChangeNotifier: a count, a summary, a profile built from AuthService. پشت بە providerـێکی تر دەبەستێت بەڵام ChangeNotifier نییە: ژمارەیەک، پوختەیەک، پرۆفایلێک کە لە AuthServiceـەوە دروست دەکرێت. |
ChangeNotifierProxyProvider | provider package provider | A ChangeNotifier depends on another provider and must update when it changes, keeping its own state.ChangeNotifierـێک پشت بە providerـێکی تر دەبەستێت و دەبێت کاتێک ئەو دەگۆڕێت نوێ ببێتەوە، لە کاتێکدا خۆی دەهێڵێتەوە. |
Provider | provider package provider | A value that never changes (a service, a configuration).نرخێک کە هەرگیز ناگۆڕێت (، ). |
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ـێکی تر دەبەستێت و کاتێک ئەو دەگۆڕێت نوێ دەبێتەوە.
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 یان |
Exercisesڕاهێنانەکان
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. هەموو بەشەکانی تری دارەکە وەک خۆیان دەمێننەوە:
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.
لەگەڵ پێنج ئەرکەکەی وانەکە نیشانەکە ٤ پیشان دەدات، چونکە یەکێکیان پێشتر تەواو کراوە.
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ـێکی تر دەبەستێت.
This code compiles but the greeting never changes after a new Settings object arrives. Find both problems and fix them.
ئەم کۆدە compile دەبێت بەڵام دوای گەیشتنی Settingsـی نوێ، سڵاوەکە هەرگیز ناگۆڕێت. هەردوو کێشەکە بدۆزەرەوە و چاکیان بکە.
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ـێکی نوێ دروست دەکات و کۆنەکە لەدەست دەدات. چاککراوە:
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),
),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)
updateruns (), badge rebuilt. - (2)
updateruns (still 6), badge not rebuilt: the value did not change. - (3)
updateruns (), badge rebuilt.
- (1)
updateجێبەجێ دەبێت ()، نیشانەکە دووبارە دروست دەکرێتەوە. - (2)
updateجێبەجێ دەبێت (هێشتا ٦)، نیشانەکە دووبارە دروست ناکرێتەوە: نرخەکە نەگۆڕا. - (3)
updateجێبەجێ دەبێت ()، نیشانەکە دووبارە دروست دەکرێتەوە.
Summaryپوختە
- A ProxyProvider holds no state: its value comes from its
updatecallback, 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
updatea quick calculation. ProxyProvider2…ProxyProvider6combine 2–6 sources; the last type is the result.- A ChangeNotifierProxyProvider builds a ChangeNotifier once and keeps it in sync:
createmakes it,updatepasses the new values in (notifier!..update(value)). Never create the notifier insideupdate. - A source that changes must notify (ChangeNotifierProvider, FutureProvider, StreamProvider); a plain Provider never does. List sources first.
- ChangeNotifier is built into Flutter; all the
…Providerclasses 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زاراوە سەرەکییەکان
Project: A To-Do App with Providersپڕۆژە: ئەپێکی ئەرکەکان بە providerـەکان
Related lecturesوانە پەیوەندیدارەکان
References and further readingسەرچاوەکان و خوێندنەوەی زیاتر
This lecture is a first edition. The Kurdish text and terminology are waiting for review by Kurdish IT teachers.ئەم وانەیە چاپی یەکەمە. دەقە کوردییەکە و زاراوەکانی چاوەڕێی پێداچوونەوەی مامۆستایانی کوردی بواری IT ن.