شارِك حالة التطبيق باستخدام ChangeNotifier وProvider، مع إتقان watch وread وselect لإعادة بناء موجَّهة.
افتح هذا الدرس في Kodokonيكفي setState ما دامت الحالة تحيا وتموت مع شاشة واحدة. لكن بمجرد أن تُشارَك البيانات - سلة، أو جلسة، أو تفضيلات - يصبح تمريرها من بانٍ إلى بانٍ غير قابل للإدارة. يوفّر ChangeNotifier أبسط قابل للرصد في الـ SDK؛ ويحقنه provider في الشجرة، فلا يعيد بناء سوى الودجتات المشترِكة. ومقارنةً بـ Riverpod أو Bloc، يبقى هذا الاقتران الأسهل تدقيقًا: سحر قليل، واعتمادية واحدة، وآلية يمكنك شرحها في جملة واحدة.
flutter pub add providerالمُبلِّغ المُصمَّم جيدًا يُغلِّف: حقول خاصة، وجوالِب (getters) للقراءة فقط، وتغييرات لا تتم إلا عبر دوال تنتهي بـ notifyListeners(). وهذا العقد هو ما يضمن ألّا يفلت أيّ تغيير في الحالة من الواجهة.
import 'package:flutter/foundation.dart';
class CartModel extends ChangeNotifier {
final List<String> _items = [];
List<String> get items => List.unmodifiable(_items);
int get count => _items.length;
void add(String item) {
_items.add(item);
notifyListeners();
}
void remove(String item) {
_items.remove(item);
notifyListeners();
}
}ثلاث نقاط دخول، وثلاثة استخدامات. يشترِك context.watch<T>() بالودجت: فيعيد بناءه عند كل notifyListeners. ويقرأ context.read<T>() دون اشتراك: فهو أداة ردود النداء مثل onPressed. ويشترِك context.select<T, R>() في إسقاط فقط: فلا يُعاد بناء الودجت إلا إذا تغيّرت تلك القيمة بالتحديد. القاعدة الذهبية: watch وselect في build فقط، وread في المعالِجات - لا العكس أبدًا.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CounterModel extends ChangeNotifier {
int _value = 0;
int get value => _value;
void increment() {
_value++;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterModel(),
child: const CounterApp(),
),
);
}
class CounterApp extends StatelessWidget {
const CounterApp({super.key});
@override
Widget build(BuildContext context) {
final value = context.watch<CounterModel>().value;
return MaterialApp(
home: Scaffold(
body: Center(child: Text('Count: $value')),
floatingActionButton: FloatingActionButton(
onPressed: () =>
context.read<CounterModel>().increment(),
child: const Icon(Icons.add),
),
),
);
}
}import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class CartModel extends ChangeNotifier {
final List<String> _items = [];
int get count => _items.length;
}
class CartBadge extends StatelessWidget {
const CartBadge({super.key});
@override
Widget build(BuildContext context) {
final count = context.select(
(CartModel cart) => cart.count,
);
return Badge(
label: Text('$count'),
child: const Icon(Icons.shopping_cart),
);
}
}
onPressed، كيف تُطلق cart.add(...) دون إنشاء اشتراك لا لزوم له؟context.watch<CartModel>().add(...)context.read<CartModel>().add(...)context.select((CartModel c) => c.add(...))notifyListeners() بالضبط؟List.unmodifiable(_items) بدلًا من _items مباشرةً؟notifyListeners، ومن ثمّ دون إعادة بناء.