Dart: متغيرات Dart وأنواع البيانات — خمسة طرق للإعلان

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

المتغيرات حاويات للبيانات، والأنواع هي شكل الحاوية — اختر الحاوية المناسبة، وسيكون كودك آمنًا.

1. ما ستتعلمه


2. قصة مطور حقيقي

(1) نقطة الألم: لماذا تعطل البرنامج بسبب خطأ إملائي في العمر؟

طُلب من سارة (متدربة جديدة) كتابة برنامج آلة حاسبة بسيط. كتبت int age = '25' (سلسلة في متغير عدد صحيح)، فحص مترجم Python: يمر. فحص مترجم JavaScript: يمر. فحص مترجم Dart 3: ❌ "نوع الوسيطة 'String' لا يمكن تعيينها للنوع 'int'". شعرت بالإحباط: "ما الفرق؟" أجابها زميلها: "Dart يحميك من الأخطاء، لكن JavaScript يلقيها عليك في وقت التشغيل."

(2) حل Dart

DART
// كود سيء — Python/JS يمر، Dart 3 يفشل
// int age = '25';  // ❌ error: نوع الوسيطة 'String' لا يمكن تعيينها للنوع 'int'

// ✅ Dart: التعليق الصريح أو استنتاج النوع
int age = 25;               // نوع صريح
var inferredAge = 25;       // استنتاج النوع (يعرف int من 25)
final constantAge = 25;     // ثابت وقت التشغيل
const compileTimeAge = 25;  // ثابت وقت الترجمة

(3) النتيجة: أخطاء وقت التشغيل → منع وقت الترجمة

الخطأ Python/JS Dart 3
String → int يمر، ينفجر في وقت التشغيل ❌ أخطاء وقت الترجمة
Null → int يمر، NullPointerException ❌ Null check error
int + String يمر، concatenation ❌ type mismatch
الوصول خارج الحدود يمر، IndexError ❌ RangeError
TEXT 📖 للعرض فقط
قبل (Python/JS): اختبار كامل 100%، خطأ وقت التشغيل في الإنتاج
بعد (Dart): أخطاء وقت الترجمة 50+، اختبار كامل 100%، إنتاج خالٍ من الأخطاء
ROI: عيوب وقت التشغيل ↓80%

3. خمسة طرق للإعلان عن المتغيرات

(1) var — استنتاج النوع

DART
// نوع المتغير يُستنتج من القيمة
var name = 'Bob';           // String
var age = 25;              // int
var height = 1.75;         // double
var isActive = true;        // bool
var items = [1, 2, 3];     // List<int>
var person = {'name': 'Bob'};  // Map<String, String>

// بعد الإعلان، لا يمكن تغيير النوع
var score = 100;     // int
// score = 'high';  // ❌ error: نوع الوسيطة 'String' لا يمكن تعيينها للنوع 'int'

(2) final — ثابت وقت التشغيل

DART
// يمكن التعيين مرة واحدة فقط
final name = 'Bob';
// name = 'Alice';  // ❌ خطأ في وقت الترجمة: لا يمكن تعيين المتغير النهائي

// لكن يمكن أن يكون الكائن الداخلي قابلاً للتغيير
final list = [1, 2, 3];
list.add(4);  // ✅ مسموح — يعدل القائمة، لا المرجع

final date = DateTime.now();  // ✅ استدعاء دالة في وقت الإعلان
// const date2 = DateTime.now();  // ❌ لا يمكن استخدام استدعاء دالة في const

(3) const — ثابت وقت الترجمة

DART
// يجب أن تكون القيمة ثابتة في وقت الترجمة
const pi = 3.14159;
const name = 'Dart';
const maxSize = 100;

// لا يمكن استخدام استدعاءات دوال أو كائنات قابلة للتغيير
// const date = DateTime.now();  // ❌
// const list = [1, 2, DateTime.now()];  // ❌

// const هو ثابت compile-time، أسرع من final

(4) late — التهيئة الكسولة

DART
// متغير لن يتم تهيئته حتى الاستخدام الأول
late String expensiveData;

// في هذه المرحلة، expensiveData لا يزال غير مهيأ
print('Step 1');

// الآن يتم التهيئة
expensiveData = _loadExpensiveData();
print('Step 2: $expensiveData');

