زبان Dart
Dart زبان رسمی Flutter است. یک زبان مدرن، شیءگرا، با type-safety قوی و null safety که توسط Google توسعه داده شده. در این فصل تمام مفاهیم Dart را که برای Flutter لازم دارید یاد میگیریم.
۲.۱ مقدمه
Dart زبان رسمی Flutter است. یک زبان مدرن، شیءگرا، با type-safety قوی و null safety که توسط Google توسعه داده شده. در این فصل تمام مفاهیم Dart را که برای Flutter لازم دارید یاد میگیریم.
هدف این فصل: تسلط بر Dart 3.x — متغیرها، توابع، کلاسها، Generics، Async/Await، Null Safety، Records، Pattern Matching و Sealed Classes.
۲.۲ چرا Dart؟
وقتی Flutter ساخته میشد، Google میتوانست JavaScript یا Kotlin انتخاب کند. اما Dart را برگزید، چون:
- JIT + AOT compilation: JIT برای Hot Reload در dev، AOT برای performance بالا در production
- Null Safety: compiler از null pointer exception جلوگیری میکند
- Type Safety + Inference: سختگیر اما باهوش
- Garbage Collection: بدون نیاز به مدیریت دستی حافظه
- Async/Await native: برای UI واکنشگرا
- Single-threaded با Isolates: بدون مشکلات thread، اما با parallelism
- Familiar syntax: اگر Java، JavaScript، C# میدانید، Dart آشناست
۲.۳ متغیرها
void main() {
// 1. var - type inference
var name = "محمدعلی";
var age = 35;
// name = 123; // ❌ ERROR: type already inferred to String
// 2. صریح تایپ
String city = "کاشان";
int year = 1404;
double height = 1.75;
bool isActive = true;
// 3. final - یکبار مقدار میگیرد (runtime)
final birthYear = DateTime.now().year - age;
// birthYear = 1990; // ❌ ERROR
// 4. const - compile-time constant
const pi = 3.14159;
const greeting = "سلام دنیا"; // باید در زمان compile مشخص باشد
// 5. dynamic - بدون type checking (مثل any در TypeScript)
dynamic anything = 42;
anything = "string now"; // ✅ OK اما خطرناک
// 6. Object - parent همه type ها (اما type-safe)
Object obj = "hello";
// obj.length; // ❌ ERROR - باید cast کنید
// 7. Late - مقداردهی دیرهنگام (اما non-null)
late String description;
description = "later";
// print
print("نام: $name، شهر: $city، سن: $age");
// String interpolation با $ یا ${expression}
print("سال تولد: ${DateTime.now().year - age}");
}
تفاوت final و const
| ویژگی | final | const |
|---|---|---|
| زمان مقداردهی | Runtime | Compile-time |
| مقدار از | هر چیزی (function، DateTime.now()) | فقط constants |
| کاربرد در class | فیلدهای instance | فقط static |
| memory | هر بار جدید | یک نسخه (canonical) |
۲.۴ Null Safety
یکی از مهمترین ویژگیهای Dart 3 — جلوگیری از null pointer exception در compile time.
void main() {
// پیشفرض: متغیر non-nullable
String name = "علی";
// name = null; // ❌ ERROR: نمیتوانید null بدهید
// با ? متغیر nullable میشود
String? nullableName;
print(nullableName); // null - OK
// اگر بخواهید method صدا بزنید
// print(nullableName.length); // ❌ ERROR
// 1. Null-aware: ?
print(nullableName?.length); // null - OK اگر null باشد
// 2. Default value: ??
String displayName = nullableName ?? "مهمان";
print(displayName); // مهمان
// 3. ??= برای assign اگر null
String? title;
title ??= "بدون عنوان";
print(title); // بدون عنوان
// 4. ! - bang operator (اطمینان دارم null نیست)
String? maybeName = "علی";
String definitelyName = maybeName!; // اگر null باشد crash میکند
// 5. late - بعداً مقدار میدهم
late String email;
email = "test@example.com";
print(email); // OK
// 6. Null check در شرایط
String? input = getInput();
if (input != null) {
// اینجا Dart میداند input non-null است
print(input.length); // ✅ OK
}
}
String? getInput() => null;
توصیه: از
! (bang) فقط زمانی استفاده کنید که ۱۰۰٪ مطمئن هستید null نیست. در غیر این صورت از ?? یا if check استفاده کنید.
۲.۵ توابع (Functions)
// 1. تابع ساده
int add(int a, int b) {
return a + b;
}
// 2. Arrow function (برای یک خط)
int multiply(int a, int b) => a * b;
// 3. Optional positional parameters
String greet(String name, [String? title]) {
return title != null ? "$title $name" : name;
}
greet("علی"); // "علی"
greet("علی", "آقای"); // "آقای علی"
// 4. Named parameters (پرکاربرد در Flutter!)
String introduce({required String name, int age = 0, String? city}) {
return "$name، $age ساله${city != null ? "، از $city" : ""}";
}
introduce(name: "علی");
introduce(name: "علی", age: 35, city: "کاشان");
// 5. تابع به عنوان first-class citizen
void main() {
// Function در متغیر
Function adder = (int a, int b) => a + b;
print(adder(2, 3)); // 5
// Function به عنوان parameter
int compute(int x, int Function(int) fn) {
return fn(x);
}
print(compute(5, (n) => n * 2)); // 10
// Anonymous function
var doubled = [1, 2, 3].map((n) => n * 2).toList();
print(doubled); // [2, 4, 6]
// Closures
Function makeCounter() {
int count = 0;
return () {
count++;
return count;
};
}
var counter = makeCounter();
print(counter()); // 1
print(counter()); // 2
print(counter()); // 3
}
// 6. Generic functions
T firstOrDefault(List items, T defaultValue) {
return items.isEmpty ? defaultValue : items.first;
}
// 7. Higher-order functions
List mapList(List items, R Function(T) mapper) {
return items.map(mapper).toList();
}
۲.۶ Collections
void main() {
// === LIST ===
List numbers = [1, 2, 3, 4, 5];
var fruits = ["سیب", "موز", "پرتقال"];
// متدهای پرکاربرد
fruits.add("انگور"); // اضافه
fruits.remove("موز"); // حذف
fruits.contains("سیب"); // true
fruits.length; // 3
fruits[0]; // "سیب"
fruits.first; // "سیب"
fruits.last; // "انگور"
fruits.isEmpty; // false
// Map، filter، reduce
var doubled = numbers.map((n) => n * 2).toList(); // [2,4,6,8,10]
var even = numbers.where((n) => n.isEven).toList(); // [2,4]
var sum = numbers.reduce((a, b) => a + b); // 15
var total = numbers.fold(100, (acc, n) => acc + n); // 115
// sort
numbers.sort(); // [1,2,3,4,5]
numbers.sort((a, b) => b.compareTo(a)); // [5,4,3,2,1]
// === SET (مجموعه - بدون تکرار) ===
Set colors = {"red", "green", "blue"};
colors.add("red"); // duplicate ignored
print(colors.length); // 3
// === MAP (key-value) ===
Map ages = {
"علی": 35,
"محمد": 28,
"زهرا": 30,
};
ages["علی"]; // 35
ages["نامشخص"]; // null
ages.containsKey("علی"); // true
ages["جدید"] = 25; // اضافه
ages.remove("محمد"); // حذف
// iteration
ages.forEach((name, age) {
print("$name: $age");
});
for (var entry in ages.entries) {
print("${entry.key} = ${entry.value}");
}
// === Spread Operator ===
var list1 = [1, 2, 3];
var list2 = [0, ...list1, 4, 5]; // [0,1,2,3,4,5]
var maybeNull = null;
var list3 = [1, ...?maybeNull, 4]; // [1,4]
// === Collection if/for ===
bool isLogged = true;
var nav = [
"Home",
"Products",
if (isLogged) "Profile",
if (isLogged) "Logout" else "Login",
];
var items = [1, 2, 3];
var doubled2 = [for (var i in items) i * 2]; // [2,4,6]
}
۲.۷ Classها
// 1. کلاس ساده
class Person {
// فیلدها
String name;
int age;
// Constructor
Person(this.name, this.age);
// Method
String introduce() => "نام: $name، سن: $age";
}
void main() {
var p = Person("علی", 35);
print(p.introduce());
}
// 2. Named Constructor
class Point {
double x;
double y;
Point(this.x, this.y);
// Named constructor
Point.origin() : x = 0, y = 0;
// با initializer list
Point.fromList(List coords)
: x = coords[0],
y = coords[1];
}
var origin = Point.origin();
var p2 = Point.fromList([3.0, 4.0]);
// 3. Const Constructor (immutable objects)
class Color {
final int r, g, b;
// فیلدها باید final باشند
const Color(this.r, this.g, this.b);
static const red = Color(255, 0, 0);
static const green = Color(0, 255, 0);
}
const c = Color(100, 100, 100); // میتواند const باشد
// 4. Getters و Setters
class Rectangle {
double width;
double height;
Rectangle(this.width, this.height);
// Getter
double get area => width * height;
double get perimeter => 2 * (width + height);
// Setter
set diagonal(double d) {
final ratio = width / height;
height = d / sqrt(1 + ratio * ratio);
width = height * ratio;
}
}
// 5. Static members
class MathUtils {
static const double pi = 3.14159;
static double square(double x) => x * x;
}
print(MathUtils.pi);
print(MathUtils.square(5)); // 25.0
// 6. Private members (با _)
class BankAccount {
String _accountNumber; // private (فقط در همین library)
double _balance;
BankAccount(this._accountNumber, this._balance);
double get balance => _balance;
void deposit(double amount) {
if (amount > 0) _balance += amount;
}
}
۲.۸ Inheritance، Mixins، Interfaces
// 1. Inheritance با extends
class Animal {
String name;
Animal(this.name);
void eat() => print("$name در حال خوردن است");
void sleep() => print("$name در حال خوابیدن است");
}
class Dog extends Animal {
Dog(super.name); // Dart 3: super parameter
void bark() => print("$name: واق واق!");
// Override
@override
void eat() {
super.eat(); // فراخوانی parent
print("استخوان دوست دارم");
}
}
var d = Dog("رکس");
d.eat(); // رکس در حال خوردن است / استخوان دوست دارم
d.bark(); // رکس: واق واق!
d.sleep(); // ارثبری شده
// 2. Abstract class (نمیتوان instance ساخت)
abstract class Shape {
String name;
Shape(this.name);
// abstract method (بدون بدنه)
double area();
void describe() {
print("$name با مساحت ${area()}");
}
}
class Circle extends Shape {
double radius;
Circle(this.radius) : super("دایره");
@override
double area() => 3.14159 * radius * radius;
}
// 3. Interface (هر class میتواند interface باشد)
class Printable {
void print() {} // implementation default
}
class Document implements Printable {
@override
void print() {
// باید همه methods پیاده شوند
println("چاپ document");
}
}
// 4. Mixins (composition - بهترین قسمت Dart!)
mixin Walking {
void walk() => print("راه میرود");
}
mixin Swimming {
void swim() => print("شنا میکند");
}
mixin Flying {
void fly() => print("پرواز میکند");
}
class Duck extends Animal with Walking, Swimming, Flying {
Duck(super.name);
}
var duck = Duck("دونالد");
duck.walk(); // ✅
duck.swim(); // ✅
duck.fly(); // ✅
// 5. Sealed class (Dart 3 - برای exhaustive pattern matching)
sealed class Result {
const Result();
}
class Success extends Result {
final T data;
const Success(this.data);
}
class Failure extends Result {
final String error;
const Failure(this.error);
}
// با switch میتوانید مطمئن شوید همه حالات handled شدند
String describe(Result result) {
return switch (result) {
Success(data: final d) => "موفق: $d",
Failure(error: final e) => "خطا: $e",
};
}
۲.۹ Async / Await
Dart برای کارهای ناهمزمان (network، file I/O) از Future و Stream استفاده میکند.
// === Future ===
Future fetchUser() async {
// شبیهسازی API call
await Future.delayed(Duration(seconds: 2));
return "اطلاعات کاربر";
}
// با await
void main() async {
print("شروع");
String user = await fetchUser();
print(user);
print("پایان");
}
// با then
fetchUser()
.then((user) => print(user))
.catchError((e) => print("خطا: $e"))
.whenComplete(() => print("تمام"));
// === مدیریت خطا ===
Future divide(int a, int b) async {
if (b == 0) throw Exception("تقسیم بر صفر");
return a ~/ b;
}
Future example() async {
try {
var result = await divide(10, 0);
print(result);
} on Exception catch (e) {
print("خطای انتظار: $e");
} catch (e) {
print("خطای ناشناخته: $e");
} finally {
print("همیشه اجرا میشود");
}
}
// === چند Future موازی ===
Future parallelExample() async {
// اجرای موازی
final results = await Future.wait([
fetchUser(),
fetchProducts(),
fetchOrders(),
]);
print(results); // [user, products, orders]
// یا با timeout
try {
final user = await fetchUser().timeout(
Duration(seconds: 5),
onTimeout: () => "default",
);
} on TimeoutException catch (_) {
print("timeout");
}
}
// === Stream ===
// Stream = Future با چندین مقدار در طول زمان
Stream countdown(int from) async* {
for (int i = from; i >= 0; i--) {
await Future.delayed(Duration(seconds: 1));
yield i; // emit مقدار
}
}
void streamExample() async {
// listen
await for (final n in countdown(5)) {
print(n); // 5, 4, 3, 2, 1, 0
}
// یا با subscription
final subscription = countdown(3).listen(
(n) => print("got $n"),
onError: (e) => print("error $e"),
onDone: () => print("done"),
);
// cancel در صورت نیاز
// subscription.cancel();
}
// === استفاده در Flutter ===
// FutureBuilder برای Future
FutureBuilder(
future: fetchUser(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text("خطا: ${snapshot.error}");
}
return Text(snapshot.data ?? "");
},
)
// StreamBuilder برای Stream
StreamBuilder(
stream: countdown(10),
builder: (context, snapshot) {
return Text("${snapshot.data ?? 0}");
},
)
۲.۱۰ Records و Pattern Matching (Dart 3)
// === Records (مثل tuple در Python) ===
void recordsExample() {
// Positional record
(int, String) tuple = (42, "hello");
print(tuple.$1); // 42
print(tuple.$2); // hello
// Named record
({String name, int age}) person = (name: "علی", age: 35);
print(person.name);
print(person.age);
// Mixed
(int, String, {bool isActive}) item = (1, "محصول", isActive: true);
// Return چند مقدار از function!
(int, int) divmod(int a, int b) {
return (a ~/ b, a % b);
}
var (quotient, remainder) = divmod(17, 5);
print("$quotient remainder $remainder"); // 3 remainder 2
}
// === Pattern Matching ===
void patternsExample() {
var json = {"name": "علی", "age": 35};
// Destructuring map
if (json case {"name": String name, "age": int age}) {
print("$name، $age ساله");
}
// Destructuring list
var list = [1, 2, 3];
if (list case [int a, int b, int c]) {
print("$a + $b + $c = ${a + b + c}");
}
// ...rest pattern
if (list case [int first, ...var rest]) {
print("اول: $first، بقیه: $rest");
}
// Switch expression (Dart 3)
int classify(num value) => switch (value) {
< 0 => -1,
0 => 0,
> 0 => 1,
_ => 0,
};
// Switch با pattern
String describe(Object obj) => switch (obj) {
int i when i > 100 => "عدد بزرگ: $i",
int i => "عدد: $i",
String s when s.isEmpty => "رشته خالی",
String s => "رشته: $s",
List l => "لیست ${l.length} تایی",
{"name": String n} => "object با name: $n",
_ => "ناشناخته",
};
print(describe(42)); // عدد: 42
print(describe(150)); // عدد بزرگ: 150
print(describe("hello")); // رشته: hello
print(describe([1, 2, 3])); // لیست 3 تایی
print(describe({"name": "علی"})); // object با name: علی
}
۲.۱۱ Extension Methods
اضافه کردن method به class های موجود — بدون inheritance.
// extension روی String
extension StringExtensions on String {
bool get isPersian {
final persianRegex = RegExp(r"[u0600-u06FF]");
return persianRegex.hasMatch(this);
}
String capitalize() {
if (isEmpty) return this;
return this[0].toUpperCase() + substring(1);
}
String truncate(int length) {
if (this.length <= length) return this;
return "${substring(0, length)}...";
}
}
void main() {
print("سلام".isPersian); // true
print("hello".capitalize()); // Hello
print("This is a long text".truncate(10)); // This is a ...
}
// extension روی int
extension IntExtensions on int {
bool get isEven2 => this % 2 == 0;
Duration get seconds => Duration(seconds: this);
Duration get minutes => Duration(minutes: this);
Duration get hours => Duration(hours: this);
Duration get days => Duration(days: this);
}
// استفاده شیک:
await Future.delayed(2.seconds);
final week = 7.days;
// extension generic
extension ListExtensions on List {
T? get firstOrNull => isEmpty ? null : first;
T? get lastOrNull => isEmpty ? null : last;
List takeRandom(int count) {
final shuffled = [...this]..shuffle();
return shuffled.take(count).toList();
}
}
[1, 2, 3, 4, 5].takeRandom(2); // مثلاً [3, 1]
[].firstOrNull; // null
۲.۱۲ بهترین تجربیات Dart
- از
finalبهصورت پیشفرض استفاده کنید. فقط وقتی نیاز به تغییر داریدvar. - Null safety را جدی بگیرید. از
!فقط با اطمینان کامل. - Named parameters در Constructor. برای readability.
- Const constructors هر کجا ممکن. performance بهتر.
- Mixins برای code reuse. به جای multiple inheritance.
- Sealed classes برای exhaustive matching. در Dart 3.
- Records به جای Map ساده. type-safe و سریعتر.
- Extension methods. برای DSL های زیبا.
- Effective Dart را بخوانید.
dart.dev/effective-dart - Linter را فعال کنید.
flutter_lintsدر pubspec.
۲.۱۳ خلاصه فصل
آنچه آموختیم:
- متغیرها: var، final، const، late، dynamic
- Null Safety با ?، ??، ??=، !
- توابع: positional، optional، named، arrow، closures
- Collections: List، Set، Map، spread، collection if/for
- Class: constructors، getters، setters، static
- OOP: inheritance، abstract، interface، mixin
- Sealed class برای exhaustive matching
- Async/Await، Future، Stream
- Records و Pattern Matching در Dart 3
- Extension methods برای code زیبا
در فصل بعد: Widgetها — قلب Flutter. StatelessWidget، StatefulWidget، Widget tree، lifecycle و BuildContext.