أساسيات حزمة Provider
تعلّم استخدام حزمة Provider، وهي حل شائع في Flutter، لإدارة الحالة ببساطة وقابلية للتوسع عبر تطبيقك
أساسيات حزمة Provider درس مجاني في Flutter Mobile Development على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Flutter Mobile Development، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Flutter Mobile Development 4 دروس في المجموع.
بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.
What is Provider?
The Provider package is a popular and simple solution for state management in Flutter. It's built on top of Flutter's own InheritedWidget, making it efficient and easy to understand.
It helps you share data and state across your widget tree without complex callbacks or prop drilling.
The Data Holder: ChangeNotifier
At the heart of Provider is the ChangeNotifier class. You extend this class for any object that holds state you want to share.
When your state changes, you call notifyListeners() to tell all listening widgets to rebuild.
import 'package:flutter/material.dart';
// 1. Extend ChangeNotifier
class MyCounter extends ChangeNotifier {
int _count = 0; // Private state variable
// Getter to access the count
int get count => _count;
// Method to modify state and notify listeners
void increment() {
_count++;
notifyListeners(); // Important! Triggers UI updates
}
}Making State Available
To make your ChangeNotifier instance available to widgets, you use a ChangeNotifierProvider. You wrap a part of your widget tree (usually your whole app) with it.
The create callback provides an instance of your state object.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// Our state class from before
class MyCounter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
void main() {
runApp(
// Wrap the app with ChangeNotifierProvider
ChangeNotifierProvider(
create: (context) => MyCounter(), // Create an instance
child: MyApp(), // The widget tree that can access MyCounter
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Setup')),
body: Center(
child: Text(
'MyCounter is now provided!',
style: TextStyle(fontSize: 24),
),
),
),
);
}
}Listening for UI Rebuilds
The most common way for a widget to listen to state changes and rebuild its UI is using context.watch. This method makes the widget dependent on the provided state.
When notifyListeners() is called, any widget using watch will rebuild.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyCounter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => MyCounter(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 1. Use context.watch<MyCounter>() to get the current state
// This widget will rebuild when MyCounter changes.
final counter = context.watch<MyCounter>();
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Watch for Changes')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Count: ${counter.count}', // Display the count
style: TextStyle(fontSize: 32),
),
SizedBox(height: 20),
// Button to increment (action will be added next)
ElevatedButton(
onPressed: () {
// How to trigger increment? See next scene!
},
child: Text('Increment (Not yet connected)'),
),
],
),
),
),
);
}
}Actions without Rebuilds
Sometimes you need to access state methods (like increment()) but don't want the widget to rebuild. For this, use context.read.
It provides a one-time access to the state object and does not establish a listening relationship.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyCounter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => MyCounter(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = context.watch<MyCounter>(); // Listen for UI updates
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Read for Actions')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Count: ${counter.count}',
style: TextStyle(fontSize: 32),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// 1. Use context.read<MyCounter>() to get the instance
// 2. Call the increment method
context.read<MyCounter>().increment();
},
child: Text('Increment Counter'),
),
],
),
),
),
);
}
}Alternative for One-Time Access
Before context.read, the common way to access state without listening was Provider.of.
It works exactly like context.read, but read is often preferred for its conciseness. Both are suitable for event handlers like onPressed.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyCounter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => MyCounter(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = context.watch<MyCounter>(); // UI listens
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider.of(listen: false)')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Count: ${counter.count}',
style: TextStyle(fontSize: 32),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// Use Provider.of with listen: false for one-time access
Provider.of<MyCounter>(context, listen: false).increment();
},
child: Text('Increment Counter (using Provider.of)'),
),
],
),
),
),
);
}
}Fine-Grained Rebuilds with Consumer
The Consumer widget is another way to listen to state changes. Unlike context.watch(), which rebuilds the entire widget, Consumer only rebuilds its direct child (the builder function).
This can be useful for performance if only a small part of a large widget needs to react to state changes.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyCounter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => MyCounter(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Consumer Widget')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'This text does NOT rebuild.',
style: TextStyle(fontSize: 20, color: Colors.grey),
),
SizedBox(height: 20),
// Only the Text widget inside Consumer rebuilds
Consumer<MyCounter>(
builder: (context, counter, child) {
print('Consumer rebuilt!'); // See this in console
return Text(
'Count: ${counter.count}',
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
);
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read<MyCounter>().increment();
},
child: Icon(Icons.add),
),
),
);
}
}Providing Multiple States
For apps with multiple independent state objects, wrapping your app with many ChangeNotifierProviders can get messy. MultiProvider simplifies this.
It takes a list of providers, making your widget tree cleaner and easier to manage.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyCounter extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class MyThemeSettings extends ChangeNotifier {
bool _isDark = false;
bool get isDark => _isDark;
void toggleTheme() {
_isDark = !_isDark;
notifyListeners();
}
}
void main() {
runApp(
MultiProvider( // Use MultiProvider for multiple states
providers: [
ChangeNotifierProvider(create: (context) => MyCounter()),
ChangeNotifierProvider(create: (context) => MyThemeSettings()),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Access both states using watch
final counter = context.watch<MyCounter>();
final theme = context.watch<MyThemeSettings>();
return MaterialApp(
theme: theme.isDark ? ThemeData.dark() : ThemeData.light(),
home: Scaffold(
appBar: AppBar(title: Text('MultiProvider Demo')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: ${counter.count}', style: TextStyle(fontSize: 28)),
SizedBox(height: 20),
Text('Dark Mode: ${theme.isDark ? "On" : "Off"}', style: TextStyle(fontSize: 28)),
SizedBox(height: 30),
ElevatedButton(
onPressed: () => context.read<MyThemeSettings>().toggleTheme(),
child: Text('Toggle Theme'),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<MyCounter>().increment(),
child: Icon(Icons.add),
),
),
);
}
}Provider Quick Check
Which of the following is the primary purpose of context.read in the Provider package?
Provider Basics Recap
You've learned the essentials of the Provider package!
ChangeNotifierholds your application state.ChangeNotifierProvidermakes that state available.context.watchlistens for state changes and rebuilds UI.() context.readaccesses state for actions without rebuilding.() Provider.of(listen: false)is an older alternative toread.Consumerallows for fine-grained UI rebuilds.MultiProviderhelps manage multiple state objects cleanly.
Provider is a powerful and flexible tool for managing state in your Flutter apps!
الأسئلة الشائعة
هل درس «أساسيات حزمة Provider» مجاني؟
نعم — نص درس «أساسيات حزمة Provider» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Flutter Mobile Development، انتقل إلى CoddyKit PRO. تتضمن دورة Flutter Mobile Development 4 دروس في المجموع.
ماذا ستتعلم في «أساسيات حزمة Provider»؟
تعلّم استخدام حزمة Provider، وهي حل شائع في Flutter، لإدارة الحالة ببساطة وقابلية للتوسع عبر تطبيقك تتمرن على Flutter Mobile Development مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Flutter Mobile Development؟
لا تُشترط خبرة سابقة. Flutter Mobile Development على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «أساسيات حزمة Provider»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Flutter Mobile Development هذا؟
نعم. كل درس في Flutter Mobile Development يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- setState وInheritedWidget
- أساسيات حزمة Provider
- Riverpod لإدارة الحالة
- نمط BLoC مع Streams