// late final — يمكن التعيين مرة واحدة، بعد التهيئة الأولى
late final String config;
if (useProduction) {
  config = 'prod-config';
} else {
  config = 'dev-config';
}
print(config);

(5) dynamic — تجاوز فحص النوع

DART
// dynamic يتخطى فحص النوع الثابت
dynamic value = 'hello';
value = 42;          // ✅ لا خطأ
value = [1, 2, 3];  // ✅ لا خطأ
value.foo();         // ✅ لا خطأ في وقت الترجمة، ❌ في وقت التشغيل إذا لم يكن موجودًا

// مفيد لـ JSON parsing، dynamic config، إلخ.
Map<String, dynamic> config = {
  'name': 'Bob',
  'age': 25,
  'isAdmin': true,
};

▶ مثال: مقارنة 5 طرق للإعلان

DART
// 1. var — استنتاج النوع، قابل للتعيين
var score = 100;       // int، قابل للتعيين
score = 200;           // ✅
// score = 'high';    // ❌ خطأ في النوع

// 2. final — ثابت وقت التشغيل، قابل للتعيين مرة واحدة
final name = 'Bob';    // String، ثابت
// name = 'Alice';    // ❌ ثابت

// 3. const — ثابت وقت الترجمة، قابل للتعيين مرة واحدة
const pi = 3.14;       // double، ثابت وقت الترجمة
// pi = 3.15;         // ❌ ثابت

// 4. late — تهيئة كسولة
late String data;      // لم يتم التهيئة
data = 'initialized';  // ✅
print(data);            // "initialized"

// 5. dynamic — تجاوز فحص النوع
dynamic anything = 'hello';
anything = 42;          // ✅
print(anything);        // 42

void main() {
  print('score=$score, name=$name, pi=$pi, data=$data, anything=$anything');
}
BASH
$ dart run
score=200, name=Bob, pi=3.14, data=initialized, anything=42
TEXT 📖 للعرض فقط
> **الإخراج:** var و final و const و late و dynamic هي 5 طرق للإعلان. كل طريقة لها سيناريوهات استخدام مختلفة.

(6) جدول المقارنة

الطريقة قابل للتعيين وقت التقييم فحص النوع حالة الاستخدام
var وقت التشغيل ✅ استنتاج المتغيرات المحلية
final وقت التشغيل ✅ استنتاج متغيرات الفئة، المتغيرات المحلية غير القابلة للتغيير
const وقت الترجمة ✅ استنتاج الثوابت، تكوين المشروع
late ✅ (تأخير) وقت التشغيل الأول ✅ استنتاج التهيئة الكسولة، خصائص الفئة التي تتطلب وقتًا
dynamic وقت التشغيل ❌ تجاوز JSON، تكوين ديناميكي، استدعاءات API

4. الأنواع المدمجة

(1) نوع num — int و double

DART
// int — عدد صحيح (64 بت على النظام الأساسي 64 بت)
int age = 25;
int hex = 0xFF;        // 255 بالنظام السداسي العشري
int binary = 0b1010;    // 10 بالنظام الثنائي
int big = 1000000000000; // تريليون (int 64 بت)

// double — رقم بفاصلة عائمة (64 بت)
double price = 19.99;
double scientific = 1.5e10;  // 15 مليار
double negative = -3.14;

// num — النوع الأب لـ int و double
num x = 10;        // int
num y = 3.14;      // double

// التحويل بين int و double
int i = 10;
double d = i.toDouble();  // 10.0
double d2 = 20.5;
int i2 = d2.toInt();       // 20 (يتجاهل الجزء العشري)
int i3 = d2.round();       // 21 (يقرّب)

// التحقق من النوع
print(i.isEven);     // true
print(d.isFinite);   // true
print(d.isNaN);      // false

(2) نوع String

DART
// علامات الاقتباس المفردة أو المزدوجة
String name = 'Bob';
String city = "New York";

// علامات الاقتباس الثلاثية — سلاسل متعددة الأسطر (تحافظ على المسافة البادئة)
String html = '''
<div>
  <h1>Hello</h1>
  <p>World</p>
</div>
''';

// سلسلة خام (r prefix) — تتجاهل أحرف الهروب
String regex = r'\d+\.\d+';  // بدلاً من '\\d+\\.\\d+'
String windowsPath = r'C:\Users\Bob\file.txt';

