Dart: التحكم في التدفق في Dart — الجمل الشرطية والحلقات

آخر تحديث: 2026-08-26

التحكم في التدفق هو عقل الكود — يحدد المسار الذي يسلكه برنامجك وعدد المرات.

1. ما ستتعلمه


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.

📖 ملخص


📝 تمارين

  1. أساسي (الصعوبة ⭐): أنشئ مشروع 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 أولاً.
  2. متوسط (الصعوبة ⭐⭐): أنشئ مشروع switch_expressions_master يحتوي على 4 فئات sealed: Success / Failure / Loading / Timeout، اكتب دالة handle لكل سيناريو: عرض بيانات النجاح (Success) / تسجيل أخطاء Failure / عرض شريط تقدم Loading / إعادة المحاولة 3 مرات عند Timeout. اكتب 8 حالات اختبار باستخدام switch expressions. أضف صنف مختوم إضافي Cancelled يجب أن تتسبب في خطأ وقت الترجمة (لأن switch يجب أن يكون شاملاً). تلميح: صنف مختوم + switch = أمان وقت الترجمة.
  3. تحدٍّ (الصعوبة ⭐⭐⭐): أنشئ مشروع 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].

← السابق: عوامل تشغيل Dart | التالي: الدوال →

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%