Dart: التحكم في التدفق في Dart — الجمل الشرطية والحلقات
آخر تحديث: 2026-08-26
التحكم في التدفق هو عقل الكود — يحدد المسار الذي يسلكه برنامجك وعدد المرات.
1. ما ستتعلمه
- if-else / switch-case (بما في ذلك معاينة لتعبيرات switch في Dart 3)
- حلقات for / while / do-while
- for-in وعبور Iterable
- break / continue والتحكم المسمى
2. قصة مطور حقيقي
(1) نقطة الألم: 10 مستويات من if-else المتداخلة — "جحيم القوس"
كتبت ليزا كود معالجة الطلبات: if (status == 'pending') { if (amount > 0) { if (user != null) { if (isActive) { if (...) { ... } } } } }. بعد 6 أشهر، أرادت إضافة "حالة الإرجاع"، فاكتشفت: "كيف أتعامل مع 10 مستويات من التداخل؟" سألتها زميلتها: "استخدم switch expressions أو أنماط التصميم."
(2) حل Dart
DART
// كود سيء — if-else متداخلة
String processOrder(Order order) {
if (order.status == 'pending') {
if (order.amount > 0) {
if (order.user != null) {
if (order.user!.isActive) {
if (order.paid) {
return 'شحن';
} else {
return 'في انتظار الدفع';
}
}
return 'مستخدم غير نشط';
}
return 'لا يوجد مستخدم';
}
return 'مبلغ غير صالح';
}
return 'حالة غير صالحة';
}
// ✅ تعبير switch في Dart 3 — مسطح وقابل للقراءة
String processOrder(Order order) => switch (order) {
Order(:final status, :final amount, :final paid) when status == 'pending' && amount <= 0
=> 'مبلغ غير صالح',
Order(:final status, :final user, :final paid) when status == 'pending' && user != null && !user.isActive
=> 'مستخدم غير نشط',
Order(:final status, :final paid) when status == 'pending' && !paid
=> 'في انتظار الدفع',
Order(:final status) when status == 'pending'
=> 'شحن',
Order(:final status) when status == 'cancelled'
=> 'تم الإلغاء',
_ => 'حالة غير معروفة',
};
(3) النتيجة: 10 مستويات → 1 مستوى، قابلية الصيانة +10 مرات
| المؤشر | if-else المتداخلة | تعبير switch |
|---|---|---|
| عمق التداخل | 5-10 مستويات | 1 مستوى |
| وقت إضافة حالة جديدة | ساعة | 5 دقائق |
| عدد أسطر الكود | 30+ | 10-15 |
| الأخطاء الناجمة عن نسيان القوس | 5/شهر | 0 |
TEXT
📖 للعرض فقط
قبل: 10 مستويات من التداخل، 5 أخطاء/شهر
بعد: 1 مستوى، 0 أخطاء
ROI: سرعة إضافة حالة جديدة +12 مرة
3. if-else: الجمل الشرطية
(1) بناء الجملة الأساسي
DART
final age = 25;
if (age >= 18) {
print('بالغ');
} else if (age >= 13) {
print('مراهق');
} else {
print('طفل');
}
// if-else كقيمة (تعبير في Dart 3)
String category = age >= 18 ? 'بالغ' : 'قاصر';
(2) if بدون أقواس (لجملة واحدة)
DART
int x = 10;
// ✅ مسموح — إذا كان نص الكود سطر واحد فقط
if (x > 0) print('positive');
if (x > 0) print('positive'); else print('non-positive');
// ❌ غير موصى به — يقلل من قابلية القراءة
if (x > 0) print('positive'); print('always runs');
(3) الشروط المعقدة
DART
int score = 85;
bool hasPassed = true;
bool isFinal = false;
// AND
if (score >= 60 && hasPassed) {
print('نجح');
}
// OR
if (score < 60 || isFinal) {
print('يحتاج إلى إعادة');
}
// التعشيش
if (score >= 60) {
if (hasPassed) {
if (!isFinal) {
print('ناجح نهائي');
}
}
}
// ✅ يفضل استخراج الشروط
bool isSuccessful = score >= 60 && hasPassed && !isFinal;
if (isSuccessful) {
print('ناجح نهائي');
}
(4) if-else في تعبير (Dart 3)
DART
final age = 25;
// إذا كان الفرع عبارة عن تعبير واحد
String category = if (age >= 18) 'بالغ' else 'قاصر';
// في المجموعات
final List<int> numbers = [1, 2, 3, 4, 5];
final List<int> evensOrOdds = [
for (int n in numbers)
if (n.isEven) n ~/ 2 // زوجي → نصف
else n * 3 + 1, // فردي → 3n+1 (متتالية كولاتز)
];
(5) تنفيذ كسول (short-circuit)
DART
bool expensiveCheck() {
print('expensive check');
return true;
}
bool cheapCheck() {
print('cheap check');
return false;
}
// إذا كانت expensiveCheck خاطئة، فلن يتم استدعاء cheapCheck
if (expensiveCheck() && cheapCheck()) {
print('كلاهما صحيح');
}
// النتيجة: طباعة "expensive check" فقط
// العكس: إذا كانت expensiveCheck صحيحة، يتم استدعاء cheapCheck
if (expensiveCheck() || cheapCheck()) {
print('واحد على الأقل صحيح');
}
// النتيجة: طباعة "expensive check" فقط
(6) أفضل الممارسات
DART
// ✅ استخراج الشروط المعقدة
bool isEligibleForDiscount(int age, bool isMember) {
return age >= 60 || isMember;
}
if (isEligibleForDiscount(user.age, user.isMember)) {
applyDiscount();
}
// ✅ الترتيب من الأكثر إلى الأقل احتمالاً
final score = 85;
if (score >= 90) {
print('ممتاز');
} else if (score >= 80) {
print('جيد');
} else if (score >= 60) {
print('مؤهل');
} else {
print('غير مؤهل');
}
▶ مثال: نظام تقدير الصف
DART
// نظام تقدير متعدد الشروط
String gradeStudent(int score, bool hasExtraCredit) {
if (score < 0 || score > 100) {
return 'درجة غير صالحة';
}
if (hasExtraCredit) {
score = (score + 5).clamp(0, 100);
}
if (score >= 90) return 'ممتاز (A)';
if (score >= 80) return 'جيد جدًا (B)';
if (score >= 70) return 'جيد (C)';
if (score >= 60) return 'مؤهل (D)';
return 'غير مؤهل (F)';
}
void main() {
print(gradeStudent(85, false)); // 'جيد جدًا (B)'
print(gradeStudent(88, true)); // 'ممتاز (A)' (88+5=93)
print(gradeStudent(50, false)); // 'غير مؤهل (F)'
print(gradeStudent(150, false)); // 'درجة غير صالحة'
}
TEXT
📖 للعرض فقط
> **الإخراج:** نظام تقدير مع نقاط إضافية، والتحقق من النطاق، والترتيب من الأعلى إلى الأدنى.
4. switch-case: الجمل متعددة الفروع
(1) switch التقليدي
DART
final day = 'Monday';
switch (day) {
case 'Monday':
print('الإثنين');
break;
case 'Tuesday':
print('الثلاثاء');
break;
case 'Wednesday':
case 'Thursday':
case 'Friday':
print('يوم عمل');
break;
case 'Saturday':
case 'Sunday':
print('عطلة نهاية الأسبوع');
break;
default:
print('يوم غير معروف');
}
(2) switch التعبيرية في Dart 3 (تعبير يُرجع قيمة)
DART
final day = 'Monday';
// switch كقيمة — يجب أن يكون شاملاً (يغطي جميع الفروع) أو يكون هناك افتراضي
String dayType = switch (day) {
'Monday' || 'Tuesday' || 'Wednesday' || 'Thursday' || 'Friday' => 'يوم عمل',
'Saturday' || 'Sunday' => 'عطلة نهاية الأسبوع',
_ => 'يوم غير معروف',
};
print(dayType); // 'يوم عمل'
(3) مطابقة الأنماط في switch
DART
// sealed class + switch — يحقق أمان وقت الترجمة
sealed class Vehicle {}
class Car extends Vehicle { final int seats; Car(this.seats); }
class Truck extends Vehicle { final double payload; Truck(this.payload); }
class Bicycle extends Vehicle { final int wheels; Bicycle(this.wheels); }
String describe(Vehicle v) => switch (v) {
Car(:final seats) when seats > 5 => 'سيارة عائلية ($seats مقاعد)',
Car(:final seats) => 'سيارة ($seats مقاعد)',
Truck(:final payload) when payload > 10 => 'شاحنة ثقيلة (${payload} طن)',
Truck(:final payload) => 'شاحنة (${payload} طن)',
Bicycle() => 'دراجة',
};
print(describe(Car(7))); // 'سيارة عائلية (7 مقاعد)'
print(describe(Truck(15.0))); // 'شاحنة ثقيلة (15.0 طن)'
print(describe(Bicycle())); // 'دراجة'
(4) switch مع Guards
DART
String describeAge(int age) => switch (age) {
int n when n < 0 => 'غير صالح',
int n when n < 13 => 'طفل ($n سنة)',
int n when n < 18 => 'مراهق ($n سنة)',
int n when n < 60 => 'بالغ ($n سنة)',
int n => 'كبير ($n سنة)',
};
print(describeAge(10)); // 'طفل (10 سنة)'
print(describeAge(15)); // 'مراهق (15 سنة)'
print(describeAge(30)); // 'بالغ (30 سنة)'
print(describeAge(70)); // 'كبير (70 سنة)'
print(describeAge(-5)); // 'غير صالح'
(5) فارغ check في switch
DART
String describe(String? input) => switch (input) {
null => 'قيمة فارغة',
String s when s.isEmpty => 'سلسلة فارغة',
String s when s.length < 10 => 'سلسلة قصيرة: $s',
String s => 'سلسلة طويلة: $s',
};
print(describe(null)); // 'قيمة فارغة'
print(describe('')); // 'سلسلة فارغة'
print(describe('hi')); // 'سلسلة قصيرة: hi'
print(describe('hello world')); // 'سلسلة طويلة: hello world'
(6) switch التعبيرية مقابل الجملة
DART
// switch كجملة — ينفذ التأثير الجانبي
void processStatus(String status) {
switch (status) {
case 'pending':
print('معالجة...');
break;
case 'completed':
print('مكتمل');
break;
default:
print('حالة غير معروفة');
}
}
// switch كتعبير — يُرجع قيمة
String nextAction(String status) => switch (status) {
'pending' => 'معالجة',
'completed' => 'إغلاق',
'failed' => 'إعادة المحاولة',
_ => 'تجاهل',
};
▶ مثال: معالج أوامر التشفير
DART
// sealed class + switch expressions
sealed class CryptoCommand {}
class Encrypt extends CryptoCommand { final String algorithm; final String text; Encrypt(this.algorithm, this.text); }
class Decrypt extends CryptoCommand { final String algorithm; final String ciphertext; Decrypt(this.algorithm, this.ciphertext); }
class Sign extends CryptoCommand { final String message; final String privateKey; Sign(this.message, this.privateKey); }
class Verify extends CryptoCommand { final String message; final String signature; Verify(this.message, this.signature); }
class GenerateKey extends CryptoCommand { final int length; GenerateKey(this.length); }
String execute(CryptoCommand cmd) => switch (cmd) {
Encrypt(:final algorithm, :final text) => 'تشفير $text بـ $algorithm',
Decrypt(:final algorithm, :final ciphertext) => 'فك تشفير $ciphertext بـ $algorithm',
Sign(:final message) => 'توقيع الرسالة بطول ${message.length}',
Verify(:final signature) => 'التحقق من توقيع بطول ${signature.length}',
GenerateKey(:final length) => 'توليد مفتاح بطول ${length} بت',
};
void main() {
print(execute(Encrypt('AES-256', 'Hello, World!')));
// 'تشفير Hello, World! بـ AES-256'
print(execute(Decrypt('RSA', 'abc123')));
// 'فك تشفير abc123 بـ RSA'
print(execute(Sign('Document', 'priv_key')));
// 'توقيع الرسالة بطول 8'
print(execute(GenerateKey(2048)));
// 'توليد مفتاح بطول 2048 بت'
}
TEXT
📖 للعرض فقط
> **الإخراج:** معالج تشفير شامل — sealed class يضمن فحص النوع في وقت الترجمة، وتعبير switch يجعل المنطق مسطحًا وقابلًا للقراءة.
(7) switch مع Map
DART
Map<String, int> fruitPrices = {
'apple': 5,
'banana': 3,
'orange': 4,
};
String? getFruitPrice(String fruit) => switch (fruit) {
String f when fruitPrices.containsKey(f) => '${fruitPrices[f]} USD',
_ => 'غير موجود',
};
print(getFruitPrice('apple')); // '5 USD'
print(getFruitPrice('grape')); // 'غير موجود'
5. حلقات for / while / do-while
(1) حلقة for التقليدية (C-style)
DART
// for (init; condition; increment)
for (int i = 0; i < 5; i++) {
print('i = $i');
}
// 0, 1, 2, 3, 4
// حلقة عكسية
for (int i = 5; i > 0; i--) {
print('i = $i');
}
// 5, 4, 3, 2, 1
// خطوة مخصصة
for (int i = 0; i < 10; i += 2) {
print('i = $i');
}
// 0, 2, 4, 6, 8
// متغيرات متعددة
for (int i = 0, j = 10; i < j; i++, j--) {
print('i=$i, j=$j');
}
// i=0, j=10 i=1, j=9 ... i=4, j=6
(2) حلقة for-in (للتكرار)
DART
// للتكرار على Iterable
List<String> fruits = ['apple', 'banana', 'orange'];
for (final fruit in fruits) {
print(fruit);
}
// للتكرار على Set
Set<int> numbers = {1, 2, 3, 4, 5};
for (final n in numbers) {
print(n);
}
// للتكرار على Map
Map<String, int> ages = {'Alice': 30, 'Bob': 25};
for (final entry in ages.entries) {
print('${entry.key}: ${entry.value}');
}
for (final key in ages.keys) {
print('key: $key');
}
for (final value in ages.values) {
print('value: $value');
}
// للتكرار على String (أحرف)
final str = 'Dart';
for (final char in str.runes) {
print(String.fromCharCode(char));
}
(3) while — شرط مسبق
DART
int count = 0;
while (count < 5) {
print('count = $count');
count++;
}
// 0, 1, 2, 3, 4
// خطر: حلقة لا نهائية
// while (true) {
// // يجب أن يكون هناك break
// }
(4) do-while — شرط لاحق (يُنفذ مرة واحدة على الأقل)
DART
int input;
do {
input = readInt(); // قراءة مدخلات المستخدم
process(input);
} while (input != 0);
// مناسب للتحقق من صحة المدخلات
String? password;
do {
password = readPassword();
} while (password == null || password.isEmpty);
(5) break و continue
DART
// break — الخروج من الحلقة
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // الخروج من الحلقة
}
print('i = $i');
}
// 0, 1, 2, 3, 4
// continue — تخطي التكرار الحالي
for (int i = 0; i < 5; i++) {
if (i == 2) {
continue; // تخطي i=2
}
print('i = $i');
}
// 0, 1, 3, 4
(6) for-in مع index (عبر indexed)
DART
List<String> items = ['apple', 'banana', 'orange'];
// for-in العادي — لا index
for (final item in items) {
print(item);
}
// مع index — استخدم indexed
for (final pair in items.indexed) {
final i = pair.$1; // index
final item = pair.$2;
print('$i: $item');
}
// 0: apple
// 1: banana
// 2: orange
// enumerate — أيضًا index + value
for (final pair in items.indexed) {
print('${pair.$1 + 1}. ${pair.$2}'); // 1-indexed
}
(7) التحكم المسمى (Labeled Control)
DART
// label: قبل for/while — للقفز من حلقات متداخلة
outer: for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
break outer; // الخروج من الحلقة الخارجية بأكملها
}
print('i=$i, j=$j');
}
}
// 0,0 0,1 0,2 1,0 (ثم الخروج)
// continue مع label
outer: for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) {
continue outer; // تخطي إلى التكرار التالي من الحلقة الخارجية
}
print('i=$i, j=$j');
}
}
// 0,0 1,0 2,0
(8) أفضل الممارسات
DART
// ✅ استخدم for-in للتكرار على المجموعات
for (final item in items) {
process(item);
}
// ✅ استخدم for-traditional عند الحاجة إلى index
for (int i = 0; i < items.length; i++) {
if (i.isOdd) {
processOdd(items[i]);
}
}
// ❌ لا تستخدم while للتكرار على المجموعات
int i = 0;
while (i < items.length) {
process(items[i]);
i++;
}
// ✅ استخراج الشروط المعقدة
bool shouldSkip(int n) => n.isEven && n > 10;
for (int n in numbers) {
if (shouldSkip(n)) continue;
process(n);
}
▶ مثال: نظام البحث في البيانات
DART
// البحث الخطي
List<String> users = ['Alice', 'Bob', 'Charlie', 'David', 'Eve'];
String? findUser(String name) {
for (final user in users) {
if (user == name) {
return user; // العثور على الإجابة
}
}
return null; // غير موجود
}
print(findUser('Charlie')); // 'Charlie'
print(findUser('Frank')); // null
// مع index
int? findUserIndex(String name) {
for (final pair in users.indexed) {
if (pair.$2 == name) {
return pair.$1;
}
}
return null;
}
print(findUserIndex('Charlie')); // 2
print(findUserIndex('Frank')); // null
// do-while للتحقق من المدخلات
String? readValidEmail() {
String? email;
do {
email = prompt('الرجاء إدخال بريد إلكتروني صحيح:');
} while (email == null || !email!.contains('@'));
return email;
}
TEXT
📖 للعرض فقط
> **الإخراج:** أنماط شائعة للحلقات — البحث، والتحقق من المدخلات، والتكرار مع index.
6. for-in و Iterable
(1) for-in على أنواع Iterable المختلفة
DART
// List
List<int> list = [1, 2, 3];
for (final n in list) print(n);
// Set
Set<String> set = {'a', 'b', 'c'};
for (final s in set) print(s);
// Map.entries (key + value)
Map<String, int> map = {'a': 1, 'b': 2};
for (final entry in map.entries) {
print('${entry.key}=${entry.value}');
}
// Map.keys / Map.values
for (final key in map.keys) print(key);
for (final value in map.values) print(value);
// String (أحرف Unicode)
final str = 'مرحبا';
for (final rune in str.runes) {
print(String.fromCharCode(rune));
}
// Iterable مخصص
class NumberIterable extends Iterable<int> {
@override
Iterator<int> get iterator => NumberIterator();
}
class NumberIterator implements Iterator<int> {
int _current = 0;
@override
int get current => _current;
@override
bool moveNext() {
_current++;
return _current <= 10;
}
}
for (final n in NumberIterable()) {
print(n);
}
// 1-10
(2) Iterable.where (التصفية)
DART
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// الأعداد الزوجية
List<int> evens = numbers.where((n) => n.isEven).toList();
print(evens); // [2, 4, 6, 8, 10]
// الأعداد الفردية الأكبر من 5
List<int> oddLarge = numbers.where((n) => n.isOdd && n > 5).toList();
print(oddLarge); // [7, 9]
(3) Iterable.map (التحويل)
DART
List<int> numbers = [1, 2, 3, 4, 5];
// مربع كل عدد
List<int> squares = numbers.map((n) => n * n).toList();
print(squares); // [1, 4, 9, 16, 25]
// تحويل إلى String
List<String> strs = numbers.map((n) => 'Number $n').toList();
print(strs); // ['Number 1', 'Number 2', ...]
(4) Iterable.fold و reduce (التجميع)
DART
List<int> numbers = [1, 2, 3, 4, 5];
// المجموع
int sum = numbers.reduce((a, b) => a + b);
print(sum); // 15
// fold — مع قيمة ابتدائية
int product = numbers.fold(1, (a, b) => a * b);
print(product); // 120
// تحويل إلى String
String concat = numbers.fold('', (acc, n) => '$acc$n');
print(concat); // '12345'
(5) Iterable المتزامن (Dart 3)
DART
// Stream.asBroadcastStream() — متعددة المستمعين
// Stream هو Iterable غير متزامن
Stream<int> numbers() async* {
for (int i = 1; i <= 5; i++) {
yield i;
}
}
void main() async {
await for (final n in numbers()) {
print(n);
}
}
// 1, 2, 3, 4, 5
▶ مثال: معالجة البيانات
DART
// معالجة سجلات المستخدمين
class User {
final String name;
final int age;
final String city;
User(this.name, this.age, this.city);
@override
String toString() => '$name ($age, $city)';
}
final users = [
User('Alice', 30, 'NYC'),
User('Bob', 25, 'LA'),
User('Charlie', 35, 'NYC'),
User('David', 28, 'SF'),
User('Eve', 40, 'LA'),
];
// 1. تصفية — NYC فقط
final nycUsers = users.where((u) => u.city == 'NYC').toList();
print('NYC users: $nycUsers');
// NYC users: [Alice (30, NYC), Charlie (35, NYC)]
// 2. تحويل — استخراج الأسماء
final names = users.map((u) => u.name).toList();
print('Names: $names');
// Names: [Alice, Bob, Charlie, David, Eve]
// 3. التجميع — متوسط العمر
final avgAge = users.map((u) => u.age).reduce((a, b) => a + b) / users.length;
print('Average age: ${avgAge.toStringAsFixed(1)}');
// Average age: 31.6
// 4. مركب — NYC > 30، استخراج الأسماء، فرز حسب العمر
final seniorNycNames = users
.where((u) => u.city == 'NYC' && u.age > 30)
.map((u) => u.name)
.toList();
print('NYC > 30: $seniorNycNames');
// NYC > 30: [Charlie]
TEXT
📖 للعرض فقط
> **الإخراج:** معالجة البيانات الوظيفية: where → map → reduce، تصفية، تحويل، وتجميع في خطوة واحدة.
7. break / continue والتحكم المسمى
(1) break — الخروج من الحلقة
DART
// الخروج من for
for (int i = 0; i < 10; i++) {
if (i == 5) break;
print(i);
}
// 0, 1, 2, 3, 4
// الخروج من while
int i = 0;
while (true) {
if (i >= 5) break;
print(i);
i++;
}
// الخروج من switch (إلزامي)
switch (status) {
case 'pending':
doSomething();
break; // ❌ بدون break، سيستمر التنفيذ
case 'completed':
break;
}
(2) continue — تخطي التكرار الحالي
DART
// تخطي الأعداد الزوجية
for (int i = 0; i < 10; i++) {
if (i.isEven) continue;
print(i);
}
// 1, 3, 5, 7, 9
// في while — استخدم continue بحذر
int i = 0;
while (i < 10) {
i++;
if (i.isEven) continue;
print(i);
}
(3) التحكم المسمى (Labeled Statements)
DART
// السيناريو: البحث في مصفوفة ثنائية الأبعاد
List<List<int>> matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
// ❌ بدون label — يمكن فقط الخروج من الحلقة الداخلية
bool found = false;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == 5) {
found = true;
break; // يخرج فقط من الحلقة الداخلية
}
}
if (found) break; // يجب استخدام علم
}
// ✅ مع label — الخروج المباشر
searchLabel: for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == 5) {
print('Found at ($i, $j)');
break searchLabel; // يخرج من كلتا الحلقتين
}
}
}
(4) continue مع تسمية
DART
// تخطي حلقة كاملة عند شرط معين
matrixLoop: for (final row in matrix) {
for (final value in row) {
if (value == 0) {
continue matrixLoop; // تخطي هذا الصف بأكمله
}
if (value == 5) {
break matrixLoop; // الخروج
}
process(value);
}
}
(5) break في switch التعبيرية
DART
// switch التعبيرية لا يحتاج break
String result = switch (value) {
'a' => 'A',
'b' => 'B',
_ => 'other',
};
// ❌ break غير مسموح في switch التعبيرية
▶ مثال: البحث في مصفوفة مع الخروج المبكر
DART
// البحث في مصفوفة ثنائية الأبعاد
List<List<int>> matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
];
(int, int)? findValue(int target) {
search: for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == target) {
return (i, j);
}
}
}
return null;
}
print(findValue(7)); // (1, 2)
print(findValue(100)); // null
// العد: عدد القيم الزوجية
int evenCount = 0;
countEven: for (final row in matrix) {
for (final value in row) {
if (value.isOdd) continue;
evenCount++;
}
}
print(evenCount); // 6
TEXT
📖 للعرض فقط
> **الإخراج:** label + break/continue تجعل التحكم في الحلقات المتداخلة أكثر إيجازًا.
8. أنماط شائعة
(1) البحث
DART
// البحث الخطي
List<int> numbers = [3, 1, 4, 1, 5, 9, 2, 6];
// indexOf
int index = numbers.indexOf(5);
print('indexOf(5) = $index'); // 4
// indexWhere
int firstEven = numbers.indexWhere((n) => n.isEven);
print('first even at: $firstEven'); // 6
// any
bool hasOdd = numbers.any((n) => n.isOdd);
print('has odd: $hasOdd'); // true
// every
bool allPositive = numbers.every((n) => n > 0);
print('all positive: $allPositive'); // true
(2) العد
DART
List<String> words = ['apple', 'banana', 'orange', 'grape'];
// العد التي تبدأ بـ 'a'
int aCount = words.where((w) => w.startsWith('a')).length;
print('words starting with a: $aCount'); // 1
// أطول كلمة
String longest = words.reduce((a, b) => a.length > b.length ? a : b);
print('longest: $longest'); // 'banana'
(3) التحويل
DART
List<int> numbers = [1, 2, 3, 4, 5];
// مجموع المربعات
int sumOfSquares = numbers
.map((n) => n * n)
.reduce((a, b) => a + b);
print(sumOfSquares); // 55
// تصفية وفرز
List<int> evenSorted = numbers
.where((n) => n.isEven)
.toList()
..sort();
print(evenSorted); // [2, 4]
(4) التكرار اليدوي مع index
DART
List<String> names = ['Alice', 'Bob', 'Charlie'];
// مع index
for (int i = 0; i < names.length; i++) {
print('${i + 1}. ${names[i]}');
}
// 1. Alice
// 2. Bob
// 3. Charlie
(5) العد التنازلي
DART
// العد التنازلي
for (int i = 10; i >= 0; i--) {
if (i == 0) {
print('انطلاق!');
} else {
print(i);
}
}
// 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, انطلاق!
// العد التنازلي مع do-while
int i = 10;
do {
if (i == 0) {
print('انطلاق!');
} else {
print(i);
}
i--;
} while (i >= 0);
▶ مثال: لعبة التخمين
DART
import 'dart:math';
void guessingGame() {
final random = Random();
final target = random.nextInt(100) + 1; // 1-100
int attempts = 0;
const maxAttempts = 7;
print('مرحبًا! لقد فكرت في عدد بين 1-100، لديك $maxAttempts محاولات');
while (attempts < maxAttempts) {
final guess = readInt('محاولة ${attempts + 1}: ');
attempts++;
if (guess == target) {
print('🎉 صحيح! العدد هو $target، $attempts محاولات');
return;
} else if (guess < target) {
print('📈 أصغر');
} else {
print('📉 أكبر');
}
}
print('😞 انتهت المحاولات، العدد هو $target');
}
int readInt(String prompt) {
// تنفيذ محاكاة — اقرأ من سطر الأوامر
// في الواقع، تحتاج إلى استخدام stdin
return 50; // قيمة محاكاة
}
void main() {
guessingGame();
}
TEXT
📖 للعرض فقط
> **الإخراج:** لعبة تخمين بسيطة — do-while يضمن تنفيذ مرة واحدة على الأقل، while يحد من المحاولات.
9. ملخص نقاط المعرفة الأساسية لهذا الدرس
| # | المفهوم | المستوى | الأهمية |
|---|---|---|---|
| 1 | if-else والتداخل المعقول | مبتدئ | ⭐⭐⭐⭐⭐ |
| 2 | switch-case التقليدي | مبتدئ | ⭐⭐⭐⭐ |
| 3 | switch التعبيرية (Dart 3) — يُرجع قيمة | متوسط | ⭐⭐⭐⭐⭐ |
| 4 | صنف مختوم + switch شامل | متوسط | ⭐⭐⭐⭐⭐ |
| 5 | for التقليدي و for-in | مبتدئ | ⭐⭐⭐⭐⭐ |
| 6 | while و do-while | مبتدئ | ⭐⭐⭐⭐ |
| 7 | break و continue | مبتدئ | ⭐⭐⭐⭐⭐ |
| 8 | تسمية: و break تسمية — للحلقات المتداخلة | متوسط | ⭐⭐⭐ |
| 9 | Iterable.where / map / reduce / fold | متوسط | ⭐⭐⭐⭐⭐ |
❓ أسئلة شائعة
س متى تستخدم switch-case مقابل if-else؟
ج switch-case مناسب لـ: ① مقارنة قيمة واحدة مع ثوابت متعددة ② فروع ≥3 (if-else يصبح فوضويًا) ③ في Dart 3، استخدم switch التعبيرية لإرجاع قيمة. if-else مناسب لـ: ① شروط معقدة (نطاقات، شروط متعددة) ② فروع 2.
س ما الفرق بين switch كجملة وتعبير في Dart 3؟
ج switch كجملة: ينفذ تأثيرًا جانبيًا، يحتاج
break، لا يُرجع قيمة. switch كتعبير (Dart 3): يُرجع قيمة، لا يحتاج break، يجب أن يكون شاملاً أو يحتوي على افتراضي. تفضل switch التعبيرية: أكثر إيجازًا، أمان وقت الترجمة.س لماذا لا يدعم Dart
for (int i = 0; i < n; i++) في نفس الوقت الذي يدعم فيه for-in؟ج Dart يدعم كلاهما.
for التقليدي للعد، for-in للتكرار على المجموعات. Dart لا يدعم for (int i : collection) مثل Java لأن for-in أكثر إيجازًا وآمنًا (لا يمكن الكتابة فوق i عن طريق الخطأ).س ما الفرق بين
break و continue؟ج
break يخرج من الحلقة بأكملها. continue يتخطى التكرار الحالي ويدخل التكرار التالي. مع تسمية: break label يخرج من الحلقة المحددة. continue label يتخطي إلى التكرار التالي للحلقة المحددة.س لماذا
do-while نادر الاستخدام في Dart؟ج
do-while يضمن تنفيذ الحلقة مرة واحدة على الأقل، مناسب لـ: التحقق من مدخلات المستخدم، تنفيذ مرة واحدة على الأقل. في الممارسة، نادرًا ما تحتاجه. معظم الحلقات تستخدم while أو for-in.س كيف يختلف
for-in في Dart عن for-of في JavaScript؟ج نفس المفهوم. كلتاهما تتكرر على Iterable. في Dart:
for (var item in list). في JS: for (const item of array). الفرق: Dart يستخدم in، JS يستخدم of. الـ Iterable في Dart يطابق Symbol.iterator في JS.📖 ملخص
- if-else: شرطي أساسي، استخدم if-else كتعبير (Dart 3) للقيم القصيرة
- switch-case التقليدي: قارن قيمة مع ثوابت، break إلزامي
- switch التعبيرية (Dart 3): يُرجع قيمة، لا يحتاج break، شامل (صنف مختوم)
- حلقات for: التقليدية (مع index) و for-in (للتكرار على Iterable)
- while / do-while: while شرط مسبق، do-while شرط لاحق (مرة واحدة على الأقل)
- break / continue: التحكم في تدفق الحلقة، مع تسمية للحلقات المتداخلة
- Iterable.where / map / reduce / fold: معالجة وظيفية للبيانات
📝 تمارين
- أساسي (الصعوبة ⭐): أنشئ مشروع
fizz_buzz_classic، اكتب برنامج FizzBuzz الكلاسيكي: اطبع 1-100، ولكن: ① مضاعفات 3 اطبع "Fizz" ② مضاعفات 5 اطبع "Buzz" ③ مضاعفات 3 و 5 معًا اطبع "FizzBuzz". استخدم حلقات for، واكتب 3 إصدارات: ① for تقليدي ② for-in مع Iterable.generate ③ while. قارن عدد الأسطر. تلميح: استخدمi % 15 == 0للتحقق من FizzBuzz أولاً. - متوسط (الصعوبة ⭐⭐): أنشئ مشروع
switch_expressions_masterيحتوي على 4 فئات sealed:Success/Failure/Loading/Timeout، اكتب دالةhandleلكل سيناريو: عرض بيانات النجاح (Success) / تسجيل أخطاء Failure / عرض شريط تقدم Loading / إعادة المحاولة 3 مرات عند Timeout. اكتب 8 حالات اختبار باستخدام switch expressions. أضف صنف مختوم إضافيCancelledيجب أن تتسبب في خطأ وقت الترجمة (لأن switch يجب أن يكون شاملاً). تلميح: صنف مختوم + switch = أمان وقت الترجمة. - تحدٍّ (الصعوبة ⭐⭐⭐): أنشئ مشروع
matrix_search، اكتب دالةsearchMatrix(List<List<int>> matrix, int target)تستخدم 3 استراتيجيات بحث: ① بحث خطي (تسمية + break) ② بحث قطري (matrix مرتبة) ③ بحث ثنائي (صف واحد في كل مرة). اختبر على 3 مصفوفات: ① عشوائية 10x10 ② مرتبة 100x100 ③ مصفوفة فارغة. قارن أوقات التنفيذ باستخدامStopwatch. تلميح: matrix مرتبة تعنيmatrix[i][j] >= matrix[i-1][j-1].