// الاستيفاء
String name = 'Bob';
String greeting = 'Hello, $name!';                    // Hello, Bob!
String calc = '1 + 2 = ${1 + 2}';                     // 1 + 2 = 3
String nested = '${'Hello, $name!'.toUpperCase()}';   // HELLO, BOB!

// السلاسل متعددة الأسطر
String multi = """
  Line 1
  Line 2
  Line 3
""";

// مقارنة مع r + multi-line
String rawMulti = r'''
  No \n escape
  Multi-line
''';

(3) نوع bool

DART
bool isActive = true;
bool isCompleted = false;

// العوامل المنطقية
bool result1 = true && false;   // false (AND)
bool result2 = true || false;   // true (OR)
bool result3 = !true;           // false (NOT)

// شروط Dart الحقيقية: فقط true هي true (وليس truthy/falsy مثل JS)
bool a = true && true;    // true
bool b = true && 0;       // ❌ error: نوع الوسيطة 'int' لا يمكن تعيينها للنوع 'bool'
bool c = true && null;    // ❌ error: نوع الوسيطة 'Null' لا يمكن تعيينها للنوع 'bool'

// مقارنة آمنة null
bool? maybe = null;
if (maybe ?? false) {  // ?? توفر قيمة افتراضية
  print('is true');
}

(4) نوع List

DART
// نوع صريح
List<int> numbers = [1, 2, 3, 4, 5];
List<String> names = ['Alice', 'Bob'];

// استنتاج النوع
var mixed = [1, 'two', 3];  // List<Object>

// القيم الفارغة
List<int>? maybeEmpty = null;

// خصائص شائعة
numbers.length;          // 5
numbers.isEmpty;         // false
numbers.first;           // 1
numbers.last;            // 5
numbers.contains(3);     // true
numbers.indexOf(3);      // 2
numbers.add(6);          // [1, 2, 3, 4, 5, 6]
numbers.removeAt(0);     // [2, 3, 4, 5, 6]
numbers.reversed;        // (6, 5, 4, 3, 2)

// التحول
List<int> doubled = numbers.map((n) => n * 2).toList();
List<int> evens = numbers.where((n) => n.isEven).toList();

(5) نوع Map

DART
// نوع صريح
Map<String, int> ages = {
  'Alice': 30,
  'Bob': 25,
};

// القيم الفارغة
Map<String, int>? maybeNull = null;

// عمليات شائعة
ages['Alice'];           // 30
ages['Charlie'];         // null
ages['Charlie'] = 35;   // إضافة
ages.length;              // 3
ages.containsKey('Bob');  // true
ages.remove('Bob');       // حذف

// التكرار
ages.forEach((name, age) {
  print('$name is $age');
});

(6) نوع Set

DART
// مجموعة غير مرتبة، لا تحتوي على عناصر مكررة
Set<int> numbers = {1, 2, 3, 4, 5};
Set<String> fruits = {'apple', 'banana', 'apple'};  // {'apple', 'banana'}

numbers.add(6);            // {1, 2, 3, 4, 5, 6}
numbers.add(1);            // {1, 2, 3, 4, 5, 6} (لا إضافة مكررة)
numbers.contains(3);        // true
numbers.difference({3, 4}); // {1, 2, 5, 6}
numbers.union({7, 8});     // {1, 2, 3, 4, 5, 6, 7, 8}
numbers.intersection({3, 4, 5}); // {3, 4, 5}

▶ مثال: نظام شامل من 5 طرق إعلان + 6 أنواع

DART
// متغيرات المشروع
var projectName = 'SalesPredict';           // String (استنتاج)
final version = '1.0.0';                    // String (ثابت)
const apiUrl = 'https://api.example.com';   // String (ثابت وقت الترجمة)
late final databaseConfig;                  // تهيئة كسولة
databaseConfig = 'postgres://localhost';   // مهمة

// التكوين
Map<String, dynamic> config = {
  'name': projectName,
  'version': version,
  'apiUrl': apiUrl,
  'databaseConfig': databaseConfig,
};

// قائمة الميزات
List<String> features = [
  'sales-forecasting',
  'user-segmentation',
  'anomaly-detection',
];

// علامات الميزات
Set<String> enabledFeatures = {'sales-forecasting', 'user-segmentation'};

// عدّاد
int recordCount = 0;

// درجة الحرارة
double temperature = 23.5;

// حالة التفعيل
bool isHealthy = true;

void main() {
  print('المشروع: $projectName v$version');
  print('API: $apiUrl');
  print('الميزات: $features');
  print('الميزات المفعّلة: $enabledFeatures');
  print('العدادات: $recordCount');
  print('درجة الحرارة: $temperature°C');
  print('الحالة الصحية: $isHealthy');
  print('التكوين: $config');
}
BASH
$ dart run
المشروع: SalesPredict v1.0.0
API: https://api.example.com
الميزات: [sales-forecasting, user-segmentation, anomaly-detection]
الميزات المفعّلة: {sales-forecasting, user-segmentation}
العدادات: 0
درجة الحرارة: 23.5°C
الحالة الصحية: true
التكوين: {name: SalesPredict, version: 1.0.0, apiUrl: https://api.example.com, databaseConfig: postgres://localhost}
TEXT 📖 للعرض فقط
> **الإخراج:** 5 طرق إعلان × 6 أنواع = 30 طريقة لاستخدام المتغيرات. اختر الطريقة المناسبة حسب السيناريو.

5. استنتاج النوع مقابل التعليق الصريح

(1) استنتاج النوع — معظم الوقت

DART
// ✅ يفضل استنتاج النوع
var name = 'Bob';
var age = 25;
var isActive = true;
var items = <int>[1, 2, 3];

// لماذا؟
// 1. أقل ضوضاءً بصريًا
// 2. Dart سيختار النوع الأكثر دقة
// 3. تحديث النوع تلقائيًا عند تعديل القيمة

(2) التعليق الصريح — API عام

DART
// ✅ يفضل التعليق الصريح
class User {
  final String name;  // واضح للقراء
  final int age;      // IDE يعرض النوع في تلميحات الأدوات
  final List<String> tags;
}

// 1. واجهة عامة (API) — القراء لا يعرفون نوع الاستنتاج
// 2. خصائص الفئة — IDE يعرضها في الإكمال التلقائي
// 3. معلمات الدالة — لتوضيح القصد
String greet(String name) => 'Hello, $name';

(3) متى تستخدم أيهما — جدول

السيناريو var تعليق صريح
متغير محلي قصير -
خاصية الفئة -
معامل الدالة -
قيمة إرجاع الدالة -
المتغير العام / static -
التكوين -

(4) مثال: نفس الكود بطريقتين

DART
// ✅ الأسلوب 1: استنتاج النوع (مفضل للمتغيرات المحلية)
final name = 'Bob';
final age = 25;
final items = [1, 2, 3];

// ✅ الأسلوب 2: تعليق صريح (مفضل لـ API)
String getName() => 'Bob';
int getAge() => 25;
List<int> getItems() => [1, 2, 3];

// ❌ تعليق زائد عن الحاجة
final String name2 = 'Bob';  // var name2 = 'Bob'; أفضل

(5) حالات خاصة: Collection literals

DART
// عند إنشاء List/Map/Set، يجب التعليق إذا كان النوع غير واضح
var numbers = <int>[1, 2, 3];      // تعليق ضروري — استنتاج <int>[]? لا
var map = <String, int>{'a': 1}; // تعليق ضروري
var set = <int>{1, 2, 3};        // تعليق ضروري

// من النوع الواضح من العناصر
var names = ['Alice', 'Bob'];  // ✅ استنتاج List<String>
var ages = {'Alice': 30};      // ✅ استنتاج Map<String, int>

▶ مثال: مشروع حقيقي — فئة المستخدم

DART
class User {
  // 1. خاصية الفئة — تعليق صريح
  final String name;
  final int age;
  final List<String> hobbies;

  // 2. مُنشئ — نوع المعلمات صريح
  User({
    required this.name,
    required this.age,
    required this.hobbies,
  });

  // 3. نوع الإرجاع — صريح
  String getGreeting() => 'Hello, $name!';

  // 4. متغير محلي — استنتاج
  bool get isAdult {
    final adult = age >= 18;  // استنتاج bool
    return adult;
  }
}
TEXT 📖 للعرض فقط
> **الإخراج:** فئة User بسيطة مع 5 حقول (name/age/hobbies) — تُظهر اصطلاح Dart: التعليق الصريح على واجهة API، واستنتاج النوع للمتغيرات المحلية.

6. استيفاء السلاسل

(1) بناء جملة الاستيفاء

DART
final name = 'Bob';
final age = 25;

// 1. استيفاء متغير بسيط
String s1 = 'Hello, $name!';      // Hello, Bob!

// 2. استيفاء تعبير
String s2 = '1 + 2 = ${1 + 2}';  // 1 + 2 = 3
String s3 = 'age = ${age}';      // age = 25

// 3. الاستيفاء المتداخل
String s4 = '${'Hello, $name!'.toUpperCase()}'; // HELLO, BOB!

// 4. لا متغيرات — $ نفسها
String s5 = 'Price: \$100';  // Price: $100 (مع escape)
String s6 = r'Price: $100';   // Price: $100 (سلسلة خام)

(2) متعدد الأسطر

DART
// علامات الاقتباس الثلاثية — علامات الاقتباس المفردة (تسمح بالاستيفاء)
String name = 'Bob';
String multi = '''
  Hello, $name!
  This is a multi-line string.
  Indentation is preserved.
''';

// علامات الاقتباس الثلاثية — علامات الاقتباس المزدوجة
String multi2 = """
  Hello, $name!
  This is a multi-line string.
""";

// سلسلة خام (r prefix) — تتجاهل أحرف الهروب
String regex = r'\d+\.\d+';  // \d+\.\d+
String windowsPath = r'C:\Users\Bob\file.txt';

// سلسلة خام متعددة الأسطر
String rawMulti = r'''
  No \n escape
  $variable is literal (not interpolated)
''';

▶ مثال: استيفاء السلسلة الواقعية

DART
// فئة سجل بسيطة
class Logger {
  final String module;
  final DateTime time;

  Logger(this.module, [DateTime? time]) : time = time ?? DateTime.now();

  void info(String message) {
    print('[$time] [$module] INFO: $message');
  }

  void error(String message, [Object? error, StackTrace? stack]) {
    print('[$time] [$module] ERROR: $message');
    if (error != null) {
      print('  خطأ: $error');
    }
    if (stack != null) {
      print('  $stack');
    }
  }
}

void main() {
  final logger = Logger('SalesPredict');
  logger.info('بدء معالجة البيانات');
  logger.error('فشل الاتصال بقاعدة البيانات', 'انتهت المهلة');
}
BASH
$ dart run
[2026-07-27 14:30:15.123] [SalesPredict] INFO: بدء معالجة البيانات
[2026-07-27 14:30:16.456] [SalesPredict] ERROR: فشل الاتصال بقاعدة البيانات
  خطأ: انتهت المهلة
TEXT 📖 للعرض فقط
> **الإخراج:** السجل المنسق — استفد من استيفاء السلسلة لتجنب التحويل اليدوي إلى String.

(3) أنماط شائعة لاستيفاء السلسلة

DART
final name = 'Bob';
final age = 25;
final items = ['apple', 'banana'];

// رسائل السجل
print('[$name] العمر: $age');
print('العناصر: $items');
print('عدد العناصر: ${items.length}');

// JSON-like
final json = '{"name": "$name", "age": $age}'; // ❌ يفتقر إلى الاقتباس لنوع String
final correctJson = '{"name": "${name.toJson()}", "age": $age}'; // ✅

// رسائل الخطأ
try {
  // ...
} catch (e) {
  print('فشل في $name: $e');
}

// URL
final url = 'https://api.example.com/users/$id/posts/$postId';

(4) تحويل إلى String

DART
// toString() — معظم الأنواع لها تنفيذ افتراضي
print(1.toString());          // '1'
print(1.5.toString());        // '1.5'
print(true.toString());       // 'true'
print([1, 2, 3].toString()); // '[1, 2, 3]'

// تحويل مخصص — تجاوز toString()
class Point {
  final int x, y;
  Point(this.x, this.y);

  @override
  String toString() => 'Point($x, $y)';
}

print(Point(1, 2));  // 'Point(1, 2)' (تلقائي)

7. عوامل النوع

(1) عوامل التحويل

DART
// as — تحويل قسري (آمن للأنواع المتوافقة)
Object value = 'hello';
String s = value as String;  // 'hello'
// int i = value as int;     // ❌ TypeError في وقت التشغيل

// is — فحص النوع
if (value is String) {
  print('length: ${value.length}');
}

// is! — فحص النوع غير المتطابق (نفي)
if (value is! int) {
  print('ليس int');
}

(2) التحويل الآمن (as?)

DART
Object value = 'hello';

// as? — تحويل آمن، يُرجع null في حالة الفشل
String? s = value as? String;  // 'hello'
int? i = value as? int;        // null (بدلاً من رمي خطأ)

if (s != null) {
  print('String length: ${s.length}');
}

(3) pattern matching (Dart 3)

DART
// switch التعبير مع مطابقة الأنماط
String describe(Object obj) => switch (obj) {
  int n when n > 0 => 'int موجب $n',
  int n => 'int $n',
  String s => 'String "$s"',
  List l when l.isEmpty => 'قائمة فارغة',
  List l => 'قائمة بطول ${l.length}',
  _ => 'unknown',
};

print(describe(42));       // 'int موجب 42'
print(describe('hello')); // 'String "hello"'
print(describe([1, 2]));  // 'قائمة بطول 2'

(4) توافق الأنواع (Variance)

DART
// التباين المشترك — List<Dog> هي List<Animal> (للقراءة فقط)
List<Animal> animals = <Dog>[].cast<Animal>();
animals.add(Cat());  // ❌ خطأ — لا يمكن الكتابة

// التباين العكسي — List<Animal> هي List<Dog> (للقراءة فقط)
List<Dog> dogs = <Animal>[Dog()] as List<Dog>;
dogs.first.bark();  // ✅

// ثابت (Invariant) — النوع نفسه فقط
List<int> ints = [1, 2, 3];
// List<num> nums = ints;  // ❌ خطأ — List<int> ليست List<num>

▶ مثال: فحص النوع + التحويل في مشروع

DART
// معالجة الاستجابة من API
Map<String, dynamic> response = {
  'status': 'success',
  'data': {
    'id': 1,
    'name': 'Bob',
    'age': 25,
    'tags': ['user', 'premium'],
  },
};

// استخراج آمن من النوع
String status = response['status'] as String;

Map<String, dynamic>? data = response['data'] as Map<String, dynamic>?;
if (data != null) {
  int id = data['id'] as int;
  String name = data['name'] as String;
  int? age = data['age'] as int?;  // قد يكون null

  // معالجة القائمة
  List<dynamic>? tags = data['tags'] as List<dynamic>?;
  if (tags != null) {
    for (final tag in tags) {
      print('tag: $tag');
    }
  }
}
TEXT 📖 للعرض فقط
> **الإخراج:** عند معالجة JSON، استخدم `as?` للسلامة، أو استخرج النوع بأمان. لا تستخدم `as!` أبدًا (ستفشل في وقت التشغيل إذا كان النوع غير صحيح).

(5) final vs const vs var — متى تستخدم أيهما

DART
// ✅ الإعداد الموصى به
class Config {
  // final — خاصية غير قابلة للتغيير، تم التعيين في المُنشئ
  final String name;
  final int maxRetries;

  // const — ثابت وقت الترجمة
  static const defaultName = 'default';
  static const defaultMaxRetries = 3;

  // late — تهيئة كسولة
  late final String computedConfig = _computeConfig();

  Config({this.name = defaultName, this.maxRetries = defaultMaxRetries});

  String _computeConfig() => '$name-$maxRetries';
}

void main() {
  print(Config.defaultName);    // 'default'
  final config = Config();
  print(config.computedConfig); // 'default-3'
}

8. ملخص نقاط المعرفة الأساسية لهذا الدرس

# المفهوم المستوى الأهمية
1 var / final / const / late / dynamic = 5 طرق إعلان مبتدئ ⭐⭐⭐⭐⭐
2 int / double / num / String / bool / List / Map / Set / null مبتدئ ⭐⭐⭐⭐⭐
3 استنتاج النوع vs التعليق الصريح مبتدئ ⭐⭐⭐⭐⭐
4 String interpolation + multi-line + r prefix مبتدئ ⭐⭐⭐⭐
5 فحص النوع is + التحويل الآمن as? متوسط ⭐⭐⭐⭐
6 Late initialization متوسط ⭐⭐⭐

❓ أسئلة شائعة

س هل يجب استخدام var أو التعليق الصريح في كل مكان؟
ج القاعدة: var للمتغيرات المحلية، التعليق الصريح لواجهات API (الفئات، الدوال، المعلمات). يفضل كود Dart الجيد 80% استنتاج و20% تعليق. الإفراط في التعليق يضيف ضوضاء، والإفراط في استنتاج يضر بقراءة API العام.
س ما الفرق بين final و const؟
ج const ثابت في وقت الترجمة (compile-time) — يجب أن تكون القيمة معروفة في وقت الترجمة. final ثابت في وقت التشغيل (runtime) — يمكن التعيين بأي قيمة في وقت التشغيل، بما في ذلك استدعاءات الدوال. عمليًا: const للتكوين والثوابت، final للمتغيرات التي لا تتغير بعد التعيين. const ضمني ضمن final، لكنهما ليسا متطابقين.
س متى تستخدم late؟
ج ثلاثة سيناريوهات: ① تهيئة متأخرة لخصائص الفئة التي تتطلب وقتًا أو موارد ② قيم تعتمد على شروط وقت التشغيل ③ حل التبعيات الدائرية. ولكن تجنب الإفراط في الاستخدام — late يخفي التهيئة، وقد يتسبب في أخطاء وقت التشغيل إذا لم تتم تهيئته قبل الوصول.
س لماذا لا يوجد int? افتراضي في Dart 3؟
ج هذا هو أمان القيم الفارغة (Null Safety) من Dart 3. لا شيء يمكن أن يكون null افتراضيًا. إذا كنت بحاجة إلى متغير يمكن أن يكون null، يجب التصريح صراحةً بـ ? (مثل int?). هذا يمنع NullPointerException الكلاسيكي. لاحظ أن Dart 2.12+ بدأ تقديم هذا، Dart 3 يكمل التحول.
س ما الفرق بين List و const [] في Dart 3؟
ج List هو نوع، const [] هو ثابت. const [] ينشئ قائمة ثابتة غير قابلة للتغيير. final list = [] ينشئ متغيرًا غير قابل للتعيين، لكن القائمة نفسها قابلة للتغيير (list.add(4) مسموح). const list = [1, 2, 3] ينشئ قائمة ثابتة وغير قابلة للتغيير. للقراءة فقط، يفضل const لتحسين الأداء.
س كيف أحول String إلى int بأمان؟
ج استخدم int.tryParse:
DART
final str = '42';
final i = int.tryParse(str);  // 42
final bad = int.tryParse('hello');  // null (بدلاً من خطأ)

📖 ملخص


📝 تمارين

  1. أساسي (الصعوبة ⭐): أنشئ مشروع type_explorer، واكتب 5 متغيرات بأنواع مختلفة (String / int / double / bool / List<String>)، واختبر استخدام كل من var و final و const و late و dynamic (5 طرق إعلان)، واستخدم dart analyze للتحقق من أن جميع الإعلانات صحيحة. أنشئ bin/test.dart مع main() يطبع جميع المتغيرات. تلميح: استخدم dynamic بحذر — طباعة dynamic.runtimeType يساعد في رؤية النوع الفعلي.
  2. متوسط (الصعوبة ⭐⭐): أنشئ مشروع null_safety_practice، واكتب دالة parseUser التي تأخذ Map<String, dynamic> (مثل استجابة API) وتستخرج 5 حقول (id, name, age, email, tags)، باستخدام as? (التحويل الآمن) و ?? (المشغل الافتراضي) للتعامل مع الحقول المفقودة أو الخاطئة. اكتب 3 حالات اختبار: ① خريطة كاملة ② خريطة بأجزاء مفقودة ③ خريطة بأنواع خاطئة. استخدم dart test لاختبار الحالات. تلميح: استخدم Map<String, dynamic>.from() في الاختبارات لإنشاء خرائط اختبار.
  3. تحدٍّ (الصعوبة ⭐⭐⭐): أنشئ مشروع string_interpolation_master يستكشف 5 أنماط لاستيفاء السلسلة: ① استيفاء متغير بسيط ($name) ② استيفاء تعبير (${1+2}) ③ استيفاء متداخل (داخل تعبير) ④ علامات اقتباس ثلاثية مع استيفاء ⑤ سلسلة خام (r prefix) مع أحرف الهروب والاستيفاء. أنشئ bin/showcase.dart مع 5 دالات، كل منها يستكشف نمطًا، ويعرض الفرق بين الاستيفاء العادي والخام في نفس الوقت. على سبيل المثال: print(r'$var لا يستيفع، ولكن ${'$var'} يستيفع'). تلميح: سلسلة خام تتجاهل أحرف الهروب \n و \t، ولكنها لا تمنع $ من العمل (لا يزال استيفاء).

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

Web-Tutorial.com

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

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

100%